From e6c8cb9ce69178f61e3218f16c32e4bdc48d3d90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B5=85=E5=BF=B5?= Date: Thu, 22 Oct 2026 16:16:03 +0800 Subject: [PATCH] =?UTF-8?q?chore(theme):=20astro=20=E9=94=81=E5=AE=9A=20~5?= =?UTF-8?q?.18.0=EF=BC=88=E7=BB=88=E5=AE=A1=20Minor=EF=BC=89+=20docs:=20Pl?= =?UTF-8?q?an=204=20=E6=94=B6=E5=AE=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Code --- .../__pycache__/py.cpython-312.pyc | Bin 0 -> 492 bytes .../__pycache__/six.cpython-312.pyc | Bin 0 -> 41340 bytes .../typing_extensions.cpython-312.pyc | Bin 0 -> 166538 bytes .deps_siteforge_v812/_pytest/__init__.py | 13 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 480 bytes .../__pycache__/_argcomplete.cpython-312.pyc | Bin 0 -> 4825 bytes .../__pycache__/_version.cpython-312.pyc | Bin 0 -> 789 bytes .../__pycache__/cacheprovider.cpython-312.pyc | Bin 0 -> 31619 bytes .../__pycache__/capture.cpython-312.pyc | Bin 0 -> 55694 bytes .../__pycache__/compat.cpython-312.pyc | Bin 0 -> 13021 bytes .../__pycache__/debugging.cpython-312.pyc | Bin 0 -> 18278 bytes .../__pycache__/deprecated.cpython-312.pyc | Bin 0 -> 3029 bytes .../__pycache__/doctest.cpython-312.pyc | Bin 0 -> 32625 bytes .../__pycache__/faulthandler.cpython-312.pyc | Bin 0 -> 5079 bytes .../__pycache__/fixtures.cpython-312.pyc | Bin 0 -> 82725 bytes .../freeze_support.cpython-312.pyc | Bin 0 -> 1824 bytes .../__pycache__/helpconfig.cpython-312.pyc | Bin 0 -> 13111 bytes .../__pycache__/hookspec.cpython-312.pyc | Bin 0 -> 45766 bytes .../__pycache__/junitxml.cpython-312.pyc | Bin 0 -> 33293 bytes .../__pycache__/legacypath.cpython-312.pyc | Bin 0 -> 24169 bytes .../__pycache__/logging.cpython-312.pyc | Bin 0 -> 45826 bytes .../_pytest/__pycache__/main.cpython-312.pyc | Bin 0 -> 47282 bytes .../__pycache__/monkeypatch.cpython-312.pyc | Bin 0 -> 17195 bytes .../_pytest/__pycache__/nodes.cpython-312.pyc | Bin 0 -> 30827 bytes .../__pycache__/outcomes.cpython-312.pyc | Bin 0 -> 11710 bytes .../__pycache__/pastebin.cpython-312.pyc | Bin 0 -> 5867 bytes .../__pycache__/pathlib.cpython-312.pyc | Bin 0 -> 41701 bytes .../__pycache__/pytester.cpython-312.pyc | Bin 0 -> 84918 bytes .../pytester_assertions.cpython-312.pyc | Bin 0 -> 2489 bytes .../__pycache__/python.cpython-312.pyc | Bin 0 -> 75140 bytes .../__pycache__/python_api.cpython-312.pyc | Bin 0 -> 35349 bytes .../__pycache__/raises.cpython-312.pyc | Bin 0 -> 59994 bytes .../__pycache__/recwarn.cpython-312.pyc | Bin 0 -> 16057 bytes .../__pycache__/reports.cpython-312.pyc | Bin 0 -> 26979 bytes .../__pycache__/runner.cpython-312.pyc | Bin 0 -> 24179 bytes .../_pytest/__pycache__/scope.cpython-312.pyc | Bin 0 -> 3581 bytes .../__pycache__/setuponly.cpython-312.pyc | Bin 0 -> 5363 bytes .../__pycache__/setupplan.cpython-312.pyc | Bin 0 -> 1905 bytes .../__pycache__/skipping.cpython-312.pyc | Bin 0 -> 14037 bytes .../_pytest/__pycache__/stash.cpython-312.pyc | Bin 0 -> 4368 bytes .../__pycache__/stepwise.cpython-312.pyc | Bin 0 -> 9617 bytes .../__pycache__/subtests.cpython-312.pyc | Bin 0 -> 17713 bytes .../__pycache__/terminal.cpython-312.pyc | Bin 0 -> 84179 bytes .../terminalprogress.cpython-312.pyc | Bin 0 -> 1141 bytes .../threadexception.cpython-312.pyc | Bin 0 -> 5753 bytes .../__pycache__/timing.cpython-312.pyc | Bin 0 -> 4783 bytes .../__pycache__/tmpdir.cpython-312.pyc | Bin 0 -> 12756 bytes .../__pycache__/tracemalloc.cpython-312.pyc | Bin 0 -> 986 bytes .../__pycache__/unittest.cpython-312.pyc | Bin 0 -> 26465 bytes .../unraisableexception.cpython-312.pyc | Bin 0 -> 6249 bytes .../__pycache__/warning_types.cpython-312.pyc | Bin 0 -> 6941 bytes .../__pycache__/warnings.cpython-312.pyc | Bin 0 -> 6783 bytes .deps_siteforge_v812/_pytest/_argcomplete.py | 117 + .../_pytest/_code/__init__.py | 26 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 686 bytes .../_code/__pycache__/code.cpython-312.pyc | Bin 0 -> 67886 bytes .../_code/__pycache__/source.cpython-312.pyc | Bin 0 -> 11930 bytes .deps_siteforge_v812/_pytest/_code/code.py | 1565 ++ .deps_siteforge_v812/_pytest/_code/source.py | 225 + .deps_siteforge_v812/_pytest/_io/__init__.py | 10 + .../_io/__pycache__/__init__.cpython-312.pyc | Bin 0 -> 377 bytes .../_io/__pycache__/pprint.cpython-312.pyc | Bin 0 -> 24387 bytes .../_io/__pycache__/saferepr.cpython-312.pyc | Bin 0 -> 5823 bytes .../terminalwriter.cpython-312.pyc | Bin 0 -> 11239 bytes .../_io/__pycache__/wcwidth.cpython-312.pyc | Bin 0 -> 1683 bytes .deps_siteforge_v812/_pytest/_io/pprint.py | 673 + .deps_siteforge_v812/_pytest/_io/saferepr.py | 130 + .../_pytest/_io/terminalwriter.py | 258 + .deps_siteforge_v812/_pytest/_io/wcwidth.py | 57 + .deps_siteforge_v812/_pytest/_py/__init__.py | 0 .../_py/__pycache__/__init__.cpython-312.pyc | Bin 0 -> 185 bytes .../_py/__pycache__/error.cpython-312.pyc | Bin 0 -> 4953 bytes .../_py/__pycache__/path.cpython-312.pyc | Bin 0 -> 68712 bytes .deps_siteforge_v812/_pytest/_py/error.py | 119 + .deps_siteforge_v812/_pytest/_py/path.py | 1475 ++ .deps_siteforge_v812/_pytest/_version.py | 34 + .../_pytest/assertion/__init__.py | 208 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 10106 bytes .../__pycache__/rewrite.cpython-312.pyc | Bin 0 -> 61169 bytes .../__pycache__/truncate.cpython-312.pyc | Bin 0 -> 4189 bytes .../__pycache__/util.cpython-312.pyc | Bin 0 -> 23819 bytes .../_pytest/assertion/rewrite.py | 1202 ++ .../_pytest/assertion/truncate.py | 137 + .../_pytest/assertion/util.py | 615 + .deps_siteforge_v812/_pytest/cacheprovider.py | 646 + .deps_siteforge_v812/_pytest/capture.py | 1144 ++ .deps_siteforge_v812/_pytest/compat.py | 314 + .../_pytest/config/__init__.py | 2197 ++ .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 91737 bytes .../__pycache__/argparsing.cpython-312.pyc | Bin 0 -> 26348 bytes .../config/__pycache__/compat.cpython-312.pyc | Bin 0 -> 3583 bytes .../__pycache__/exceptions.cpython-312.pyc | Bin 0 -> 813 bytes .../__pycache__/findpaths.cpython-312.pyc | Bin 0 -> 13254 bytes .../_pytest/config/argparsing.py | 578 + .deps_siteforge_v812/_pytest/config/compat.py | 85 + .../_pytest/config/exceptions.py | 15 + .../_pytest/config/findpaths.py | 350 + .deps_siteforge_v812/_pytest/debugging.py | 407 + .deps_siteforge_v812/_pytest/deprecated.py | 99 + .deps_siteforge_v812/_pytest/doctest.py | 736 + .deps_siteforge_v812/_pytest/faulthandler.py | 119 + .deps_siteforge_v812/_pytest/fixtures.py | 2047 ++ .../_pytest/freeze_support.py | 45 + .deps_siteforge_v812/_pytest/helpconfig.py | 293 + .deps_siteforge_v812/_pytest/hookspec.py | 1342 ++ .deps_siteforge_v812/_pytest/junitxml.py | 695 + .deps_siteforge_v812/_pytest/legacypath.py | 468 + .deps_siteforge_v812/_pytest/logging.py | 960 + .deps_siteforge_v812/_pytest/main.py | 1203 ++ .deps_siteforge_v812/_pytest/mark/__init__.py | 301 + .../mark/__pycache__/__init__.cpython-312.pyc | Bin 0 -> 13049 bytes .../__pycache__/expression.cpython-312.pyc | Bin 0 -> 17362 bytes .../__pycache__/structures.cpython-312.pyc | Bin 0 -> 27483 bytes .../_pytest/mark/expression.py | 353 + .../_pytest/mark/structures.py | 664 + .deps_siteforge_v812/_pytest/monkeypatch.py | 435 + .deps_siteforge_v812/_pytest/nodes.py | 772 + .deps_siteforge_v812/_pytest/outcomes.py | 308 + .deps_siteforge_v812/_pytest/pastebin.py | 117 + .deps_siteforge_v812/_pytest/pathlib.py | 1063 + .deps_siteforge_v812/_pytest/py.typed | 0 .deps_siteforge_v812/_pytest/pytester.py | 1791 ++ .../_pytest/pytester_assertions.py | 74 + .deps_siteforge_v812/_pytest/python.py | 1772 ++ .deps_siteforge_v812/_pytest/python_api.py | 820 + .deps_siteforge_v812/_pytest/raises.py | 1517 ++ .deps_siteforge_v812/_pytest/recwarn.py | 367 + .deps_siteforge_v812/_pytest/reports.py | 694 + .deps_siteforge_v812/_pytest/runner.py | 580 + .deps_siteforge_v812/_pytest/scope.py | 91 + .deps_siteforge_v812/_pytest/setuponly.py | 98 + .deps_siteforge_v812/_pytest/setupplan.py | 39 + .deps_siteforge_v812/_pytest/skipping.py | 321 + .deps_siteforge_v812/_pytest/stash.py | 116 + .deps_siteforge_v812/_pytest/stepwise.py | 209 + .deps_siteforge_v812/_pytest/subtests.py | 411 + .deps_siteforge_v812/_pytest/terminal.py | 1763 ++ .../_pytest/terminalprogress.py | 30 + .../_pytest/threadexception.py | 152 + .deps_siteforge_v812/_pytest/timing.py | 95 + .deps_siteforge_v812/_pytest/tmpdir.py | 315 + .deps_siteforge_v812/_pytest/tracemalloc.py | 24 + .deps_siteforge_v812/_pytest/unittest.py | 628 + .../_pytest/unraisableexception.py | 163 + .deps_siteforge_v812/_pytest/warning_types.py | 172 + .deps_siteforge_v812/_pytest/warnings.py | 151 + .deps_siteforge_v812/_yaml/__init__.py | 33 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 861 bytes .../annotated_doc-0.0.4.dist-info/INSTALLER | 1 + .../annotated_doc-0.0.4.dist-info/METADATA | 145 + .../annotated_doc-0.0.4.dist-info/RECORD | 12 + .../annotated_doc-0.0.4.dist-info/REQUESTED | 0 .../annotated_doc-0.0.4.dist-info/WHEEL | 4 + .../entry_points.txt | 4 + .../licenses/LICENSE | 21 + .../annotated_doc/__init__.py | 3 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 251 bytes .../__pycache__/main.cpython-312.pyc | Bin 0 -> 1899 bytes .deps_siteforge_v812/annotated_doc/main.py | 36 + .deps_siteforge_v812/annotated_doc/py.typed | 0 .../annotated_types-0.8.0.dist-info/INSTALLER | 1 + .../annotated_types-0.8.0.dist-info/METADATA | 295 + .../annotated_types-0.8.0.dist-info/RECORD | 11 + .../annotated_types-0.8.0.dist-info/REQUESTED | 0 .../annotated_types-0.8.0.dist-info/WHEEL | 4 + .../licenses/LICENSE | 21 + .../annotated_types/__init__.py | 416 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 17790 bytes .../__pycache__/test_cases.cpython-312.pyc | Bin 0 -> 13064 bytes .deps_siteforge_v812/annotated_types/py.typed | 0 .../annotated_types/test_cases.py | 146 + .../anyio-4.13.0.dist-info/INSTALLER | 1 + .../anyio-4.13.0.dist-info/METADATA | 105 + .../anyio-4.13.0.dist-info/RECORD | 93 + .../anyio-4.13.0.dist-info/REQUESTED | 0 .../anyio-4.13.0.dist-info/WHEEL | 5 + .../anyio-4.13.0.dist-info/entry_points.txt | 2 + .../anyio-4.13.0.dist-info/licenses/LICENSE | 20 + .../anyio-4.13.0.dist-info/top_level.txt | 1 + .deps_siteforge_v812/anyio/__init__.py | 111 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 4623 bytes .../__pycache__/from_thread.cpython-312.pyc | Bin 0 -> 25824 bytes .../__pycache__/functools.cpython-312.pyc | Bin 0 -> 16566 bytes .../__pycache__/lowlevel.cpython-312.pyc | Bin 0 -> 7994 bytes .../__pycache__/pytest_plugin.cpython-312.pyc | Bin 0 -> 16774 bytes .../to_interpreter.cpython-312.pyc | Bin 0 -> 10326 bytes .../__pycache__/to_process.cpython-312.pyc | Bin 0 -> 11940 bytes .../__pycache__/to_thread.cpython-312.pyc | Bin 0 -> 3203 bytes .../anyio/_backends/__init__.py | 0 .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 189 bytes .../__pycache__/_asyncio.cpython-312.pyc | Bin 0 -> 138620 bytes .../__pycache__/_trio.cpython-312.pyc | Bin 0 -> 71477 bytes .../anyio/_backends/_asyncio.py | 2996 +++ .deps_siteforge_v812/anyio/_backends/_trio.py | 1343 ++ .deps_siteforge_v812/anyio/_core/__init__.py | 0 .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 185 bytes .../_asyncio_selector_thread.cpython-312.pyc | Bin 0 -> 8400 bytes .../_contextmanagers.cpython-312.pyc | Bin 0 -> 8995 bytes .../__pycache__/_eventloop.cpython-312.pyc | Bin 0 -> 8179 bytes .../__pycache__/_exceptions.cpython-312.pyc | Bin 0 -> 7422 bytes .../_core/__pycache__/_fileio.cpython-312.pyc | Bin 0 -> 43300 bytes .../__pycache__/_resources.cpython-312.pyc | Bin 0 -> 927 bytes .../__pycache__/_signals.cpython-312.pyc | Bin 0 -> 1381 bytes .../__pycache__/_sockets.cpython-312.pyc | Bin 0 -> 40592 bytes .../__pycache__/_streams.cpython-312.pyc | Bin 0 -> 2334 bytes .../__pycache__/_subprocesses.cpython-312.pyc | Bin 0 -> 9465 bytes .../_synchronization.cpython-312.pyc | Bin 0 -> 33093 bytes .../_core/__pycache__/_tasks.cpython-312.pyc | Bin 0 -> 7684 bytes .../__pycache__/_tempfile.cpython-312.pyc | Bin 0 -> 28037 bytes .../__pycache__/_testing.cpython-312.pyc | Bin 0 -> 3787 bytes .../__pycache__/_typedattr.cpython-312.pyc | Bin 0 -> 3830 bytes .../anyio/_core/_asyncio_selector_thread.py | 167 + .../anyio/_core/_contextmanagers.py | 200 + .../anyio/_core/_eventloop.py | 234 + .../anyio/_core/_exceptions.py | 156 + .deps_siteforge_v812/anyio/_core/_fileio.py | 799 + .../anyio/_core/_resources.py | 18 + .deps_siteforge_v812/anyio/_core/_signals.py | 29 + .deps_siteforge_v812/anyio/_core/_sockets.py | 1003 + .deps_siteforge_v812/anyio/_core/_streams.py | 52 + .../anyio/_core/_subprocesses.py | 196 + .../anyio/_core/_synchronization.py | 757 + .deps_siteforge_v812/anyio/_core/_tasks.py | 173 + .deps_siteforge_v812/anyio/_core/_tempfile.py | 613 + .deps_siteforge_v812/anyio/_core/_testing.py | 82 + .../anyio/_core/_typedattr.py | 81 + .deps_siteforge_v812/anyio/abc/__init__.py | 58 + .../abc/__pycache__/__init__.cpython-312.pyc | Bin 0 -> 2360 bytes .../__pycache__/_eventloop.cpython-312.pyc | Bin 0 -> 16521 bytes .../__pycache__/_resources.cpython-312.pyc | Bin 0 -> 1632 bytes .../abc/__pycache__/_sockets.cpython-312.pyc | Bin 0 -> 18263 bytes .../abc/__pycache__/_streams.cpython-312.pyc | Bin 0 -> 9666 bytes .../__pycache__/_subprocesses.cpython-312.pyc | Bin 0 -> 3240 bytes .../abc/__pycache__/_tasks.cpython-312.pyc | Bin 0 -> 5139 bytes .../abc/__pycache__/_testing.cpython-312.pyc | Bin 0 -> 2835 bytes .deps_siteforge_v812/anyio/abc/_eventloop.py | 409 + .deps_siteforge_v812/anyio/abc/_resources.py | 33 + .deps_siteforge_v812/anyio/abc/_sockets.py | 399 + .deps_siteforge_v812/anyio/abc/_streams.py | 233 + .../anyio/abc/_subprocesses.py | 79 + .deps_siteforge_v812/anyio/abc/_tasks.py | 117 + .deps_siteforge_v812/anyio/abc/_testing.py | 65 + .deps_siteforge_v812/anyio/from_thread.py | 578 + .deps_siteforge_v812/anyio/functools.py | 409 + .deps_siteforge_v812/anyio/lowlevel.py | 196 + .deps_siteforge_v812/anyio/py.typed | 0 .deps_siteforge_v812/anyio/pytest_plugin.py | 363 + .../anyio/streams/__init__.py | 0 .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 187 bytes .../__pycache__/buffered.cpython-312.pyc | Bin 0 -> 9066 bytes .../streams/__pycache__/file.cpython-312.pyc | Bin 0 -> 7458 bytes .../__pycache__/memory.cpython-312.pyc | Bin 0 -> 15008 bytes .../__pycache__/stapled.cpython-312.pyc | Bin 0 -> 7537 bytes .../streams/__pycache__/text.cpython-312.pyc | Bin 0 -> 9333 bytes .../streams/__pycache__/tls.cpython-312.pyc | Bin 0 -> 20135 bytes .../anyio/streams/buffered.py | 188 + .deps_siteforge_v812/anyio/streams/file.py | 154 + .deps_siteforge_v812/anyio/streams/memory.py | 325 + .deps_siteforge_v812/anyio/streams/stapled.py | 147 + .deps_siteforge_v812/anyio/streams/text.py | 176 + .deps_siteforge_v812/anyio/streams/tls.py | 421 + .deps_siteforge_v812/anyio/to_interpreter.py | 246 + .deps_siteforge_v812/anyio/to_process.py | 266 + .deps_siteforge_v812/anyio/to_thread.py | 78 + .../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 + .deps_siteforge_v812/bin/fastapi.exe | Bin 0 -> 108411 bytes .deps_siteforge_v812/bin/httpx.exe | Bin 0 -> 108405 bytes .deps_siteforge_v812/bin/idna.exe | Bin 0 -> 108408 bytes .deps_siteforge_v812/bin/py.test.exe | Bin 0 -> 108422 bytes .deps_siteforge_v812/bin/pygmentize.exe | Bin 0 -> 108416 bytes .deps_siteforge_v812/bin/pytest.exe | Bin 0 -> 108422 bytes .deps_siteforge_v812/bin/uvicorn.exe | Bin 0 -> 108412 bytes .../bleach-4.1.0.dist-info/INSTALLER | 1 + .../bleach-4.1.0.dist-info/LICENSE | 13 + .../bleach-4.1.0.dist-info/METADATA | 1017 + .../bleach-4.1.0.dist-info/RECORD | 98 + .../bleach-4.1.0.dist-info/REQUESTED | 0 .../bleach-4.1.0.dist-info/WHEEL | 6 + .../bleach-4.1.0.dist-info/top_level.txt | 1 + .deps_siteforge_v812/bleach/__init__.py | 131 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 3980 bytes .../__pycache__/callbacks.cpython-312.pyc | Bin 0 -> 1272 bytes .../__pycache__/html5lib_shim.cpython-312.pyc | Bin 0 -> 17403 bytes .../__pycache__/linkifier.cpython-312.pyc | Bin 0 -> 16516 bytes .../__pycache__/sanitizer.cpython-312.pyc | Bin 0 -> 18440 bytes .../bleach/__pycache__/utils.cpython-312.pyc | Bin 0 -> 1111 bytes .../bleach/_vendor/README.rst | 61 + .../bleach/_vendor/__init__.py | 0 .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 188 bytes .../_vendor/__pycache__/parse.cpython-312.pyc | Bin 0 -> 43360 bytes .../html5lib-1.1.dist-info/AUTHORS.rst | 66 + .../_vendor/html5lib-1.1.dist-info/INSTALLER | 1 + .../_vendor/html5lib-1.1.dist-info/METADATA | 552 + .../_vendor/html5lib-1.1.dist-info/RECORD | 41 + .../_vendor/html5lib-1.1.dist-info/WHEEL | 6 + .../html5lib-1.1.dist-info/top_level.txt | 1 + .../bleach/_vendor/html5lib/__init__.py | 35 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 1319 bytes .../__pycache__/_ihatexml.cpython-312.pyc | Bin 0 -> 17742 bytes .../__pycache__/_inputstream.cpython-312.pyc | Bin 0 -> 35302 bytes .../__pycache__/_tokenizer.cpython-312.pyc | Bin 0 -> 87160 bytes .../__pycache__/_utils.cpython-312.pyc | Bin 0 -> 6676 bytes .../__pycache__/constants.cpython-312.pyc | Bin 0 -> 117450 bytes .../__pycache__/html5parser.cpython-312.pyc | Bin 0 -> 155867 bytes .../__pycache__/serializer.cpython-312.pyc | Bin 0 -> 15804 bytes .../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 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 369 bytes .../_trie/__pycache__/_base.cpython-312.pyc | Bin 0 -> 1954 bytes .../_trie/__pycache__/py.cpython-312.pyc | Bin 0 -> 3652 bytes .../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 .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 205 bytes .../alphabeticalattributes.cpython-312.pyc | Bin 0 -> 1664 bytes .../filters/__pycache__/base.cpython-312.pyc | Bin 0 -> 1011 bytes .../inject_meta_charset.cpython-312.pyc | Bin 0 -> 2931 bytes .../filters/__pycache__/lint.cpython-312.pyc | Bin 0 -> 4038 bytes .../__pycache__/optionaltags.cpython-312.pyc | Bin 0 -> 3895 bytes .../__pycache__/sanitizer.cpython-312.pyc | Bin 0 -> 28478 bytes .../__pycache__/whitespace.cpython-312.pyc | Bin 0 -> 1855 bytes .../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 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 999 bytes .../__pycache__/genshi.cpython-312.pyc | Bin 0 -> 2131 bytes .../__pycache__/sax.cpython-312.pyc | Bin 0 -> 2169 bytes .../_vendor/html5lib/treeadapters/genshi.py | 54 + .../_vendor/html5lib/treeadapters/sax.py | 50 + .../_vendor/html5lib/treebuilders/__init__.py | 88 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 3707 bytes .../__pycache__/base.cpython-312.pyc | Bin 0 -> 16049 bytes .../__pycache__/dom.cpython-312.pyc | Bin 0 -> 15585 bytes .../__pycache__/etree.cpython-312.pyc | Bin 0 -> 19787 bytes .../__pycache__/etree_lxml.cpython-312.pyc | Bin 0 -> 22386 bytes .../_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 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 5623 bytes .../__pycache__/base.cpython-312.pyc | Bin 0 -> 8698 bytes .../__pycache__/dom.cpython-312.pyc | Bin 0 -> 2998 bytes .../__pycache__/etree.cpython-312.pyc | Bin 0 -> 5502 bytes .../__pycache__/etree_lxml.cpython-312.pyc | Bin 0 -> 10591 bytes .../__pycache__/genshi.cpython-312.pyc | Bin 0 -> 3003 bytes .../_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 + .deps_siteforge_v812/bleach/_vendor/parse.py | 1078 + .../bleach/_vendor/vendor.txt | 3 + .../bleach/_vendor/vendor_install.sh | 14 + .deps_siteforge_v812/bleach/callbacks.py | 32 + .deps_siteforge_v812/bleach/html5lib_shim.py | 665 + .deps_siteforge_v812/bleach/linkifier.py | 574 + .deps_siteforge_v812/bleach/sanitizer.py | 645 + .deps_siteforge_v812/bleach/utils.py | 21 + .deps_siteforge_v812/bs4/__init__.py | 1174 ++ .../bs4/__pycache__/__init__.cpython-312.pyc | Bin 0 -> 39810 bytes .../__pycache__/_deprecation.cpython-312.pyc | Bin 0 -> 3745 bytes .../bs4/__pycache__/_typing.cpython-312.pyc | Bin 0 -> 3908 bytes .../bs4/__pycache__/_warnings.cpython-312.pyc | Bin 0 -> 5598 bytes .../bs4/__pycache__/css.cpython-312.pyc | Bin 0 -> 14462 bytes .../bs4/__pycache__/dammit.cpython-312.pyc | Bin 0 -> 48302 bytes .../bs4/__pycache__/diagnose.cpython-312.pyc | Bin 0 -> 12617 bytes .../bs4/__pycache__/element.cpython-312.pyc | Bin 0 -> 115854 bytes .../__pycache__/exceptions.cpython-312.pyc | Bin 0 -> 1761 bytes .../bs4/__pycache__/filter.cpython-312.pyc | Bin 0 -> 28701 bytes .../bs4/__pycache__/formatter.cpython-312.pyc | Bin 0 -> 10529 bytes .deps_siteforge_v812/bs4/_deprecation.py | 80 + .deps_siteforge_v812/bs4/_typing.py | 205 + .deps_siteforge_v812/bs4/_warnings.py | 98 + .deps_siteforge_v812/bs4/builder/__init__.py | 848 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 27457 bytes .../__pycache__/_html5lib.cpython-312.pyc | Bin 0 -> 23784 bytes .../__pycache__/_htmlparser.cpython-312.pyc | Bin 0 -> 14529 bytes .../builder/__pycache__/_lxml.cpython-312.pyc | Bin 0 -> 18624 bytes .deps_siteforge_v812/bs4/builder/_html5lib.py | 611 + .../bs4/builder/_htmlparser.py | 462 + .deps_siteforge_v812/bs4/builder/_lxml.py | 501 + .deps_siteforge_v812/bs4/css.py | 339 + .deps_siteforge_v812/bs4/dammit.py | 1516 ++ .deps_siteforge_v812/bs4/diagnose.py | 268 + .deps_siteforge_v812/bs4/element.py | 3211 +++ .deps_siteforge_v812/bs4/exceptions.py | 28 + .deps_siteforge_v812/bs4/filter.py | 764 + .deps_siteforge_v812/bs4/formatter.py | 276 + .deps_siteforge_v812/bs4/py.typed | 0 .../certifi-2026.5.20.dist-info/INSTALLER | 1 + .../certifi-2026.5.20.dist-info/METADATA | 78 + .../certifi-2026.5.20.dist-info/RECORD | 15 + .../certifi-2026.5.20.dist-info/REQUESTED | 0 .../certifi-2026.5.20.dist-info/WHEEL | 5 + .../licenses/LICENSE | 20 + .../certifi-2026.5.20.dist-info/top_level.txt | 1 + .deps_siteforge_v812/certifi/__init__.py | 4 + .deps_siteforge_v812/certifi/__main__.py | 12 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 308 bytes .../__pycache__/__main__.cpython-312.pyc | Bin 0 -> 623 bytes .../certifi/__pycache__/core.cpython-312.pyc | Bin 0 -> 2055 bytes .deps_siteforge_v812/certifi/cacert.pem | 3892 ++++ .deps_siteforge_v812/certifi/core.py | 83 + .deps_siteforge_v812/certifi/py.typed | 0 .../click-8.1.8.dist-info/INSTALLER | 1 + .../click-8.1.8.dist-info/LICENSE.txt | 28 + .../click-8.1.8.dist-info/METADATA | 74 + .../click-8.1.8.dist-info/RECORD | 39 + .../click-8.1.8.dist-info/REQUESTED | 0 .../click-8.1.8.dist-info/WHEEL | 4 + .deps_siteforge_v812/click/__init__.py | 75 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 2732 bytes .../click/__pycache__/_compat.cpython-312.pyc | Bin 0 -> 27443 bytes .../__pycache__/_termui_impl.cpython-312.pyc | Bin 0 -> 30578 bytes .../__pycache__/_textwrap.cpython-312.pyc | Bin 0 -> 2439 bytes .../__pycache__/_winconsole.cpython-312.pyc | Bin 0 -> 11978 bytes .../click/__pycache__/core.cpython-312.pyc | Bin 0 -> 135693 bytes .../__pycache__/decorators.cpython-312.pyc | Bin 0 -> 24729 bytes .../__pycache__/exceptions.cpython-312.pyc | Bin 0 -> 14879 bytes .../__pycache__/formatting.cpython-312.pyc | Bin 0 -> 14053 bytes .../click/__pycache__/globals.cpython-312.pyc | Bin 0 -> 3111 bytes .../click/__pycache__/parser.cpython-312.pyc | Bin 0 -> 21483 bytes .../shell_completion.cpython-312.pyc | Bin 0 -> 22889 bytes .../click/__pycache__/termui.cpython-312.pyc | Bin 0 -> 32788 bytes .../click/__pycache__/testing.cpython-312.pyc | Bin 0 -> 24755 bytes .../click/__pycache__/types.cpython-312.pyc | Bin 0 -> 49484 bytes .../click/__pycache__/utils.cpython-312.pyc | Bin 0 -> 26359 bytes .deps_siteforge_v812/click/_compat.py | 623 + .deps_siteforge_v812/click/_termui_impl.py | 788 + .deps_siteforge_v812/click/_textwrap.py | 49 + .deps_siteforge_v812/click/_winconsole.py | 279 + .deps_siteforge_v812/click/core.py | 3047 +++ .deps_siteforge_v812/click/decorators.py | 562 + .deps_siteforge_v812/click/exceptions.py | 296 + .deps_siteforge_v812/click/formatting.py | 301 + .deps_siteforge_v812/click/globals.py | 67 + .deps_siteforge_v812/click/parser.py | 531 + .deps_siteforge_v812/click/py.typed | 0 .../click/shell_completion.py | 603 + .deps_siteforge_v812/click/termui.py | 784 + .deps_siteforge_v812/click/testing.py | 483 + .deps_siteforge_v812/click/types.py | 1093 + .deps_siteforge_v812/click/utils.py | 624 + .../colorama-0.4.6.dist-info/INSTALLER | 1 + .../colorama-0.4.6.dist-info/METADATA | 441 + .../colorama-0.4.6.dist-info/RECORD | 31 + .../colorama-0.4.6.dist-info/WHEEL | 5 + .../licenses/LICENSE.txt | 27 + .deps_siteforge_v812/colorama/__init__.py | 7 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 482 bytes .../colorama/__pycache__/ansi.cpython-312.pyc | Bin 0 -> 3935 bytes .../__pycache__/ansitowin32.cpython-312.pyc | Bin 0 -> 16402 bytes .../__pycache__/initialise.cpython-312.pyc | Bin 0 -> 3540 bytes .../__pycache__/win32.cpython-312.pyc | Bin 0 -> 8125 bytes .../__pycache__/winterm.cpython-312.pyc | Bin 0 -> 9078 bytes .deps_siteforge_v812/colorama/ansi.py | 102 + .deps_siteforge_v812/colorama/ansitowin32.py | 277 + .deps_siteforge_v812/colorama/initialise.py | 121 + .../colorama/tests/__init__.py | 1 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 188 bytes .../__pycache__/ansi_test.cpython-312.pyc | Bin 0 -> 5457 bytes .../ansitowin32_test.cpython-312.pyc | Bin 0 -> 17996 bytes .../initialise_test.cpython-312.pyc | Bin 0 -> 11644 bytes .../__pycache__/isatty_test.cpython-312.pyc | Bin 0 -> 4771 bytes .../tests/__pycache__/utils.cpython-312.pyc | Bin 0 -> 2460 bytes .../__pycache__/winterm_test.cpython-312.pyc | Bin 0 -> 6602 bytes .../colorama/tests/ansi_test.py | 76 + .../colorama/tests/ansitowin32_test.py | 294 + .../colorama/tests/initialise_test.py | 189 + .../colorama/tests/isatty_test.py | 57 + .deps_siteforge_v812/colorama/tests/utils.py | 49 + .../colorama/tests/winterm_test.py | 131 + .deps_siteforge_v812/colorama/win32.py | 180 + .deps_siteforge_v812/colorama/winterm.py | 195 + .../defusedxml-0.7.1.dist-info/INSTALLER | 1 + .../defusedxml-0.7.1.dist-info/LICENSE | 49 + .../defusedxml-0.7.1.dist-info/METADATA | 978 + .../defusedxml-0.7.1.dist-info/RECORD | 29 + .../defusedxml-0.7.1.dist-info/REQUESTED | 0 .../defusedxml-0.7.1.dist-info/WHEEL | 6 + .../defusedxml-0.7.1.dist-info/top_level.txt | 1 + .../defusedxml/ElementTree.py | 154 + .deps_siteforge_v812/defusedxml/__init__.py | 67 + .../__pycache__/ElementTree.cpython-312.pyc | Bin 0 -> 5189 bytes .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 1586 bytes .../__pycache__/cElementTree.cpython-312.pyc | Bin 0 -> 1219 bytes .../__pycache__/common.cpython-312.pyc | Bin 0 -> 5917 bytes .../__pycache__/expatbuilder.cpython-312.pyc | Bin 0 -> 4891 bytes .../__pycache__/expatreader.cpython-312.pyc | Bin 0 -> 3053 bytes .../__pycache__/lxml.cpython-312.pyc | Bin 0 -> 7091 bytes .../__pycache__/minidom.cpython-312.pyc | Bin 0 -> 1446 bytes .../__pycache__/pulldom.cpython-312.pyc | Bin 0 -> 1167 bytes .../__pycache__/sax.cpython-312.pyc | Bin 0 -> 1846 bytes .../__pycache__/xmlrpc.cpython-312.pyc | Bin 0 -> 6829 bytes .../defusedxml/cElementTree.py | 62 + .deps_siteforge_v812/defusedxml/common.py | 129 + .../defusedxml/expatbuilder.py | 107 + .../defusedxml/expatreader.py | 61 + .deps_siteforge_v812/defusedxml/lxml.py | 153 + .deps_siteforge_v812/defusedxml/minidom.py | 63 + .deps_siteforge_v812/defusedxml/pulldom.py | 41 + .deps_siteforge_v812/defusedxml/sax.py | 60 + .deps_siteforge_v812/defusedxml/xmlrpc.py | 153 + .../et_xmlfile-2.0.0.dist-info/AUTHORS.txt | 5 + .../et_xmlfile-2.0.0.dist-info/INSTALLER | 1 + .../et_xmlfile-2.0.0.dist-info/LICENCE.python | 298 + .../et_xmlfile-2.0.0.dist-info/LICENCE.rst | 23 + .../et_xmlfile-2.0.0.dist-info/METADATA | 51 + .../et_xmlfile-2.0.0.dist-info/RECORD | 15 + .../et_xmlfile-2.0.0.dist-info/REQUESTED | 0 .../et_xmlfile-2.0.0.dist-info/WHEEL | 5 + .../et_xmlfile-2.0.0.dist-info/top_level.txt | 1 + .deps_siteforge_v812/et_xmlfile/__init__.py | 8 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 441 bytes .../incremental_tree.cpython-312.pyc | Bin 0 -> 24455 bytes .../__pycache__/xmlfile.cpython-312.pyc | Bin 0 -> 5478 bytes .../et_xmlfile/incremental_tree.py | 917 + .deps_siteforge_v812/et_xmlfile/xmlfile.py | 158 + .../fastapi-0.128.2.dist-info/INSTALLER | 1 + .../fastapi-0.128.2.dist-info/METADATA | 640 + .../fastapi-0.128.2.dist-info/RECORD | 103 + .../fastapi-0.128.2.dist-info/REQUESTED | 0 .../fastapi-0.128.2.dist-info/WHEEL | 4 + .../entry_points.txt | 5 + .../licenses/LICENSE | 21 + .deps_siteforge_v812/fastapi/__init__.py | 25 + .deps_siteforge_v812/fastapi/__main__.py | 3 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 1096 bytes .../__pycache__/__main__.cpython-312.pyc | Bin 0 -> 243 bytes .../__pycache__/applications.cpython-312.pyc | Bin 0 -> 86868 bytes .../__pycache__/background.cpython-312.pyc | Bin 0 -> 2397 bytes .../fastapi/__pycache__/cli.cpython-312.pyc | Bin 0 -> 657 bytes .../__pycache__/concurrency.cpython-312.pyc | Bin 0 -> 1718 bytes .../datastructures.cpython-312.pyc | Bin 0 -> 7208 bytes .../__pycache__/encoders.cpython-312.pyc | Bin 0 -> 11056 bytes .../exception_handlers.cpython-312.pyc | Bin 0 -> 2023 bytes .../__pycache__/exceptions.cpython-312.pyc | Bin 0 -> 10482 bytes .../__pycache__/logger.cpython-312.pyc | Bin 0 -> 283 bytes .../param_functions.cpython-312.pyc | Bin 0 -> 41549 bytes .../__pycache__/params.cpython-312.pyc | Bin 0 -> 24336 bytes .../__pycache__/requests.cpython-312.pyc | Bin 0 -> 272 bytes .../__pycache__/responses.cpython-312.pyc | Bin 0 -> 2400 bytes .../__pycache__/routing.cpython-312.pyc | Bin 0 -> 86416 bytes .../__pycache__/staticfiles.cpython-312.pyc | Bin 0 -> 244 bytes .../__pycache__/templating.cpython-312.pyc | Bin 0 -> 246 bytes .../__pycache__/testclient.cpython-312.pyc | Bin 0 -> 241 bytes .../fastapi/__pycache__/types.cpython-312.pyc | Bin 0 -> 784 bytes .../fastapi/__pycache__/utils.cpython-312.pyc | Bin 0 -> 6426 bytes .../__pycache__/websockets.cpython-312.pyc | Bin 0 -> 321 bytes .../fastapi/_compat/__init__.py | 41 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 1761 bytes .../__pycache__/shared.cpython-312.pyc | Bin 0 -> 9388 bytes .../_compat/__pycache__/v2.cpython-312.pyc | Bin 0 -> 23006 bytes .../fastapi/_compat/shared.py | 206 + .deps_siteforge_v812/fastapi/_compat/v2.py | 568 + .deps_siteforge_v812/fastapi/applications.py | 4672 +++++ .deps_siteforge_v812/fastapi/background.py | 60 + .deps_siteforge_v812/fastapi/cli.py | 13 + .deps_siteforge_v812/fastapi/concurrency.py | 41 + .../fastapi/datastructures.py | 183 + .../fastapi/dependencies/__init__.py | 0 .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 194 bytes .../__pycache__/models.cpython-312.pyc | Bin 0 -> 9211 bytes .../__pycache__/utils.cpython-312.pyc | Bin 0 -> 40862 bytes .../fastapi/dependencies/models.py | 193 + .../fastapi/dependencies/utils.py | 1039 + .deps_siteforge_v812/fastapi/encoders.py | 346 + .../fastapi/exception_handlers.py | 34 + .deps_siteforge_v812/fastapi/exceptions.py | 256 + .deps_siteforge_v812/fastapi/logger.py | 3 + .../fastapi/middleware/__init__.py | 1 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 250 bytes .../asyncexitstack.cpython-312.pyc | Bin 0 -> 1485 bytes .../__pycache__/cors.cpython-312.pyc | Bin 0 -> 255 bytes .../__pycache__/gzip.cpython-312.pyc | Bin 0 -> 255 bytes .../__pycache__/httpsredirect.cpython-312.pyc | Bin 0 -> 284 bytes .../__pycache__/trustedhost.cpython-312.pyc | Bin 0 -> 278 bytes .../__pycache__/wsgi.cpython-312.pyc | Bin 0 -> 257 bytes .../fastapi/middleware/asyncexitstack.py | 18 + .../fastapi/middleware/cors.py | 1 + .../fastapi/middleware/gzip.py | 1 + .../fastapi/middleware/httpsredirect.py | 3 + .../fastapi/middleware/trustedhost.py | 3 + .../fastapi/middleware/wsgi.py | 3 + .../fastapi/openapi/__init__.py | 0 .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 189 bytes .../__pycache__/constants.cpython-312.pyc | Bin 0 -> 359 bytes .../openapi/__pycache__/docs.cpython-312.pyc | Bin 0 -> 12412 bytes .../__pycache__/models.cpython-312.pyc | Bin 0 -> 22796 bytes .../openapi/__pycache__/utils.cpython-312.pyc | Bin 0 -> 21274 bytes .../fastapi/openapi/constants.py | 3 + .deps_siteforge_v812/fastapi/openapi/docs.py | 375 + .../fastapi/openapi/models.py | 438 + .deps_siteforge_v812/fastapi/openapi/utils.py | 570 + .../fastapi/param_functions.py | 2461 +++ .deps_siteforge_v812/fastapi/params.py | 755 + .deps_siteforge_v812/fastapi/py.typed | 0 .deps_siteforge_v812/fastapi/requests.py | 2 + .deps_siteforge_v812/fastapi/responses.py | 48 + .deps_siteforge_v812/fastapi/routing.py | 4508 +++++ .../fastapi/security/__init__.py | 15 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 846 bytes .../__pycache__/api_key.cpython-312.pyc | Bin 0 -> 10483 bytes .../security/__pycache__/base.cpython-312.pyc | Bin 0 -> 508 bytes .../security/__pycache__/http.cpython-312.pyc | Bin 0 -> 14536 bytes .../__pycache__/oauth2.cpython-312.pyc | Bin 0 -> 20668 bytes .../open_id_connect_url.cpython-312.pyc | Bin 0 -> 3762 bytes .../__pycache__/utils.cpython-312.pyc | Bin 0 -> 634 bytes .../fastapi/security/api_key.py | 318 + .deps_siteforge_v812/fastapi/security/base.py | 6 + .deps_siteforge_v812/fastapi/security/http.py | 423 + .../fastapi/security/oauth2.py | 693 + .../fastapi/security/open_id_connect_url.py | 94 + .../fastapi/security/utils.py | 10 + .deps_siteforge_v812/fastapi/staticfiles.py | 1 + .deps_siteforge_v812/fastapi/templating.py | 1 + .deps_siteforge_v812/fastapi/testclient.py | 1 + .deps_siteforge_v812/fastapi/types.py | 11 + .deps_siteforge_v812/fastapi/utils.py | 164 + .deps_siteforge_v812/fastapi/websockets.py | 3 + .../h11-0.16.0.dist-info/INSTALLER | 1 + .../h11-0.16.0.dist-info/METADATA | 202 + .../h11-0.16.0.dist-info/RECORD | 30 + .../h11-0.16.0.dist-info/REQUESTED | 0 .../h11-0.16.0.dist-info/WHEEL | 5 + .../h11-0.16.0.dist-info/licenses/LICENSE.txt | 22 + .../h11-0.16.0.dist-info/top_level.txt | 1 + .deps_siteforge_v812/h11/__init__.py | 62 + .../h11/__pycache__/__init__.cpython-312.pyc | Bin 0 -> 1068 bytes .../h11/__pycache__/_abnf.cpython-312.pyc | Bin 0 -> 1774 bytes .../__pycache__/_connection.cpython-312.pyc | Bin 0 -> 23124 bytes .../h11/__pycache__/_events.cpython-312.pyc | Bin 0 -> 13214 bytes .../h11/__pycache__/_headers.cpython-312.pyc | Bin 0 -> 7976 bytes .../h11/__pycache__/_readers.cpython-312.pyc | Bin 0 -> 9641 bytes .../_receivebuffer.cpython-312.pyc | Bin 0 -> 4692 bytes .../h11/__pycache__/_state.cpython-312.pyc | Bin 0 -> 8456 bytes .../h11/__pycache__/_util.cpython-312.pyc | Bin 0 -> 4712 bytes .../h11/__pycache__/_version.cpython-312.pyc | Bin 0 -> 206 bytes .../h11/__pycache__/_writers.cpython-312.pyc | Bin 0 -> 6278 bytes .deps_siteforge_v812/h11/_abnf.py | 132 + .deps_siteforge_v812/h11/_connection.py | 659 + .deps_siteforge_v812/h11/_events.py | 369 + .deps_siteforge_v812/h11/_headers.py | 282 + .deps_siteforge_v812/h11/_readers.py | 250 + .deps_siteforge_v812/h11/_receivebuffer.py | 153 + .deps_siteforge_v812/h11/_state.py | 365 + .deps_siteforge_v812/h11/_util.py | 135 + .deps_siteforge_v812/h11/_version.py | 16 + .deps_siteforge_v812/h11/_writers.py | 145 + .deps_siteforge_v812/h11/py.typed | 1 + .../httpcore-1.0.9.dist-info/INSTALLER | 1 + .../httpcore-1.0.9.dist-info/METADATA | 625 + .../httpcore-1.0.9.dist-info/RECORD | 69 + .../httpcore-1.0.9.dist-info/REQUESTED | 0 .../httpcore-1.0.9.dist-info/WHEEL | 4 + .../licenses/LICENSE.md | 27 + .deps_siteforge_v812/httpcore/__init__.py | 141 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 3159 bytes .../httpcore/__pycache__/_api.cpython-312.pyc | Bin 0 -> 3745 bytes .../__pycache__/_exceptions.cpython-312.pyc | Bin 0 -> 3178 bytes .../__pycache__/_models.cpython-312.pyc | Bin 0 -> 23125 bytes .../httpcore/__pycache__/_ssl.cpython-312.pyc | Bin 0 -> 603 bytes .../_synchronization.cpython-312.pyc | Bin 0 -> 14146 bytes .../__pycache__/_trace.cpython-312.pyc | Bin 0 -> 5603 bytes .../__pycache__/_utils.cpython-312.pyc | Bin 0 -> 1283 bytes .deps_siteforge_v812/httpcore/_api.py | 94 + .../httpcore/_async/__init__.py | 39 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 1616 bytes .../__pycache__/connection.cpython-312.pyc | Bin 0 -> 11709 bytes .../connection_pool.cpython-312.pyc | Bin 0 -> 19591 bytes .../_async/__pycache__/http11.cpython-312.pyc | Bin 0 -> 20102 bytes .../_async/__pycache__/http2.cpython-312.pyc | Bin 0 -> 31168 bytes .../__pycache__/http_proxy.cpython-312.pyc | Bin 0 -> 17963 bytes .../__pycache__/interfaces.cpython-312.pyc | Bin 0 -> 5747 bytes .../__pycache__/socks_proxy.cpython-312.pyc | Bin 0 -> 16837 bytes .../httpcore/_async/connection.py | 222 + .../httpcore/_async/connection_pool.py | 420 + .../httpcore/_async/http11.py | 379 + .deps_siteforge_v812/httpcore/_async/http2.py | 592 + .../httpcore/_async/http_proxy.py | 367 + .../httpcore/_async/interfaces.py | 137 + .../httpcore/_async/socks_proxy.py | 341 + .../httpcore/_backends/__init__.py | 0 .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 192 bytes .../__pycache__/anyio.cpython-312.pyc | Bin 0 -> 8536 bytes .../__pycache__/auto.cpython-312.pyc | Bin 0 -> 2684 bytes .../__pycache__/base.cpython-312.pyc | Bin 0 -> 4886 bytes .../__pycache__/mock.cpython-312.pyc | Bin 0 -> 7150 bytes .../__pycache__/sync.cpython-312.pyc | Bin 0 -> 11357 bytes .../__pycache__/trio.cpython-312.pyc | Bin 0 -> 8875 bytes .../httpcore/_backends/anyio.py | 146 + .../httpcore/_backends/auto.py | 52 + .../httpcore/_backends/base.py | 101 + .../httpcore/_backends/mock.py | 143 + .../httpcore/_backends/sync.py | 241 + .../httpcore/_backends/trio.py | 159 + .deps_siteforge_v812/httpcore/_exceptions.py | 81 + .deps_siteforge_v812/httpcore/_models.py | 516 + .deps_siteforge_v812/httpcore/_ssl.py | 9 + .../httpcore/_sync/__init__.py | 39 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 1570 bytes .../__pycache__/connection.cpython-312.pyc | Bin 0 -> 10366 bytes .../connection_pool.cpython-312.pyc | Bin 0 -> 18668 bytes .../_sync/__pycache__/http11.cpython-312.pyc | Bin 0 -> 17689 bytes .../_sync/__pycache__/http2.cpython-312.pyc | Bin 0 -> 27300 bytes .../__pycache__/http_proxy.cpython-312.pyc | Bin 0 -> 17166 bytes .../__pycache__/interfaces.cpython-312.pyc | Bin 0 -> 5294 bytes .../__pycache__/socks_proxy.cpython-312.pyc | Bin 0 -> 15615 bytes .../httpcore/_sync/connection.py | 222 + .../httpcore/_sync/connection_pool.py | 420 + .deps_siteforge_v812/httpcore/_sync/http11.py | 379 + .deps_siteforge_v812/httpcore/_sync/http2.py | 592 + .../httpcore/_sync/http_proxy.py | 367 + .../httpcore/_sync/interfaces.py | 137 + .../httpcore/_sync/socks_proxy.py | 341 + .../httpcore/_synchronization.py | 318 + .deps_siteforge_v812/httpcore/_trace.py | 107 + .deps_siteforge_v812/httpcore/_utils.py | 37 + .deps_siteforge_v812/httpcore/py.typed | 0 .../httpx-0.28.1.dist-info/INSTALLER | 1 + .../httpx-0.28.1.dist-info/METADATA | 203 + .../httpx-0.28.1.dist-info/RECORD | 55 + .../httpx-0.28.1.dist-info/REQUESTED | 0 .../httpx-0.28.1.dist-info/WHEEL | 4 + .../httpx-0.28.1.dist-info/entry_points.txt | 2 + .../licenses/LICENSE.md | 12 + .deps_siteforge_v812/httpx/__init__.py | 105 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 2113 bytes .../__pycache__/__version__.cpython-312.pyc | Bin 0 -> 308 bytes .../httpx/__pycache__/_api.cpython-312.pyc | Bin 0 -> 10328 bytes .../httpx/__pycache__/_auth.cpython-312.pyc | Bin 0 -> 15580 bytes .../httpx/__pycache__/_client.cpython-312.pyc | Bin 0 -> 64124 bytes .../httpx/__pycache__/_config.cpython-312.pyc | Bin 0 -> 10976 bytes .../__pycache__/_content.cpython-312.pyc | Bin 0 -> 10388 bytes .../__pycache__/_decoders.cpython-312.pyc | Bin 0 -> 16746 bytes .../__pycache__/_exceptions.cpython-312.pyc | Bin 0 -> 12078 bytes .../httpx/__pycache__/_main.cpython-312.pyc | Bin 0 -> 20583 bytes .../httpx/__pycache__/_models.cpython-312.pyc | Bin 0 -> 58243 bytes .../__pycache__/_multipart.cpython-312.pyc | Bin 0 -> 13562 bytes .../__pycache__/_status_codes.cpython-312.pyc | Bin 0 -> 7183 bytes .../httpx/__pycache__/_types.cpython-312.pyc | Bin 0 -> 3820 bytes .../__pycache__/_urlparse.cpython-312.pyc | Bin 0 -> 17508 bytes .../httpx/__pycache__/_urls.cpython-312.pyc | Bin 0 -> 27936 bytes .../httpx/__pycache__/_utils.cpython-312.pyc | Bin 0 -> 9350 bytes .deps_siteforge_v812/httpx/__version__.py | 3 + .deps_siteforge_v812/httpx/_api.py | 438 + .deps_siteforge_v812/httpx/_auth.py | 348 + .deps_siteforge_v812/httpx/_client.py | 2019 ++ .deps_siteforge_v812/httpx/_config.py | 248 + .deps_siteforge_v812/httpx/_content.py | 240 + .deps_siteforge_v812/httpx/_decoders.py | 393 + .deps_siteforge_v812/httpx/_exceptions.py | 379 + .deps_siteforge_v812/httpx/_main.py | 506 + .deps_siteforge_v812/httpx/_models.py | 1277 ++ .deps_siteforge_v812/httpx/_multipart.py | 300 + .deps_siteforge_v812/httpx/_status_codes.py | 162 + .../httpx/_transports/__init__.py | 15 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 437 bytes .../__pycache__/asgi.cpython-312.pyc | Bin 0 -> 7580 bytes .../__pycache__/base.cpython-312.pyc | Bin 0 -> 3867 bytes .../__pycache__/default.cpython-312.pyc | Bin 0 -> 17046 bytes .../__pycache__/mock.cpython-312.pyc | Bin 0 -> 1935 bytes .../__pycache__/wsgi.cpython-312.pyc | Bin 0 -> 6799 bytes .../httpx/_transports/asgi.py | 187 + .../httpx/_transports/base.py | 86 + .../httpx/_transports/default.py | 406 + .../httpx/_transports/mock.py | 43 + .../httpx/_transports/wsgi.py | 149 + .deps_siteforge_v812/httpx/_types.py | 114 + .deps_siteforge_v812/httpx/_urlparse.py | 527 + .deps_siteforge_v812/httpx/_urls.py | 641 + .deps_siteforge_v812/httpx/_utils.py | 242 + .deps_siteforge_v812/httpx/py.typed | 0 .../idna-3.17.dist-info/INSTALLER | 1 + .../idna-3.17.dist-info/METADATA | 164 + .../idna-3.17.dist-info/RECORD | 29 + .../idna-3.17.dist-info/REQUESTED | 0 .../idna-3.17.dist-info/WHEEL | 4 + .../idna-3.17.dist-info/entry_points.txt | 3 + .../idna-3.17.dist-info/licenses/LICENSE.md | 31 + .deps_siteforge_v812/idna/__init__.py | 45 + .deps_siteforge_v812/idna/__main__.py | 6 + .../idna/__pycache__/__init__.cpython-312.pyc | Bin 0 -> 876 bytes .../idna/__pycache__/__main__.cpython-312.pyc | Bin 0 -> 347 bytes .../idna/__pycache__/cli.cpython-312.pyc | Bin 0 -> 5852 bytes .../idna/__pycache__/codec.cpython-312.pyc | Bin 0 -> 6318 bytes .../idna/__pycache__/compat.cpython-312.pyc | Bin 0 -> 1898 bytes .../idna/__pycache__/core.cpython-312.pyc | Bin 0 -> 27297 bytes .../idna/__pycache__/idnadata.cpython-312.pyc | Bin 0 -> 22023 bytes .../__pycache__/intranges.cpython-312.pyc | Bin 0 -> 2581 bytes .../__pycache__/package_data.cpython-312.pyc | Bin 0 -> 207 bytes .../__pycache__/uts46data.cpython-312.pyc | Bin 0 -> 96229 bytes .deps_siteforge_v812/idna/cli.py | 128 + .deps_siteforge_v812/idna/codec.py | 159 + .deps_siteforge_v812/idna/compat.py | 41 + .deps_siteforge_v812/idna/core.py | 634 + .deps_siteforge_v812/idna/idnadata.py | 1897 ++ .deps_siteforge_v812/idna/intranges.py | 55 + .deps_siteforge_v812/idna/package_data.py | 1 + .deps_siteforge_v812/idna/py.typed | 0 .deps_siteforge_v812/idna/uts46data.py | 16896 ++++++++++++++++ .../iniconfig-2.3.0.dist-info/INSTALLER | 1 + .../iniconfig-2.3.0.dist-info/METADATA | 79 + .../iniconfig-2.3.0.dist-info/RECORD | 16 + .../iniconfig-2.3.0.dist-info/REQUESTED | 0 .../iniconfig-2.3.0.dist-info/WHEEL | 5 + .../licenses/LICENSE | 21 + .../iniconfig-2.3.0.dist-info/top_level.txt | 1 + .deps_siteforge_v812/iniconfig/__init__.py | 249 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 10852 bytes .../__pycache__/_parse.cpython-312.pyc | Bin 0 -> 5551 bytes .../__pycache__/_version.cpython-312.pyc | Bin 0 -> 791 bytes .../__pycache__/exceptions.cpython-312.pyc | Bin 0 -> 1195 bytes .deps_siteforge_v812/iniconfig/_parse.py | 163 + .deps_siteforge_v812/iniconfig/_version.py | 34 + .deps_siteforge_v812/iniconfig/exceptions.py | 16 + .deps_siteforge_v812/iniconfig/py.typed | 0 .../itsdangerous-2.2.0.dist-info/INSTALLER | 1 + .../itsdangerous-2.2.0.dist-info/LICENSE.txt | 28 + .../itsdangerous-2.2.0.dist-info/METADATA | 60 + .../itsdangerous-2.2.0.dist-info/RECORD | 23 + .../itsdangerous-2.2.0.dist-info/REQUESTED | 0 .../itsdangerous-2.2.0.dist-info/WHEEL | 4 + .deps_siteforge_v812/itsdangerous/__init__.py | 38 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 1624 bytes .../__pycache__/_json.cpython-312.pyc | Bin 0 -> 1178 bytes .../__pycache__/encoding.cpython-312.pyc | Bin 0 -> 2678 bytes .../__pycache__/exc.cpython-312.pyc | Bin 0 -> 3938 bytes .../__pycache__/serializer.cpython-312.pyc | Bin 0 -> 15406 bytes .../__pycache__/signer.cpython-312.pyc | Bin 0 -> 11283 bytes .../__pycache__/timed.cpython-312.pyc | Bin 0 -> 8727 bytes .../__pycache__/url_safe.cpython-312.pyc | Bin 0 -> 3528 bytes .deps_siteforge_v812/itsdangerous/_json.py | 18 + .deps_siteforge_v812/itsdangerous/encoding.py | 54 + .deps_siteforge_v812/itsdangerous/exc.py | 106 + .deps_siteforge_v812/itsdangerous/py.typed | 0 .../itsdangerous/serializer.py | 406 + .deps_siteforge_v812/itsdangerous/signer.py | 266 + .deps_siteforge_v812/itsdangerous/timed.py | 228 + .deps_siteforge_v812/itsdangerous/url_safe.py | 83 + .../jinja2-3.1.6.dist-info/INSTALLER | 1 + .../jinja2-3.1.6.dist-info/METADATA | 84 + .../jinja2-3.1.6.dist-info/RECORD | 58 + .../jinja2-3.1.6.dist-info/REQUESTED | 0 .../jinja2-3.1.6.dist-info/WHEEL | 4 + .../jinja2-3.1.6.dist-info/entry_points.txt | 3 + .../licenses/LICENSE.txt | 28 + .deps_siteforge_v812/jinja2/__init__.py | 38 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 1649 bytes .../__pycache__/_identifier.cpython-312.pyc | Bin 0 -> 2130 bytes .../__pycache__/async_utils.cpython-312.pyc | Bin 0 -> 4970 bytes .../__pycache__/bccache.cpython-312.pyc | Bin 0 -> 19321 bytes .../__pycache__/compiler.cpython-312.pyc | Bin 0 -> 103888 bytes .../__pycache__/constants.cpython-312.pyc | Bin 0 -> 1552 bytes .../jinja2/__pycache__/debug.cpython-312.pyc | Bin 0 -> 6562 bytes .../__pycache__/defaults.cpython-312.pyc | Bin 0 -> 1602 bytes .../__pycache__/environment.cpython-312.pyc | Bin 0 -> 76631 bytes .../__pycache__/exceptions.cpython-312.pyc | Bin 0 -> 7711 bytes .../jinja2/__pycache__/ext.cpython-312.pyc | Bin 0 -> 41873 bytes .../__pycache__/filters.cpython-312.pyc | Bin 0 -> 72265 bytes .../__pycache__/idtracking.cpython-312.pyc | Bin 0 -> 19116 bytes .../jinja2/__pycache__/lexer.cpython-312.pyc | Bin 0 -> 32061 bytes .../__pycache__/loaders.cpython-312.pyc | Bin 0 -> 32271 bytes .../jinja2/__pycache__/meta.cpython-312.pyc | Bin 0 -> 5466 bytes .../__pycache__/nativetypes.cpython-312.pyc | Bin 0 -> 7015 bytes .../jinja2/__pycache__/nodes.cpython-312.pyc | Bin 0 -> 58215 bytes .../__pycache__/optimizer.cpython-312.pyc | Bin 0 -> 2685 bytes .../jinja2/__pycache__/parser.cpython-312.pyc | Bin 0 -> 61176 bytes .../__pycache__/runtime.cpython-312.pyc | Bin 0 -> 48880 bytes .../__pycache__/sandbox.cpython-312.pyc | Bin 0 -> 18093 bytes .../jinja2/__pycache__/tests.cpython-312.pyc | Bin 0 -> 9046 bytes .../jinja2/__pycache__/utils.cpython-312.pyc | Bin 0 -> 34794 bytes .../__pycache__/visitor.cpython-312.pyc | Bin 0 -> 5349 bytes .deps_siteforge_v812/jinja2/_identifier.py | 6 + .deps_siteforge_v812/jinja2/async_utils.py | 99 + .deps_siteforge_v812/jinja2/bccache.py | 408 + .deps_siteforge_v812/jinja2/compiler.py | 1998 ++ .deps_siteforge_v812/jinja2/constants.py | 20 + .deps_siteforge_v812/jinja2/debug.py | 191 + .deps_siteforge_v812/jinja2/defaults.py | 48 + .deps_siteforge_v812/jinja2/environment.py | 1672 ++ .deps_siteforge_v812/jinja2/exceptions.py | 166 + .deps_siteforge_v812/jinja2/ext.py | 870 + .deps_siteforge_v812/jinja2/filters.py | 1873 ++ .deps_siteforge_v812/jinja2/idtracking.py | 318 + .deps_siteforge_v812/jinja2/lexer.py | 868 + .deps_siteforge_v812/jinja2/loaders.py | 693 + .deps_siteforge_v812/jinja2/meta.py | 112 + .deps_siteforge_v812/jinja2/nativetypes.py | 130 + .deps_siteforge_v812/jinja2/nodes.py | 1206 ++ .deps_siteforge_v812/jinja2/optimizer.py | 48 + .deps_siteforge_v812/jinja2/parser.py | 1049 + .deps_siteforge_v812/jinja2/py.typed | 0 .deps_siteforge_v812/jinja2/runtime.py | 1062 + .deps_siteforge_v812/jinja2/sandbox.py | 436 + .deps_siteforge_v812/jinja2/tests.py | 256 + .deps_siteforge_v812/jinja2/utils.py | 766 + .deps_siteforge_v812/jinja2/visitor.py | 92 + .../markupsafe-3.0.3.dist-info/INSTALLER | 1 + .../markupsafe-3.0.3.dist-info/METADATA | 74 + .../markupsafe-3.0.3.dist-info/RECORD | 15 + .../markupsafe-3.0.3.dist-info/REQUESTED | 0 .../markupsafe-3.0.3.dist-info/WHEEL | 5 + .../licenses/LICENSE.txt | 28 + .../markupsafe-3.0.3.dist-info/top_level.txt | 1 + .deps_siteforge_v812/markupsafe/__init__.py | 396 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 20984 bytes .../__pycache__/_native.cpython-312.pyc | Bin 0 -> 611 bytes .deps_siteforge_v812/markupsafe/_native.py | 8 + .deps_siteforge_v812/markupsafe/_speedups.c | 200 + .../markupsafe/_speedups.cp312-win_amd64.pyd | Bin 0 -> 13312 bytes .deps_siteforge_v812/markupsafe/_speedups.pyi | 1 + .deps_siteforge_v812/markupsafe/py.typed | 0 .deps_siteforge_v812/multipart/__init__.py | 38 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 1356 bytes .../__pycache__/decoders.cpython-312.pyc | Bin 0 -> 230 bytes .../__pycache__/exceptions.cpython-312.pyc | Bin 0 -> 234 bytes .../__pycache__/multipart.cpython-312.pyc | Bin 0 -> 232 bytes .deps_siteforge_v812/multipart/decoders.py | 1 + .deps_siteforge_v812/multipart/exceptions.py | 1 + .deps_siteforge_v812/multipart/multipart.py | 1 + .../openpyxl-3.1.5.dist-info/INSTALLER | 1 + .../openpyxl-3.1.5.dist-info/LICENCE.rst | 23 + .../openpyxl-3.1.5.dist-info/METADATA | 86 + .../openpyxl-3.1.5.dist-info/RECORD | 387 + .../openpyxl-3.1.5.dist-info/REQUESTED | 0 .../openpyxl-3.1.5.dist-info/WHEEL | 6 + .../openpyxl-3.1.5.dist-info/top_level.txt | 1 + .deps_siteforge_v812/openpyxl/__init__.py | 19 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 812 bytes .../__pycache__/_constants.cpython-312.pyc | Bin 0 -> 516 bytes .deps_siteforge_v812/openpyxl/_constants.py | 13 + .../openpyxl/cell/__init__.py | 4 + .../cell/__pycache__/__init__.cpython-312.pyc | Bin 0 -> 320 bytes .../cell/__pycache__/_writer.cpython-312.pyc | Bin 0 -> 5404 bytes .../cell/__pycache__/cell.cpython-312.pyc | Bin 0 -> 12339 bytes .../__pycache__/read_only.cpython-312.pyc | Bin 0 -> 6578 bytes .../__pycache__/rich_text.cpython-312.pyc | Bin 0 -> 9297 bytes .../cell/__pycache__/text.cpython-312.pyc | Bin 0 -> 5553 bytes .deps_siteforge_v812/openpyxl/cell/_writer.py | 136 + .deps_siteforge_v812/openpyxl/cell/cell.py | 332 + .../openpyxl/cell/read_only.py | 136 + .../openpyxl/cell/rich_text.py | 202 + .deps_siteforge_v812/openpyxl/cell/text.py | 184 + .deps_siteforge_v812/openpyxl/chart/_3d.py | 105 + .../openpyxl/chart/__init__.py | 19 + .../chart/__pycache__/_3d.cpython-312.pyc | Bin 0 -> 3434 bytes .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 890 bytes .../chart/__pycache__/_chart.cpython-312.pyc | Bin 0 -> 8509 bytes .../__pycache__/area_chart.cpython-312.pyc | Bin 0 -> 3905 bytes .../chart/__pycache__/axis.cpython-312.pyc | Bin 0 -> 13338 bytes .../__pycache__/bar_chart.cpython-312.pyc | Bin 0 -> 4997 bytes .../__pycache__/bubble_chart.cpython-312.pyc | Bin 0 -> 2374 bytes .../__pycache__/chartspace.cpython-312.pyc | Bin 0 -> 6002 bytes .../__pycache__/data_source.cpython-312.pyc | Bin 0 -> 7821 bytes .../__pycache__/descriptors.cpython-312.pyc | Bin 0 -> 1423 bytes .../__pycache__/error_bar.cpython-312.pyc | Bin 0 -> 2104 bytes .../chart/__pycache__/label.cpython-312.pyc | Bin 0 -> 4523 bytes .../chart/__pycache__/layout.cpython-312.pyc | Bin 0 -> 2366 bytes .../chart/__pycache__/legend.cpython-312.pyc | Bin 0 -> 2426 bytes .../__pycache__/line_chart.cpython-312.pyc | Bin 0 -> 4549 bytes .../chart/__pycache__/marker.cpython-312.pyc | Bin 0 -> 2745 bytes .../chart/__pycache__/picture.cpython-312.pyc | Bin 0 -> 1289 bytes .../__pycache__/pie_chart.cpython-312.pyc | Bin 0 -> 6050 bytes .../chart/__pycache__/pivot.cpython-312.pyc | Bin 0 -> 2209 bytes .../__pycache__/plotarea.cpython-312.pyc | Bin 0 -> 6415 bytes .../print_settings.cpython-312.pyc | Bin 0 -> 2123 bytes .../__pycache__/radar_chart.cpython-312.pyc | Bin 0 -> 2026 bytes .../chart/__pycache__/reader.cpython-312.pyc | Bin 0 -> 1789 bytes .../__pycache__/reference.cpython-312.pyc | Bin 0 -> 4968 bytes .../__pycache__/scatter_chart.cpython-312.pyc | Bin 0 -> 2037 bytes .../chart/__pycache__/series.cpython-312.pyc | Bin 0 -> 5912 bytes .../series_factory.cpython-312.pyc | Bin 0 -> 1742 bytes .../chart/__pycache__/shapes.cpython-312.pyc | Bin 0 -> 2841 bytes .../__pycache__/stock_chart.cpython-312.pyc | Bin 0 -> 1969 bytes .../__pycache__/surface_chart.cpython-312.pyc | Bin 0 -> 4433 bytes .../chart/__pycache__/text.cpython-312.pyc | Bin 0 -> 2498 bytes .../chart/__pycache__/title.cpython-312.pyc | Bin 0 -> 2908 bytes .../__pycache__/trendline.cpython-312.pyc | Bin 0 -> 3129 bytes .../__pycache__/updown_bars.cpython-312.pyc | Bin 0 -> 1219 bytes .deps_siteforge_v812/openpyxl/chart/_chart.py | 199 + .../openpyxl/chart/area_chart.py | 106 + .deps_siteforge_v812/openpyxl/chart/axis.py | 401 + .../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 + .../openpyxl/chart/error_bar.py | 62 + .deps_siteforge_v812/openpyxl/chart/label.py | 127 + .deps_siteforge_v812/openpyxl/chart/layout.py | 74 + .deps_siteforge_v812/openpyxl/chart/legend.py | 75 + .../openpyxl/chart/line_chart.py | 129 + .deps_siteforge_v812/openpyxl/chart/marker.py | 90 + .../openpyxl/chart/picture.py | 35 + .../openpyxl/chart/pie_chart.py | 177 + .deps_siteforge_v812/openpyxl/chart/pivot.py | 65 + .../openpyxl/chart/plotarea.py | 162 + .../openpyxl/chart/print_settings.py | 57 + .../openpyxl/chart/radar_chart.py | 55 + .deps_siteforge_v812/openpyxl/chart/reader.py | 32 + .../openpyxl/chart/reference.py | 124 + .../openpyxl/chart/scatter_chart.py | 53 + .deps_siteforge_v812/openpyxl/chart/series.py | 197 + .../openpyxl/chart/series_factory.py | 41 + .deps_siteforge_v812/openpyxl/chart/shapes.py | 89 + .../openpyxl/chart/stock_chart.py | 54 + .../openpyxl/chart/surface_chart.py | 119 + .deps_siteforge_v812/openpyxl/chart/text.py | 78 + .deps_siteforge_v812/openpyxl/chart/title.py | 76 + .../openpyxl/chart/trendline.py | 98 + .../openpyxl/chart/updown_bars.py | 31 + .../openpyxl/chartsheet/__init__.py | 3 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 243 bytes .../__pycache__/chartsheet.cpython-312.pyc | Bin 0 -> 4181 bytes .../__pycache__/custom.cpython-312.pyc | Bin 0 -> 2103 bytes .../__pycache__/properties.cpython-312.pyc | Bin 0 -> 1074 bytes .../__pycache__/protection.cpython-312.pyc | Bin 0 -> 1506 bytes .../__pycache__/publish.cpython-312.pyc | Bin 0 -> 1989 bytes .../__pycache__/relation.cpython-312.pyc | Bin 0 -> 3401 bytes .../__pycache__/views.cpython-312.pyc | Bin 0 -> 1769 bytes .../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 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 236 bytes .../__pycache__/author.cpython-312.pyc | Bin 0 -> 809 bytes .../__pycache__/comment_sheet.cpython-312.pyc | Bin 0 -> 7227 bytes .../__pycache__/comments.cpython-312.pyc | Bin 0 -> 2891 bytes .../__pycache__/shape_writer.cpython-312.pyc | Bin 0 -> 4879 bytes .../openpyxl/comments/author.py | 21 + .../openpyxl/comments/comment_sheet.py | 211 + .../openpyxl/comments/comments.py | 62 + .../openpyxl/comments/shape_writer.py | 112 + .../openpyxl/compat/__init__.py | 54 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 2162 bytes .../compat/__pycache__/abc.cpython-312.pyc | Bin 0 -> 369 bytes .../__pycache__/numbers.cpython-312.pyc | Bin 0 -> 1479 bytes .../__pycache__/product.cpython-312.pyc | Bin 0 -> 626 bytes .../__pycache__/singleton.cpython-312.pyc | Bin 0 -> 2033 bytes .../__pycache__/strings.cpython-312.pyc | Bin 0 -> 973 bytes .deps_siteforge_v812/openpyxl/compat/abc.py | 8 + .../openpyxl/compat/numbers.py | 43 + .../openpyxl/compat/product.py | 17 + .../openpyxl/compat/singleton.py | 40 + .../openpyxl/compat/strings.py | 25 + .../openpyxl/descriptors/__init__.py | 58 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 3130 bytes .../__pycache__/base.cpython-312.pyc | Bin 0 -> 13235 bytes .../__pycache__/container.cpython-312.pyc | Bin 0 -> 2066 bytes .../__pycache__/excel.cpython-312.pyc | Bin 0 -> 4280 bytes .../__pycache__/namespace.cpython-312.pyc | Bin 0 -> 499 bytes .../__pycache__/nested.cpython-312.pyc | Bin 0 -> 4676 bytes .../__pycache__/sequence.cpython-312.pyc | Bin 0 -> 6353 bytes .../__pycache__/serialisable.cpython-312.pyc | Bin 0 -> 9246 bytes .../__pycache__/slots.cpython-312.pyc | Bin 0 -> 1433 bytes .../openpyxl/descriptors/base.py | 272 + .../openpyxl/descriptors/container.py | 41 + .../openpyxl/descriptors/excel.py | 112 + .../openpyxl/descriptors/namespace.py | 12 + .../openpyxl/descriptors/nested.py | 129 + .../openpyxl/descriptors/sequence.py | 136 + .../openpyxl/descriptors/serialisable.py | 240 + .../openpyxl/descriptors/slots.py | 18 + .../openpyxl/drawing/__init__.py | 4 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 234 bytes .../__pycache__/colors.cpython-312.pyc | Bin 0 -> 13191 bytes .../__pycache__/connector.cpython-312.pyc | Bin 0 -> 4931 bytes .../__pycache__/drawing.cpython-312.pyc | Bin 0 -> 3954 bytes .../__pycache__/effect.cpython-312.pyc | Bin 0 -> 13209 bytes .../drawing/__pycache__/fill.cpython-312.pyc | Bin 0 -> 12670 bytes .../__pycache__/geometry.cpython-312.pyc | Bin 0 -> 19883 bytes .../__pycache__/graphic.cpython-312.pyc | Bin 0 -> 5594 bytes .../drawing/__pycache__/image.cpython-312.pyc | Bin 0 -> 2598 bytes .../drawing/__pycache__/line.cpython-312.pyc | Bin 0 -> 4436 bytes .../__pycache__/picture.cpython-312.pyc | Bin 0 -> 4375 bytes .../__pycache__/properties.cpython-312.pyc | Bin 0 -> 5437 bytes .../__pycache__/relation.cpython-312.pyc | Bin 0 -> 792 bytes .../spreadsheet_drawing.cpython-312.pyc | Bin 0 -> 14791 bytes .../drawing/__pycache__/text.cpython-312.pyc | Bin 0 -> 21235 bytes .../drawing/__pycache__/xdr.cpython-312.pyc | Bin 0 -> 1352 bytes .../openpyxl/drawing/colors.py | 435 + .../openpyxl/drawing/connector.py | 144 + .../openpyxl/drawing/drawing.py | 92 + .../openpyxl/drawing/effect.py | 407 + .deps_siteforge_v812/openpyxl/drawing/fill.py | 425 + .../openpyxl/drawing/geometry.py | 584 + .../openpyxl/drawing/graphic.py | 177 + .../openpyxl/drawing/image.py | 65 + .deps_siteforge_v812/openpyxl/drawing/line.py | 144 + .../openpyxl/drawing/picture.py | 144 + .../openpyxl/drawing/properties.py | 174 + .../openpyxl/drawing/relation.py | 17 + .../openpyxl/drawing/spreadsheet_drawing.py | 382 + .deps_siteforge_v812/openpyxl/drawing/text.py | 717 + .deps_siteforge_v812/openpyxl/drawing/xdr.py | 33 + .../openpyxl/formatting/__init__.py | 3 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 231 bytes .../__pycache__/formatting.cpython-312.pyc | Bin 0 -> 5109 bytes .../__pycache__/rule.cpython-312.pyc | Bin 0 -> 9636 bytes .../openpyxl/formatting/formatting.py | 114 + .../openpyxl/formatting/rule.py | 291 + .../openpyxl/formula/__init__.py | 3 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 238 bytes .../__pycache__/tokenizer.cpython-312.pyc | Bin 0 -> 20487 bytes .../__pycache__/translate.cpython-312.pyc | Bin 0 -> 7487 bytes .../openpyxl/formula/tokenizer.py | 446 + .../openpyxl/formula/translate.py | 166 + .../openpyxl/packaging/__init__.py | 3 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 292 bytes .../__pycache__/core.cpython-312.pyc | Bin 0 -> 4244 bytes .../__pycache__/custom.cpython-312.pyc | Bin 0 -> 11307 bytes .../__pycache__/extended.cpython-312.pyc | Bin 0 -> 4461 bytes .../__pycache__/interface.cpython-312.pyc | Bin 0 -> 1748 bytes .../__pycache__/manifest.cpython-312.pyc | Bin 0 -> 8249 bytes .../__pycache__/relationship.cpython-312.pyc | Bin 0 -> 6315 bytes .../__pycache__/workbook.cpython-312.pyc | Bin 0 -> 6587 bytes .../openpyxl/packaging/core.py | 115 + .../openpyxl/packaging/custom.py | 289 + .../openpyxl/packaging/extended.py | 137 + .../openpyxl/packaging/interface.py | 56 + .../openpyxl/packaging/manifest.py | 194 + .../openpyxl/packaging/relationship.py | 158 + .../openpyxl/packaging/workbook.py | 185 + .../openpyxl/pivot/__init__.py | 1 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 188 bytes .../pivot/__pycache__/cache.cpython-312.pyc | Bin 0 -> 28673 bytes .../pivot/__pycache__/fields.cpython-312.pyc | Bin 0 -> 8172 bytes .../pivot/__pycache__/record.cpython-312.pyc | Bin 0 -> 3896 bytes .../pivot/__pycache__/table.cpython-312.pyc | Bin 0 -> 38472 bytes .deps_siteforge_v812/openpyxl/pivot/cache.py | 965 + .deps_siteforge_v812/openpyxl/pivot/fields.py | 326 + .deps_siteforge_v812/openpyxl/pivot/record.py | 111 + .deps_siteforge_v812/openpyxl/pivot/table.py | 1261 ++ .../openpyxl/reader/__init__.py | 1 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 189 bytes .../__pycache__/drawings.cpython-312.pyc | Bin 0 -> 3235 bytes .../reader/__pycache__/excel.cpython-312.pyc | Bin 0 -> 16855 bytes .../__pycache__/strings.cpython-312.pyc | Bin 0 -> 1711 bytes .../__pycache__/workbook.cpython-312.pyc | Bin 0 -> 6669 bytes .../openpyxl/reader/drawings.py | 71 + .deps_siteforge_v812/openpyxl/reader/excel.py | 349 + .../openpyxl/reader/strings.py | 44 + .../openpyxl/reader/workbook.py | 133 + .../openpyxl/styles/__init__.py | 11 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 667 bytes .../__pycache__/alignment.cpython-312.pyc | Bin 0 -> 2516 bytes .../__pycache__/borders.cpython-312.pyc | Bin 0 -> 3714 bytes .../__pycache__/builtins.cpython-312.pyc | Bin 0 -> 33163 bytes .../__pycache__/cell_style.cpython-312.pyc | Bin 0 -> 7607 bytes .../styles/__pycache__/colors.cpython-312.pyc | Bin 0 -> 6449 bytes .../__pycache__/differential.cpython-312.pyc | Bin 0 -> 3494 bytes .../styles/__pycache__/fills.cpython-312.pyc | Bin 0 -> 8988 bytes .../styles/__pycache__/fonts.cpython-312.pyc | Bin 0 -> 4004 bytes .../__pycache__/named_styles.cpython-312.pyc | Bin 0 -> 11064 bytes .../__pycache__/numbers.cpython-312.pyc | Bin 0 -> 7845 bytes .../__pycache__/protection.cpython-312.pyc | Bin 0 -> 827 bytes .../styles/__pycache__/proxy.cpython-312.pyc | Bin 0 -> 2801 bytes .../__pycache__/styleable.cpython-312.pyc | Bin 0 -> 7541 bytes .../__pycache__/stylesheet.cpython-312.pyc | Bin 0 -> 11600 bytes .../styles/__pycache__/table.cpython-312.pyc | Bin 0 -> 3253 bytes .../openpyxl/styles/alignment.py | 62 + .../openpyxl/styles/borders.py | 103 + .../openpyxl/styles/builtins.py | 1397 ++ .../openpyxl/styles/cell_style.py | 206 + .../openpyxl/styles/colors.py | 172 + .../openpyxl/styles/differential.py | 95 + .deps_siteforge_v812/openpyxl/styles/fills.py | 224 + .deps_siteforge_v812/openpyxl/styles/fonts.py | 113 + .../openpyxl/styles/named_styles.py | 282 + .../openpyxl/styles/numbers.py | 200 + .../openpyxl/styles/protection.py | 17 + .deps_siteforge_v812/openpyxl/styles/proxy.py | 62 + .../openpyxl/styles/styleable.py | 151 + .../openpyxl/styles/stylesheet.py | 274 + .deps_siteforge_v812/openpyxl/styles/table.py | 94 + .../openpyxl/utils/__init__.py | 17 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 548 bytes .../bound_dictionary.cpython-312.pyc | Bin 0 -> 1355 bytes .../utils/__pycache__/cell.cpython-312.pyc | Bin 0 -> 9858 bytes .../__pycache__/dataframe.cpython-312.pyc | Bin 0 -> 3225 bytes .../__pycache__/datetime.cpython-312.pyc | Bin 0 -> 6296 bytes .../utils/__pycache__/escape.cpython-312.pyc | Bin 0 -> 1529 bytes .../__pycache__/exceptions.cpython-312.pyc | Bin 0 -> 1877 bytes .../__pycache__/formulas.cpython-312.pyc | Bin 0 -> 4091 bytes .../__pycache__/indexed_list.cpython-312.pyc | Bin 0 -> 2334 bytes .../__pycache__/inference.cpython-312.pyc | Bin 0 -> 2266 bytes .../__pycache__/protection.cpython-312.pyc | Bin 0 -> 1214 bytes .../utils/__pycache__/units.cpython-312.pyc | Bin 0 -> 3104 bytes .../openpyxl/utils/bound_dictionary.py | 26 + .deps_siteforge_v812/openpyxl/utils/cell.py | 240 + .../openpyxl/utils/dataframe.py | 87 + .../openpyxl/utils/datetime.py | 140 + .deps_siteforge_v812/openpyxl/utils/escape.py | 43 + .../openpyxl/utils/exceptions.py | 34 + .../openpyxl/utils/formulas.py | 24 + .../openpyxl/utils/indexed_list.py | 49 + .../openpyxl/utils/inference.py | 60 + .../openpyxl/utils/protection.py | 22 + .deps_siteforge_v812/openpyxl/utils/units.py | 108 + .../openpyxl/workbook/__init__.py | 4 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 237 bytes .../__pycache__/_writer.cpython-312.pyc | Bin 0 -> 10555 bytes .../__pycache__/child.cpython-312.pyc | Bin 0 -> 7252 bytes .../__pycache__/defined_name.cpython-312.pyc | Bin 0 -> 7620 bytes .../external_reference.cpython-312.pyc | Bin 0 -> 800 bytes .../function_group.cpython-312.pyc | Bin 0 -> 1353 bytes .../__pycache__/properties.cpython-312.pyc | Bin 0 -> 4675 bytes .../__pycache__/protection.cpython-312.pyc | Bin 0 -> 5881 bytes .../__pycache__/smart_tags.cpython-312.pyc | Bin 0 -> 1918 bytes .../__pycache__/views.cpython-312.pyc | Bin 0 -> 4466 bytes .../workbook/__pycache__/web.cpython-312.pyc | Bin 0 -> 3168 bytes .../__pycache__/workbook.cpython-312.pyc | Bin 0 -> 18840 bytes .../openpyxl/workbook/_writer.py | 197 + .../openpyxl/workbook/child.py | 166 + .../openpyxl/workbook/defined_name.py | 189 + .../workbook/external_link/__init__.py | 3 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 255 bytes .../__pycache__/external.cpython-312.pyc | Bin 0 -> 6405 bytes .../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 + .../openpyxl/workbook/views.py | 155 + .deps_siteforge_v812/openpyxl/workbook/web.py | 98 + .../openpyxl/workbook/workbook.py | 438 + .../openpyxl/worksheet/__init__.py | 1 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 192 bytes .../__pycache__/_read_only.cpython-312.pyc | Bin 0 -> 7540 bytes .../__pycache__/_reader.cpython-312.pyc | Bin 0 -> 22524 bytes .../__pycache__/_write_only.cpython-312.pyc | Bin 0 -> 6597 bytes .../__pycache__/_writer.cpython-312.pyc | Bin 0 -> 19598 bytes .../__pycache__/cell_range.cpython-312.pyc | Bin 0 -> 22835 bytes .../__pycache__/cell_watch.cpython-312.pyc | Bin 0 -> 1172 bytes .../__pycache__/controls.cpython-312.pyc | Bin 0 -> 3118 bytes .../__pycache__/copier.cpython-312.pyc | Bin 0 -> 4125 bytes .../__pycache__/custom.cpython-312.pyc | Bin 0 -> 1206 bytes .../datavalidation.cpython-312.pyc | Bin 0 -> 7795 bytes .../__pycache__/dimensions.cpython-312.pyc | Bin 0 -> 11327 bytes .../__pycache__/drawing.cpython-312.pyc | Bin 0 -> 709 bytes .../__pycache__/errors.cpython-312.pyc | Bin 0 -> 2942 bytes .../__pycache__/filters.cpython-312.pyc | Bin 0 -> 16619 bytes .../__pycache__/formula.cpython-312.pyc | Bin 0 -> 1799 bytes .../__pycache__/header_footer.cpython-312.pyc | Bin 0 -> 10454 bytes .../__pycache__/hyperlink.cpython-312.pyc | Bin 0 -> 1669 bytes .../__pycache__/merge.cpython-312.pyc | Bin 0 -> 6491 bytes .../worksheet/__pycache__/ole.cpython-312.pyc | Bin 0 -> 4010 bytes .../__pycache__/page.cpython-312.pyc | Bin 0 -> 6072 bytes .../__pycache__/pagebreak.cpython-312.pyc | Bin 0 -> 3156 bytes .../__pycache__/picture.cpython-312.pyc | Bin 0 -> 494 bytes .../print_settings.cpython-312.pyc | Bin 0 -> 8992 bytes .../__pycache__/properties.cpython-312.pyc | Bin 0 -> 3138 bytes .../__pycache__/protection.cpython-312.pyc | Bin 0 -> 4436 bytes .../__pycache__/related.cpython-312.pyc | Bin 0 -> 918 bytes .../__pycache__/scenario.cpython-312.pyc | Bin 0 -> 3341 bytes .../__pycache__/smart_tag.cpython-312.pyc | Bin 0 -> 2434 bytes .../__pycache__/table.cpython-312.pyc | Bin 0 -> 14171 bytes .../__pycache__/views.cpython-312.pyc | Bin 0 -> 5065 bytes .../__pycache__/worksheet.cpython-312.pyc | Bin 0 -> 37101 bytes .../openpyxl/worksheet/_read_only.py | 190 + .../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 | 306 + .../openpyxl/worksheet/drawing.py | 14 + .../openpyxl/worksheet/errors.py | 93 + .../openpyxl/worksheet/filters.py | 486 + .../openpyxl/worksheet/formula.py | 51 + .../openpyxl/worksheet/header_footer.py | 270 + .../openpyxl/worksheet/hyperlink.py | 46 + .../openpyxl/worksheet/merge.py | 141 + .../openpyxl/worksheet/ole.py | 133 + .../openpyxl/worksheet/page.py | 174 + .../openpyxl/worksheet/pagebreak.py | 94 + .../openpyxl/worksheet/picture.py | 8 + .../openpyxl/worksheet/print_settings.py | 184 + .../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 + .../openpyxl/worksheet/table.py | 385 + .../openpyxl/worksheet/views.py | 155 + .../openpyxl/worksheet/worksheet.py | 907 + .../openpyxl/writer/__init__.py | 1 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 189 bytes .../writer/__pycache__/excel.cpython-312.pyc | Bin 0 -> 16470 bytes .../writer/__pycache__/theme.cpython-312.pyc | Bin 0 -> 10558 bytes .deps_siteforge_v812/openpyxl/writer/excel.py | 295 + .deps_siteforge_v812/openpyxl/writer/theme.py | 291 + .deps_siteforge_v812/openpyxl/xml/__init__.py | 42 + .../xml/__pycache__/__init__.cpython-312.pyc | Bin 0 -> 1552 bytes .../xml/__pycache__/constants.cpython-312.pyc | Bin 0 -> 5124 bytes .../xml/__pycache__/functions.cpython-312.pyc | Bin 0 -> 2545 bytes .../openpyxl/xml/constants.py | 129 + .../openpyxl/xml/functions.py | 87 + .../packaging-25.0.dist-info/INSTALLER | 1 + .../packaging-25.0.dist-info/METADATA | 105 + .../packaging-25.0.dist-info/RECORD | 41 + .../packaging-25.0.dist-info/REQUESTED | 0 .../packaging-25.0.dist-info/WHEEL | 4 + .../packaging-25.0.dist-info/licenses/LICENSE | 3 + .../licenses/LICENSE.APACHE | 177 + .../licenses/LICENSE.BSD | 23 + .deps_siteforge_v812/packaging/__init__.py | 15 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 548 bytes .../__pycache__/_elffile.cpython-312.pyc | Bin 0 -> 5006 bytes .../__pycache__/_manylinux.cpython-312.pyc | Bin 0 -> 9696 bytes .../__pycache__/_musllinux.cpython-312.pyc | Bin 0 -> 4544 bytes .../__pycache__/_parser.cpython-312.pyc | Bin 0 -> 13973 bytes .../__pycache__/_structures.cpython-312.pyc | Bin 0 -> 3231 bytes .../__pycache__/_tokenizer.cpython-312.pyc | Bin 0 -> 7942 bytes .../__pycache__/markers.cpython-312.pyc | Bin 0 -> 12756 bytes .../__pycache__/metadata.cpython-312.pyc | Bin 0 -> 27198 bytes .../__pycache__/requirements.cpython-312.pyc | Bin 0 -> 4400 bytes .../__pycache__/specifiers.cpython-312.pyc | Bin 0 -> 39005 bytes .../__pycache__/tags.cpython-312.pyc | Bin 0 -> 24663 bytes .../__pycache__/utils.cpython-312.pyc | Bin 0 -> 6625 bytes .../__pycache__/version.cpython-312.pyc | Bin 0 -> 20464 bytes .deps_siteforge_v812/packaging/_elffile.py | 109 + .deps_siteforge_v812/packaging/_manylinux.py | 262 + .deps_siteforge_v812/packaging/_musllinux.py | 85 + .deps_siteforge_v812/packaging/_parser.py | 353 + .deps_siteforge_v812/packaging/_structures.py | 61 + .deps_siteforge_v812/packaging/_tokenizer.py | 195 + .../packaging/licenses/__init__.py | 145 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 4098 bytes .../__pycache__/_spdx.cpython-312.pyc | Bin 0 -> 47354 bytes .../packaging/licenses/_spdx.py | 759 + .deps_siteforge_v812/packaging/markers.py | 362 + .deps_siteforge_v812/packaging/metadata.py | 862 + .deps_siteforge_v812/packaging/py.typed | 0 .../packaging/requirements.py | 91 + .deps_siteforge_v812/packaging/specifiers.py | 1019 + .deps_siteforge_v812/packaging/tags.py | 656 + .deps_siteforge_v812/packaging/utils.py | 163 + .deps_siteforge_v812/packaging/version.py | 582 + .../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 + .deps_siteforge_v812/pluggy/__init__.py | 30 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 856 bytes .../__pycache__/_callers.cpython-312.pyc | Bin 0 -> 7011 bytes .../pluggy/__pycache__/_hooks.cpython-312.pyc | Bin 0 -> 27413 bytes .../__pycache__/_manager.cpython-312.pyc | Bin 0 -> 25575 bytes .../__pycache__/_result.cpython-312.pyc | Bin 0 -> 4143 bytes .../__pycache__/_tracing.cpython-312.pyc | Bin 0 -> 4015 bytes .../__pycache__/_version.cpython-312.pyc | Bin 0 -> 641 bytes .../__pycache__/_warnings.cpython-312.pyc | Bin 0 -> 1294 bytes .deps_siteforge_v812/pluggy/_callers.py | 169 + .deps_siteforge_v812/pluggy/_hooks.py | 714 + .deps_siteforge_v812/pluggy/_manager.py | 523 + .deps_siteforge_v812/pluggy/_result.py | 107 + .deps_siteforge_v812/pluggy/_tracing.py | 72 + .deps_siteforge_v812/pluggy/_version.py | 21 + .deps_siteforge_v812/pluggy/_warnings.py | 27 + .deps_siteforge_v812/pluggy/py.typed | 0 .deps_siteforge_v812/py.py | 15 + .../pydantic-2.13.5.dist-info/INSTALLER | 1 + .../pydantic-2.13.5.dist-info/METADATA | 1307 ++ .../pydantic-2.13.5.dist-info/RECORD | 218 + .../pydantic-2.13.5.dist-info/REQUESTED | 0 .../pydantic-2.13.5.dist-info/WHEEL | 4 + .../licenses/LICENSE | 21 + .deps_siteforge_v812/pydantic/__init__.py | 456 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 14422 bytes .../__pycache__/_migration.cpython-312.pyc | Bin 0 -> 11029 bytes .../alias_generators.cpython-312.pyc | Bin 0 -> 3279 bytes .../__pycache__/aliases.cpython-312.pyc | Bin 0 -> 6559 bytes .../annotated_handlers.cpython-312.pyc | Bin 0 -> 5493 bytes .../class_validators.cpython-312.pyc | Bin 0 -> 367 bytes .../__pycache__/color.cpython-312.pyc | Bin 0 -> 30173 bytes .../__pycache__/config.cpython-312.pyc | Bin 0 -> 7551 bytes .../__pycache__/dataclasses.cpython-312.pyc | Bin 0 -> 17360 bytes .../datetime_parse.cpython-312.pyc | Bin 0 -> 367 bytes .../__pycache__/decorator.cpython-312.pyc | Bin 0 -> 357 bytes .../__pycache__/env_settings.cpython-312.pyc | Bin 0 -> 363 bytes .../error_wrappers.cpython-312.pyc | Bin 0 -> 367 bytes .../__pycache__/errors.cpython-312.pyc | Bin 0 -> 7639 bytes .../__pycache__/fields.cpython-312.pyc | Bin 0 -> 75228 bytes .../functional_serializers.cpython-312.pyc | Bin 0 -> 19083 bytes .../functional_validators.cpython-312.pyc | Bin 0 -> 34643 bytes .../__pycache__/generics.cpython-312.pyc | Bin 0 -> 355 bytes .../pydantic/__pycache__/json.cpython-312.pyc | Bin 0 -> 347 bytes .../__pycache__/json_schema.cpython-312.pyc | Bin 0 -> 121812 bytes .../pydantic/__pycache__/main.cpython-312.pyc | Bin 0 -> 78086 bytes .../pydantic/__pycache__/mypy.cpython-312.pyc | Bin 0 -> 64149 bytes .../__pycache__/networks.cpython-312.pyc | Bin 0 -> 50214 bytes .../__pycache__/parse.cpython-312.pyc | Bin 0 -> 349 bytes .../__pycache__/root_model.cpython-312.pyc | Bin 0 -> 7913 bytes .../__pycache__/schema.cpython-312.pyc | Bin 0 -> 351 bytes .../__pycache__/tools.cpython-312.pyc | Bin 0 -> 349 bytes .../__pycache__/type_adapter.cpython-312.pyc | Bin 0 -> 35894 bytes .../__pycache__/types.cpython-312.pyc | Bin 0 -> 97181 bytes .../__pycache__/typing.cpython-312.pyc | Bin 0 -> 347 bytes .../__pycache__/utils.cpython-312.pyc | Bin 0 -> 349 bytes .../validate_call_decorator.cpython-312.pyc | Bin 0 -> 5486 bytes .../__pycache__/validators.cpython-312.pyc | Bin 0 -> 359 bytes .../__pycache__/version.cpython-312.pyc | Bin 0 -> 4795 bytes .../__pycache__/warnings.cpython-312.pyc | Bin 0 -> 7211 bytes .../pydantic/_internal/__init__.py | 0 .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 192 bytes .../__pycache__/_config.cpython-312.pyc | Bin 0 -> 15797 bytes .../_core_metadata.cpython-312.pyc | Bin 0 -> 4612 bytes .../__pycache__/_core_utils.cpython-312.pyc | Bin 0 -> 7709 bytes .../__pycache__/_dataclasses.cpython-312.pyc | Bin 0 -> 13429 bytes .../__pycache__/_decorators.cpython-312.pyc | Bin 0 -> 38130 bytes .../_decorators_v1.cpython-312.pyc | Bin 0 -> 8612 bytes .../_discriminated_union.cpython-312.pyc | Bin 0 -> 21334 bytes .../_docs_extraction.cpython-312.pyc | Bin 0 -> 5305 bytes .../__pycache__/_fields.cpython-312.pyc | Bin 0 -> 27343 bytes .../__pycache__/_forward_ref.cpython-312.pyc | Bin 0 -> 1301 bytes .../_generate_schema.cpython-312.pyc | Bin 0 -> 132407 bytes .../__pycache__/_generics.cpython-312.pyc | Bin 0 -> 23811 bytes .../__pycache__/_git.cpython-312.pyc | Bin 0 -> 1507 bytes .../__pycache__/_import_utils.cpython-312.pyc | Bin 0 -> 820 bytes .../_internal_dataclass.cpython-312.pyc | Bin 0 -> 341 bytes .../_known_annotated_metadata.cpython-312.pyc | Bin 0 -> 14254 bytes .../__pycache__/_mock_val_ser.cpython-312.pyc | Bin 0 -> 11090 bytes .../_model_construction.cpython-312.pyc | Bin 0 -> 35993 bytes .../_namespace_utils.cpython-312.pyc | Bin 0 -> 12310 bytes .../__pycache__/_repr.cpython-312.pyc | Bin 0 -> 7850 bytes .../_schema_gather.cpython-312.pyc | Bin 0 -> 7577 bytes .../_schema_generation_shared.cpython-312.pyc | Bin 0 -> 6218 bytes .../__pycache__/_serializers.cpython-312.pyc | Bin 0 -> 2108 bytes .../__pycache__/_signature.cpython-312.pyc | Bin 0 -> 6777 bytes .../__pycache__/_typing_extra.cpython-312.pyc | Bin 0 -> 29091 bytes .../__pycache__/_utils.cpython-312.pyc | Bin 0 -> 19822 bytes .../_validate_call.cpython-312.pyc | Bin 0 -> 7009 bytes .../__pycache__/_validators.cpython-312.pyc | Bin 0 -> 22851 bytes .../pydantic/_internal/_config.py | 386 + .../pydantic/_internal/_core_metadata.py | 97 + .../pydantic/_internal/_core_utils.py | 174 + .../pydantic/_internal/_dataclasses.py | 315 + .../pydantic/_internal/_decorators.py | 873 + .../pydantic/_internal/_decorators_v1.py | 174 + .../_internal/_discriminated_union.py | 494 + .../pydantic/_internal/_docs_extraction.py | 113 + .../pydantic/_internal/_fields.py | 729 + .../pydantic/_internal/_forward_ref.py | 23 + .../pydantic/_internal/_generate_schema.py | 2934 +++ .../pydantic/_internal/_generics.py | 530 + .../pydantic/_internal/_git.py | 27 + .../pydantic/_internal/_import_utils.py | 20 + .../pydantic/_internal/_internal_dataclass.py | 7 + .../_internal/_known_annotated_metadata.py | 403 + .../pydantic/_internal/_mock_val_ser.py | 228 + .../pydantic/_internal/_model_construction.py | 868 + .../pydantic/_internal/_namespace_utils.py | 293 + .../pydantic/_internal/_repr.py | 124 + .../pydantic/_internal/_schema_gather.py | 212 + .../_internal/_schema_generation_shared.py | 125 + .../pydantic/_internal/_serializers.py | 53 + .../pydantic/_internal/_signature.py | 189 + .../pydantic/_internal/_typing_extra.py | 785 + .../pydantic/_internal/_utils.py | 446 + .../pydantic/_internal/_validate_call.py | 141 + .../pydantic/_internal/_validators.py | 534 + .deps_siteforge_v812/pydantic/_migration.py | 316 + .../pydantic/alias_generators.py | 62 + .deps_siteforge_v812/pydantic/aliases.py | 135 + .../pydantic/annotated_handlers.py | 122 + .../pydantic/class_validators.py | 5 + .deps_siteforge_v812/pydantic/color.py | 604 + .deps_siteforge_v812/pydantic/config.py | 1296 ++ .deps_siteforge_v812/pydantic/dataclasses.py | 413 + .../pydantic/datetime_parse.py | 5 + .deps_siteforge_v812/pydantic/decorator.py | 5 + .../pydantic/deprecated/__init__.py | 0 .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 193 bytes .../class_validators.cpython-312.pyc | Bin 0 -> 11759 bytes .../__pycache__/config.cpython-312.pyc | Bin 0 -> 4070 bytes .../copy_internals.cpython-312.pyc | Bin 0 -> 8657 bytes .../__pycache__/decorator.cpython-312.pyc | Bin 0 -> 14021 bytes .../__pycache__/json.cpython-312.pyc | Bin 0 -> 6169 bytes .../__pycache__/parse.cpython-312.pyc | Bin 0 -> 3399 bytes .../__pycache__/tools.cpython-312.pyc | Bin 0 -> 3535 bytes .../pydantic/deprecated/class_validators.py | 256 + .../pydantic/deprecated/config.py | 72 + .../pydantic/deprecated/copy_internals.py | 224 + .../pydantic/deprecated/decorator.py | 284 + .../pydantic/deprecated/json.py | 141 + .../pydantic/deprecated/parse.py | 80 + .../pydantic/deprecated/tools.py | 103 + .deps_siteforge_v812/pydantic/env_settings.py | 5 + .../pydantic/error_wrappers.py | 5 + .deps_siteforge_v812/pydantic/errors.py | 189 + .../pydantic/experimental/__init__.py | 1 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 307 bytes .../arguments_schema.cpython-312.pyc | Bin 0 -> 2399 bytes .../missing_sentinel.cpython-312.pyc | Bin 0 -> 348 bytes .../__pycache__/pipeline.cpython-312.pyc | Bin 0 -> 34685 bytes .../pydantic/experimental/arguments_schema.py | 44 + .../pydantic/experimental/missing_sentinel.py | 5 + .../pydantic/experimental/pipeline.py | 663 + .deps_siteforge_v812/pydantic/fields.py | 1892 ++ .../pydantic/functional_serializers.py | 470 + .../pydantic/functional_validators.py | 889 + .deps_siteforge_v812/pydantic/generics.py | 5 + .deps_siteforge_v812/pydantic/json.py | 5 + .deps_siteforge_v812/pydantic/json_schema.py | 2911 +++ .deps_siteforge_v812/pydantic/main.py | 1838 ++ .deps_siteforge_v812/pydantic/mypy.py | 1414 ++ .deps_siteforge_v812/pydantic/networks.py | 1332 ++ .deps_siteforge_v812/pydantic/parse.py | 5 + .../pydantic/plugin/__init__.py | 193 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 8935 bytes .../__pycache__/_loader.cpython-312.pyc | Bin 0 -> 2425 bytes .../_schema_validator.cpython-312.pyc | Bin 0 -> 7448 bytes .../pydantic/plugin/_loader.py | 58 + .../pydantic/plugin/_schema_validator.py | 152 + .deps_siteforge_v812/pydantic/py.typed | 0 .deps_siteforge_v812/pydantic/root_model.py | 157 + .deps_siteforge_v812/pydantic/schema.py | 5 + .deps_siteforge_v812/pydantic/tools.py | 5 + .deps_siteforge_v812/pydantic/type_adapter.py | 801 + .deps_siteforge_v812/pydantic/types.py | 3310 +++ .deps_siteforge_v812/pydantic/typing.py | 5 + .deps_siteforge_v812/pydantic/utils.py | 5 + .deps_siteforge_v812/pydantic/v1/__init__.py | 131 + .../v1/__pycache__/__init__.cpython-312.pyc | Bin 0 -> 2216 bytes .../_hypothesis_plugin.cpython-312.pyc | Bin 0 -> 20501 bytes .../annotated_types.cpython-312.pyc | Bin 0 -> 3880 bytes .../class_validators.cpython-312.pyc | Bin 0 -> 19640 bytes .../v1/__pycache__/color.cpython-312.pyc | Bin 0 -> 25846 bytes .../v1/__pycache__/config.cpython-312.pyc | Bin 0 -> 8395 bytes .../__pycache__/dataclasses.cpython-312.pyc | Bin 0 -> 22740 bytes .../datetime_parse.cpython-312.pyc | Bin 0 -> 10348 bytes .../v1/__pycache__/decorator.cpython-312.pyc | Bin 0 -> 13913 bytes .../__pycache__/env_settings.cpython-312.pyc | Bin 0 -> 17715 bytes .../error_wrappers.cpython-312.pyc | Bin 0 -> 8923 bytes .../v1/__pycache__/errors.cpython-312.pyc | Bin 0 -> 29598 bytes .../v1/__pycache__/fields.cpython-312.pyc | Bin 0 -> 57438 bytes .../v1/__pycache__/generics.cpython-312.pyc | Bin 0 -> 17016 bytes .../v1/__pycache__/json.cpython-312.pyc | Bin 0 -> 5206 bytes .../v1/__pycache__/main.cpython-312.pyc | Bin 0 -> 48665 bytes .../v1/__pycache__/mypy.cpython-312.pyc | Bin 0 -> 46354 bytes .../v1/__pycache__/networks.cpython-312.pyc | Bin 0 -> 29545 bytes .../v1/__pycache__/parse.cpython-312.pyc | Bin 0 -> 2743 bytes .../v1/__pycache__/schema.cpython-312.pyc | Bin 0 -> 48471 bytes .../v1/__pycache__/tools.cpython-312.pyc | Bin 0 -> 3876 bytes .../v1/__pycache__/types.cpython-312.pyc | Bin 0 -> 48483 bytes .../v1/__pycache__/typing.cpython-312.pyc | Bin 0 -> 23005 bytes .../v1/__pycache__/utils.cpython-312.pyc | Bin 0 -> 35252 bytes .../v1/__pycache__/validators.cpython-312.pyc | Bin 0 -> 30891 bytes .../v1/__pycache__/version.cpython-312.pyc | Bin 0 -> 1954 bytes .../pydantic/v1/_hypothesis_plugin.py | 391 + .../pydantic/v1/annotated_types.py | 72 + .../pydantic/v1/class_validators.py | 361 + .deps_siteforge_v812/pydantic/v1/color.py | 494 + .deps_siteforge_v812/pydantic/v1/config.py | 191 + .../pydantic/v1/dataclasses.py | 500 + .../pydantic/v1/datetime_parse.py | 248 + .deps_siteforge_v812/pydantic/v1/decorator.py | 264 + .../pydantic/v1/env_settings.py | 350 + .../pydantic/v1/error_wrappers.py | 161 + .deps_siteforge_v812/pydantic/v1/errors.py | 646 + .deps_siteforge_v812/pydantic/v1/fields.py | 1253 ++ .deps_siteforge_v812/pydantic/v1/generics.py | 400 + .deps_siteforge_v812/pydantic/v1/json.py | 112 + .deps_siteforge_v812/pydantic/v1/main.py | 1130 ++ .deps_siteforge_v812/pydantic/v1/mypy.py | 949 + .deps_siteforge_v812/pydantic/v1/networks.py | 747 + .deps_siteforge_v812/pydantic/v1/parse.py | 66 + .deps_siteforge_v812/pydantic/v1/py.typed | 0 .deps_siteforge_v812/pydantic/v1/schema.py | 1163 ++ .deps_siteforge_v812/pydantic/v1/tools.py | 92 + .deps_siteforge_v812/pydantic/v1/types.py | 1205 ++ .deps_siteforge_v812/pydantic/v1/typing.py | 627 + .deps_siteforge_v812/pydantic/v1/utils.py | 807 + .../pydantic/v1/validators.py | 768 + .deps_siteforge_v812/pydantic/v1/version.py | 38 + .../pydantic/validate_call_decorator.py | 116 + .deps_siteforge_v812/pydantic/validators.py | 5 + .deps_siteforge_v812/pydantic/version.py | 113 + .deps_siteforge_v812/pydantic/warnings.py | 122 + .../pydantic_core-2.46.5.dist-info/INSTALLER | 1 + .../pydantic_core-2.46.5.dist-info/METADATA | 173 + .../pydantic_core-2.46.5.dist-info/RECORD | 14 + .../pydantic_core-2.46.5.dist-info/REQUESTED | 0 .../pydantic_core-2.46.5.dist-info/WHEEL | 4 + .../licenses/LICENSE | 21 + .../sboms/pydantic-core.cyclonedx.json | 3961 ++++ .../pydantic_core/__init__.py | 171 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 3148 bytes .../__pycache__/core_schema.cpython-312.pyc | Bin 0 -> 156014 bytes .../_pydantic_core.cp312-win_amd64.pyd | Bin 0 -> 5157888 bytes .../pydantic_core/_pydantic_core.pyi | 1056 + .../pydantic_core/core_schema.py | 4461 ++++ .deps_siteforge_v812/pydantic_core/py.typed | 0 .../pygments-2.20.0.dist-info/INSTALLER | 1 + .../pygments-2.20.0.dist-info/METADATA | 57 + .../pygments-2.20.0.dist-info/RECORD | 687 + .../pygments-2.20.0.dist-info/REQUESTED | 0 .../pygments-2.20.0.dist-info/WHEEL | 4 + .../entry_points.txt | 2 + .../licenses/AUTHORS | 292 + .../licenses/LICENSE | 25 + .deps_siteforge_v812/pygments/__init__.py | 82 + .deps_siteforge_v812/pygments/__main__.py | 17 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 3458 bytes .../__pycache__/__main__.cpython-312.pyc | Bin 0 -> 777 bytes .../__pycache__/cmdline.cpython-312.pyc | Bin 0 -> 26461 bytes .../__pycache__/console.cpython-312.pyc | Bin 0 -> 2622 bytes .../__pycache__/filter.cpython-312.pyc | Bin 0 -> 3215 bytes .../__pycache__/formatter.cpython-312.pyc | Bin 0 -> 4690 bytes .../__pycache__/lexer.cpython-312.pyc | Bin 0 -> 38730 bytes .../__pycache__/modeline.cpython-312.pyc | Bin 0 -> 1553 bytes .../__pycache__/plugin.cpython-312.pyc | Bin 0 -> 2680 bytes .../__pycache__/regexopt.cpython-312.pyc | Bin 0 -> 4441 bytes .../__pycache__/scanner.cpython-312.pyc | Bin 0 -> 4750 bytes .../__pycache__/sphinxext.cpython-312.pyc | Bin 0 -> 12062 bytes .../__pycache__/style.cpython-312.pyc | Bin 0 -> 6675 bytes .../__pycache__/token.cpython-312.pyc | Bin 0 -> 8183 bytes .../__pycache__/unistring.cpython-312.pyc | Bin 0 -> 32966 bytes .../pygments/__pycache__/util.cpython-312.pyc | Bin 0 -> 14068 bytes .deps_siteforge_v812/pygments/cmdline.py | 668 + .deps_siteforge_v812/pygments/console.py | 70 + .deps_siteforge_v812/pygments/filter.py | 70 + .../pygments/filters/__init__.py | 942 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 37859 bytes .deps_siteforge_v812/pygments/formatter.py | 129 + .../pygments/formatters/__init__.py | 157 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 6860 bytes .../__pycache__/_mapping.cpython-312.pyc | Bin 0 -> 4206 bytes .../__pycache__/bbcode.cpython-312.pyc | Bin 0 -> 4192 bytes .../__pycache__/groff.cpython-312.pyc | Bin 0 -> 7263 bytes .../__pycache__/html.cpython-312.pyc | Bin 0 -> 41620 bytes .../__pycache__/img.cpython-312.pyc | Bin 0 -> 28571 bytes .../__pycache__/irc.cpython-312.pyc | Bin 0 -> 6013 bytes .../__pycache__/latex.cpython-312.pyc | Bin 0 -> 20071 bytes .../__pycache__/other.cpython-312.pyc | Bin 0 -> 6823 bytes .../__pycache__/pangomarkup.cpython-312.pyc | Bin 0 -> 2940 bytes .../__pycache__/rtf.cpython-312.pyc | Bin 0 -> 13731 bytes .../__pycache__/svg.cpython-312.pyc | Bin 0 -> 9097 bytes .../__pycache__/terminal.cpython-312.pyc | Bin 0 -> 5765 bytes .../__pycache__/terminal256.cpython-312.pyc | Bin 0 -> 15067 bytes .../pygments/formatters/_mapping.py | 23 + .../pygments/formatters/bbcode.py | 108 + .../pygments/formatters/groff.py | 170 + .../pygments/formatters/html.py | 997 + .../pygments/formatters/img.py | 686 + .../pygments/formatters/irc.py | 153 + .../pygments/formatters/latex.py | 518 + .../pygments/formatters/other.py | 160 + .../pygments/formatters/pangomarkup.py | 83 + .../pygments/formatters/rtf.py | 349 + .../pygments/formatters/svg.py | 185 + .../pygments/formatters/terminal.py | 127 + .../pygments/formatters/terminal256.py | 338 + .deps_siteforge_v812/pygments/lexer.py | 963 + .../pygments/lexers/__init__.py | 362 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 14567 bytes .../__pycache__/_ada_builtins.cpython-312.pyc | Bin 0 -> 1239 bytes .../__pycache__/_asy_builtins.cpython-312.pyc | Bin 0 -> 17632 bytes .../__pycache__/_cl_builtins.cpython-312.pyc | Bin 0 -> 11668 bytes .../_cocoa_builtins.cpython-312.pyc | Bin 0 -> 97530 bytes .../_csound_builtins.cpython-312.pyc | Bin 0 -> 16380 bytes .../__pycache__/_css_builtins.cpython-312.pyc | Bin 0 -> 9389 bytes .../_googlesql_builtins.cpython-312.pyc | Bin 0 -> 10827 bytes .../_julia_builtins.cpython-312.pyc | Bin 0 -> 8254 bytes .../_lasso_builtins.cpython-312.pyc | Bin 0 -> 76732 bytes .../_lilypond_builtins.cpython-312.pyc | Bin 0 -> 94213 bytes .../__pycache__/_lua_builtins.cpython-312.pyc | Bin 0 -> 8349 bytes .../_luau_builtins.cpython-312.pyc | Bin 0 -> 1053 bytes .../__pycache__/_mapping.cpython-312.pyc | Bin 0 -> 67226 bytes .../__pycache__/_mql_builtins.cpython-312.pyc | Bin 0 -> 18013 bytes .../_mysql_builtins.cpython-312.pyc | Bin 0 -> 20059 bytes .../_openedge_builtins.cpython-312.pyc | Bin 0 -> 34095 bytes .../__pycache__/_php_builtins.cpython-312.pyc | Bin 0 -> 65661 bytes .../_postgres_builtins.cpython-312.pyc | Bin 0 -> 11250 bytes .../_qlik_builtins.cpython-312.pyc | Bin 0 -> 6380 bytes .../_scheme_builtins.cpython-312.pyc | Bin 0 -> 23177 bytes .../_scilab_builtins.cpython-312.pyc | Bin 0 -> 35198 bytes .../_sourcemod_builtins.cpython-312.pyc | Bin 0 -> 21811 bytes .../__pycache__/_sql_builtins.cpython-312.pyc | Bin 0 -> 5571 bytes .../_stan_builtins.cpython-312.pyc | Bin 0 -> 12432 bytes .../_stata_builtins.cpython-312.pyc | Bin 0 -> 21240 bytes .../_tsql_builtins.cpython-312.pyc | Bin 0 -> 8872 bytes .../__pycache__/_usd_builtins.cpython-312.pyc | Bin 0 -> 1402 bytes .../_vbscript_builtins.cpython-312.pyc | Bin 0 -> 2931 bytes .../__pycache__/_vim_builtins.cpython-312.pyc | Bin 0 -> 30734 bytes .../__pycache__/actionscript.cpython-312.pyc | Bin 0 -> 11127 bytes .../lexers/__pycache__/ada.cpython-312.pyc | Bin 0 -> 5515 bytes .../lexers/__pycache__/agile.cpython-312.pyc | Bin 0 -> 1310 bytes .../__pycache__/algebra.cpython-312.pyc | Bin 0 -> 11047 bytes .../__pycache__/ambient.cpython-312.pyc | Bin 0 -> 3127 bytes .../lexers/__pycache__/amdgpu.cpython-312.pyc | Bin 0 -> 2250 bytes .../lexers/__pycache__/ampl.cpython-312.pyc | Bin 0 -> 4098 bytes .../__pycache__/apdlexer.cpython-312.pyc | Bin 0 -> 19039 bytes .../lexers/__pycache__/apl.cpython-312.pyc | Bin 0 -> 2519 bytes .../__pycache__/archetype.cpython-312.pyc | Bin 0 -> 9227 bytes .../lexers/__pycache__/arrow.cpython-312.pyc | Bin 0 -> 3569 bytes .../lexers/__pycache__/arturo.cpython-312.pyc | Bin 0 -> 9746 bytes .../lexers/__pycache__/asc.cpython-312.pyc | Bin 0 -> 2049 bytes .../lexers/__pycache__/asm.cpython-312.pyc | Bin 0 -> 36129 bytes .../lexers/__pycache__/asn1.cpython-312.pyc | Bin 0 -> 4493 bytes .../__pycache__/automation.cpython-312.pyc | Bin 0 -> 18417 bytes .../lexers/__pycache__/bare.cpython-312.pyc | Bin 0 -> 2876 bytes .../lexers/__pycache__/basic.cpython-312.pyc | Bin 0 -> 26973 bytes .../lexers/__pycache__/bdd.cpython-312.pyc | Bin 0 -> 2085 bytes .../lexers/__pycache__/berry.cpython-312.pyc | Bin 0 -> 3559 bytes .../lexers/__pycache__/bibtex.cpython-312.pyc | Bin 0 -> 5225 bytes .../__pycache__/blueprint.cpython-312.pyc | Bin 0 -> 5313 bytes .../lexers/__pycache__/boa.cpython-312.pyc | Bin 0 -> 3535 bytes .../lexers/__pycache__/bqn.cpython-312.pyc | Bin 0 -> 2549 bytes .../__pycache__/business.cpython-312.pyc | Bin 0 -> 22066 bytes .../lexers/__pycache__/c_cpp.cpython-312.pyc | Bin 0 -> 16299 bytes .../lexers/__pycache__/c_like.cpython-312.pyc | Bin 0 -> 27556 bytes .../__pycache__/capnproto.cpython-312.pyc | Bin 0 -> 2408 bytes .../lexers/__pycache__/carbon.cpython-312.pyc | Bin 0 -> 3546 bytes .../lexers/__pycache__/cddl.cpython-312.pyc | Bin 0 -> 4238 bytes .../lexers/__pycache__/chapel.cpython-312.pyc | Bin 0 -> 4272 bytes .../lexers/__pycache__/clean.cpython-312.pyc | Bin 0 -> 6081 bytes .../lexers/__pycache__/codeql.cpython-312.pyc | Bin 0 -> 2754 bytes .../lexers/__pycache__/comal.cpython-312.pyc | Bin 0 -> 3228 bytes .../__pycache__/compiled.cpython-312.pyc | Bin 0 -> 2001 bytes .../__pycache__/configs.cpython-312.pyc | Bin 0 -> 44562 bytes .../__pycache__/console.cpython-312.pyc | Bin 0 -> 4251 bytes .../lexers/__pycache__/cplint.cpython-312.pyc | Bin 0 -> 1763 bytes .../__pycache__/crystal.cpython-312.pyc | Bin 0 -> 15151 bytes .../lexers/__pycache__/csound.cpython-312.pyc | Bin 0 -> 14122 bytes .../lexers/__pycache__/css.cpython-312.pyc | Bin 0 -> 22615 bytes .../lexers/__pycache__/d.cpython-312.pyc | Bin 0 -> 8337 bytes .../lexers/__pycache__/dalvik.cpython-312.pyc | Bin 0 -> 4547 bytes .../lexers/__pycache__/data.cpython-312.pyc | Bin 0 -> 21180 bytes .../lexers/__pycache__/dax.cpython-312.pyc | Bin 0 -> 6248 bytes .../__pycache__/devicetree.cpython-312.pyc | Bin 0 -> 4579 bytes .../lexers/__pycache__/diff.cpython-312.pyc | Bin 0 -> 5683 bytes .../lexers/__pycache__/dns.cpython-312.pyc | Bin 0 -> 3782 bytes .../lexers/__pycache__/dotnet.cpython-312.pyc | Bin 0 -> 35216 bytes .../lexers/__pycache__/dsls.cpython-312.pyc | Bin 0 -> 33811 bytes .../lexers/__pycache__/dylan.cpython-312.pyc | Bin 0 -> 9728 bytes .../lexers/__pycache__/ecl.cpython-312.pyc | Bin 0 -> 5591 bytes .../lexers/__pycache__/eiffel.cpython-312.pyc | Bin 0 -> 2998 bytes .../lexers/__pycache__/elm.cpython-312.pyc | Bin 0 -> 3242 bytes .../lexers/__pycache__/elpi.cpython-312.pyc | Bin 0 -> 8203 bytes .../lexers/__pycache__/email.cpython-312.pyc | Bin 0 -> 5988 bytes .../lexers/__pycache__/erlang.cpython-312.pyc | Bin 0 -> 20529 bytes .../__pycache__/esoteric.cpython-312.pyc | Bin 0 -> 9765 bytes .../lexers/__pycache__/ezhil.cpython-312.pyc | Bin 0 -> 3864 bytes .../lexers/__pycache__/factor.cpython-312.pyc | Bin 0 -> 16931 bytes .../lexers/__pycache__/fantom.cpython-312.pyc | Bin 0 -> 8000 bytes .../lexers/__pycache__/felix.cpython-312.pyc | Bin 0 -> 8255 bytes .../lexers/__pycache__/fift.cpython-312.pyc | Bin 0 -> 1967 bytes .../__pycache__/floscript.cpython-312.pyc | Bin 0 -> 2985 bytes .../lexers/__pycache__/forth.cpython-312.pyc | Bin 0 -> 5361 bytes .../__pycache__/fortran.cpython-312.pyc | Bin 0 -> 8718 bytes .../lexers/__pycache__/foxpro.cpython-312.pyc | Bin 0 -> 20815 bytes .../__pycache__/freefem.cpython-312.pyc | Bin 0 -> 12786 bytes .../lexers/__pycache__/func.cpython-312.pyc | Bin 0 -> 3341 bytes .../__pycache__/functional.cpython-312.pyc | Bin 0 -> 1052 bytes .../__pycache__/futhark.cpython-312.pyc | Bin 0 -> 4083 bytes .../__pycache__/gcodelexer.cpython-312.pyc | Bin 0 -> 1323 bytes .../__pycache__/gdscript.cpython-312.pyc | Bin 0 -> 7217 bytes .../lexers/__pycache__/gleam.cpython-312.pyc | Bin 0 -> 2715 bytes .../lexers/__pycache__/go.cpython-312.pyc | Bin 0 -> 3390 bytes .../grammar_notation.cpython-312.pyc | Bin 0 -> 7720 bytes .../lexers/__pycache__/graph.cpython-312.pyc | Bin 0 -> 3817 bytes .../__pycache__/graphics.cpython-312.pyc | Bin 0 -> 29703 bytes .../__pycache__/graphql.cpython-312.pyc | Bin 0 -> 4450 bytes .../__pycache__/graphviz.cpython-312.pyc | Bin 0 -> 2224 bytes .../lexers/__pycache__/gsql.cpython-312.pyc | Bin 0 -> 3784 bytes .../lexers/__pycache__/hare.cpython-312.pyc | Bin 0 -> 2957 bytes .../__pycache__/haskell.cpython-312.pyc | Bin 0 -> 30545 bytes .../lexers/__pycache__/haxe.cpython-312.pyc | Bin 0 -> 22579 bytes .../lexers/__pycache__/hdl.cpython-312.pyc | Bin 0 -> 17482 bytes .../__pycache__/hexdump.cpython-312.pyc | Bin 0 -> 3658 bytes .../lexers/__pycache__/html.cpython-312.pyc | Bin 0 -> 20747 bytes .../lexers/__pycache__/idl.cpython-312.pyc | Bin 0 -> 12476 bytes .../lexers/__pycache__/igor.cpython-312.pyc | Bin 0 -> 25701 bytes .../__pycache__/inferno.cpython-312.pyc | Bin 0 -> 3254 bytes .../__pycache__/installers.cpython-312.pyc | Bin 0 -> 13818 bytes .../__pycache__/int_fiction.cpython-312.pyc | Bin 0 -> 48089 bytes .../lexers/__pycache__/iolang.cpython-312.pyc | Bin 0 -> 2218 bytes .../lexers/__pycache__/j.cpython-312.pyc | Bin 0 -> 4310 bytes .../__pycache__/javascript.cpython-312.pyc | Bin 0 -> 56985 bytes .../__pycache__/jmespath.cpython-312.pyc | Bin 0 -> 2408 bytes .../lexers/__pycache__/jslt.cpython-312.pyc | Bin 0 -> 3766 bytes .../lexers/__pycache__/json5.cpython-312.pyc | Bin 0 -> 2888 bytes .../__pycache__/jsonnet.cpython-312.pyc | Bin 0 -> 4864 bytes .../lexers/__pycache__/jsx.cpython-312.pyc | Bin 0 -> 2920 bytes .../lexers/__pycache__/julia.cpython-312.pyc | Bin 0 -> 10943 bytes .../lexers/__pycache__/jvm.cpython-312.pyc | Bin 0 -> 64101 bytes .../lexers/__pycache__/kuin.cpython-312.pyc | Bin 0 -> 9888 bytes .../lexers/__pycache__/kusto.cpython-312.pyc | Bin 0 -> 2850 bytes .../lexers/__pycache__/ldap.cpython-312.pyc | Bin 0 -> 6434 bytes .../lexers/__pycache__/lean.cpython-312.pyc | Bin 0 -> 8011 bytes .../__pycache__/lilypond.cpython-312.pyc | Bin 0 -> 8379 bytes .../lexers/__pycache__/lisp.cpython-312.pyc | Bin 0 -> 121733 bytes .../__pycache__/macaulay2.cpython-312.pyc | Bin 0 -> 23735 bytes .../lexers/__pycache__/make.cpython-312.pyc | Bin 0 -> 6672 bytes .../lexers/__pycache__/maple.cpython-312.pyc | Bin 0 -> 4986 bytes .../lexers/__pycache__/markup.cpython-312.pyc | Bin 0 -> 60589 bytes .../lexers/__pycache__/math.cpython-312.pyc | Bin 0 -> 1047 bytes .../lexers/__pycache__/matlab.cpython-312.pyc | Bin 0 -> 55741 bytes .../lexers/__pycache__/maxima.cpython-312.pyc | Bin 0 -> 3195 bytes .../lexers/__pycache__/meson.cpython-312.pyc | Bin 0 -> 3533 bytes .../lexers/__pycache__/mime.cpython-312.pyc | Bin 0 -> 10124 bytes .../__pycache__/minecraft.cpython-312.pyc | Bin 0 -> 10705 bytes .../lexers/__pycache__/mips.cpython-312.pyc | Bin 0 -> 3442 bytes .../lexers/__pycache__/ml.cpython-312.pyc | Bin 0 -> 26174 bytes .../__pycache__/modeling.cpython-312.pyc | Bin 0 -> 12116 bytes .../__pycache__/modula2.cpython-312.pyc | Bin 0 -> 26425 bytes .../lexers/__pycache__/mojo.cpython-312.pyc | Bin 0 -> 14369 bytes .../lexers/__pycache__/monte.cpython-312.pyc | Bin 0 -> 5120 bytes .../lexers/__pycache__/mosel.cpython-312.pyc | Bin 0 -> 6969 bytes .../lexers/__pycache__/ncl.cpython-312.pyc | Bin 0 -> 45921 bytes .../lexers/__pycache__/nimrod.cpython-312.pyc | Bin 0 -> 6457 bytes .../lexers/__pycache__/nit.cpython-312.pyc | Bin 0 -> 2763 bytes .../lexers/__pycache__/nix.cpython-312.pyc | Bin 0 -> 5475 bytes .../__pycache__/numbair.cpython-312.pyc | Bin 0 -> 2138 bytes .../lexers/__pycache__/oberon.cpython-312.pyc | Bin 0 -> 3758 bytes .../__pycache__/objective.cpython-312.pyc | Bin 0 -> 19471 bytes .../lexers/__pycache__/ooc.cpython-312.pyc | Bin 0 -> 3123 bytes .../__pycache__/openscad.cpython-312.pyc | Bin 0 -> 3735 bytes .../lexers/__pycache__/other.cpython-312.pyc | Bin 0 -> 2457 bytes .../__pycache__/parasail.cpython-312.pyc | Bin 0 -> 2892 bytes .../__pycache__/parsers.cpython-312.pyc | Bin 0 -> 24406 bytes .../lexers/__pycache__/pascal.cpython-312.pyc | Bin 0 -> 23943 bytes .../lexers/__pycache__/pawn.cpython-312.pyc | Bin 0 -> 7867 bytes .../lexers/__pycache__/pddl.cpython-312.pyc | Bin 0 -> 2818 bytes .../lexers/__pycache__/perl.cpython-312.pyc | Bin 0 -> 39015 bytes .../lexers/__pycache__/phix.cpython-312.pyc | Bin 0 -> 18429 bytes .../lexers/__pycache__/php.cpython-312.pyc | Bin 0 -> 14348 bytes .../__pycache__/pointless.cpython-312.pyc | Bin 0 -> 2302 bytes .../lexers/__pycache__/pony.cpython-312.pyc | Bin 0 -> 3431 bytes .../lexers/__pycache__/praat.cpython-312.pyc | Bin 0 -> 10293 bytes .../__pycache__/procfile.cpython-312.pyc | Bin 0 -> 1636 bytes .../lexers/__pycache__/prolog.cpython-312.pyc | Bin 0 -> 10538 bytes .../lexers/__pycache__/promql.cpython-312.pyc | Bin 0 -> 3341 bytes .../lexers/__pycache__/prql.cpython-312.pyc | Bin 0 -> 8389 bytes .../lexers/__pycache__/ptx.cpython-312.pyc | Bin 0 -> 3777 bytes .../lexers/__pycache__/python.cpython-312.pyc | Bin 0 -> 43263 bytes .../lexers/__pycache__/q.cpython-312.pyc | Bin 0 -> 5851 bytes .../lexers/__pycache__/qlik.cpython-312.pyc | Bin 0 -> 3517 bytes .../lexers/__pycache__/qvt.cpython-312.pyc | Bin 0 -> 5385 bytes .../lexers/__pycache__/r.cpython-312.pyc | Bin 0 -> 6090 bytes .../lexers/__pycache__/rdf.cpython-312.pyc | Bin 0 -> 12264 bytes .../lexers/__pycache__/rebol.cpython-312.pyc | Bin 0 -> 19256 bytes .../lexers/__pycache__/rego.cpython-312.pyc | Bin 0 -> 1881 bytes .../lexers/__pycache__/rell.cpython-312.pyc | Bin 0 -> 2934 bytes .../__pycache__/resource.cpython-312.pyc | Bin 0 -> 3593 bytes .../lexers/__pycache__/ride.cpython-312.pyc | Bin 0 -> 4495 bytes .../lexers/__pycache__/rita.cpython-312.pyc | Bin 0 -> 1476 bytes .../lexers/__pycache__/rnc.cpython-312.pyc | Bin 0 -> 2018 bytes .../__pycache__/roboconf.cpython-312.pyc | Bin 0 -> 2369 bytes .../robotframework.cpython-312.pyc | Bin 0 -> 29578 bytes .../lexers/__pycache__/ruby.cpython-312.pyc | Bin 0 -> 22548 bytes .../lexers/__pycache__/rust.cpython-312.pyc | Bin 0 -> 7311 bytes .../lexers/__pycache__/sas.cpython-312.pyc | Bin 0 -> 7046 bytes .../lexers/__pycache__/savi.cpython-312.pyc | Bin 0 -> 3987 bytes .../lexers/__pycache__/scdoc.cpython-312.pyc | Bin 0 -> 2825 bytes .../__pycache__/scripting.cpython-312.pyc | Bin 0 -> 72155 bytes .../lexers/__pycache__/sgf.cpython-312.pyc | Bin 0 -> 2086 bytes .../lexers/__pycache__/shell.cpython-312.pyc | Bin 0 -> 37090 bytes .../lexers/__pycache__/sieve.cpython-312.pyc | Bin 0 -> 2756 bytes .../lexers/__pycache__/slash.cpython-312.pyc | Bin 0 -> 8399 bytes .../__pycache__/smalltalk.cpython-312.pyc | Bin 0 -> 6708 bytes .../lexers/__pycache__/smithy.cpython-312.pyc | Bin 0 -> 3133 bytes .../lexers/__pycache__/smv.cpython-312.pyc | Bin 0 -> 2815 bytes .../lexers/__pycache__/snobol.cpython-312.pyc | Bin 0 -> 2511 bytes .../__pycache__/solidity.cpython-312.pyc | Bin 0 -> 3414 bytes .../lexers/__pycache__/soong.cpython-312.pyc | Bin 0 -> 2279 bytes .../lexers/__pycache__/sophia.cpython-312.pyc | Bin 0 -> 3859 bytes .../__pycache__/special.cpython-312.pyc | Bin 0 -> 5421 bytes .../lexers/__pycache__/spice.cpython-312.pyc | Bin 0 -> 3191 bytes .../lexers/__pycache__/sql.cpython-312.pyc | Bin 0 -> 40813 bytes .../__pycache__/srcinfo.cpython-312.pyc | Bin 0 -> 2014 bytes .../lexers/__pycache__/stata.cpython-312.pyc | Bin 0 -> 5166 bytes .../__pycache__/supercollider.cpython-312.pyc | Bin 0 -> 3916 bytes .../__pycache__/tablegen.cpython-312.pyc | Bin 0 -> 3356 bytes .../lexers/__pycache__/tact.cpython-312.pyc | Bin 0 -> 9043 bytes .../lexers/__pycache__/tal.cpython-312.pyc | Bin 0 -> 2980 bytes .../lexers/__pycache__/tcl.cpython-312.pyc | Bin 0 -> 5154 bytes .../lexers/__pycache__/teal.cpython-312.pyc | Bin 0 -> 3563 bytes .../__pycache__/templates.cpython-312.pyc | Bin 0 -> 83644 bytes .../__pycache__/teraterm.cpython-312.pyc | Bin 0 -> 5695 bytes .../__pycache__/testing.cpython-312.pyc | Bin 0 -> 10079 bytes .../lexers/__pycache__/text.cpython-312.pyc | Bin 0 -> 1561 bytes .../__pycache__/textedit.cpython-312.pyc | Bin 0 -> 8520 bytes .../__pycache__/textfmts.cpython-312.pyc | Bin 0 -> 15601 bytes .../__pycache__/theorem.cpython-312.pyc | Bin 0 -> 14960 bytes .../__pycache__/thingsdb.cpython-312.pyc | Bin 0 -> 5815 bytes .../lexers/__pycache__/tlb.cpython-312.pyc | Bin 0 -> 1878 bytes .../lexers/__pycache__/tls.cpython-312.pyc | Bin 0 -> 1936 bytes .../lexers/__pycache__/tnt.cpython-312.pyc | Bin 0 -> 13592 bytes .../__pycache__/trafficscript.cpython-312.pyc | Bin 0 -> 1855 bytes .../__pycache__/typoscript.cpython-312.pyc | Bin 0 -> 7371 bytes .../lexers/__pycache__/typst.cpython-312.pyc | Bin 0 -> 6916 bytes .../lexers/__pycache__/ul4.cpython-312.pyc | Bin 0 -> 8152 bytes .../lexers/__pycache__/unicon.cpython-312.pyc | Bin 0 -> 12519 bytes .../lexers/__pycache__/urbi.cpython-312.pyc | Bin 0 -> 5921 bytes .../lexers/__pycache__/usd.cpython-312.pyc | Bin 0 -> 4034 bytes .../__pycache__/varnish.cpython-312.pyc | Bin 0 -> 6956 bytes .../__pycache__/verification.cpython-312.pyc | Bin 0 -> 4040 bytes .../__pycache__/verifpal.cpython-312.pyc | Bin 0 -> 2982 bytes .../lexers/__pycache__/vip.cpython-312.pyc | Bin 0 -> 5705 bytes .../lexers/__pycache__/vyper.cpython-312.pyc | Bin 0 -> 4935 bytes .../lexers/__pycache__/web.cpython-312.pyc | Bin 0 -> 1325 bytes .../__pycache__/webassembly.cpython-312.pyc | Bin 0 -> 5831 bytes .../lexers/__pycache__/webidl.cpython-312.pyc | Bin 0 -> 8115 bytes .../__pycache__/webmisc.cpython-312.pyc | Bin 0 -> 43552 bytes .../lexers/__pycache__/wgsl.cpython-312.pyc | Bin 0 -> 10861 bytes .../lexers/__pycache__/whiley.cpython-312.pyc | Bin 0 -> 3646 bytes .../lexers/__pycache__/wowtoc.cpython-312.pyc | Bin 0 -> 3251 bytes .../lexers/__pycache__/wren.cpython-312.pyc | Bin 0 -> 3116 bytes .../lexers/__pycache__/x10.cpython-312.pyc | Bin 0 -> 2407 bytes .../lexers/__pycache__/xorg.cpython-312.pyc | Bin 0 -> 1396 bytes .../lexers/__pycache__/yang.cpython-312.pyc | Bin 0 -> 4160 bytes .../lexers/__pycache__/yara.cpython-312.pyc | Bin 0 -> 2742 bytes .../lexers/__pycache__/zig.cpython-312.pyc | Bin 0 -> 3901 bytes .../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/_googlesql_builtins.py | 918 + .../pygments/lexers/_julia_builtins.py | 411 + .../pygments/lexers/_lasso_builtins.py | 5326 +++++ .../pygments/lexers/_lilypond_builtins.py | 5184 +++++ .../pygments/lexers/_lua_builtins.py | 285 + .../pygments/lexers/_luau_builtins.py | 62 + .../pygments/lexers/_mapping.py | 603 + .../pygments/lexers/_mql_builtins.py | 1171 ++ .../pygments/lexers/_mysql_builtins.py | 1384 ++ .../pygments/lexers/_openedge_builtins.py | 2600 +++ .../pygments/lexers/_php_builtins.py | 3328 +++ .../pygments/lexers/_postgres_builtins.py | 739 + .../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/_sql_builtins.py | 106 + .../pygments/lexers/_stan_builtins.py | 761 + .../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 | 243 + .deps_siteforge_v812/pygments/lexers/ada.py | 144 + .deps_siteforge_v812/pygments/lexers/agile.py | 25 + .../pygments/lexers/algebra.py | 300 + .../pygments/lexers/ambient.py | 75 + .../pygments/lexers/amdgpu.py | 54 + .deps_siteforge_v812/pygments/lexers/ampl.py | 87 + .../pygments/lexers/apdlexer.py | 593 + .deps_siteforge_v812/pygments/lexers/apl.py | 103 + .../pygments/lexers/archetype.py | 315 + .deps_siteforge_v812/pygments/lexers/arrow.py | 116 + .../pygments/lexers/arturo.py | 249 + .deps_siteforge_v812/pygments/lexers/asc.py | 55 + .deps_siteforge_v812/pygments/lexers/asm.py | 1058 + .deps_siteforge_v812/pygments/lexers/asn1.py | 178 + .../pygments/lexers/automation.py | 379 + .deps_siteforge_v812/pygments/lexers/bare.py | 101 + .deps_siteforge_v812/pygments/lexers/basic.py | 656 + .deps_siteforge_v812/pygments/lexers/bdd.py | 57 + .deps_siteforge_v812/pygments/lexers/berry.py | 99 + .../pygments/lexers/bibtex.py | 159 + .../pygments/lexers/blueprint.py | 173 + .deps_siteforge_v812/pygments/lexers/boa.py | 97 + .deps_siteforge_v812/pygments/lexers/bqn.py | 112 + .../pygments/lexers/business.py | 625 + .deps_siteforge_v812/pygments/lexers/c_cpp.py | 415 + .../pygments/lexers/c_like.py | 738 + .../pygments/lexers/capnproto.py | 74 + .../pygments/lexers/carbon.py | 95 + .deps_siteforge_v812/pygments/lexers/cddl.py | 172 + .../pygments/lexers/chapel.py | 139 + .deps_siteforge_v812/pygments/lexers/clean.py | 180 + .../pygments/lexers/codeql.py | 80 + .deps_siteforge_v812/pygments/lexers/comal.py | 81 + .../pygments/lexers/compiled.py | 35 + .../pygments/lexers/configs.py | 1433 ++ .../pygments/lexers/console.py | 114 + .../pygments/lexers/cplint.py | 43 + .../pygments/lexers/crystal.py | 364 + .../pygments/lexers/csound.py | 466 + .deps_siteforge_v812/pygments/lexers/css.py | 632 + .deps_siteforge_v812/pygments/lexers/d.py | 259 + .../pygments/lexers/dalvik.py | 126 + .deps_siteforge_v812/pygments/lexers/data.py | 763 + .deps_siteforge_v812/pygments/lexers/dax.py | 135 + .../pygments/lexers/devicetree.py | 120 + .deps_siteforge_v812/pygments/lexers/diff.py | 169 + .deps_siteforge_v812/pygments/lexers/dns.py | 109 + .../pygments/lexers/dotnet.py | 873 + .deps_siteforge_v812/pygments/lexers/dsls.py | 970 + .deps_siteforge_v812/pygments/lexers/dylan.py | 279 + .deps_siteforge_v812/pygments/lexers/ecl.py | 144 + .../pygments/lexers/eiffel.py | 68 + .deps_siteforge_v812/pygments/lexers/elm.py | 123 + .deps_siteforge_v812/pygments/lexers/elpi.py | 201 + .deps_siteforge_v812/pygments/lexers/email.py | 132 + .../pygments/lexers/erlang.py | 526 + .../pygments/lexers/esoteric.py | 300 + .deps_siteforge_v812/pygments/lexers/ezhil.py | 76 + .../pygments/lexers/factor.py | 363 + .../pygments/lexers/fantom.py | 251 + .deps_siteforge_v812/pygments/lexers/felix.py | 275 + .deps_siteforge_v812/pygments/lexers/fift.py | 68 + .../pygments/lexers/floscript.py | 81 + .deps_siteforge_v812/pygments/lexers/forth.py | 178 + .../pygments/lexers/fortran.py | 212 + .../pygments/lexers/foxpro.py | 427 + .../pygments/lexers/freefem.py | 893 + .deps_siteforge_v812/pygments/lexers/func.py | 110 + .../pygments/lexers/functional.py | 21 + .../pygments/lexers/futhark.py | 105 + .../pygments/lexers/gcodelexer.py | 35 + .../pygments/lexers/gdscript.py | 189 + .deps_siteforge_v812/pygments/lexers/gleam.py | 74 + .deps_siteforge_v812/pygments/lexers/go.py | 97 + .../pygments/lexers/grammar_notation.py | 262 + .deps_siteforge_v812/pygments/lexers/graph.py | 108 + .../pygments/lexers/graphics.py | 794 + .../pygments/lexers/graphql.py | 176 + .../pygments/lexers/graphviz.py | 58 + .deps_siteforge_v812/pygments/lexers/gsql.py | 103 + .deps_siteforge_v812/pygments/lexers/hare.py | 73 + .../pygments/lexers/haskell.py | 867 + .deps_siteforge_v812/pygments/lexers/haxe.py | 941 + .deps_siteforge_v812/pygments/lexers/hdl.py | 466 + .../pygments/lexers/hexdump.py | 102 + .deps_siteforge_v812/pygments/lexers/html.py | 670 + .deps_siteforge_v812/pygments/lexers/idl.py | 284 + .deps_siteforge_v812/pygments/lexers/igor.py | 435 + .../pygments/lexers/inferno.py | 95 + .../pygments/lexers/installers.py | 352 + .../pygments/lexers/int_fiction.py | 1370 ++ .../pygments/lexers/iolang.py | 61 + .deps_siteforge_v812/pygments/lexers/j.py | 151 + .../pygments/lexers/javascript.py | 1591 ++ .../pygments/lexers/jmespath.py | 69 + .deps_siteforge_v812/pygments/lexers/jslt.py | 94 + .deps_siteforge_v812/pygments/lexers/json5.py | 83 + .../pygments/lexers/jsonnet.py | 169 + .deps_siteforge_v812/pygments/lexers/jsx.py | 100 + .deps_siteforge_v812/pygments/lexers/julia.py | 294 + .deps_siteforge_v812/pygments/lexers/jvm.py | 1807 ++ .deps_siteforge_v812/pygments/lexers/kuin.py | 332 + .deps_siteforge_v812/pygments/lexers/kusto.py | 93 + .deps_siteforge_v812/pygments/lexers/ldap.py | 155 + .deps_siteforge_v812/pygments/lexers/lean.py | 241 + .../pygments/lexers/lilypond.py | 225 + .deps_siteforge_v812/pygments/lexers/lisp.py | 3152 +++ .../pygments/lexers/macaulay2.py | 1847 ++ .deps_siteforge_v812/pygments/lexers/make.py | 212 + .deps_siteforge_v812/pygments/lexers/maple.py | 291 + .../pygments/lexers/markup.py | 1659 ++ .deps_siteforge_v812/pygments/lexers/math.py | 21 + .../pygments/lexers/matlab.py | 3307 +++ .../pygments/lexers/maxima.py | 84 + .deps_siteforge_v812/pygments/lexers/meson.py | 139 + .deps_siteforge_v812/pygments/lexers/mime.py | 210 + .../pygments/lexers/minecraft.py | 392 + .deps_siteforge_v812/pygments/lexers/mips.py | 130 + .deps_siteforge_v812/pygments/lexers/ml.py | 958 + .../pygments/lexers/modeling.py | 368 + .../pygments/lexers/modula2.py | 1579 ++ .deps_siteforge_v812/pygments/lexers/mojo.py | 707 + .deps_siteforge_v812/pygments/lexers/monte.py | 203 + .deps_siteforge_v812/pygments/lexers/mosel.py | 447 + .deps_siteforge_v812/pygments/lexers/ncl.py | 894 + .../pygments/lexers/nimrod.py | 199 + .deps_siteforge_v812/pygments/lexers/nit.py | 63 + .deps_siteforge_v812/pygments/lexers/nix.py | 144 + .../pygments/lexers/numbair.py | 63 + .../pygments/lexers/oberon.py | 120 + .../pygments/lexers/objective.py | 513 + .deps_siteforge_v812/pygments/lexers/ooc.py | 84 + .../pygments/lexers/openscad.py | 96 + .deps_siteforge_v812/pygments/lexers/other.py | 41 + .../pygments/lexers/parasail.py | 78 + .../pygments/lexers/parsers.py | 798 + .../pygments/lexers/pascal.py | 644 + .deps_siteforge_v812/pygments/lexers/pawn.py | 202 + .deps_siteforge_v812/pygments/lexers/pddl.py | 82 + .deps_siteforge_v812/pygments/lexers/perl.py | 733 + .deps_siteforge_v812/pygments/lexers/phix.py | 363 + .deps_siteforge_v812/pygments/lexers/php.py | 335 + .../pygments/lexers/pointless.py | 70 + .deps_siteforge_v812/pygments/lexers/pony.py | 93 + .deps_siteforge_v812/pygments/lexers/praat.py | 303 + .../pygments/lexers/procfile.py | 41 + .../pygments/lexers/prolog.py | 318 + .../pygments/lexers/promql.py | 176 + .deps_siteforge_v812/pygments/lexers/prql.py | 251 + .deps_siteforge_v812/pygments/lexers/ptx.py | 119 + .../pygments/lexers/python.py | 1204 ++ .deps_siteforge_v812/pygments/lexers/q.py | 187 + .deps_siteforge_v812/pygments/lexers/qlik.py | 117 + .deps_siteforge_v812/pygments/lexers/qvt.py | 153 + .deps_siteforge_v812/pygments/lexers/r.py | 196 + .deps_siteforge_v812/pygments/lexers/rdf.py | 468 + .deps_siteforge_v812/pygments/lexers/rebol.py | 419 + .deps_siteforge_v812/pygments/lexers/rego.py | 57 + .deps_siteforge_v812/pygments/lexers/rell.py | 68 + .../pygments/lexers/resource.py | 83 + .deps_siteforge_v812/pygments/lexers/ride.py | 138 + .deps_siteforge_v812/pygments/lexers/rita.py | 42 + .deps_siteforge_v812/pygments/lexers/rnc.py | 66 + .../pygments/lexers/roboconf.py | 81 + .../pygments/lexers/robotframework.py | 551 + .deps_siteforge_v812/pygments/lexers/ruby.py | 518 + .deps_siteforge_v812/pygments/lexers/rust.py | 222 + .deps_siteforge_v812/pygments/lexers/sas.py | 227 + .deps_siteforge_v812/pygments/lexers/savi.py | 171 + .deps_siteforge_v812/pygments/lexers/scdoc.py | 85 + .../pygments/lexers/scripting.py | 1638 ++ .deps_siteforge_v812/pygments/lexers/sgf.py | 59 + .deps_siteforge_v812/pygments/lexers/shell.py | 902 + .deps_siteforge_v812/pygments/lexers/sieve.py | 78 + .deps_siteforge_v812/pygments/lexers/slash.py | 183 + .../pygments/lexers/smalltalk.py | 194 + .../pygments/lexers/smithy.py | 77 + .deps_siteforge_v812/pygments/lexers/smv.py | 78 + .../pygments/lexers/snobol.py | 82 + .../pygments/lexers/solidity.py | 87 + .deps_siteforge_v812/pygments/lexers/soong.py | 78 + .../pygments/lexers/sophia.py | 102 + .../pygments/lexers/special.py | 122 + .deps_siteforge_v812/pygments/lexers/spice.py | 70 + .deps_siteforge_v812/pygments/lexers/sql.py | 1111 + .../pygments/lexers/srcinfo.py | 62 + .deps_siteforge_v812/pygments/lexers/stata.py | 170 + .../pygments/lexers/supercollider.py | 94 + .../pygments/lexers/tablegen.py | 177 + .deps_siteforge_v812/pygments/lexers/tact.py | 303 + .deps_siteforge_v812/pygments/lexers/tal.py | 77 + .deps_siteforge_v812/pygments/lexers/tcl.py | 148 + .deps_siteforge_v812/pygments/lexers/teal.py | 88 + .../pygments/lexers/templates.py | 2355 +++ .../pygments/lexers/teraterm.py | 335 + .../pygments/lexers/testing.py | 209 + .deps_siteforge_v812/pygments/lexers/text.py | 27 + .../pygments/lexers/textedit.py | 205 + .../pygments/lexers/textfmts.py | 436 + .../pygments/lexers/theorem.py | 410 + .../pygments/lexers/thingsdb.py | 143 + .deps_siteforge_v812/pygments/lexers/tlb.py | 59 + .deps_siteforge_v812/pygments/lexers/tls.py | 54 + .deps_siteforge_v812/pygments/lexers/tnt.py | 270 + .../pygments/lexers/trafficscript.py | 51 + .../pygments/lexers/typoscript.py | 216 + .deps_siteforge_v812/pygments/lexers/typst.py | 160 + .deps_siteforge_v812/pygments/lexers/ul4.py | 309 + .../pygments/lexers/unicon.py | 413 + .deps_siteforge_v812/pygments/lexers/urbi.py | 145 + .deps_siteforge_v812/pygments/lexers/usd.py | 85 + .../pygments/lexers/varnish.py | 189 + .../pygments/lexers/verification.py | 113 + .../pygments/lexers/verifpal.py | 65 + .deps_siteforge_v812/pygments/lexers/vip.py | 150 + .deps_siteforge_v812/pygments/lexers/vyper.py | 140 + .deps_siteforge_v812/pygments/lexers/web.py | 24 + .../pygments/lexers/webassembly.py | 119 + .../pygments/lexers/webidl.py | 298 + .../pygments/lexers/webmisc.py | 1006 + .deps_siteforge_v812/pygments/lexers/wgsl.py | 406 + .../pygments/lexers/whiley.py | 115 + .../pygments/lexers/wowtoc.py | 120 + .deps_siteforge_v812/pygments/lexers/wren.py | 98 + .deps_siteforge_v812/pygments/lexers/x10.py | 66 + .deps_siteforge_v812/pygments/lexers/xorg.py | 38 + .deps_siteforge_v812/pygments/lexers/yang.py | 103 + .deps_siteforge_v812/pygments/lexers/yara.py | 69 + .deps_siteforge_v812/pygments/lexers/zig.py | 125 + .deps_siteforge_v812/pygments/modeline.py | 43 + .deps_siteforge_v812/pygments/plugin.py | 74 + .deps_siteforge_v812/pygments/regexopt.py | 102 + .deps_siteforge_v812/pygments/scanner.py | 104 + .deps_siteforge_v812/pygments/sphinxext.py | 247 + .deps_siteforge_v812/pygments/style.py | 203 + .../pygments/styles/__init__.py | 61 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 2622 bytes .../__pycache__/_mapping.cpython-312.pyc | Bin 0 -> 3639 bytes .../styles/__pycache__/abap.cpython-312.pyc | Bin 0 -> 1077 bytes .../styles/__pycache__/algol.cpython-312.pyc | Bin 0 -> 2597 bytes .../__pycache__/algol_nu.cpython-312.pyc | Bin 0 -> 2612 bytes .../__pycache__/arduino.cpython-312.pyc | Bin 0 -> 4271 bytes .../styles/__pycache__/autumn.cpython-312.pyc | Bin 0 -> 2853 bytes .../__pycache__/borland.cpython-312.pyc | Bin 0 -> 2222 bytes .../styles/__pycache__/bw.cpython-312.pyc | Bin 0 -> 1899 bytes .../styles/__pycache__/coffee.cpython-312.pyc | Bin 0 -> 3443 bytes .../__pycache__/colorful.cpython-312.pyc | Bin 0 -> 3711 bytes .../__pycache__/default.cpython-312.pyc | Bin 0 -> 3213 bytes .../__pycache__/dracula.cpython-312.pyc | Bin 0 -> 3026 bytes .../styles/__pycache__/emacs.cpython-312.pyc | Bin 0 -> 3253 bytes .../__pycache__/friendly.cpython-312.pyc | Bin 0 -> 3349 bytes .../friendly_grayscale.cpython-312.pyc | Bin 0 -> 3559 bytes .../styles/__pycache__/fruity.cpython-312.pyc | Bin 0 -> 1928 bytes .../__pycache__/gh_dark.cpython-312.pyc | Bin 0 -> 4017 bytes .../__pycache__/gruvbox.cpython-312.pyc | Bin 0 -> 4365 bytes .../styles/__pycache__/igor.cpython-312.pyc | Bin 0 -> 1122 bytes .../styles/__pycache__/inkpot.cpython-312.pyc | Bin 0 -> 2984 bytes .../__pycache__/lightbulb.cpython-312.pyc | Bin 0 -> 4355 bytes .../__pycache__/lilypond.cpython-312.pyc | Bin 0 -> 3496 bytes .../__pycache__/lovelace.cpython-312.pyc | Bin 0 -> 4249 bytes .../styles/__pycache__/manni.cpython-312.pyc | Bin 0 -> 3484 bytes .../__pycache__/material.cpython-312.pyc | Bin 0 -> 4797 bytes .../__pycache__/monokai.cpython-312.pyc | Bin 0 -> 4778 bytes .../styles/__pycache__/murphy.cpython-312.pyc | Bin 0 -> 3669 bytes .../styles/__pycache__/native.cpython-312.pyc | Bin 0 -> 2946 bytes .../styles/__pycache__/nord.cpython-312.pyc | Bin 0 -> 5575 bytes .../__pycache__/onedark.cpython-312.pyc | Bin 0 -> 2687 bytes .../__pycache__/paraiso_dark.cpython-312.pyc | Bin 0 -> 5118 bytes .../__pycache__/paraiso_light.cpython-312.pyc | Bin 0 -> 5124 bytes .../styles/__pycache__/pastie.cpython-312.pyc | Bin 0 -> 3458 bytes .../__pycache__/perldoc.cpython-312.pyc | Bin 0 -> 3044 bytes .../__pycache__/rainbow_dash.cpython-312.pyc | Bin 0 -> 3690 bytes .../styles/__pycache__/rrt.cpython-312.pyc | Bin 0 -> 1885 bytes .../styles/__pycache__/sas.cpython-312.pyc | Bin 0 -> 1797 bytes .../__pycache__/solarized.cpython-312.pyc | Bin 0 -> 5749 bytes .../__pycache__/staroffice.cpython-312.pyc | Bin 0 -> 1103 bytes .../__pycache__/stata_dark.cpython-312.pyc | Bin 0 -> 1667 bytes .../__pycache__/stata_light.cpython-312.pyc | Bin 0 -> 1668 bytes .../styles/__pycache__/tango.cpython-312.pyc | Bin 0 -> 6121 bytes .../styles/__pycache__/trac.cpython-312.pyc | Bin 0 -> 2593 bytes .../styles/__pycache__/vim.cpython-312.pyc | Bin 0 -> 2476 bytes .../styles/__pycache__/vs.cpython-312.pyc | Bin 0 -> 1481 bytes .../styles/__pycache__/xcode.cpython-312.pyc | Bin 0 -> 1819 bytes .../__pycache__/zenburn.cpython-312.pyc | Bin 0 -> 3328 bytes .../pygments/styles/_mapping.py | 54 + .deps_siteforge_v812/pygments/styles/abap.py | 32 + .deps_siteforge_v812/pygments/styles/algol.py | 65 + .../pygments/styles/algol_nu.py | 65 + .../pygments/styles/arduino.py | 100 + .../pygments/styles/autumn.py | 67 + .../pygments/styles/borland.py | 53 + .deps_siteforge_v812/pygments/styles/bw.py | 52 + .../pygments/styles/coffee.py | 80 + .../pygments/styles/colorful.py | 83 + .../pygments/styles/default.py | 76 + .../pygments/styles/dracula.py | 90 + .deps_siteforge_v812/pygments/styles/emacs.py | 75 + .../pygments/styles/friendly.py | 76 + .../pygments/styles/friendly_grayscale.py | 80 + .../pygments/styles/fruity.py | 47 + .../pygments/styles/gh_dark.py | 113 + .../pygments/styles/gruvbox.py | 118 + .deps_siteforge_v812/pygments/styles/igor.py | 32 + .../pygments/styles/inkpot.py | 72 + .../pygments/styles/lightbulb.py | 110 + .../pygments/styles/lilypond.py | 62 + .../pygments/styles/lovelace.py | 100 + .deps_siteforge_v812/pygments/styles/manni.py | 79 + .../pygments/styles/material.py | 124 + .../pygments/styles/monokai.py | 112 + .../pygments/styles/murphy.py | 82 + .../pygments/styles/native.py | 70 + .deps_siteforge_v812/pygments/styles/nord.py | 156 + .../pygments/styles/onedark.py | 73 + .../pygments/styles/paraiso_dark.py | 124 + .../pygments/styles/paraiso_light.py | 124 + .../pygments/styles/pastie.py | 78 + .../pygments/styles/perldoc.py | 73 + .../pygments/styles/rainbow_dash.py | 95 + .deps_siteforge_v812/pygments/styles/rrt.py | 54 + .deps_siteforge_v812/pygments/styles/sas.py | 46 + .../pygments/styles/solarized.py | 144 + .../pygments/styles/staroffice.py | 31 + .../pygments/styles/stata_dark.py | 42 + .../pygments/styles/stata_light.py | 42 + .deps_siteforge_v812/pygments/styles/tango.py | 143 + .deps_siteforge_v812/pygments/styles/trac.py | 66 + .deps_siteforge_v812/pygments/styles/vim.py | 67 + .deps_siteforge_v812/pygments/styles/vs.py | 41 + .deps_siteforge_v812/pygments/styles/xcode.py | 53 + .../pygments/styles/zenburn.py | 83 + .deps_siteforge_v812/pygments/token.py | 214 + .deps_siteforge_v812/pygments/unistring.py | 153 + .deps_siteforge_v812/pygments/util.py | 324 + .../pymysql-1.1.2.dist-info/INSTALLER | 1 + .../pymysql-1.1.2.dist-info/METADATA | 131 + .../pymysql-1.1.2.dist-info/RECORD | 43 + .../pymysql-1.1.2.dist-info/REQUESTED | 0 .../pymysql-1.1.2.dist-info/WHEEL | 5 + .../pymysql-1.1.2.dist-info/licenses/LICENSE | 19 + .../pymysql-1.1.2.dist-info/top_level.txt | 1 + .deps_siteforge_v812/pymysql/__init__.py | 183 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 5089 bytes .../pymysql/__pycache__/_auth.cpython-312.pyc | Bin 0 -> 9998 bytes .../__pycache__/charset.cpython-312.pyc | Bin 0 -> 16797 bytes .../__pycache__/connections.cpython-312.pyc | Bin 0 -> 59546 bytes .../__pycache__/converters.cpython-312.pyc | Bin 0 -> 13595 bytes .../__pycache__/cursors.cpython-312.pyc | Bin 0 -> 22406 bytes .../pymysql/__pycache__/err.cpython-312.pyc | Bin 0 -> 6536 bytes .../__pycache__/optionfile.cpython-312.pyc | Bin 0 -> 1588 bytes .../__pycache__/protocol.cpython-312.pyc | Bin 0 -> 18726 bytes .../pymysql/__pycache__/times.cpython-312.pyc | Bin 0 -> 921 bytes .deps_siteforge_v812/pymysql/_auth.py | 272 + .deps_siteforge_v812/pymysql/charset.py | 217 + .deps_siteforge_v812/pymysql/connections.py | 1435 ++ .../pymysql/constants/CLIENT.py | 38 + .../pymysql/constants/COMMAND.py | 32 + .deps_siteforge_v812/pymysql/constants/CR.py | 79 + .deps_siteforge_v812/pymysql/constants/ER.py | 477 + .../pymysql/constants/FIELD_TYPE.py | 31 + .../pymysql/constants/FLAG.py | 15 + .../pymysql/constants/SERVER_STATUS.py | 10 + .../pymysql/constants/__init__.py | 0 .../__pycache__/CLIENT.cpython-312.pyc | Bin 0 -> 1035 bytes .../__pycache__/COMMAND.cpython-312.pyc | Bin 0 -> 1130 bytes .../constants/__pycache__/CR.cpython-312.pyc | Bin 0 -> 3132 bytes .../constants/__pycache__/ER.cpython-312.pyc | Bin 0 -> 17432 bytes .../__pycache__/FIELD_TYPE.cpython-312.pyc | Bin 0 -> 844 bytes .../__pycache__/FLAG.cpython-312.pyc | Bin 0 -> 536 bytes .../__pycache__/SERVER_STATUS.cpython-312.pyc | Bin 0 -> 634 bytes .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 191 bytes .deps_siteforge_v812/pymysql/converters.py | 363 + .deps_siteforge_v812/pymysql/cursors.py | 531 + .deps_siteforge_v812/pymysql/err.py | 150 + .deps_siteforge_v812/pymysql/optionfile.py | 21 + .deps_siteforge_v812/pymysql/protocol.py | 356 + .deps_siteforge_v812/pymysql/times.py | 20 + .../pytest-9.0.2.dist-info/INSTALLER | 1 + .../pytest-9.0.2.dist-info/METADATA | 212 + .../pytest-9.0.2.dist-info/RECORD | 160 + .../pytest-9.0.2.dist-info/REQUESTED | 0 .../pytest-9.0.2.dist-info/WHEEL | 5 + .../pytest-9.0.2.dist-info/entry_points.txt | 3 + .../pytest-9.0.2.dist-info/licenses/LICENSE | 21 + .../pytest-9.0.2.dist-info/top_level.txt | 3 + .deps_siteforge_v812/pytest/__init__.py | 186 + .deps_siteforge_v812/pytest/__main__.py | 9 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 4493 bytes .../__pycache__/__main__.cpython-312.pyc | Bin 0 -> 429 bytes .deps_siteforge_v812/pytest/py.typed | 0 .../INSTALLER | 1 + .../METADATA | 43 + .../python_multipart-0.0.29.dist-info/RECORD | 23 + .../REQUESTED | 0 .../python_multipart-0.0.29.dist-info/WHEEL | 4 + .../licenses/LICENSE.txt | 202 + .../python_multipart/__init__.py | 35 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 454 bytes .../__pycache__/decoders.cpython-312.pyc | Bin 0 -> 8360 bytes .../__pycache__/exceptions.cpython-312.pyc | Bin 0 -> 2081 bytes .../__pycache__/multipart.cpython-312.pyc | Bin 0 -> 68117 bytes .../python_multipart/decoders.py | 185 + .../python_multipart/exceptions.py | 34 + .../python_multipart/multipart.py | 1907 ++ .../python_multipart/py.typed | 0 .../pyyaml-6.0.3.dist-info/INSTALLER | 1 + .../pyyaml-6.0.3.dist-info/METADATA | 59 + .../pyyaml-6.0.3.dist-info/RECORD | 44 + .../pyyaml-6.0.3.dist-info/REQUESTED | 0 .../pyyaml-6.0.3.dist-info/WHEEL | 5 + .../pyyaml-6.0.3.dist-info/licenses/LICENSE | 20 + .../pyyaml-6.0.3.dist-info/top_level.txt | 2 + .../six-1.17.0.dist-info/INSTALLER | 1 + .../six-1.17.0.dist-info/LICENSE | 18 + .../six-1.17.0.dist-info/METADATA | 43 + .../six-1.17.0.dist-info/RECORD | 9 + .../six-1.17.0.dist-info/REQUESTED | 0 .../six-1.17.0.dist-info/WHEEL | 6 + .../six-1.17.0.dist-info/top_level.txt | 1 + .deps_siteforge_v812/six.py | 1003 + .../soupsieve-2.8.4.dist-info/INSTALLER | 1 + .../soupsieve-2.8.4.dist-info/METADATA | 115 + .../soupsieve-2.8.4.dist-info/RECORD | 21 + .../soupsieve-2.8.4.dist-info/REQUESTED | 0 .../soupsieve-2.8.4.dist-info/WHEEL | 4 + .../licenses/LICENSE.md | 21 + .deps_siteforge_v812/soupsieve/__init__.py | 168 + .deps_siteforge_v812/soupsieve/__meta__.py | 197 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 6108 bytes .../__pycache__/__meta__.cpython-312.pyc | Bin 0 -> 8108 bytes .../__pycache__/css_match.cpython-312.pyc | Bin 0 -> 63619 bytes .../__pycache__/css_parser.cpython-312.pyc | Bin 0 -> 53087 bytes .../__pycache__/css_types.cpython-312.pyc | Bin 0 -> 16808 bytes .../__pycache__/pretty.cpython-312.pyc | Bin 0 -> 5537 bytes .../__pycache__/util.cpython-312.pyc | Bin 0 -> 4708 bytes .deps_siteforge_v812/soupsieve/css_match.py | 1654 ++ .deps_siteforge_v812/soupsieve/css_parser.py | 1367 ++ .deps_siteforge_v812/soupsieve/css_types.py | 410 + .deps_siteforge_v812/soupsieve/pretty.py | 148 + .deps_siteforge_v812/soupsieve/py.typed | 0 .deps_siteforge_v812/soupsieve/util.py | 117 + .../starlette-0.50.0.dist-info/INSTALLER | 1 + .../starlette-0.50.0.dist-info/METADATA | 177 + .../starlette-0.50.0.dist-info/RECORD | 75 + .../starlette-0.50.0.dist-info/REQUESTED | 0 .../starlette-0.50.0.dist-info/WHEEL | 4 + .../licenses/LICENSE.md | 27 + .deps_siteforge_v812/starlette/__init__.py | 1 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 210 bytes .../_exception_handler.cpython-312.pyc | Bin 0 -> 3069 bytes .../__pycache__/_utils.cpython-312.pyc | Bin 0 -> 5116 bytes .../__pycache__/applications.cpython-312.pyc | Bin 0 -> 12880 bytes .../authentication.cpython-312.pyc | Bin 0 -> 7711 bytes .../__pycache__/background.cpython-312.pyc | Bin 0 -> 2531 bytes .../__pycache__/concurrency.cpython-312.pyc | Bin 0 -> 3171 bytes .../__pycache__/config.cpython-312.pyc | Bin 0 -> 7509 bytes .../__pycache__/convertors.cpython-312.pyc | Bin 0 -> 4762 bytes .../datastructures.cpython-312.pyc | Bin 0 -> 39496 bytes .../__pycache__/endpoints.cpython-312.pyc | Bin 0 -> 7951 bytes .../__pycache__/exceptions.cpython-312.pyc | Bin 0 -> 2394 bytes .../__pycache__/formparsers.cpython-312.pyc | Bin 0 -> 14266 bytes .../__pycache__/requests.cpython-312.pyc | Bin 0 -> 16335 bytes .../__pycache__/responses.cpython-312.pyc | Bin 0 -> 30227 bytes .../__pycache__/routing.cpython-312.pyc | Bin 0 -> 43921 bytes .../__pycache__/schemas.cpython-312.pyc | Bin 0 -> 7061 bytes .../__pycache__/staticfiles.cpython-312.pyc | Bin 0 -> 11537 bytes .../__pycache__/status.cpython-312.pyc | Bin 0 -> 5180 bytes .../__pycache__/templating.cpython-312.pyc | Bin 0 -> 9982 bytes .../__pycache__/testclient.cpython-312.pyc | Bin 0 -> 33131 bytes .../__pycache__/types.cpython-312.pyc | Bin 0 -> 1315 bytes .../__pycache__/websockets.cpython-312.pyc | Bin 0 -> 11819 bytes .../starlette/_exception_handler.py | 65 + .deps_siteforge_v812/starlette/_utils.py | 103 + .../starlette/applications.py | 244 + .../starlette/authentication.py | 142 + .deps_siteforge_v812/starlette/background.py | 36 + .deps_siteforge_v812/starlette/concurrency.py | 57 + .deps_siteforge_v812/starlette/config.py | 140 + .deps_siteforge_v812/starlette/convertors.py | 89 + .../starlette/datastructures.py | 691 + .deps_siteforge_v812/starlette/endpoints.py | 123 + .deps_siteforge_v812/starlette/exceptions.py | 33 + .deps_siteforge_v812/starlette/formparsers.py | 276 + .../starlette/middleware/__init__.py | 37 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 2534 bytes .../authentication.cpython-312.pyc | Bin 0 -> 2894 bytes .../__pycache__/base.cpython-312.pyc | Bin 0 -> 11797 bytes .../__pycache__/cors.cpython-312.pyc | Bin 0 -> 7455 bytes .../__pycache__/errors.cpython-312.pyc | Bin 0 -> 9934 bytes .../__pycache__/exceptions.cpython-312.pyc | Bin 0 -> 4100 bytes .../__pycache__/gzip.cpython-312.pyc | Bin 0 -> 8789 bytes .../__pycache__/httpsredirect.cpython-312.pyc | Bin 0 -> 1769 bytes .../__pycache__/sessions.cpython-312.pyc | Bin 0 -> 4662 bytes .../__pycache__/trustedhost.cpython-312.pyc | Bin 0 -> 3135 bytes .../__pycache__/wsgi.cpython-312.pyc | Bin 0 -> 8533 bytes .../starlette/middleware/authentication.py | 52 + .../starlette/middleware/base.py | 244 + .../starlette/middleware/cors.py | 172 + .../starlette/middleware/errors.py | 259 + .../starlette/middleware/exceptions.py | 73 + .../starlette/middleware/gzip.py | 145 + .../starlette/middleware/httpsredirect.py | 19 + .../starlette/middleware/sessions.py | 85 + .../starlette/middleware/trustedhost.py | 60 + .../starlette/middleware/wsgi.py | 153 + .deps_siteforge_v812/starlette/py.typed | 0 .deps_siteforge_v812/starlette/requests.py | 324 + .deps_siteforge_v812/starlette/responses.py | 566 + .deps_siteforge_v812/starlette/routing.py | 876 + .deps_siteforge_v812/starlette/schemas.py | 148 + .deps_siteforge_v812/starlette/staticfiles.py | 217 + .deps_siteforge_v812/starlette/status.py | 209 + .deps_siteforge_v812/starlette/templating.py | 217 + .deps_siteforge_v812/starlette/testclient.py | 739 + .deps_siteforge_v812/starlette/types.py | 26 + .deps_siteforge_v812/starlette/websockets.py | 196 + .../INSTALLER | 1 + .../METADATA | 73 + .../typing_extensions-4.16.0.dist-info/RECORD | 8 + .../REQUESTED | 0 .../typing_extensions-4.16.0.dist-info/WHEEL | 4 + .../licenses/LICENSE | 279 + .deps_siteforge_v812/typing_extensions.py | 4422 ++++ .../INSTALLER | 1 + .../METADATA | 49 + .../typing_inspection-0.4.4.dist-info/RECORD | 14 + .../REQUESTED | 0 .../typing_inspection-0.4.4.dist-info/WHEEL | 4 + .../licenses/LICENSE | 21 + .../typing_inspection/__init__.py | 0 .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 191 bytes .../__pycache__/introspection.cpython-312.pyc | Bin 0 -> 17761 bytes .../typing_objects.cpython-312.pyc | Bin 0 -> 17495 bytes .../typing_inspection/introspection.py | 587 + .../typing_inspection/py.typed | 0 .../typing_inspection/typing_objects.py | 620 + .../typing_inspection/typing_objects.pyi | 421 + .../uvicorn-0.48.0.dist-info/INSTALLER | 1 + .../uvicorn-0.48.0.dist-info/METADATA | 191 + .../uvicorn-0.48.0.dist-info/RECORD | 89 + .../uvicorn-0.48.0.dist-info/REQUESTED | 0 .../uvicorn-0.48.0.dist-info/WHEEL | 4 + .../uvicorn-0.48.0.dist-info/entry_points.txt | 2 + .../licenses/LICENSE.md | 27 + .deps_siteforge_v812/uvicorn/__init__.py | 5 + .deps_siteforge_v812/uvicorn/__main__.py | 4 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 379 bytes .../__pycache__/__main__.cpython-312.pyc | Bin 0 -> 299 bytes .../__pycache__/_compat.cpython-312.pyc | Bin 0 -> 3997 bytes .../__pycache__/_subprocess.cpython-312.pyc | Bin 0 -> 2942 bytes .../__pycache__/_types.cpython-312.pyc | Bin 0 -> 11410 bytes .../__pycache__/config.cpython-312.pyc | Bin 0 -> 28176 bytes .../__pycache__/importer.cpython-312.pyc | Bin 0 -> 1772 bytes .../__pycache__/logging.cpython-312.pyc | Bin 0 -> 7781 bytes .../uvicorn/__pycache__/main.cpython-312.pyc | Bin 0 -> 21963 bytes .../__pycache__/server.cpython-312.pyc | Bin 0 -> 17239 bytes .../__pycache__/workers.cpython-312.pyc | Bin 0 -> 6560 bytes .deps_siteforge_v812/uvicorn/_compat.py | 91 + .deps_siteforge_v812/uvicorn/_subprocess.py | 84 + .deps_siteforge_v812/uvicorn/_types.py | 274 + .deps_siteforge_v812/uvicorn/config.py | 588 + .deps_siteforge_v812/uvicorn/importer.py | 34 + .../uvicorn/lifespan/__init__.py | 0 .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 190 bytes .../lifespan/__pycache__/off.cpython-312.pyc | Bin 0 -> 979 bytes .../lifespan/__pycache__/on.cpython-312.pyc | Bin 0 -> 7939 bytes .deps_siteforge_v812/uvicorn/lifespan/off.py | 17 + .deps_siteforge_v812/uvicorn/lifespan/on.py | 137 + .deps_siteforge_v812/uvicorn/logging.py | 117 + .../uvicorn/loops/__init__.py | 0 .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 187 bytes .../loops/__pycache__/asyncio.cpython-312.pyc | Bin 0 -> 719 bytes .../loops/__pycache__/auto.cpython-312.pyc | Bin 0 -> 819 bytes .../loops/__pycache__/uvloop.cpython-312.pyc | Bin 0 -> 576 bytes .deps_siteforge_v812/uvicorn/loops/asyncio.py | 11 + .deps_siteforge_v812/uvicorn/loops/auto.py | 17 + .deps_siteforge_v812/uvicorn/loops/uvloop.py | 10 + .deps_siteforge_v812/uvicorn/main.py | 644 + .../uvicorn/middleware/__init__.py | 0 .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 192 bytes .../__pycache__/asgi2.cpython-312.pyc | Bin 0 -> 1002 bytes .../message_logger.cpython-312.pyc | Bin 0 -> 4393 bytes .../__pycache__/proxy_headers.cpython-312.pyc | Bin 0 -> 7473 bytes .../__pycache__/wsgi.cpython-312.pyc | Bin 0 -> 9933 bytes .../uvicorn/middleware/asgi2.py | 15 + .../uvicorn/middleware/message_logger.py | 87 + .../uvicorn/middleware/proxy_headers.py | 181 + .../uvicorn/middleware/wsgi.py | 199 + .../uvicorn/protocols/__init__.py | 0 .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 191 bytes .../__pycache__/utils.cpython-312.pyc | Bin 0 -> 3143 bytes .../uvicorn/protocols/http/__init__.py | 0 .../http/__pycache__/__init__.cpython-312.pyc | Bin 0 -> 196 bytes .../http/__pycache__/auto.cpython-312.pyc | Bin 0 -> 607 bytes .../__pycache__/flow_control.cpython-312.pyc | Bin 0 -> 3037 bytes .../http/__pycache__/h11_impl.cpython-312.pyc | Bin 0 -> 27673 bytes .../httptools_impl.cpython-312.pyc | Bin 0 -> 29586 bytes .../uvicorn/protocols/http/auto.py | 15 + .../uvicorn/protocols/http/flow_control.py | 54 + .../uvicorn/protocols/http/h11_impl.py | 544 + .../uvicorn/protocols/http/httptools_impl.py | 576 + .../uvicorn/protocols/utils.py | 62 + .../uvicorn/protocols/websockets/__init__.py | 0 .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 202 bytes .../__pycache__/auto.cpython-312.pyc | Bin 0 -> 811 bytes .../websockets_impl.cpython-312.pyc | Bin 0 -> 19952 bytes .../websockets_sansio_impl.cpython-312.pyc | Bin 0 -> 28859 bytes .../__pycache__/wsproto_impl.cpython-312.pyc | Bin 0 -> 27563 bytes .../uvicorn/protocols/websockets/auto.py | 21 + .../protocols/websockets/websockets_impl.py | 372 + .../websockets/websockets_sansio_impl.py | 477 + .../protocols/websockets/wsproto_impl.py | 458 + .deps_siteforge_v812/uvicorn/py.typed | 1 + .deps_siteforge_v812/uvicorn/server.py | 346 + .../uvicorn/supervisors/__init__.py | 16 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 756 bytes .../__pycache__/basereload.cpython-312.pyc | Bin 0 -> 7028 bytes .../__pycache__/multiprocess.cpython-312.pyc | Bin 0 -> 13447 bytes .../__pycache__/statreload.cpython-312.pyc | Bin 0 -> 2822 bytes .../watchfilesreload.cpython-312.pyc | Bin 0 -> 4273 bytes .../uvicorn/supervisors/basereload.py | 125 + .../uvicorn/supervisors/multiprocess.py | 223 + .../uvicorn/supervisors/statreload.py | 52 + .../uvicorn/supervisors/watchfilesreload.py | 86 + .deps_siteforge_v812/uvicorn/workers.py | 111 + .../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 + .deps_siteforge_v812/webencodings/__init__.py | 342 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 11977 bytes .../__pycache__/labels.cpython-312.pyc | Bin 0 -> 7123 bytes .../__pycache__/mklabels.cpython-312.pyc | Bin 0 -> 2674 bytes .../__pycache__/tests.cpython-312.pyc | Bin 0 -> 9225 bytes .../x_user_defined.cpython-312.pyc | Bin 0 -> 3286 bytes .deps_siteforge_v812/webencodings/labels.py | 231 + .deps_siteforge_v812/webencodings/mklabels.py | 59 + .deps_siteforge_v812/webencodings/tests.py | 153 + .../webencodings/x_user_defined.py | 325 + .deps_siteforge_v812/yaml/__init__.py | 390 + .../yaml/__pycache__/__init__.cpython-312.pyc | Bin 0 -> 15593 bytes .../yaml/__pycache__/composer.cpython-312.pyc | Bin 0 -> 6524 bytes .../__pycache__/constructor.cpython-312.pyc | Bin 0 -> 34846 bytes .../yaml/__pycache__/cyaml.cpython-312.pyc | Bin 0 -> 4625 bytes .../yaml/__pycache__/dumper.cpython-312.pyc | Bin 0 -> 2461 bytes .../yaml/__pycache__/emitter.cpython-312.pyc | Bin 0 -> 50108 bytes .../yaml/__pycache__/error.cpython-312.pyc | Bin 0 -> 4267 bytes .../yaml/__pycache__/events.cpython-312.pyc | Bin 0 -> 4715 bytes .../yaml/__pycache__/loader.cpython-312.pyc | Bin 0 -> 3523 bytes .../yaml/__pycache__/nodes.cpython-312.pyc | Bin 0 -> 2208 bytes .../yaml/__pycache__/parser.cpython-312.pyc | Bin 0 -> 24703 bytes .../yaml/__pycache__/reader.cpython-312.pyc | Bin 0 -> 8843 bytes .../__pycache__/representer.cpython-312.pyc | Bin 0 -> 16897 bytes .../yaml/__pycache__/resolver.cpython-312.pyc | Bin 0 -> 9024 bytes .../yaml/__pycache__/scanner.cpython-312.pyc | Bin 0 -> 49824 bytes .../__pycache__/serializer.cpython-312.pyc | Bin 0 -> 6178 bytes .../yaml/__pycache__/tokens.cpython-312.pyc | Bin 0 -> 5782 bytes .../yaml/_yaml.cp312-win_amd64.pyd | Bin 0 -> 253952 bytes .deps_siteforge_v812/yaml/composer.py | 139 + .deps_siteforge_v812/yaml/constructor.py | 748 + .deps_siteforge_v812/yaml/cyaml.py | 101 + .deps_siteforge_v812/yaml/dumper.py | 62 + .deps_siteforge_v812/yaml/emitter.py | 1137 ++ .deps_siteforge_v812/yaml/error.py | 75 + .deps_siteforge_v812/yaml/events.py | 86 + .deps_siteforge_v812/yaml/loader.py | 63 + .deps_siteforge_v812/yaml/nodes.py | 49 + .deps_siteforge_v812/yaml/parser.py | 589 + .deps_siteforge_v812/yaml/reader.py | 185 + .deps_siteforge_v812/yaml/representer.py | 389 + .deps_siteforge_v812/yaml/resolver.py | 227 + .deps_siteforge_v812/yaml/scanner.py | 1435 ++ .deps_siteforge_v812/yaml/serializer.py | 111 + .deps_siteforge_v812/yaml/tokens.py | 104 + .../2026-10-21-slice-build-preview-phase4.md | 2 +- pnpm-lock.yaml | 4 +- themes/pilot-minimal/package.json | 4 +- 2657 files changed, 439197 insertions(+), 4 deletions(-) create mode 100644 .deps_siteforge_v812/__pycache__/py.cpython-312.pyc create mode 100644 .deps_siteforge_v812/__pycache__/six.cpython-312.pyc create mode 100644 .deps_siteforge_v812/__pycache__/typing_extensions.cpython-312.pyc create mode 100644 .deps_siteforge_v812/_pytest/__init__.py create mode 100644 .deps_siteforge_v812/_pytest/__pycache__/__init__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/_pytest/__pycache__/_argcomplete.cpython-312.pyc create mode 100644 .deps_siteforge_v812/_pytest/__pycache__/_version.cpython-312.pyc create mode 100644 .deps_siteforge_v812/_pytest/__pycache__/cacheprovider.cpython-312.pyc create mode 100644 .deps_siteforge_v812/_pytest/__pycache__/capture.cpython-312.pyc create mode 100644 .deps_siteforge_v812/_pytest/__pycache__/compat.cpython-312.pyc create mode 100644 .deps_siteforge_v812/_pytest/__pycache__/debugging.cpython-312.pyc create mode 100644 .deps_siteforge_v812/_pytest/__pycache__/deprecated.cpython-312.pyc create mode 100644 .deps_siteforge_v812/_pytest/__pycache__/doctest.cpython-312.pyc create mode 100644 .deps_siteforge_v812/_pytest/__pycache__/faulthandler.cpython-312.pyc create mode 100644 .deps_siteforge_v812/_pytest/__pycache__/fixtures.cpython-312.pyc create mode 100644 .deps_siteforge_v812/_pytest/__pycache__/freeze_support.cpython-312.pyc create mode 100644 .deps_siteforge_v812/_pytest/__pycache__/helpconfig.cpython-312.pyc create mode 100644 .deps_siteforge_v812/_pytest/__pycache__/hookspec.cpython-312.pyc create mode 100644 .deps_siteforge_v812/_pytest/__pycache__/junitxml.cpython-312.pyc create mode 100644 .deps_siteforge_v812/_pytest/__pycache__/legacypath.cpython-312.pyc create mode 100644 .deps_siteforge_v812/_pytest/__pycache__/logging.cpython-312.pyc create mode 100644 .deps_siteforge_v812/_pytest/__pycache__/main.cpython-312.pyc create mode 100644 .deps_siteforge_v812/_pytest/__pycache__/monkeypatch.cpython-312.pyc create mode 100644 .deps_siteforge_v812/_pytest/__pycache__/nodes.cpython-312.pyc create mode 100644 .deps_siteforge_v812/_pytest/__pycache__/outcomes.cpython-312.pyc create mode 100644 .deps_siteforge_v812/_pytest/__pycache__/pastebin.cpython-312.pyc create mode 100644 .deps_siteforge_v812/_pytest/__pycache__/pathlib.cpython-312.pyc create mode 100644 .deps_siteforge_v812/_pytest/__pycache__/pytester.cpython-312.pyc create mode 100644 .deps_siteforge_v812/_pytest/__pycache__/pytester_assertions.cpython-312.pyc create mode 100644 .deps_siteforge_v812/_pytest/__pycache__/python.cpython-312.pyc create mode 100644 .deps_siteforge_v812/_pytest/__pycache__/python_api.cpython-312.pyc create mode 100644 .deps_siteforge_v812/_pytest/__pycache__/raises.cpython-312.pyc create mode 100644 .deps_siteforge_v812/_pytest/__pycache__/recwarn.cpython-312.pyc create mode 100644 .deps_siteforge_v812/_pytest/__pycache__/reports.cpython-312.pyc create mode 100644 .deps_siteforge_v812/_pytest/__pycache__/runner.cpython-312.pyc create mode 100644 .deps_siteforge_v812/_pytest/__pycache__/scope.cpython-312.pyc create mode 100644 .deps_siteforge_v812/_pytest/__pycache__/setuponly.cpython-312.pyc create mode 100644 .deps_siteforge_v812/_pytest/__pycache__/setupplan.cpython-312.pyc create mode 100644 .deps_siteforge_v812/_pytest/__pycache__/skipping.cpython-312.pyc create mode 100644 .deps_siteforge_v812/_pytest/__pycache__/stash.cpython-312.pyc create mode 100644 .deps_siteforge_v812/_pytest/__pycache__/stepwise.cpython-312.pyc create mode 100644 .deps_siteforge_v812/_pytest/__pycache__/subtests.cpython-312.pyc create mode 100644 .deps_siteforge_v812/_pytest/__pycache__/terminal.cpython-312.pyc create mode 100644 .deps_siteforge_v812/_pytest/__pycache__/terminalprogress.cpython-312.pyc create mode 100644 .deps_siteforge_v812/_pytest/__pycache__/threadexception.cpython-312.pyc create mode 100644 .deps_siteforge_v812/_pytest/__pycache__/timing.cpython-312.pyc create mode 100644 .deps_siteforge_v812/_pytest/__pycache__/tmpdir.cpython-312.pyc create mode 100644 .deps_siteforge_v812/_pytest/__pycache__/tracemalloc.cpython-312.pyc create mode 100644 .deps_siteforge_v812/_pytest/__pycache__/unittest.cpython-312.pyc create mode 100644 .deps_siteforge_v812/_pytest/__pycache__/unraisableexception.cpython-312.pyc create mode 100644 .deps_siteforge_v812/_pytest/__pycache__/warning_types.cpython-312.pyc create mode 100644 .deps_siteforge_v812/_pytest/__pycache__/warnings.cpython-312.pyc create mode 100644 .deps_siteforge_v812/_pytest/_argcomplete.py create mode 100644 .deps_siteforge_v812/_pytest/_code/__init__.py create mode 100644 .deps_siteforge_v812/_pytest/_code/__pycache__/__init__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/_pytest/_code/__pycache__/code.cpython-312.pyc create mode 100644 .deps_siteforge_v812/_pytest/_code/__pycache__/source.cpython-312.pyc create mode 100644 .deps_siteforge_v812/_pytest/_code/code.py create mode 100644 .deps_siteforge_v812/_pytest/_code/source.py create mode 100644 .deps_siteforge_v812/_pytest/_io/__init__.py create mode 100644 .deps_siteforge_v812/_pytest/_io/__pycache__/__init__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/_pytest/_io/__pycache__/pprint.cpython-312.pyc create mode 100644 .deps_siteforge_v812/_pytest/_io/__pycache__/saferepr.cpython-312.pyc create mode 100644 .deps_siteforge_v812/_pytest/_io/__pycache__/terminalwriter.cpython-312.pyc create mode 100644 .deps_siteforge_v812/_pytest/_io/__pycache__/wcwidth.cpython-312.pyc create mode 100644 .deps_siteforge_v812/_pytest/_io/pprint.py create mode 100644 .deps_siteforge_v812/_pytest/_io/saferepr.py create mode 100644 .deps_siteforge_v812/_pytest/_io/terminalwriter.py create mode 100644 .deps_siteforge_v812/_pytest/_io/wcwidth.py create mode 100644 .deps_siteforge_v812/_pytest/_py/__init__.py create mode 100644 .deps_siteforge_v812/_pytest/_py/__pycache__/__init__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/_pytest/_py/__pycache__/error.cpython-312.pyc create mode 100644 .deps_siteforge_v812/_pytest/_py/__pycache__/path.cpython-312.pyc create mode 100644 .deps_siteforge_v812/_pytest/_py/error.py create mode 100644 .deps_siteforge_v812/_pytest/_py/path.py create mode 100644 .deps_siteforge_v812/_pytest/_version.py create mode 100644 .deps_siteforge_v812/_pytest/assertion/__init__.py create mode 100644 .deps_siteforge_v812/_pytest/assertion/__pycache__/__init__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/_pytest/assertion/__pycache__/rewrite.cpython-312.pyc create mode 100644 .deps_siteforge_v812/_pytest/assertion/__pycache__/truncate.cpython-312.pyc create mode 100644 .deps_siteforge_v812/_pytest/assertion/__pycache__/util.cpython-312.pyc create mode 100644 .deps_siteforge_v812/_pytest/assertion/rewrite.py create mode 100644 .deps_siteforge_v812/_pytest/assertion/truncate.py create mode 100644 .deps_siteforge_v812/_pytest/assertion/util.py create mode 100644 .deps_siteforge_v812/_pytest/cacheprovider.py create mode 100644 .deps_siteforge_v812/_pytest/capture.py create mode 100644 .deps_siteforge_v812/_pytest/compat.py create mode 100644 .deps_siteforge_v812/_pytest/config/__init__.py create mode 100644 .deps_siteforge_v812/_pytest/config/__pycache__/__init__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/_pytest/config/__pycache__/argparsing.cpython-312.pyc create mode 100644 .deps_siteforge_v812/_pytest/config/__pycache__/compat.cpython-312.pyc create mode 100644 .deps_siteforge_v812/_pytest/config/__pycache__/exceptions.cpython-312.pyc create mode 100644 .deps_siteforge_v812/_pytest/config/__pycache__/findpaths.cpython-312.pyc create mode 100644 .deps_siteforge_v812/_pytest/config/argparsing.py create mode 100644 .deps_siteforge_v812/_pytest/config/compat.py create mode 100644 .deps_siteforge_v812/_pytest/config/exceptions.py create mode 100644 .deps_siteforge_v812/_pytest/config/findpaths.py create mode 100644 .deps_siteforge_v812/_pytest/debugging.py create mode 100644 .deps_siteforge_v812/_pytest/deprecated.py create mode 100644 .deps_siteforge_v812/_pytest/doctest.py create mode 100644 .deps_siteforge_v812/_pytest/faulthandler.py create mode 100644 .deps_siteforge_v812/_pytest/fixtures.py create mode 100644 .deps_siteforge_v812/_pytest/freeze_support.py create mode 100644 .deps_siteforge_v812/_pytest/helpconfig.py create mode 100644 .deps_siteforge_v812/_pytest/hookspec.py create mode 100644 .deps_siteforge_v812/_pytest/junitxml.py create mode 100644 .deps_siteforge_v812/_pytest/legacypath.py create mode 100644 .deps_siteforge_v812/_pytest/logging.py create mode 100644 .deps_siteforge_v812/_pytest/main.py create mode 100644 .deps_siteforge_v812/_pytest/mark/__init__.py create mode 100644 .deps_siteforge_v812/_pytest/mark/__pycache__/__init__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/_pytest/mark/__pycache__/expression.cpython-312.pyc create mode 100644 .deps_siteforge_v812/_pytest/mark/__pycache__/structures.cpython-312.pyc create mode 100644 .deps_siteforge_v812/_pytest/mark/expression.py create mode 100644 .deps_siteforge_v812/_pytest/mark/structures.py create mode 100644 .deps_siteforge_v812/_pytest/monkeypatch.py create mode 100644 .deps_siteforge_v812/_pytest/nodes.py create mode 100644 .deps_siteforge_v812/_pytest/outcomes.py create mode 100644 .deps_siteforge_v812/_pytest/pastebin.py create mode 100644 .deps_siteforge_v812/_pytest/pathlib.py create mode 100644 .deps_siteforge_v812/_pytest/py.typed create mode 100644 .deps_siteforge_v812/_pytest/pytester.py create mode 100644 .deps_siteforge_v812/_pytest/pytester_assertions.py create mode 100644 .deps_siteforge_v812/_pytest/python.py create mode 100644 .deps_siteforge_v812/_pytest/python_api.py create mode 100644 .deps_siteforge_v812/_pytest/raises.py create mode 100644 .deps_siteforge_v812/_pytest/recwarn.py create mode 100644 .deps_siteforge_v812/_pytest/reports.py create mode 100644 .deps_siteforge_v812/_pytest/runner.py create mode 100644 .deps_siteforge_v812/_pytest/scope.py create mode 100644 .deps_siteforge_v812/_pytest/setuponly.py create mode 100644 .deps_siteforge_v812/_pytest/setupplan.py create mode 100644 .deps_siteforge_v812/_pytest/skipping.py create mode 100644 .deps_siteforge_v812/_pytest/stash.py create mode 100644 .deps_siteforge_v812/_pytest/stepwise.py create mode 100644 .deps_siteforge_v812/_pytest/subtests.py create mode 100644 .deps_siteforge_v812/_pytest/terminal.py create mode 100644 .deps_siteforge_v812/_pytest/terminalprogress.py create mode 100644 .deps_siteforge_v812/_pytest/threadexception.py create mode 100644 .deps_siteforge_v812/_pytest/timing.py create mode 100644 .deps_siteforge_v812/_pytest/tmpdir.py create mode 100644 .deps_siteforge_v812/_pytest/tracemalloc.py create mode 100644 .deps_siteforge_v812/_pytest/unittest.py create mode 100644 .deps_siteforge_v812/_pytest/unraisableexception.py create mode 100644 .deps_siteforge_v812/_pytest/warning_types.py create mode 100644 .deps_siteforge_v812/_pytest/warnings.py create mode 100644 .deps_siteforge_v812/_yaml/__init__.py create mode 100644 .deps_siteforge_v812/_yaml/__pycache__/__init__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/annotated_doc-0.0.4.dist-info/INSTALLER create mode 100644 .deps_siteforge_v812/annotated_doc-0.0.4.dist-info/METADATA create mode 100644 .deps_siteforge_v812/annotated_doc-0.0.4.dist-info/RECORD create mode 100644 .deps_siteforge_v812/annotated_doc-0.0.4.dist-info/REQUESTED create mode 100644 .deps_siteforge_v812/annotated_doc-0.0.4.dist-info/WHEEL create mode 100644 .deps_siteforge_v812/annotated_doc-0.0.4.dist-info/entry_points.txt create mode 100644 .deps_siteforge_v812/annotated_doc-0.0.4.dist-info/licenses/LICENSE create mode 100644 .deps_siteforge_v812/annotated_doc/__init__.py create mode 100644 .deps_siteforge_v812/annotated_doc/__pycache__/__init__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/annotated_doc/__pycache__/main.cpython-312.pyc create mode 100644 .deps_siteforge_v812/annotated_doc/main.py create mode 100644 .deps_siteforge_v812/annotated_doc/py.typed create mode 100644 .deps_siteforge_v812/annotated_types-0.8.0.dist-info/INSTALLER create mode 100644 .deps_siteforge_v812/annotated_types-0.8.0.dist-info/METADATA create mode 100644 .deps_siteforge_v812/annotated_types-0.8.0.dist-info/RECORD create mode 100644 .deps_siteforge_v812/annotated_types-0.8.0.dist-info/REQUESTED create mode 100644 .deps_siteforge_v812/annotated_types-0.8.0.dist-info/WHEEL create mode 100644 .deps_siteforge_v812/annotated_types-0.8.0.dist-info/licenses/LICENSE create mode 100644 .deps_siteforge_v812/annotated_types/__init__.py create mode 100644 .deps_siteforge_v812/annotated_types/__pycache__/__init__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/annotated_types/__pycache__/test_cases.cpython-312.pyc create mode 100644 .deps_siteforge_v812/annotated_types/py.typed create mode 100644 .deps_siteforge_v812/annotated_types/test_cases.py create mode 100644 .deps_siteforge_v812/anyio-4.13.0.dist-info/INSTALLER create mode 100644 .deps_siteforge_v812/anyio-4.13.0.dist-info/METADATA create mode 100644 .deps_siteforge_v812/anyio-4.13.0.dist-info/RECORD create mode 100644 .deps_siteforge_v812/anyio-4.13.0.dist-info/REQUESTED create mode 100644 .deps_siteforge_v812/anyio-4.13.0.dist-info/WHEEL create mode 100644 .deps_siteforge_v812/anyio-4.13.0.dist-info/entry_points.txt create mode 100644 .deps_siteforge_v812/anyio-4.13.0.dist-info/licenses/LICENSE create mode 100644 .deps_siteforge_v812/anyio-4.13.0.dist-info/top_level.txt create mode 100644 .deps_siteforge_v812/anyio/__init__.py create mode 100644 .deps_siteforge_v812/anyio/__pycache__/__init__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/anyio/__pycache__/from_thread.cpython-312.pyc create mode 100644 .deps_siteforge_v812/anyio/__pycache__/functools.cpython-312.pyc create mode 100644 .deps_siteforge_v812/anyio/__pycache__/lowlevel.cpython-312.pyc create mode 100644 .deps_siteforge_v812/anyio/__pycache__/pytest_plugin.cpython-312.pyc create mode 100644 .deps_siteforge_v812/anyio/__pycache__/to_interpreter.cpython-312.pyc create mode 100644 .deps_siteforge_v812/anyio/__pycache__/to_process.cpython-312.pyc create mode 100644 .deps_siteforge_v812/anyio/__pycache__/to_thread.cpython-312.pyc create mode 100644 .deps_siteforge_v812/anyio/_backends/__init__.py create mode 100644 .deps_siteforge_v812/anyio/_backends/__pycache__/__init__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/anyio/_backends/__pycache__/_asyncio.cpython-312.pyc create mode 100644 .deps_siteforge_v812/anyio/_backends/__pycache__/_trio.cpython-312.pyc create mode 100644 .deps_siteforge_v812/anyio/_backends/_asyncio.py create mode 100644 .deps_siteforge_v812/anyio/_backends/_trio.py create mode 100644 .deps_siteforge_v812/anyio/_core/__init__.py create mode 100644 .deps_siteforge_v812/anyio/_core/__pycache__/__init__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/anyio/_core/__pycache__/_asyncio_selector_thread.cpython-312.pyc create mode 100644 .deps_siteforge_v812/anyio/_core/__pycache__/_contextmanagers.cpython-312.pyc create mode 100644 .deps_siteforge_v812/anyio/_core/__pycache__/_eventloop.cpython-312.pyc create mode 100644 .deps_siteforge_v812/anyio/_core/__pycache__/_exceptions.cpython-312.pyc create mode 100644 .deps_siteforge_v812/anyio/_core/__pycache__/_fileio.cpython-312.pyc create mode 100644 .deps_siteforge_v812/anyio/_core/__pycache__/_resources.cpython-312.pyc create mode 100644 .deps_siteforge_v812/anyio/_core/__pycache__/_signals.cpython-312.pyc create mode 100644 .deps_siteforge_v812/anyio/_core/__pycache__/_sockets.cpython-312.pyc create mode 100644 .deps_siteforge_v812/anyio/_core/__pycache__/_streams.cpython-312.pyc create mode 100644 .deps_siteforge_v812/anyio/_core/__pycache__/_subprocesses.cpython-312.pyc create mode 100644 .deps_siteforge_v812/anyio/_core/__pycache__/_synchronization.cpython-312.pyc create mode 100644 .deps_siteforge_v812/anyio/_core/__pycache__/_tasks.cpython-312.pyc create mode 100644 .deps_siteforge_v812/anyio/_core/__pycache__/_tempfile.cpython-312.pyc create mode 100644 .deps_siteforge_v812/anyio/_core/__pycache__/_testing.cpython-312.pyc create mode 100644 .deps_siteforge_v812/anyio/_core/__pycache__/_typedattr.cpython-312.pyc create mode 100644 .deps_siteforge_v812/anyio/_core/_asyncio_selector_thread.py create mode 100644 .deps_siteforge_v812/anyio/_core/_contextmanagers.py create mode 100644 .deps_siteforge_v812/anyio/_core/_eventloop.py create mode 100644 .deps_siteforge_v812/anyio/_core/_exceptions.py create mode 100644 .deps_siteforge_v812/anyio/_core/_fileio.py create mode 100644 .deps_siteforge_v812/anyio/_core/_resources.py create mode 100644 .deps_siteforge_v812/anyio/_core/_signals.py create mode 100644 .deps_siteforge_v812/anyio/_core/_sockets.py create mode 100644 .deps_siteforge_v812/anyio/_core/_streams.py create mode 100644 .deps_siteforge_v812/anyio/_core/_subprocesses.py create mode 100644 .deps_siteforge_v812/anyio/_core/_synchronization.py create mode 100644 .deps_siteforge_v812/anyio/_core/_tasks.py create mode 100644 .deps_siteforge_v812/anyio/_core/_tempfile.py create mode 100644 .deps_siteforge_v812/anyio/_core/_testing.py create mode 100644 .deps_siteforge_v812/anyio/_core/_typedattr.py create mode 100644 .deps_siteforge_v812/anyio/abc/__init__.py create mode 100644 .deps_siteforge_v812/anyio/abc/__pycache__/__init__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/anyio/abc/__pycache__/_eventloop.cpython-312.pyc create mode 100644 .deps_siteforge_v812/anyio/abc/__pycache__/_resources.cpython-312.pyc create mode 100644 .deps_siteforge_v812/anyio/abc/__pycache__/_sockets.cpython-312.pyc create mode 100644 .deps_siteforge_v812/anyio/abc/__pycache__/_streams.cpython-312.pyc create mode 100644 .deps_siteforge_v812/anyio/abc/__pycache__/_subprocesses.cpython-312.pyc create mode 100644 .deps_siteforge_v812/anyio/abc/__pycache__/_tasks.cpython-312.pyc create mode 100644 .deps_siteforge_v812/anyio/abc/__pycache__/_testing.cpython-312.pyc create mode 100644 .deps_siteforge_v812/anyio/abc/_eventloop.py create mode 100644 .deps_siteforge_v812/anyio/abc/_resources.py create mode 100644 .deps_siteforge_v812/anyio/abc/_sockets.py create mode 100644 .deps_siteforge_v812/anyio/abc/_streams.py create mode 100644 .deps_siteforge_v812/anyio/abc/_subprocesses.py create mode 100644 .deps_siteforge_v812/anyio/abc/_tasks.py create mode 100644 .deps_siteforge_v812/anyio/abc/_testing.py create mode 100644 .deps_siteforge_v812/anyio/from_thread.py create mode 100644 .deps_siteforge_v812/anyio/functools.py create mode 100644 .deps_siteforge_v812/anyio/lowlevel.py create mode 100644 .deps_siteforge_v812/anyio/py.typed create mode 100644 .deps_siteforge_v812/anyio/pytest_plugin.py create mode 100644 .deps_siteforge_v812/anyio/streams/__init__.py create mode 100644 .deps_siteforge_v812/anyio/streams/__pycache__/__init__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/anyio/streams/__pycache__/buffered.cpython-312.pyc create mode 100644 .deps_siteforge_v812/anyio/streams/__pycache__/file.cpython-312.pyc create mode 100644 .deps_siteforge_v812/anyio/streams/__pycache__/memory.cpython-312.pyc create mode 100644 .deps_siteforge_v812/anyio/streams/__pycache__/stapled.cpython-312.pyc create mode 100644 .deps_siteforge_v812/anyio/streams/__pycache__/text.cpython-312.pyc create mode 100644 .deps_siteforge_v812/anyio/streams/__pycache__/tls.cpython-312.pyc create mode 100644 .deps_siteforge_v812/anyio/streams/buffered.py create mode 100644 .deps_siteforge_v812/anyio/streams/file.py create mode 100644 .deps_siteforge_v812/anyio/streams/memory.py create mode 100644 .deps_siteforge_v812/anyio/streams/stapled.py create mode 100644 .deps_siteforge_v812/anyio/streams/text.py create mode 100644 .deps_siteforge_v812/anyio/streams/tls.py create mode 100644 .deps_siteforge_v812/anyio/to_interpreter.py create mode 100644 .deps_siteforge_v812/anyio/to_process.py create mode 100644 .deps_siteforge_v812/anyio/to_thread.py create mode 100644 .deps_siteforge_v812/beautifulsoup4-4.14.3.dist-info/INSTALLER create mode 100644 .deps_siteforge_v812/beautifulsoup4-4.14.3.dist-info/METADATA create mode 100644 .deps_siteforge_v812/beautifulsoup4-4.14.3.dist-info/RECORD create mode 100644 .deps_siteforge_v812/beautifulsoup4-4.14.3.dist-info/REQUESTED create mode 100644 .deps_siteforge_v812/beautifulsoup4-4.14.3.dist-info/WHEEL create mode 100644 .deps_siteforge_v812/beautifulsoup4-4.14.3.dist-info/licenses/AUTHORS create mode 100644 .deps_siteforge_v812/beautifulsoup4-4.14.3.dist-info/licenses/LICENSE create mode 100644 .deps_siteforge_v812/bin/fastapi.exe create mode 100644 .deps_siteforge_v812/bin/httpx.exe create mode 100644 .deps_siteforge_v812/bin/idna.exe create mode 100644 .deps_siteforge_v812/bin/py.test.exe create mode 100644 .deps_siteforge_v812/bin/pygmentize.exe create mode 100644 .deps_siteforge_v812/bin/pytest.exe create mode 100644 .deps_siteforge_v812/bin/uvicorn.exe create mode 100644 .deps_siteforge_v812/bleach-4.1.0.dist-info/INSTALLER create mode 100644 .deps_siteforge_v812/bleach-4.1.0.dist-info/LICENSE create mode 100644 .deps_siteforge_v812/bleach-4.1.0.dist-info/METADATA create mode 100644 .deps_siteforge_v812/bleach-4.1.0.dist-info/RECORD create mode 100644 .deps_siteforge_v812/bleach-4.1.0.dist-info/REQUESTED create mode 100644 .deps_siteforge_v812/bleach-4.1.0.dist-info/WHEEL create mode 100644 .deps_siteforge_v812/bleach-4.1.0.dist-info/top_level.txt create mode 100644 .deps_siteforge_v812/bleach/__init__.py create mode 100644 .deps_siteforge_v812/bleach/__pycache__/__init__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/bleach/__pycache__/callbacks.cpython-312.pyc create mode 100644 .deps_siteforge_v812/bleach/__pycache__/html5lib_shim.cpython-312.pyc create mode 100644 .deps_siteforge_v812/bleach/__pycache__/linkifier.cpython-312.pyc create mode 100644 .deps_siteforge_v812/bleach/__pycache__/sanitizer.cpython-312.pyc create mode 100644 .deps_siteforge_v812/bleach/__pycache__/utils.cpython-312.pyc create mode 100644 .deps_siteforge_v812/bleach/_vendor/README.rst create mode 100644 .deps_siteforge_v812/bleach/_vendor/__init__.py create mode 100644 .deps_siteforge_v812/bleach/_vendor/__pycache__/__init__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/bleach/_vendor/__pycache__/parse.cpython-312.pyc create mode 100644 .deps_siteforge_v812/bleach/_vendor/html5lib-1.1.dist-info/AUTHORS.rst create mode 100644 .deps_siteforge_v812/bleach/_vendor/html5lib-1.1.dist-info/INSTALLER create mode 100644 .deps_siteforge_v812/bleach/_vendor/html5lib-1.1.dist-info/METADATA create mode 100644 .deps_siteforge_v812/bleach/_vendor/html5lib-1.1.dist-info/RECORD create mode 100644 .deps_siteforge_v812/bleach/_vendor/html5lib-1.1.dist-info/WHEEL create mode 100644 .deps_siteforge_v812/bleach/_vendor/html5lib-1.1.dist-info/top_level.txt create mode 100644 .deps_siteforge_v812/bleach/_vendor/html5lib/__init__.py create mode 100644 .deps_siteforge_v812/bleach/_vendor/html5lib/__pycache__/__init__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/bleach/_vendor/html5lib/__pycache__/_ihatexml.cpython-312.pyc create mode 100644 .deps_siteforge_v812/bleach/_vendor/html5lib/__pycache__/_inputstream.cpython-312.pyc create mode 100644 .deps_siteforge_v812/bleach/_vendor/html5lib/__pycache__/_tokenizer.cpython-312.pyc create mode 100644 .deps_siteforge_v812/bleach/_vendor/html5lib/__pycache__/_utils.cpython-312.pyc create mode 100644 .deps_siteforge_v812/bleach/_vendor/html5lib/__pycache__/constants.cpython-312.pyc create mode 100644 .deps_siteforge_v812/bleach/_vendor/html5lib/__pycache__/html5parser.cpython-312.pyc create mode 100644 .deps_siteforge_v812/bleach/_vendor/html5lib/__pycache__/serializer.cpython-312.pyc create mode 100644 .deps_siteforge_v812/bleach/_vendor/html5lib/_ihatexml.py create mode 100644 .deps_siteforge_v812/bleach/_vendor/html5lib/_inputstream.py create mode 100644 .deps_siteforge_v812/bleach/_vendor/html5lib/_tokenizer.py create mode 100644 .deps_siteforge_v812/bleach/_vendor/html5lib/_trie/__init__.py create mode 100644 .deps_siteforge_v812/bleach/_vendor/html5lib/_trie/__pycache__/__init__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/bleach/_vendor/html5lib/_trie/__pycache__/_base.cpython-312.pyc create mode 100644 .deps_siteforge_v812/bleach/_vendor/html5lib/_trie/__pycache__/py.cpython-312.pyc create mode 100644 .deps_siteforge_v812/bleach/_vendor/html5lib/_trie/_base.py create mode 100644 .deps_siteforge_v812/bleach/_vendor/html5lib/_trie/py.py create mode 100644 .deps_siteforge_v812/bleach/_vendor/html5lib/_utils.py create mode 100644 .deps_siteforge_v812/bleach/_vendor/html5lib/constants.py create mode 100644 .deps_siteforge_v812/bleach/_vendor/html5lib/filters/__init__.py create mode 100644 .deps_siteforge_v812/bleach/_vendor/html5lib/filters/__pycache__/__init__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/bleach/_vendor/html5lib/filters/__pycache__/alphabeticalattributes.cpython-312.pyc create mode 100644 .deps_siteforge_v812/bleach/_vendor/html5lib/filters/__pycache__/base.cpython-312.pyc create mode 100644 .deps_siteforge_v812/bleach/_vendor/html5lib/filters/__pycache__/inject_meta_charset.cpython-312.pyc create mode 100644 .deps_siteforge_v812/bleach/_vendor/html5lib/filters/__pycache__/lint.cpython-312.pyc create mode 100644 .deps_siteforge_v812/bleach/_vendor/html5lib/filters/__pycache__/optionaltags.cpython-312.pyc create mode 100644 .deps_siteforge_v812/bleach/_vendor/html5lib/filters/__pycache__/sanitizer.cpython-312.pyc create mode 100644 .deps_siteforge_v812/bleach/_vendor/html5lib/filters/__pycache__/whitespace.cpython-312.pyc create mode 100644 .deps_siteforge_v812/bleach/_vendor/html5lib/filters/alphabeticalattributes.py create mode 100644 .deps_siteforge_v812/bleach/_vendor/html5lib/filters/base.py create mode 100644 .deps_siteforge_v812/bleach/_vendor/html5lib/filters/inject_meta_charset.py create mode 100644 .deps_siteforge_v812/bleach/_vendor/html5lib/filters/lint.py create mode 100644 .deps_siteforge_v812/bleach/_vendor/html5lib/filters/optionaltags.py create mode 100644 .deps_siteforge_v812/bleach/_vendor/html5lib/filters/sanitizer.py create mode 100644 .deps_siteforge_v812/bleach/_vendor/html5lib/filters/whitespace.py create mode 100644 .deps_siteforge_v812/bleach/_vendor/html5lib/html5parser.py create mode 100644 .deps_siteforge_v812/bleach/_vendor/html5lib/serializer.py create mode 100644 .deps_siteforge_v812/bleach/_vendor/html5lib/treeadapters/__init__.py create mode 100644 .deps_siteforge_v812/bleach/_vendor/html5lib/treeadapters/__pycache__/__init__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/bleach/_vendor/html5lib/treeadapters/__pycache__/genshi.cpython-312.pyc create mode 100644 .deps_siteforge_v812/bleach/_vendor/html5lib/treeadapters/__pycache__/sax.cpython-312.pyc create mode 100644 .deps_siteforge_v812/bleach/_vendor/html5lib/treeadapters/genshi.py create mode 100644 .deps_siteforge_v812/bleach/_vendor/html5lib/treeadapters/sax.py create mode 100644 .deps_siteforge_v812/bleach/_vendor/html5lib/treebuilders/__init__.py create mode 100644 .deps_siteforge_v812/bleach/_vendor/html5lib/treebuilders/__pycache__/__init__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/bleach/_vendor/html5lib/treebuilders/__pycache__/base.cpython-312.pyc create mode 100644 .deps_siteforge_v812/bleach/_vendor/html5lib/treebuilders/__pycache__/dom.cpython-312.pyc create mode 100644 .deps_siteforge_v812/bleach/_vendor/html5lib/treebuilders/__pycache__/etree.cpython-312.pyc create mode 100644 .deps_siteforge_v812/bleach/_vendor/html5lib/treebuilders/__pycache__/etree_lxml.cpython-312.pyc create mode 100644 .deps_siteforge_v812/bleach/_vendor/html5lib/treebuilders/base.py create mode 100644 .deps_siteforge_v812/bleach/_vendor/html5lib/treebuilders/dom.py create mode 100644 .deps_siteforge_v812/bleach/_vendor/html5lib/treebuilders/etree.py create mode 100644 .deps_siteforge_v812/bleach/_vendor/html5lib/treebuilders/etree_lxml.py create mode 100644 .deps_siteforge_v812/bleach/_vendor/html5lib/treewalkers/__init__.py create mode 100644 .deps_siteforge_v812/bleach/_vendor/html5lib/treewalkers/__pycache__/__init__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/bleach/_vendor/html5lib/treewalkers/__pycache__/base.cpython-312.pyc create mode 100644 .deps_siteforge_v812/bleach/_vendor/html5lib/treewalkers/__pycache__/dom.cpython-312.pyc create mode 100644 .deps_siteforge_v812/bleach/_vendor/html5lib/treewalkers/__pycache__/etree.cpython-312.pyc create mode 100644 .deps_siteforge_v812/bleach/_vendor/html5lib/treewalkers/__pycache__/etree_lxml.cpython-312.pyc create mode 100644 .deps_siteforge_v812/bleach/_vendor/html5lib/treewalkers/__pycache__/genshi.cpython-312.pyc create mode 100644 .deps_siteforge_v812/bleach/_vendor/html5lib/treewalkers/base.py create mode 100644 .deps_siteforge_v812/bleach/_vendor/html5lib/treewalkers/dom.py create mode 100644 .deps_siteforge_v812/bleach/_vendor/html5lib/treewalkers/etree.py create mode 100644 .deps_siteforge_v812/bleach/_vendor/html5lib/treewalkers/etree_lxml.py create mode 100644 .deps_siteforge_v812/bleach/_vendor/html5lib/treewalkers/genshi.py create mode 100644 .deps_siteforge_v812/bleach/_vendor/parse.py create mode 100644 .deps_siteforge_v812/bleach/_vendor/vendor.txt create mode 100644 .deps_siteforge_v812/bleach/_vendor/vendor_install.sh create mode 100644 .deps_siteforge_v812/bleach/callbacks.py create mode 100644 .deps_siteforge_v812/bleach/html5lib_shim.py create mode 100644 .deps_siteforge_v812/bleach/linkifier.py create mode 100644 .deps_siteforge_v812/bleach/sanitizer.py create mode 100644 .deps_siteforge_v812/bleach/utils.py create mode 100644 .deps_siteforge_v812/bs4/__init__.py create mode 100644 .deps_siteforge_v812/bs4/__pycache__/__init__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/bs4/__pycache__/_deprecation.cpython-312.pyc create mode 100644 .deps_siteforge_v812/bs4/__pycache__/_typing.cpython-312.pyc create mode 100644 .deps_siteforge_v812/bs4/__pycache__/_warnings.cpython-312.pyc create mode 100644 .deps_siteforge_v812/bs4/__pycache__/css.cpython-312.pyc create mode 100644 .deps_siteforge_v812/bs4/__pycache__/dammit.cpython-312.pyc create mode 100644 .deps_siteforge_v812/bs4/__pycache__/diagnose.cpython-312.pyc create mode 100644 .deps_siteforge_v812/bs4/__pycache__/element.cpython-312.pyc create mode 100644 .deps_siteforge_v812/bs4/__pycache__/exceptions.cpython-312.pyc create mode 100644 .deps_siteforge_v812/bs4/__pycache__/filter.cpython-312.pyc create mode 100644 .deps_siteforge_v812/bs4/__pycache__/formatter.cpython-312.pyc create mode 100644 .deps_siteforge_v812/bs4/_deprecation.py create mode 100644 .deps_siteforge_v812/bs4/_typing.py create mode 100644 .deps_siteforge_v812/bs4/_warnings.py create mode 100644 .deps_siteforge_v812/bs4/builder/__init__.py create mode 100644 .deps_siteforge_v812/bs4/builder/__pycache__/__init__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/bs4/builder/__pycache__/_html5lib.cpython-312.pyc create mode 100644 .deps_siteforge_v812/bs4/builder/__pycache__/_htmlparser.cpython-312.pyc create mode 100644 .deps_siteforge_v812/bs4/builder/__pycache__/_lxml.cpython-312.pyc create mode 100644 .deps_siteforge_v812/bs4/builder/_html5lib.py create mode 100644 .deps_siteforge_v812/bs4/builder/_htmlparser.py create mode 100644 .deps_siteforge_v812/bs4/builder/_lxml.py create mode 100644 .deps_siteforge_v812/bs4/css.py create mode 100644 .deps_siteforge_v812/bs4/dammit.py create mode 100644 .deps_siteforge_v812/bs4/diagnose.py create mode 100644 .deps_siteforge_v812/bs4/element.py create mode 100644 .deps_siteforge_v812/bs4/exceptions.py create mode 100644 .deps_siteforge_v812/bs4/filter.py create mode 100644 .deps_siteforge_v812/bs4/formatter.py create mode 100644 .deps_siteforge_v812/bs4/py.typed create mode 100644 .deps_siteforge_v812/certifi-2026.5.20.dist-info/INSTALLER create mode 100644 .deps_siteforge_v812/certifi-2026.5.20.dist-info/METADATA create mode 100644 .deps_siteforge_v812/certifi-2026.5.20.dist-info/RECORD create mode 100644 .deps_siteforge_v812/certifi-2026.5.20.dist-info/REQUESTED create mode 100644 .deps_siteforge_v812/certifi-2026.5.20.dist-info/WHEEL create mode 100644 .deps_siteforge_v812/certifi-2026.5.20.dist-info/licenses/LICENSE create mode 100644 .deps_siteforge_v812/certifi-2026.5.20.dist-info/top_level.txt create mode 100644 .deps_siteforge_v812/certifi/__init__.py create mode 100644 .deps_siteforge_v812/certifi/__main__.py create mode 100644 .deps_siteforge_v812/certifi/__pycache__/__init__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/certifi/__pycache__/__main__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/certifi/__pycache__/core.cpython-312.pyc create mode 100644 .deps_siteforge_v812/certifi/cacert.pem create mode 100644 .deps_siteforge_v812/certifi/core.py create mode 100644 .deps_siteforge_v812/certifi/py.typed create mode 100644 .deps_siteforge_v812/click-8.1.8.dist-info/INSTALLER create mode 100644 .deps_siteforge_v812/click-8.1.8.dist-info/LICENSE.txt create mode 100644 .deps_siteforge_v812/click-8.1.8.dist-info/METADATA create mode 100644 .deps_siteforge_v812/click-8.1.8.dist-info/RECORD create mode 100644 .deps_siteforge_v812/click-8.1.8.dist-info/REQUESTED create mode 100644 .deps_siteforge_v812/click-8.1.8.dist-info/WHEEL create mode 100644 .deps_siteforge_v812/click/__init__.py create mode 100644 .deps_siteforge_v812/click/__pycache__/__init__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/click/__pycache__/_compat.cpython-312.pyc create mode 100644 .deps_siteforge_v812/click/__pycache__/_termui_impl.cpython-312.pyc create mode 100644 .deps_siteforge_v812/click/__pycache__/_textwrap.cpython-312.pyc create mode 100644 .deps_siteforge_v812/click/__pycache__/_winconsole.cpython-312.pyc create mode 100644 .deps_siteforge_v812/click/__pycache__/core.cpython-312.pyc create mode 100644 .deps_siteforge_v812/click/__pycache__/decorators.cpython-312.pyc create mode 100644 .deps_siteforge_v812/click/__pycache__/exceptions.cpython-312.pyc create mode 100644 .deps_siteforge_v812/click/__pycache__/formatting.cpython-312.pyc create mode 100644 .deps_siteforge_v812/click/__pycache__/globals.cpython-312.pyc create mode 100644 .deps_siteforge_v812/click/__pycache__/parser.cpython-312.pyc create mode 100644 .deps_siteforge_v812/click/__pycache__/shell_completion.cpython-312.pyc create mode 100644 .deps_siteforge_v812/click/__pycache__/termui.cpython-312.pyc create mode 100644 .deps_siteforge_v812/click/__pycache__/testing.cpython-312.pyc create mode 100644 .deps_siteforge_v812/click/__pycache__/types.cpython-312.pyc create mode 100644 .deps_siteforge_v812/click/__pycache__/utils.cpython-312.pyc create mode 100644 .deps_siteforge_v812/click/_compat.py create mode 100644 .deps_siteforge_v812/click/_termui_impl.py create mode 100644 .deps_siteforge_v812/click/_textwrap.py create mode 100644 .deps_siteforge_v812/click/_winconsole.py create mode 100644 .deps_siteforge_v812/click/core.py create mode 100644 .deps_siteforge_v812/click/decorators.py create mode 100644 .deps_siteforge_v812/click/exceptions.py create mode 100644 .deps_siteforge_v812/click/formatting.py create mode 100644 .deps_siteforge_v812/click/globals.py create mode 100644 .deps_siteforge_v812/click/parser.py create mode 100644 .deps_siteforge_v812/click/py.typed create mode 100644 .deps_siteforge_v812/click/shell_completion.py create mode 100644 .deps_siteforge_v812/click/termui.py create mode 100644 .deps_siteforge_v812/click/testing.py create mode 100644 .deps_siteforge_v812/click/types.py create mode 100644 .deps_siteforge_v812/click/utils.py create mode 100644 .deps_siteforge_v812/colorama-0.4.6.dist-info/INSTALLER create mode 100644 .deps_siteforge_v812/colorama-0.4.6.dist-info/METADATA create mode 100644 .deps_siteforge_v812/colorama-0.4.6.dist-info/RECORD create mode 100644 .deps_siteforge_v812/colorama-0.4.6.dist-info/WHEEL create mode 100644 .deps_siteforge_v812/colorama-0.4.6.dist-info/licenses/LICENSE.txt create mode 100644 .deps_siteforge_v812/colorama/__init__.py create mode 100644 .deps_siteforge_v812/colorama/__pycache__/__init__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/colorama/__pycache__/ansi.cpython-312.pyc create mode 100644 .deps_siteforge_v812/colorama/__pycache__/ansitowin32.cpython-312.pyc create mode 100644 .deps_siteforge_v812/colorama/__pycache__/initialise.cpython-312.pyc create mode 100644 .deps_siteforge_v812/colorama/__pycache__/win32.cpython-312.pyc create mode 100644 .deps_siteforge_v812/colorama/__pycache__/winterm.cpython-312.pyc create mode 100644 .deps_siteforge_v812/colorama/ansi.py create mode 100644 .deps_siteforge_v812/colorama/ansitowin32.py create mode 100644 .deps_siteforge_v812/colorama/initialise.py create mode 100644 .deps_siteforge_v812/colorama/tests/__init__.py create mode 100644 .deps_siteforge_v812/colorama/tests/__pycache__/__init__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/colorama/tests/__pycache__/ansi_test.cpython-312.pyc create mode 100644 .deps_siteforge_v812/colorama/tests/__pycache__/ansitowin32_test.cpython-312.pyc create mode 100644 .deps_siteforge_v812/colorama/tests/__pycache__/initialise_test.cpython-312.pyc create mode 100644 .deps_siteforge_v812/colorama/tests/__pycache__/isatty_test.cpython-312.pyc create mode 100644 .deps_siteforge_v812/colorama/tests/__pycache__/utils.cpython-312.pyc create mode 100644 .deps_siteforge_v812/colorama/tests/__pycache__/winterm_test.cpython-312.pyc create mode 100644 .deps_siteforge_v812/colorama/tests/ansi_test.py create mode 100644 .deps_siteforge_v812/colorama/tests/ansitowin32_test.py create mode 100644 .deps_siteforge_v812/colorama/tests/initialise_test.py create mode 100644 .deps_siteforge_v812/colorama/tests/isatty_test.py create mode 100644 .deps_siteforge_v812/colorama/tests/utils.py create mode 100644 .deps_siteforge_v812/colorama/tests/winterm_test.py create mode 100644 .deps_siteforge_v812/colorama/win32.py create mode 100644 .deps_siteforge_v812/colorama/winterm.py create mode 100644 .deps_siteforge_v812/defusedxml-0.7.1.dist-info/INSTALLER create mode 100644 .deps_siteforge_v812/defusedxml-0.7.1.dist-info/LICENSE create mode 100644 .deps_siteforge_v812/defusedxml-0.7.1.dist-info/METADATA create mode 100644 .deps_siteforge_v812/defusedxml-0.7.1.dist-info/RECORD create mode 100644 .deps_siteforge_v812/defusedxml-0.7.1.dist-info/REQUESTED create mode 100644 .deps_siteforge_v812/defusedxml-0.7.1.dist-info/WHEEL create mode 100644 .deps_siteforge_v812/defusedxml-0.7.1.dist-info/top_level.txt create mode 100644 .deps_siteforge_v812/defusedxml/ElementTree.py create mode 100644 .deps_siteforge_v812/defusedxml/__init__.py create mode 100644 .deps_siteforge_v812/defusedxml/__pycache__/ElementTree.cpython-312.pyc create mode 100644 .deps_siteforge_v812/defusedxml/__pycache__/__init__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/defusedxml/__pycache__/cElementTree.cpython-312.pyc create mode 100644 .deps_siteforge_v812/defusedxml/__pycache__/common.cpython-312.pyc create mode 100644 .deps_siteforge_v812/defusedxml/__pycache__/expatbuilder.cpython-312.pyc create mode 100644 .deps_siteforge_v812/defusedxml/__pycache__/expatreader.cpython-312.pyc create mode 100644 .deps_siteforge_v812/defusedxml/__pycache__/lxml.cpython-312.pyc create mode 100644 .deps_siteforge_v812/defusedxml/__pycache__/minidom.cpython-312.pyc create mode 100644 .deps_siteforge_v812/defusedxml/__pycache__/pulldom.cpython-312.pyc create mode 100644 .deps_siteforge_v812/defusedxml/__pycache__/sax.cpython-312.pyc create mode 100644 .deps_siteforge_v812/defusedxml/__pycache__/xmlrpc.cpython-312.pyc create mode 100644 .deps_siteforge_v812/defusedxml/cElementTree.py create mode 100644 .deps_siteforge_v812/defusedxml/common.py create mode 100644 .deps_siteforge_v812/defusedxml/expatbuilder.py create mode 100644 .deps_siteforge_v812/defusedxml/expatreader.py create mode 100644 .deps_siteforge_v812/defusedxml/lxml.py create mode 100644 .deps_siteforge_v812/defusedxml/minidom.py create mode 100644 .deps_siteforge_v812/defusedxml/pulldom.py create mode 100644 .deps_siteforge_v812/defusedxml/sax.py create mode 100644 .deps_siteforge_v812/defusedxml/xmlrpc.py create mode 100644 .deps_siteforge_v812/et_xmlfile-2.0.0.dist-info/AUTHORS.txt create mode 100644 .deps_siteforge_v812/et_xmlfile-2.0.0.dist-info/INSTALLER create mode 100644 .deps_siteforge_v812/et_xmlfile-2.0.0.dist-info/LICENCE.python create mode 100644 .deps_siteforge_v812/et_xmlfile-2.0.0.dist-info/LICENCE.rst create mode 100644 .deps_siteforge_v812/et_xmlfile-2.0.0.dist-info/METADATA create mode 100644 .deps_siteforge_v812/et_xmlfile-2.0.0.dist-info/RECORD create mode 100644 .deps_siteforge_v812/et_xmlfile-2.0.0.dist-info/REQUESTED create mode 100644 .deps_siteforge_v812/et_xmlfile-2.0.0.dist-info/WHEEL create mode 100644 .deps_siteforge_v812/et_xmlfile-2.0.0.dist-info/top_level.txt create mode 100644 .deps_siteforge_v812/et_xmlfile/__init__.py create mode 100644 .deps_siteforge_v812/et_xmlfile/__pycache__/__init__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/et_xmlfile/__pycache__/incremental_tree.cpython-312.pyc create mode 100644 .deps_siteforge_v812/et_xmlfile/__pycache__/xmlfile.cpython-312.pyc create mode 100644 .deps_siteforge_v812/et_xmlfile/incremental_tree.py create mode 100644 .deps_siteforge_v812/et_xmlfile/xmlfile.py create mode 100644 .deps_siteforge_v812/fastapi-0.128.2.dist-info/INSTALLER create mode 100644 .deps_siteforge_v812/fastapi-0.128.2.dist-info/METADATA create mode 100644 .deps_siteforge_v812/fastapi-0.128.2.dist-info/RECORD create mode 100644 .deps_siteforge_v812/fastapi-0.128.2.dist-info/REQUESTED create mode 100644 .deps_siteforge_v812/fastapi-0.128.2.dist-info/WHEEL create mode 100644 .deps_siteforge_v812/fastapi-0.128.2.dist-info/entry_points.txt create mode 100644 .deps_siteforge_v812/fastapi-0.128.2.dist-info/licenses/LICENSE create mode 100644 .deps_siteforge_v812/fastapi/__init__.py create mode 100644 .deps_siteforge_v812/fastapi/__main__.py create mode 100644 .deps_siteforge_v812/fastapi/__pycache__/__init__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/fastapi/__pycache__/__main__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/fastapi/__pycache__/applications.cpython-312.pyc create mode 100644 .deps_siteforge_v812/fastapi/__pycache__/background.cpython-312.pyc create mode 100644 .deps_siteforge_v812/fastapi/__pycache__/cli.cpython-312.pyc create mode 100644 .deps_siteforge_v812/fastapi/__pycache__/concurrency.cpython-312.pyc create mode 100644 .deps_siteforge_v812/fastapi/__pycache__/datastructures.cpython-312.pyc create mode 100644 .deps_siteforge_v812/fastapi/__pycache__/encoders.cpython-312.pyc create mode 100644 .deps_siteforge_v812/fastapi/__pycache__/exception_handlers.cpython-312.pyc create mode 100644 .deps_siteforge_v812/fastapi/__pycache__/exceptions.cpython-312.pyc create mode 100644 .deps_siteforge_v812/fastapi/__pycache__/logger.cpython-312.pyc create mode 100644 .deps_siteforge_v812/fastapi/__pycache__/param_functions.cpython-312.pyc create mode 100644 .deps_siteforge_v812/fastapi/__pycache__/params.cpython-312.pyc create mode 100644 .deps_siteforge_v812/fastapi/__pycache__/requests.cpython-312.pyc create mode 100644 .deps_siteforge_v812/fastapi/__pycache__/responses.cpython-312.pyc create mode 100644 .deps_siteforge_v812/fastapi/__pycache__/routing.cpython-312.pyc create mode 100644 .deps_siteforge_v812/fastapi/__pycache__/staticfiles.cpython-312.pyc create mode 100644 .deps_siteforge_v812/fastapi/__pycache__/templating.cpython-312.pyc create mode 100644 .deps_siteforge_v812/fastapi/__pycache__/testclient.cpython-312.pyc create mode 100644 .deps_siteforge_v812/fastapi/__pycache__/types.cpython-312.pyc create mode 100644 .deps_siteforge_v812/fastapi/__pycache__/utils.cpython-312.pyc create mode 100644 .deps_siteforge_v812/fastapi/__pycache__/websockets.cpython-312.pyc create mode 100644 .deps_siteforge_v812/fastapi/_compat/__init__.py create mode 100644 .deps_siteforge_v812/fastapi/_compat/__pycache__/__init__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/fastapi/_compat/__pycache__/shared.cpython-312.pyc create mode 100644 .deps_siteforge_v812/fastapi/_compat/__pycache__/v2.cpython-312.pyc create mode 100644 .deps_siteforge_v812/fastapi/_compat/shared.py create mode 100644 .deps_siteforge_v812/fastapi/_compat/v2.py create mode 100644 .deps_siteforge_v812/fastapi/applications.py create mode 100644 .deps_siteforge_v812/fastapi/background.py create mode 100644 .deps_siteforge_v812/fastapi/cli.py create mode 100644 .deps_siteforge_v812/fastapi/concurrency.py create mode 100644 .deps_siteforge_v812/fastapi/datastructures.py create mode 100644 .deps_siteforge_v812/fastapi/dependencies/__init__.py create mode 100644 .deps_siteforge_v812/fastapi/dependencies/__pycache__/__init__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/fastapi/dependencies/__pycache__/models.cpython-312.pyc create mode 100644 .deps_siteforge_v812/fastapi/dependencies/__pycache__/utils.cpython-312.pyc create mode 100644 .deps_siteforge_v812/fastapi/dependencies/models.py create mode 100644 .deps_siteforge_v812/fastapi/dependencies/utils.py create mode 100644 .deps_siteforge_v812/fastapi/encoders.py create mode 100644 .deps_siteforge_v812/fastapi/exception_handlers.py create mode 100644 .deps_siteforge_v812/fastapi/exceptions.py create mode 100644 .deps_siteforge_v812/fastapi/logger.py create mode 100644 .deps_siteforge_v812/fastapi/middleware/__init__.py create mode 100644 .deps_siteforge_v812/fastapi/middleware/__pycache__/__init__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/fastapi/middleware/__pycache__/asyncexitstack.cpython-312.pyc create mode 100644 .deps_siteforge_v812/fastapi/middleware/__pycache__/cors.cpython-312.pyc create mode 100644 .deps_siteforge_v812/fastapi/middleware/__pycache__/gzip.cpython-312.pyc create mode 100644 .deps_siteforge_v812/fastapi/middleware/__pycache__/httpsredirect.cpython-312.pyc create mode 100644 .deps_siteforge_v812/fastapi/middleware/__pycache__/trustedhost.cpython-312.pyc create mode 100644 .deps_siteforge_v812/fastapi/middleware/__pycache__/wsgi.cpython-312.pyc create mode 100644 .deps_siteforge_v812/fastapi/middleware/asyncexitstack.py create mode 100644 .deps_siteforge_v812/fastapi/middleware/cors.py create mode 100644 .deps_siteforge_v812/fastapi/middleware/gzip.py create mode 100644 .deps_siteforge_v812/fastapi/middleware/httpsredirect.py create mode 100644 .deps_siteforge_v812/fastapi/middleware/trustedhost.py create mode 100644 .deps_siteforge_v812/fastapi/middleware/wsgi.py create mode 100644 .deps_siteforge_v812/fastapi/openapi/__init__.py create mode 100644 .deps_siteforge_v812/fastapi/openapi/__pycache__/__init__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/fastapi/openapi/__pycache__/constants.cpython-312.pyc create mode 100644 .deps_siteforge_v812/fastapi/openapi/__pycache__/docs.cpython-312.pyc create mode 100644 .deps_siteforge_v812/fastapi/openapi/__pycache__/models.cpython-312.pyc create mode 100644 .deps_siteforge_v812/fastapi/openapi/__pycache__/utils.cpython-312.pyc create mode 100644 .deps_siteforge_v812/fastapi/openapi/constants.py create mode 100644 .deps_siteforge_v812/fastapi/openapi/docs.py create mode 100644 .deps_siteforge_v812/fastapi/openapi/models.py create mode 100644 .deps_siteforge_v812/fastapi/openapi/utils.py create mode 100644 .deps_siteforge_v812/fastapi/param_functions.py create mode 100644 .deps_siteforge_v812/fastapi/params.py create mode 100644 .deps_siteforge_v812/fastapi/py.typed create mode 100644 .deps_siteforge_v812/fastapi/requests.py create mode 100644 .deps_siteforge_v812/fastapi/responses.py create mode 100644 .deps_siteforge_v812/fastapi/routing.py create mode 100644 .deps_siteforge_v812/fastapi/security/__init__.py create mode 100644 .deps_siteforge_v812/fastapi/security/__pycache__/__init__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/fastapi/security/__pycache__/api_key.cpython-312.pyc create mode 100644 .deps_siteforge_v812/fastapi/security/__pycache__/base.cpython-312.pyc create mode 100644 .deps_siteforge_v812/fastapi/security/__pycache__/http.cpython-312.pyc create mode 100644 .deps_siteforge_v812/fastapi/security/__pycache__/oauth2.cpython-312.pyc create mode 100644 .deps_siteforge_v812/fastapi/security/__pycache__/open_id_connect_url.cpython-312.pyc create mode 100644 .deps_siteforge_v812/fastapi/security/__pycache__/utils.cpython-312.pyc create mode 100644 .deps_siteforge_v812/fastapi/security/api_key.py create mode 100644 .deps_siteforge_v812/fastapi/security/base.py create mode 100644 .deps_siteforge_v812/fastapi/security/http.py create mode 100644 .deps_siteforge_v812/fastapi/security/oauth2.py create mode 100644 .deps_siteforge_v812/fastapi/security/open_id_connect_url.py create mode 100644 .deps_siteforge_v812/fastapi/security/utils.py create mode 100644 .deps_siteforge_v812/fastapi/staticfiles.py create mode 100644 .deps_siteforge_v812/fastapi/templating.py create mode 100644 .deps_siteforge_v812/fastapi/testclient.py create mode 100644 .deps_siteforge_v812/fastapi/types.py create mode 100644 .deps_siteforge_v812/fastapi/utils.py create mode 100644 .deps_siteforge_v812/fastapi/websockets.py create mode 100644 .deps_siteforge_v812/h11-0.16.0.dist-info/INSTALLER create mode 100644 .deps_siteforge_v812/h11-0.16.0.dist-info/METADATA create mode 100644 .deps_siteforge_v812/h11-0.16.0.dist-info/RECORD create mode 100644 .deps_siteforge_v812/h11-0.16.0.dist-info/REQUESTED create mode 100644 .deps_siteforge_v812/h11-0.16.0.dist-info/WHEEL create mode 100644 .deps_siteforge_v812/h11-0.16.0.dist-info/licenses/LICENSE.txt create mode 100644 .deps_siteforge_v812/h11-0.16.0.dist-info/top_level.txt create mode 100644 .deps_siteforge_v812/h11/__init__.py create mode 100644 .deps_siteforge_v812/h11/__pycache__/__init__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/h11/__pycache__/_abnf.cpython-312.pyc create mode 100644 .deps_siteforge_v812/h11/__pycache__/_connection.cpython-312.pyc create mode 100644 .deps_siteforge_v812/h11/__pycache__/_events.cpython-312.pyc create mode 100644 .deps_siteforge_v812/h11/__pycache__/_headers.cpython-312.pyc create mode 100644 .deps_siteforge_v812/h11/__pycache__/_readers.cpython-312.pyc create mode 100644 .deps_siteforge_v812/h11/__pycache__/_receivebuffer.cpython-312.pyc create mode 100644 .deps_siteforge_v812/h11/__pycache__/_state.cpython-312.pyc create mode 100644 .deps_siteforge_v812/h11/__pycache__/_util.cpython-312.pyc create mode 100644 .deps_siteforge_v812/h11/__pycache__/_version.cpython-312.pyc create mode 100644 .deps_siteforge_v812/h11/__pycache__/_writers.cpython-312.pyc create mode 100644 .deps_siteforge_v812/h11/_abnf.py create mode 100644 .deps_siteforge_v812/h11/_connection.py create mode 100644 .deps_siteforge_v812/h11/_events.py create mode 100644 .deps_siteforge_v812/h11/_headers.py create mode 100644 .deps_siteforge_v812/h11/_readers.py create mode 100644 .deps_siteforge_v812/h11/_receivebuffer.py create mode 100644 .deps_siteforge_v812/h11/_state.py create mode 100644 .deps_siteforge_v812/h11/_util.py create mode 100644 .deps_siteforge_v812/h11/_version.py create mode 100644 .deps_siteforge_v812/h11/_writers.py create mode 100644 .deps_siteforge_v812/h11/py.typed create mode 100644 .deps_siteforge_v812/httpcore-1.0.9.dist-info/INSTALLER create mode 100644 .deps_siteforge_v812/httpcore-1.0.9.dist-info/METADATA create mode 100644 .deps_siteforge_v812/httpcore-1.0.9.dist-info/RECORD create mode 100644 .deps_siteforge_v812/httpcore-1.0.9.dist-info/REQUESTED create mode 100644 .deps_siteforge_v812/httpcore-1.0.9.dist-info/WHEEL create mode 100644 .deps_siteforge_v812/httpcore-1.0.9.dist-info/licenses/LICENSE.md create mode 100644 .deps_siteforge_v812/httpcore/__init__.py create mode 100644 .deps_siteforge_v812/httpcore/__pycache__/__init__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/httpcore/__pycache__/_api.cpython-312.pyc create mode 100644 .deps_siteforge_v812/httpcore/__pycache__/_exceptions.cpython-312.pyc create mode 100644 .deps_siteforge_v812/httpcore/__pycache__/_models.cpython-312.pyc create mode 100644 .deps_siteforge_v812/httpcore/__pycache__/_ssl.cpython-312.pyc create mode 100644 .deps_siteforge_v812/httpcore/__pycache__/_synchronization.cpython-312.pyc create mode 100644 .deps_siteforge_v812/httpcore/__pycache__/_trace.cpython-312.pyc create mode 100644 .deps_siteforge_v812/httpcore/__pycache__/_utils.cpython-312.pyc create mode 100644 .deps_siteforge_v812/httpcore/_api.py create mode 100644 .deps_siteforge_v812/httpcore/_async/__init__.py create mode 100644 .deps_siteforge_v812/httpcore/_async/__pycache__/__init__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/httpcore/_async/__pycache__/connection.cpython-312.pyc create mode 100644 .deps_siteforge_v812/httpcore/_async/__pycache__/connection_pool.cpython-312.pyc create mode 100644 .deps_siteforge_v812/httpcore/_async/__pycache__/http11.cpython-312.pyc create mode 100644 .deps_siteforge_v812/httpcore/_async/__pycache__/http2.cpython-312.pyc create mode 100644 .deps_siteforge_v812/httpcore/_async/__pycache__/http_proxy.cpython-312.pyc create mode 100644 .deps_siteforge_v812/httpcore/_async/__pycache__/interfaces.cpython-312.pyc create mode 100644 .deps_siteforge_v812/httpcore/_async/__pycache__/socks_proxy.cpython-312.pyc create mode 100644 .deps_siteforge_v812/httpcore/_async/connection.py create mode 100644 .deps_siteforge_v812/httpcore/_async/connection_pool.py create mode 100644 .deps_siteforge_v812/httpcore/_async/http11.py create mode 100644 .deps_siteforge_v812/httpcore/_async/http2.py create mode 100644 .deps_siteforge_v812/httpcore/_async/http_proxy.py create mode 100644 .deps_siteforge_v812/httpcore/_async/interfaces.py create mode 100644 .deps_siteforge_v812/httpcore/_async/socks_proxy.py create mode 100644 .deps_siteforge_v812/httpcore/_backends/__init__.py create mode 100644 .deps_siteforge_v812/httpcore/_backends/__pycache__/__init__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/httpcore/_backends/__pycache__/anyio.cpython-312.pyc create mode 100644 .deps_siteforge_v812/httpcore/_backends/__pycache__/auto.cpython-312.pyc create mode 100644 .deps_siteforge_v812/httpcore/_backends/__pycache__/base.cpython-312.pyc create mode 100644 .deps_siteforge_v812/httpcore/_backends/__pycache__/mock.cpython-312.pyc create mode 100644 .deps_siteforge_v812/httpcore/_backends/__pycache__/sync.cpython-312.pyc create mode 100644 .deps_siteforge_v812/httpcore/_backends/__pycache__/trio.cpython-312.pyc create mode 100644 .deps_siteforge_v812/httpcore/_backends/anyio.py create mode 100644 .deps_siteforge_v812/httpcore/_backends/auto.py create mode 100644 .deps_siteforge_v812/httpcore/_backends/base.py create mode 100644 .deps_siteforge_v812/httpcore/_backends/mock.py create mode 100644 .deps_siteforge_v812/httpcore/_backends/sync.py create mode 100644 .deps_siteforge_v812/httpcore/_backends/trio.py create mode 100644 .deps_siteforge_v812/httpcore/_exceptions.py create mode 100644 .deps_siteforge_v812/httpcore/_models.py create mode 100644 .deps_siteforge_v812/httpcore/_ssl.py create mode 100644 .deps_siteforge_v812/httpcore/_sync/__init__.py create mode 100644 .deps_siteforge_v812/httpcore/_sync/__pycache__/__init__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/httpcore/_sync/__pycache__/connection.cpython-312.pyc create mode 100644 .deps_siteforge_v812/httpcore/_sync/__pycache__/connection_pool.cpython-312.pyc create mode 100644 .deps_siteforge_v812/httpcore/_sync/__pycache__/http11.cpython-312.pyc create mode 100644 .deps_siteforge_v812/httpcore/_sync/__pycache__/http2.cpython-312.pyc create mode 100644 .deps_siteforge_v812/httpcore/_sync/__pycache__/http_proxy.cpython-312.pyc create mode 100644 .deps_siteforge_v812/httpcore/_sync/__pycache__/interfaces.cpython-312.pyc create mode 100644 .deps_siteforge_v812/httpcore/_sync/__pycache__/socks_proxy.cpython-312.pyc create mode 100644 .deps_siteforge_v812/httpcore/_sync/connection.py create mode 100644 .deps_siteforge_v812/httpcore/_sync/connection_pool.py create mode 100644 .deps_siteforge_v812/httpcore/_sync/http11.py create mode 100644 .deps_siteforge_v812/httpcore/_sync/http2.py create mode 100644 .deps_siteforge_v812/httpcore/_sync/http_proxy.py create mode 100644 .deps_siteforge_v812/httpcore/_sync/interfaces.py create mode 100644 .deps_siteforge_v812/httpcore/_sync/socks_proxy.py create mode 100644 .deps_siteforge_v812/httpcore/_synchronization.py create mode 100644 .deps_siteforge_v812/httpcore/_trace.py create mode 100644 .deps_siteforge_v812/httpcore/_utils.py create mode 100644 .deps_siteforge_v812/httpcore/py.typed create mode 100644 .deps_siteforge_v812/httpx-0.28.1.dist-info/INSTALLER create mode 100644 .deps_siteforge_v812/httpx-0.28.1.dist-info/METADATA create mode 100644 .deps_siteforge_v812/httpx-0.28.1.dist-info/RECORD create mode 100644 .deps_siteforge_v812/httpx-0.28.1.dist-info/REQUESTED create mode 100644 .deps_siteforge_v812/httpx-0.28.1.dist-info/WHEEL create mode 100644 .deps_siteforge_v812/httpx-0.28.1.dist-info/entry_points.txt create mode 100644 .deps_siteforge_v812/httpx-0.28.1.dist-info/licenses/LICENSE.md create mode 100644 .deps_siteforge_v812/httpx/__init__.py create mode 100644 .deps_siteforge_v812/httpx/__pycache__/__init__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/httpx/__pycache__/__version__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/httpx/__pycache__/_api.cpython-312.pyc create mode 100644 .deps_siteforge_v812/httpx/__pycache__/_auth.cpython-312.pyc create mode 100644 .deps_siteforge_v812/httpx/__pycache__/_client.cpython-312.pyc create mode 100644 .deps_siteforge_v812/httpx/__pycache__/_config.cpython-312.pyc create mode 100644 .deps_siteforge_v812/httpx/__pycache__/_content.cpython-312.pyc create mode 100644 .deps_siteforge_v812/httpx/__pycache__/_decoders.cpython-312.pyc create mode 100644 .deps_siteforge_v812/httpx/__pycache__/_exceptions.cpython-312.pyc create mode 100644 .deps_siteforge_v812/httpx/__pycache__/_main.cpython-312.pyc create mode 100644 .deps_siteforge_v812/httpx/__pycache__/_models.cpython-312.pyc create mode 100644 .deps_siteforge_v812/httpx/__pycache__/_multipart.cpython-312.pyc create mode 100644 .deps_siteforge_v812/httpx/__pycache__/_status_codes.cpython-312.pyc create mode 100644 .deps_siteforge_v812/httpx/__pycache__/_types.cpython-312.pyc create mode 100644 .deps_siteforge_v812/httpx/__pycache__/_urlparse.cpython-312.pyc create mode 100644 .deps_siteforge_v812/httpx/__pycache__/_urls.cpython-312.pyc create mode 100644 .deps_siteforge_v812/httpx/__pycache__/_utils.cpython-312.pyc create mode 100644 .deps_siteforge_v812/httpx/__version__.py create mode 100644 .deps_siteforge_v812/httpx/_api.py create mode 100644 .deps_siteforge_v812/httpx/_auth.py create mode 100644 .deps_siteforge_v812/httpx/_client.py create mode 100644 .deps_siteforge_v812/httpx/_config.py create mode 100644 .deps_siteforge_v812/httpx/_content.py create mode 100644 .deps_siteforge_v812/httpx/_decoders.py create mode 100644 .deps_siteforge_v812/httpx/_exceptions.py create mode 100644 .deps_siteforge_v812/httpx/_main.py create mode 100644 .deps_siteforge_v812/httpx/_models.py create mode 100644 .deps_siteforge_v812/httpx/_multipart.py create mode 100644 .deps_siteforge_v812/httpx/_status_codes.py create mode 100644 .deps_siteforge_v812/httpx/_transports/__init__.py create mode 100644 .deps_siteforge_v812/httpx/_transports/__pycache__/__init__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/httpx/_transports/__pycache__/asgi.cpython-312.pyc create mode 100644 .deps_siteforge_v812/httpx/_transports/__pycache__/base.cpython-312.pyc create mode 100644 .deps_siteforge_v812/httpx/_transports/__pycache__/default.cpython-312.pyc create mode 100644 .deps_siteforge_v812/httpx/_transports/__pycache__/mock.cpython-312.pyc create mode 100644 .deps_siteforge_v812/httpx/_transports/__pycache__/wsgi.cpython-312.pyc create mode 100644 .deps_siteforge_v812/httpx/_transports/asgi.py create mode 100644 .deps_siteforge_v812/httpx/_transports/base.py create mode 100644 .deps_siteforge_v812/httpx/_transports/default.py create mode 100644 .deps_siteforge_v812/httpx/_transports/mock.py create mode 100644 .deps_siteforge_v812/httpx/_transports/wsgi.py create mode 100644 .deps_siteforge_v812/httpx/_types.py create mode 100644 .deps_siteforge_v812/httpx/_urlparse.py create mode 100644 .deps_siteforge_v812/httpx/_urls.py create mode 100644 .deps_siteforge_v812/httpx/_utils.py create mode 100644 .deps_siteforge_v812/httpx/py.typed create mode 100644 .deps_siteforge_v812/idna-3.17.dist-info/INSTALLER create mode 100644 .deps_siteforge_v812/idna-3.17.dist-info/METADATA create mode 100644 .deps_siteforge_v812/idna-3.17.dist-info/RECORD create mode 100644 .deps_siteforge_v812/idna-3.17.dist-info/REQUESTED create mode 100644 .deps_siteforge_v812/idna-3.17.dist-info/WHEEL create mode 100644 .deps_siteforge_v812/idna-3.17.dist-info/entry_points.txt create mode 100644 .deps_siteforge_v812/idna-3.17.dist-info/licenses/LICENSE.md create mode 100644 .deps_siteforge_v812/idna/__init__.py create mode 100644 .deps_siteforge_v812/idna/__main__.py create mode 100644 .deps_siteforge_v812/idna/__pycache__/__init__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/idna/__pycache__/__main__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/idna/__pycache__/cli.cpython-312.pyc create mode 100644 .deps_siteforge_v812/idna/__pycache__/codec.cpython-312.pyc create mode 100644 .deps_siteforge_v812/idna/__pycache__/compat.cpython-312.pyc create mode 100644 .deps_siteforge_v812/idna/__pycache__/core.cpython-312.pyc create mode 100644 .deps_siteforge_v812/idna/__pycache__/idnadata.cpython-312.pyc create mode 100644 .deps_siteforge_v812/idna/__pycache__/intranges.cpython-312.pyc create mode 100644 .deps_siteforge_v812/idna/__pycache__/package_data.cpython-312.pyc create mode 100644 .deps_siteforge_v812/idna/__pycache__/uts46data.cpython-312.pyc create mode 100644 .deps_siteforge_v812/idna/cli.py create mode 100644 .deps_siteforge_v812/idna/codec.py create mode 100644 .deps_siteforge_v812/idna/compat.py create mode 100644 .deps_siteforge_v812/idna/core.py create mode 100644 .deps_siteforge_v812/idna/idnadata.py create mode 100644 .deps_siteforge_v812/idna/intranges.py create mode 100644 .deps_siteforge_v812/idna/package_data.py create mode 100644 .deps_siteforge_v812/idna/py.typed create mode 100644 .deps_siteforge_v812/idna/uts46data.py create mode 100644 .deps_siteforge_v812/iniconfig-2.3.0.dist-info/INSTALLER create mode 100644 .deps_siteforge_v812/iniconfig-2.3.0.dist-info/METADATA create mode 100644 .deps_siteforge_v812/iniconfig-2.3.0.dist-info/RECORD create mode 100644 .deps_siteforge_v812/iniconfig-2.3.0.dist-info/REQUESTED create mode 100644 .deps_siteforge_v812/iniconfig-2.3.0.dist-info/WHEEL create mode 100644 .deps_siteforge_v812/iniconfig-2.3.0.dist-info/licenses/LICENSE create mode 100644 .deps_siteforge_v812/iniconfig-2.3.0.dist-info/top_level.txt create mode 100644 .deps_siteforge_v812/iniconfig/__init__.py create mode 100644 .deps_siteforge_v812/iniconfig/__pycache__/__init__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/iniconfig/__pycache__/_parse.cpython-312.pyc create mode 100644 .deps_siteforge_v812/iniconfig/__pycache__/_version.cpython-312.pyc create mode 100644 .deps_siteforge_v812/iniconfig/__pycache__/exceptions.cpython-312.pyc create mode 100644 .deps_siteforge_v812/iniconfig/_parse.py create mode 100644 .deps_siteforge_v812/iniconfig/_version.py create mode 100644 .deps_siteforge_v812/iniconfig/exceptions.py create mode 100644 .deps_siteforge_v812/iniconfig/py.typed create mode 100644 .deps_siteforge_v812/itsdangerous-2.2.0.dist-info/INSTALLER create mode 100644 .deps_siteforge_v812/itsdangerous-2.2.0.dist-info/LICENSE.txt create mode 100644 .deps_siteforge_v812/itsdangerous-2.2.0.dist-info/METADATA create mode 100644 .deps_siteforge_v812/itsdangerous-2.2.0.dist-info/RECORD create mode 100644 .deps_siteforge_v812/itsdangerous-2.2.0.dist-info/REQUESTED create mode 100644 .deps_siteforge_v812/itsdangerous-2.2.0.dist-info/WHEEL create mode 100644 .deps_siteforge_v812/itsdangerous/__init__.py create mode 100644 .deps_siteforge_v812/itsdangerous/__pycache__/__init__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/itsdangerous/__pycache__/_json.cpython-312.pyc create mode 100644 .deps_siteforge_v812/itsdangerous/__pycache__/encoding.cpython-312.pyc create mode 100644 .deps_siteforge_v812/itsdangerous/__pycache__/exc.cpython-312.pyc create mode 100644 .deps_siteforge_v812/itsdangerous/__pycache__/serializer.cpython-312.pyc create mode 100644 .deps_siteforge_v812/itsdangerous/__pycache__/signer.cpython-312.pyc create mode 100644 .deps_siteforge_v812/itsdangerous/__pycache__/timed.cpython-312.pyc create mode 100644 .deps_siteforge_v812/itsdangerous/__pycache__/url_safe.cpython-312.pyc create mode 100644 .deps_siteforge_v812/itsdangerous/_json.py create mode 100644 .deps_siteforge_v812/itsdangerous/encoding.py create mode 100644 .deps_siteforge_v812/itsdangerous/exc.py create mode 100644 .deps_siteforge_v812/itsdangerous/py.typed create mode 100644 .deps_siteforge_v812/itsdangerous/serializer.py create mode 100644 .deps_siteforge_v812/itsdangerous/signer.py create mode 100644 .deps_siteforge_v812/itsdangerous/timed.py create mode 100644 .deps_siteforge_v812/itsdangerous/url_safe.py create mode 100644 .deps_siteforge_v812/jinja2-3.1.6.dist-info/INSTALLER create mode 100644 .deps_siteforge_v812/jinja2-3.1.6.dist-info/METADATA create mode 100644 .deps_siteforge_v812/jinja2-3.1.6.dist-info/RECORD create mode 100644 .deps_siteforge_v812/jinja2-3.1.6.dist-info/REQUESTED create mode 100644 .deps_siteforge_v812/jinja2-3.1.6.dist-info/WHEEL create mode 100644 .deps_siteforge_v812/jinja2-3.1.6.dist-info/entry_points.txt create mode 100644 .deps_siteforge_v812/jinja2-3.1.6.dist-info/licenses/LICENSE.txt create mode 100644 .deps_siteforge_v812/jinja2/__init__.py create mode 100644 .deps_siteforge_v812/jinja2/__pycache__/__init__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/jinja2/__pycache__/_identifier.cpython-312.pyc create mode 100644 .deps_siteforge_v812/jinja2/__pycache__/async_utils.cpython-312.pyc create mode 100644 .deps_siteforge_v812/jinja2/__pycache__/bccache.cpython-312.pyc create mode 100644 .deps_siteforge_v812/jinja2/__pycache__/compiler.cpython-312.pyc create mode 100644 .deps_siteforge_v812/jinja2/__pycache__/constants.cpython-312.pyc create mode 100644 .deps_siteforge_v812/jinja2/__pycache__/debug.cpython-312.pyc create mode 100644 .deps_siteforge_v812/jinja2/__pycache__/defaults.cpython-312.pyc create mode 100644 .deps_siteforge_v812/jinja2/__pycache__/environment.cpython-312.pyc create mode 100644 .deps_siteforge_v812/jinja2/__pycache__/exceptions.cpython-312.pyc create mode 100644 .deps_siteforge_v812/jinja2/__pycache__/ext.cpython-312.pyc create mode 100644 .deps_siteforge_v812/jinja2/__pycache__/filters.cpython-312.pyc create mode 100644 .deps_siteforge_v812/jinja2/__pycache__/idtracking.cpython-312.pyc create mode 100644 .deps_siteforge_v812/jinja2/__pycache__/lexer.cpython-312.pyc create mode 100644 .deps_siteforge_v812/jinja2/__pycache__/loaders.cpython-312.pyc create mode 100644 .deps_siteforge_v812/jinja2/__pycache__/meta.cpython-312.pyc create mode 100644 .deps_siteforge_v812/jinja2/__pycache__/nativetypes.cpython-312.pyc create mode 100644 .deps_siteforge_v812/jinja2/__pycache__/nodes.cpython-312.pyc create mode 100644 .deps_siteforge_v812/jinja2/__pycache__/optimizer.cpython-312.pyc create mode 100644 .deps_siteforge_v812/jinja2/__pycache__/parser.cpython-312.pyc create mode 100644 .deps_siteforge_v812/jinja2/__pycache__/runtime.cpython-312.pyc create mode 100644 .deps_siteforge_v812/jinja2/__pycache__/sandbox.cpython-312.pyc create mode 100644 .deps_siteforge_v812/jinja2/__pycache__/tests.cpython-312.pyc create mode 100644 .deps_siteforge_v812/jinja2/__pycache__/utils.cpython-312.pyc create mode 100644 .deps_siteforge_v812/jinja2/__pycache__/visitor.cpython-312.pyc create mode 100644 .deps_siteforge_v812/jinja2/_identifier.py create mode 100644 .deps_siteforge_v812/jinja2/async_utils.py create mode 100644 .deps_siteforge_v812/jinja2/bccache.py create mode 100644 .deps_siteforge_v812/jinja2/compiler.py create mode 100644 .deps_siteforge_v812/jinja2/constants.py create mode 100644 .deps_siteforge_v812/jinja2/debug.py create mode 100644 .deps_siteforge_v812/jinja2/defaults.py create mode 100644 .deps_siteforge_v812/jinja2/environment.py create mode 100644 .deps_siteforge_v812/jinja2/exceptions.py create mode 100644 .deps_siteforge_v812/jinja2/ext.py create mode 100644 .deps_siteforge_v812/jinja2/filters.py create mode 100644 .deps_siteforge_v812/jinja2/idtracking.py create mode 100644 .deps_siteforge_v812/jinja2/lexer.py create mode 100644 .deps_siteforge_v812/jinja2/loaders.py create mode 100644 .deps_siteforge_v812/jinja2/meta.py create mode 100644 .deps_siteforge_v812/jinja2/nativetypes.py create mode 100644 .deps_siteforge_v812/jinja2/nodes.py create mode 100644 .deps_siteforge_v812/jinja2/optimizer.py create mode 100644 .deps_siteforge_v812/jinja2/parser.py create mode 100644 .deps_siteforge_v812/jinja2/py.typed create mode 100644 .deps_siteforge_v812/jinja2/runtime.py create mode 100644 .deps_siteforge_v812/jinja2/sandbox.py create mode 100644 .deps_siteforge_v812/jinja2/tests.py create mode 100644 .deps_siteforge_v812/jinja2/utils.py create mode 100644 .deps_siteforge_v812/jinja2/visitor.py create mode 100644 .deps_siteforge_v812/markupsafe-3.0.3.dist-info/INSTALLER create mode 100644 .deps_siteforge_v812/markupsafe-3.0.3.dist-info/METADATA create mode 100644 .deps_siteforge_v812/markupsafe-3.0.3.dist-info/RECORD create mode 100644 .deps_siteforge_v812/markupsafe-3.0.3.dist-info/REQUESTED create mode 100644 .deps_siteforge_v812/markupsafe-3.0.3.dist-info/WHEEL create mode 100644 .deps_siteforge_v812/markupsafe-3.0.3.dist-info/licenses/LICENSE.txt create mode 100644 .deps_siteforge_v812/markupsafe-3.0.3.dist-info/top_level.txt create mode 100644 .deps_siteforge_v812/markupsafe/__init__.py create mode 100644 .deps_siteforge_v812/markupsafe/__pycache__/__init__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/markupsafe/__pycache__/_native.cpython-312.pyc create mode 100644 .deps_siteforge_v812/markupsafe/_native.py create mode 100644 .deps_siteforge_v812/markupsafe/_speedups.c create mode 100644 .deps_siteforge_v812/markupsafe/_speedups.cp312-win_amd64.pyd create mode 100644 .deps_siteforge_v812/markupsafe/_speedups.pyi create mode 100644 .deps_siteforge_v812/markupsafe/py.typed create mode 100644 .deps_siteforge_v812/multipart/__init__.py create mode 100644 .deps_siteforge_v812/multipart/__pycache__/__init__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/multipart/__pycache__/decoders.cpython-312.pyc create mode 100644 .deps_siteforge_v812/multipart/__pycache__/exceptions.cpython-312.pyc create mode 100644 .deps_siteforge_v812/multipart/__pycache__/multipart.cpython-312.pyc create mode 100644 .deps_siteforge_v812/multipart/decoders.py create mode 100644 .deps_siteforge_v812/multipart/exceptions.py create mode 100644 .deps_siteforge_v812/multipart/multipart.py create mode 100644 .deps_siteforge_v812/openpyxl-3.1.5.dist-info/INSTALLER create mode 100644 .deps_siteforge_v812/openpyxl-3.1.5.dist-info/LICENCE.rst create mode 100644 .deps_siteforge_v812/openpyxl-3.1.5.dist-info/METADATA create mode 100644 .deps_siteforge_v812/openpyxl-3.1.5.dist-info/RECORD create mode 100644 .deps_siteforge_v812/openpyxl-3.1.5.dist-info/REQUESTED create mode 100644 .deps_siteforge_v812/openpyxl-3.1.5.dist-info/WHEEL create mode 100644 .deps_siteforge_v812/openpyxl-3.1.5.dist-info/top_level.txt create mode 100644 .deps_siteforge_v812/openpyxl/__init__.py create mode 100644 .deps_siteforge_v812/openpyxl/__pycache__/__init__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/__pycache__/_constants.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/_constants.py create mode 100644 .deps_siteforge_v812/openpyxl/cell/__init__.py create mode 100644 .deps_siteforge_v812/openpyxl/cell/__pycache__/__init__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/cell/__pycache__/_writer.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/cell/__pycache__/cell.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/cell/__pycache__/read_only.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/cell/__pycache__/rich_text.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/cell/__pycache__/text.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/cell/_writer.py create mode 100644 .deps_siteforge_v812/openpyxl/cell/cell.py create mode 100644 .deps_siteforge_v812/openpyxl/cell/read_only.py create mode 100644 .deps_siteforge_v812/openpyxl/cell/rich_text.py create mode 100644 .deps_siteforge_v812/openpyxl/cell/text.py create mode 100644 .deps_siteforge_v812/openpyxl/chart/_3d.py create mode 100644 .deps_siteforge_v812/openpyxl/chart/__init__.py create mode 100644 .deps_siteforge_v812/openpyxl/chart/__pycache__/_3d.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/chart/__pycache__/__init__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/chart/__pycache__/_chart.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/chart/__pycache__/area_chart.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/chart/__pycache__/axis.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/chart/__pycache__/bar_chart.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/chart/__pycache__/bubble_chart.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/chart/__pycache__/chartspace.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/chart/__pycache__/data_source.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/chart/__pycache__/descriptors.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/chart/__pycache__/error_bar.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/chart/__pycache__/label.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/chart/__pycache__/layout.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/chart/__pycache__/legend.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/chart/__pycache__/line_chart.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/chart/__pycache__/marker.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/chart/__pycache__/picture.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/chart/__pycache__/pie_chart.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/chart/__pycache__/pivot.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/chart/__pycache__/plotarea.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/chart/__pycache__/print_settings.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/chart/__pycache__/radar_chart.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/chart/__pycache__/reader.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/chart/__pycache__/reference.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/chart/__pycache__/scatter_chart.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/chart/__pycache__/series.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/chart/__pycache__/series_factory.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/chart/__pycache__/shapes.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/chart/__pycache__/stock_chart.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/chart/__pycache__/surface_chart.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/chart/__pycache__/text.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/chart/__pycache__/title.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/chart/__pycache__/trendline.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/chart/__pycache__/updown_bars.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/chart/_chart.py create mode 100644 .deps_siteforge_v812/openpyxl/chart/area_chart.py create mode 100644 .deps_siteforge_v812/openpyxl/chart/axis.py create mode 100644 .deps_siteforge_v812/openpyxl/chart/bar_chart.py create mode 100644 .deps_siteforge_v812/openpyxl/chart/bubble_chart.py create mode 100644 .deps_siteforge_v812/openpyxl/chart/chartspace.py create mode 100644 .deps_siteforge_v812/openpyxl/chart/data_source.py create mode 100644 .deps_siteforge_v812/openpyxl/chart/descriptors.py create mode 100644 .deps_siteforge_v812/openpyxl/chart/error_bar.py create mode 100644 .deps_siteforge_v812/openpyxl/chart/label.py create mode 100644 .deps_siteforge_v812/openpyxl/chart/layout.py create mode 100644 .deps_siteforge_v812/openpyxl/chart/legend.py create mode 100644 .deps_siteforge_v812/openpyxl/chart/line_chart.py create mode 100644 .deps_siteforge_v812/openpyxl/chart/marker.py create mode 100644 .deps_siteforge_v812/openpyxl/chart/picture.py create mode 100644 .deps_siteforge_v812/openpyxl/chart/pie_chart.py create mode 100644 .deps_siteforge_v812/openpyxl/chart/pivot.py create mode 100644 .deps_siteforge_v812/openpyxl/chart/plotarea.py create mode 100644 .deps_siteforge_v812/openpyxl/chart/print_settings.py create mode 100644 .deps_siteforge_v812/openpyxl/chart/radar_chart.py create mode 100644 .deps_siteforge_v812/openpyxl/chart/reader.py create mode 100644 .deps_siteforge_v812/openpyxl/chart/reference.py create mode 100644 .deps_siteforge_v812/openpyxl/chart/scatter_chart.py create mode 100644 .deps_siteforge_v812/openpyxl/chart/series.py create mode 100644 .deps_siteforge_v812/openpyxl/chart/series_factory.py create mode 100644 .deps_siteforge_v812/openpyxl/chart/shapes.py create mode 100644 .deps_siteforge_v812/openpyxl/chart/stock_chart.py create mode 100644 .deps_siteforge_v812/openpyxl/chart/surface_chart.py create mode 100644 .deps_siteforge_v812/openpyxl/chart/text.py create mode 100644 .deps_siteforge_v812/openpyxl/chart/title.py create mode 100644 .deps_siteforge_v812/openpyxl/chart/trendline.py create mode 100644 .deps_siteforge_v812/openpyxl/chart/updown_bars.py create mode 100644 .deps_siteforge_v812/openpyxl/chartsheet/__init__.py create mode 100644 .deps_siteforge_v812/openpyxl/chartsheet/__pycache__/__init__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/chartsheet/__pycache__/chartsheet.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/chartsheet/__pycache__/custom.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/chartsheet/__pycache__/properties.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/chartsheet/__pycache__/protection.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/chartsheet/__pycache__/publish.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/chartsheet/__pycache__/relation.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/chartsheet/__pycache__/views.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/chartsheet/chartsheet.py create mode 100644 .deps_siteforge_v812/openpyxl/chartsheet/custom.py create mode 100644 .deps_siteforge_v812/openpyxl/chartsheet/properties.py create mode 100644 .deps_siteforge_v812/openpyxl/chartsheet/protection.py create mode 100644 .deps_siteforge_v812/openpyxl/chartsheet/publish.py create mode 100644 .deps_siteforge_v812/openpyxl/chartsheet/relation.py create mode 100644 .deps_siteforge_v812/openpyxl/chartsheet/views.py create mode 100644 .deps_siteforge_v812/openpyxl/comments/__init__.py create mode 100644 .deps_siteforge_v812/openpyxl/comments/__pycache__/__init__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/comments/__pycache__/author.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/comments/__pycache__/comment_sheet.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/comments/__pycache__/comments.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/comments/__pycache__/shape_writer.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/comments/author.py create mode 100644 .deps_siteforge_v812/openpyxl/comments/comment_sheet.py create mode 100644 .deps_siteforge_v812/openpyxl/comments/comments.py create mode 100644 .deps_siteforge_v812/openpyxl/comments/shape_writer.py create mode 100644 .deps_siteforge_v812/openpyxl/compat/__init__.py create mode 100644 .deps_siteforge_v812/openpyxl/compat/__pycache__/__init__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/compat/__pycache__/abc.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/compat/__pycache__/numbers.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/compat/__pycache__/product.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/compat/__pycache__/singleton.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/compat/__pycache__/strings.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/compat/abc.py create mode 100644 .deps_siteforge_v812/openpyxl/compat/numbers.py create mode 100644 .deps_siteforge_v812/openpyxl/compat/product.py create mode 100644 .deps_siteforge_v812/openpyxl/compat/singleton.py create mode 100644 .deps_siteforge_v812/openpyxl/compat/strings.py create mode 100644 .deps_siteforge_v812/openpyxl/descriptors/__init__.py create mode 100644 .deps_siteforge_v812/openpyxl/descriptors/__pycache__/__init__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/descriptors/__pycache__/base.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/descriptors/__pycache__/container.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/descriptors/__pycache__/excel.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/descriptors/__pycache__/namespace.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/descriptors/__pycache__/nested.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/descriptors/__pycache__/sequence.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/descriptors/__pycache__/serialisable.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/descriptors/__pycache__/slots.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/descriptors/base.py create mode 100644 .deps_siteforge_v812/openpyxl/descriptors/container.py create mode 100644 .deps_siteforge_v812/openpyxl/descriptors/excel.py create mode 100644 .deps_siteforge_v812/openpyxl/descriptors/namespace.py create mode 100644 .deps_siteforge_v812/openpyxl/descriptors/nested.py create mode 100644 .deps_siteforge_v812/openpyxl/descriptors/sequence.py create mode 100644 .deps_siteforge_v812/openpyxl/descriptors/serialisable.py create mode 100644 .deps_siteforge_v812/openpyxl/descriptors/slots.py create mode 100644 .deps_siteforge_v812/openpyxl/drawing/__init__.py create mode 100644 .deps_siteforge_v812/openpyxl/drawing/__pycache__/__init__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/drawing/__pycache__/colors.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/drawing/__pycache__/connector.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/drawing/__pycache__/drawing.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/drawing/__pycache__/effect.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/drawing/__pycache__/fill.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/drawing/__pycache__/geometry.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/drawing/__pycache__/graphic.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/drawing/__pycache__/image.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/drawing/__pycache__/line.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/drawing/__pycache__/picture.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/drawing/__pycache__/properties.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/drawing/__pycache__/relation.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/drawing/__pycache__/spreadsheet_drawing.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/drawing/__pycache__/text.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/drawing/__pycache__/xdr.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/drawing/colors.py create mode 100644 .deps_siteforge_v812/openpyxl/drawing/connector.py create mode 100644 .deps_siteforge_v812/openpyxl/drawing/drawing.py create mode 100644 .deps_siteforge_v812/openpyxl/drawing/effect.py create mode 100644 .deps_siteforge_v812/openpyxl/drawing/fill.py create mode 100644 .deps_siteforge_v812/openpyxl/drawing/geometry.py create mode 100644 .deps_siteforge_v812/openpyxl/drawing/graphic.py create mode 100644 .deps_siteforge_v812/openpyxl/drawing/image.py create mode 100644 .deps_siteforge_v812/openpyxl/drawing/line.py create mode 100644 .deps_siteforge_v812/openpyxl/drawing/picture.py create mode 100644 .deps_siteforge_v812/openpyxl/drawing/properties.py create mode 100644 .deps_siteforge_v812/openpyxl/drawing/relation.py create mode 100644 .deps_siteforge_v812/openpyxl/drawing/spreadsheet_drawing.py create mode 100644 .deps_siteforge_v812/openpyxl/drawing/text.py create mode 100644 .deps_siteforge_v812/openpyxl/drawing/xdr.py create mode 100644 .deps_siteforge_v812/openpyxl/formatting/__init__.py create mode 100644 .deps_siteforge_v812/openpyxl/formatting/__pycache__/__init__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/formatting/__pycache__/formatting.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/formatting/__pycache__/rule.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/formatting/formatting.py create mode 100644 .deps_siteforge_v812/openpyxl/formatting/rule.py create mode 100644 .deps_siteforge_v812/openpyxl/formula/__init__.py create mode 100644 .deps_siteforge_v812/openpyxl/formula/__pycache__/__init__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/formula/__pycache__/tokenizer.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/formula/__pycache__/translate.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/formula/tokenizer.py create mode 100644 .deps_siteforge_v812/openpyxl/formula/translate.py create mode 100644 .deps_siteforge_v812/openpyxl/packaging/__init__.py create mode 100644 .deps_siteforge_v812/openpyxl/packaging/__pycache__/__init__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/packaging/__pycache__/core.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/packaging/__pycache__/custom.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/packaging/__pycache__/extended.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/packaging/__pycache__/interface.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/packaging/__pycache__/manifest.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/packaging/__pycache__/relationship.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/packaging/__pycache__/workbook.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/packaging/core.py create mode 100644 .deps_siteforge_v812/openpyxl/packaging/custom.py create mode 100644 .deps_siteforge_v812/openpyxl/packaging/extended.py create mode 100644 .deps_siteforge_v812/openpyxl/packaging/interface.py create mode 100644 .deps_siteforge_v812/openpyxl/packaging/manifest.py create mode 100644 .deps_siteforge_v812/openpyxl/packaging/relationship.py create mode 100644 .deps_siteforge_v812/openpyxl/packaging/workbook.py create mode 100644 .deps_siteforge_v812/openpyxl/pivot/__init__.py create mode 100644 .deps_siteforge_v812/openpyxl/pivot/__pycache__/__init__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/pivot/__pycache__/cache.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/pivot/__pycache__/fields.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/pivot/__pycache__/record.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/pivot/__pycache__/table.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/pivot/cache.py create mode 100644 .deps_siteforge_v812/openpyxl/pivot/fields.py create mode 100644 .deps_siteforge_v812/openpyxl/pivot/record.py create mode 100644 .deps_siteforge_v812/openpyxl/pivot/table.py create mode 100644 .deps_siteforge_v812/openpyxl/reader/__init__.py create mode 100644 .deps_siteforge_v812/openpyxl/reader/__pycache__/__init__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/reader/__pycache__/drawings.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/reader/__pycache__/excel.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/reader/__pycache__/strings.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/reader/__pycache__/workbook.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/reader/drawings.py create mode 100644 .deps_siteforge_v812/openpyxl/reader/excel.py create mode 100644 .deps_siteforge_v812/openpyxl/reader/strings.py create mode 100644 .deps_siteforge_v812/openpyxl/reader/workbook.py create mode 100644 .deps_siteforge_v812/openpyxl/styles/__init__.py create mode 100644 .deps_siteforge_v812/openpyxl/styles/__pycache__/__init__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/styles/__pycache__/alignment.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/styles/__pycache__/borders.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/styles/__pycache__/builtins.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/styles/__pycache__/cell_style.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/styles/__pycache__/colors.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/styles/__pycache__/differential.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/styles/__pycache__/fills.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/styles/__pycache__/fonts.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/styles/__pycache__/named_styles.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/styles/__pycache__/numbers.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/styles/__pycache__/protection.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/styles/__pycache__/proxy.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/styles/__pycache__/styleable.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/styles/__pycache__/stylesheet.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/styles/__pycache__/table.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/styles/alignment.py create mode 100644 .deps_siteforge_v812/openpyxl/styles/borders.py create mode 100644 .deps_siteforge_v812/openpyxl/styles/builtins.py create mode 100644 .deps_siteforge_v812/openpyxl/styles/cell_style.py create mode 100644 .deps_siteforge_v812/openpyxl/styles/colors.py create mode 100644 .deps_siteforge_v812/openpyxl/styles/differential.py create mode 100644 .deps_siteforge_v812/openpyxl/styles/fills.py create mode 100644 .deps_siteforge_v812/openpyxl/styles/fonts.py create mode 100644 .deps_siteforge_v812/openpyxl/styles/named_styles.py create mode 100644 .deps_siteforge_v812/openpyxl/styles/numbers.py create mode 100644 .deps_siteforge_v812/openpyxl/styles/protection.py create mode 100644 .deps_siteforge_v812/openpyxl/styles/proxy.py create mode 100644 .deps_siteforge_v812/openpyxl/styles/styleable.py create mode 100644 .deps_siteforge_v812/openpyxl/styles/stylesheet.py create mode 100644 .deps_siteforge_v812/openpyxl/styles/table.py create mode 100644 .deps_siteforge_v812/openpyxl/utils/__init__.py create mode 100644 .deps_siteforge_v812/openpyxl/utils/__pycache__/__init__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/utils/__pycache__/bound_dictionary.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/utils/__pycache__/cell.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/utils/__pycache__/dataframe.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/utils/__pycache__/datetime.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/utils/__pycache__/escape.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/utils/__pycache__/exceptions.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/utils/__pycache__/formulas.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/utils/__pycache__/indexed_list.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/utils/__pycache__/inference.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/utils/__pycache__/protection.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/utils/__pycache__/units.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/utils/bound_dictionary.py create mode 100644 .deps_siteforge_v812/openpyxl/utils/cell.py create mode 100644 .deps_siteforge_v812/openpyxl/utils/dataframe.py create mode 100644 .deps_siteforge_v812/openpyxl/utils/datetime.py create mode 100644 .deps_siteforge_v812/openpyxl/utils/escape.py create mode 100644 .deps_siteforge_v812/openpyxl/utils/exceptions.py create mode 100644 .deps_siteforge_v812/openpyxl/utils/formulas.py create mode 100644 .deps_siteforge_v812/openpyxl/utils/indexed_list.py create mode 100644 .deps_siteforge_v812/openpyxl/utils/inference.py create mode 100644 .deps_siteforge_v812/openpyxl/utils/protection.py create mode 100644 .deps_siteforge_v812/openpyxl/utils/units.py create mode 100644 .deps_siteforge_v812/openpyxl/workbook/__init__.py create mode 100644 .deps_siteforge_v812/openpyxl/workbook/__pycache__/__init__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/workbook/__pycache__/_writer.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/workbook/__pycache__/child.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/workbook/__pycache__/defined_name.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/workbook/__pycache__/external_reference.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/workbook/__pycache__/function_group.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/workbook/__pycache__/properties.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/workbook/__pycache__/protection.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/workbook/__pycache__/smart_tags.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/workbook/__pycache__/views.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/workbook/__pycache__/web.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/workbook/__pycache__/workbook.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/workbook/_writer.py create mode 100644 .deps_siteforge_v812/openpyxl/workbook/child.py create mode 100644 .deps_siteforge_v812/openpyxl/workbook/defined_name.py create mode 100644 .deps_siteforge_v812/openpyxl/workbook/external_link/__init__.py create mode 100644 .deps_siteforge_v812/openpyxl/workbook/external_link/__pycache__/__init__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/workbook/external_link/__pycache__/external.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/workbook/external_link/external.py create mode 100644 .deps_siteforge_v812/openpyxl/workbook/external_reference.py create mode 100644 .deps_siteforge_v812/openpyxl/workbook/function_group.py create mode 100644 .deps_siteforge_v812/openpyxl/workbook/properties.py create mode 100644 .deps_siteforge_v812/openpyxl/workbook/protection.py create mode 100644 .deps_siteforge_v812/openpyxl/workbook/smart_tags.py create mode 100644 .deps_siteforge_v812/openpyxl/workbook/views.py create mode 100644 .deps_siteforge_v812/openpyxl/workbook/web.py create mode 100644 .deps_siteforge_v812/openpyxl/workbook/workbook.py create mode 100644 .deps_siteforge_v812/openpyxl/worksheet/__init__.py create mode 100644 .deps_siteforge_v812/openpyxl/worksheet/__pycache__/__init__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/worksheet/__pycache__/_read_only.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/worksheet/__pycache__/_reader.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/worksheet/__pycache__/_write_only.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/worksheet/__pycache__/_writer.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/worksheet/__pycache__/cell_range.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/worksheet/__pycache__/cell_watch.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/worksheet/__pycache__/controls.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/worksheet/__pycache__/copier.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/worksheet/__pycache__/custom.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/worksheet/__pycache__/datavalidation.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/worksheet/__pycache__/dimensions.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/worksheet/__pycache__/drawing.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/worksheet/__pycache__/errors.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/worksheet/__pycache__/filters.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/worksheet/__pycache__/formula.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/worksheet/__pycache__/header_footer.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/worksheet/__pycache__/hyperlink.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/worksheet/__pycache__/merge.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/worksheet/__pycache__/ole.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/worksheet/__pycache__/page.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/worksheet/__pycache__/pagebreak.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/worksheet/__pycache__/picture.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/worksheet/__pycache__/print_settings.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/worksheet/__pycache__/properties.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/worksheet/__pycache__/protection.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/worksheet/__pycache__/related.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/worksheet/__pycache__/scenario.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/worksheet/__pycache__/smart_tag.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/worksheet/__pycache__/table.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/worksheet/__pycache__/views.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/worksheet/__pycache__/worksheet.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/worksheet/_read_only.py create mode 100644 .deps_siteforge_v812/openpyxl/worksheet/_reader.py create mode 100644 .deps_siteforge_v812/openpyxl/worksheet/_write_only.py create mode 100644 .deps_siteforge_v812/openpyxl/worksheet/_writer.py create mode 100644 .deps_siteforge_v812/openpyxl/worksheet/cell_range.py create mode 100644 .deps_siteforge_v812/openpyxl/worksheet/cell_watch.py create mode 100644 .deps_siteforge_v812/openpyxl/worksheet/controls.py create mode 100644 .deps_siteforge_v812/openpyxl/worksheet/copier.py create mode 100644 .deps_siteforge_v812/openpyxl/worksheet/custom.py create mode 100644 .deps_siteforge_v812/openpyxl/worksheet/datavalidation.py create mode 100644 .deps_siteforge_v812/openpyxl/worksheet/dimensions.py create mode 100644 .deps_siteforge_v812/openpyxl/worksheet/drawing.py create mode 100644 .deps_siteforge_v812/openpyxl/worksheet/errors.py create mode 100644 .deps_siteforge_v812/openpyxl/worksheet/filters.py create mode 100644 .deps_siteforge_v812/openpyxl/worksheet/formula.py create mode 100644 .deps_siteforge_v812/openpyxl/worksheet/header_footer.py create mode 100644 .deps_siteforge_v812/openpyxl/worksheet/hyperlink.py create mode 100644 .deps_siteforge_v812/openpyxl/worksheet/merge.py create mode 100644 .deps_siteforge_v812/openpyxl/worksheet/ole.py create mode 100644 .deps_siteforge_v812/openpyxl/worksheet/page.py create mode 100644 .deps_siteforge_v812/openpyxl/worksheet/pagebreak.py create mode 100644 .deps_siteforge_v812/openpyxl/worksheet/picture.py create mode 100644 .deps_siteforge_v812/openpyxl/worksheet/print_settings.py create mode 100644 .deps_siteforge_v812/openpyxl/worksheet/properties.py create mode 100644 .deps_siteforge_v812/openpyxl/worksheet/protection.py create mode 100644 .deps_siteforge_v812/openpyxl/worksheet/related.py create mode 100644 .deps_siteforge_v812/openpyxl/worksheet/scenario.py create mode 100644 .deps_siteforge_v812/openpyxl/worksheet/smart_tag.py create mode 100644 .deps_siteforge_v812/openpyxl/worksheet/table.py create mode 100644 .deps_siteforge_v812/openpyxl/worksheet/views.py create mode 100644 .deps_siteforge_v812/openpyxl/worksheet/worksheet.py create mode 100644 .deps_siteforge_v812/openpyxl/writer/__init__.py create mode 100644 .deps_siteforge_v812/openpyxl/writer/__pycache__/__init__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/writer/__pycache__/excel.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/writer/__pycache__/theme.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/writer/excel.py create mode 100644 .deps_siteforge_v812/openpyxl/writer/theme.py create mode 100644 .deps_siteforge_v812/openpyxl/xml/__init__.py create mode 100644 .deps_siteforge_v812/openpyxl/xml/__pycache__/__init__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/xml/__pycache__/constants.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/xml/__pycache__/functions.cpython-312.pyc create mode 100644 .deps_siteforge_v812/openpyxl/xml/constants.py create mode 100644 .deps_siteforge_v812/openpyxl/xml/functions.py create mode 100644 .deps_siteforge_v812/packaging-25.0.dist-info/INSTALLER create mode 100644 .deps_siteforge_v812/packaging-25.0.dist-info/METADATA create mode 100644 .deps_siteforge_v812/packaging-25.0.dist-info/RECORD create mode 100644 .deps_siteforge_v812/packaging-25.0.dist-info/REQUESTED create mode 100644 .deps_siteforge_v812/packaging-25.0.dist-info/WHEEL create mode 100644 .deps_siteforge_v812/packaging-25.0.dist-info/licenses/LICENSE create mode 100644 .deps_siteforge_v812/packaging-25.0.dist-info/licenses/LICENSE.APACHE create mode 100644 .deps_siteforge_v812/packaging-25.0.dist-info/licenses/LICENSE.BSD create mode 100644 .deps_siteforge_v812/packaging/__init__.py create mode 100644 .deps_siteforge_v812/packaging/__pycache__/__init__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/packaging/__pycache__/_elffile.cpython-312.pyc create mode 100644 .deps_siteforge_v812/packaging/__pycache__/_manylinux.cpython-312.pyc create mode 100644 .deps_siteforge_v812/packaging/__pycache__/_musllinux.cpython-312.pyc create mode 100644 .deps_siteforge_v812/packaging/__pycache__/_parser.cpython-312.pyc create mode 100644 .deps_siteforge_v812/packaging/__pycache__/_structures.cpython-312.pyc create mode 100644 .deps_siteforge_v812/packaging/__pycache__/_tokenizer.cpython-312.pyc create mode 100644 .deps_siteforge_v812/packaging/__pycache__/markers.cpython-312.pyc create mode 100644 .deps_siteforge_v812/packaging/__pycache__/metadata.cpython-312.pyc create mode 100644 .deps_siteforge_v812/packaging/__pycache__/requirements.cpython-312.pyc create mode 100644 .deps_siteforge_v812/packaging/__pycache__/specifiers.cpython-312.pyc create mode 100644 .deps_siteforge_v812/packaging/__pycache__/tags.cpython-312.pyc create mode 100644 .deps_siteforge_v812/packaging/__pycache__/utils.cpython-312.pyc create mode 100644 .deps_siteforge_v812/packaging/__pycache__/version.cpython-312.pyc create mode 100644 .deps_siteforge_v812/packaging/_elffile.py create mode 100644 .deps_siteforge_v812/packaging/_manylinux.py create mode 100644 .deps_siteforge_v812/packaging/_musllinux.py create mode 100644 .deps_siteforge_v812/packaging/_parser.py create mode 100644 .deps_siteforge_v812/packaging/_structures.py create mode 100644 .deps_siteforge_v812/packaging/_tokenizer.py create mode 100644 .deps_siteforge_v812/packaging/licenses/__init__.py create mode 100644 .deps_siteforge_v812/packaging/licenses/__pycache__/__init__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/packaging/licenses/__pycache__/_spdx.cpython-312.pyc create mode 100644 .deps_siteforge_v812/packaging/licenses/_spdx.py create mode 100644 .deps_siteforge_v812/packaging/markers.py create mode 100644 .deps_siteforge_v812/packaging/metadata.py create mode 100644 .deps_siteforge_v812/packaging/py.typed create mode 100644 .deps_siteforge_v812/packaging/requirements.py create mode 100644 .deps_siteforge_v812/packaging/specifiers.py create mode 100644 .deps_siteforge_v812/packaging/tags.py create mode 100644 .deps_siteforge_v812/packaging/utils.py create mode 100644 .deps_siteforge_v812/packaging/version.py create mode 100644 .deps_siteforge_v812/pluggy-1.6.0.dist-info/INSTALLER create mode 100644 .deps_siteforge_v812/pluggy-1.6.0.dist-info/METADATA create mode 100644 .deps_siteforge_v812/pluggy-1.6.0.dist-info/RECORD create mode 100644 .deps_siteforge_v812/pluggy-1.6.0.dist-info/REQUESTED create mode 100644 .deps_siteforge_v812/pluggy-1.6.0.dist-info/WHEEL create mode 100644 .deps_siteforge_v812/pluggy-1.6.0.dist-info/licenses/LICENSE create mode 100644 .deps_siteforge_v812/pluggy-1.6.0.dist-info/top_level.txt create mode 100644 .deps_siteforge_v812/pluggy/__init__.py create mode 100644 .deps_siteforge_v812/pluggy/__pycache__/__init__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pluggy/__pycache__/_callers.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pluggy/__pycache__/_hooks.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pluggy/__pycache__/_manager.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pluggy/__pycache__/_result.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pluggy/__pycache__/_tracing.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pluggy/__pycache__/_version.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pluggy/__pycache__/_warnings.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pluggy/_callers.py create mode 100644 .deps_siteforge_v812/pluggy/_hooks.py create mode 100644 .deps_siteforge_v812/pluggy/_manager.py create mode 100644 .deps_siteforge_v812/pluggy/_result.py create mode 100644 .deps_siteforge_v812/pluggy/_tracing.py create mode 100644 .deps_siteforge_v812/pluggy/_version.py create mode 100644 .deps_siteforge_v812/pluggy/_warnings.py create mode 100644 .deps_siteforge_v812/pluggy/py.typed create mode 100644 .deps_siteforge_v812/py.py create mode 100644 .deps_siteforge_v812/pydantic-2.13.5.dist-info/INSTALLER create mode 100644 .deps_siteforge_v812/pydantic-2.13.5.dist-info/METADATA create mode 100644 .deps_siteforge_v812/pydantic-2.13.5.dist-info/RECORD create mode 100644 .deps_siteforge_v812/pydantic-2.13.5.dist-info/REQUESTED create mode 100644 .deps_siteforge_v812/pydantic-2.13.5.dist-info/WHEEL create mode 100644 .deps_siteforge_v812/pydantic-2.13.5.dist-info/licenses/LICENSE create mode 100644 .deps_siteforge_v812/pydantic/__init__.py create mode 100644 .deps_siteforge_v812/pydantic/__pycache__/__init__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pydantic/__pycache__/_migration.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pydantic/__pycache__/alias_generators.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pydantic/__pycache__/aliases.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pydantic/__pycache__/annotated_handlers.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pydantic/__pycache__/class_validators.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pydantic/__pycache__/color.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pydantic/__pycache__/config.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pydantic/__pycache__/dataclasses.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pydantic/__pycache__/datetime_parse.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pydantic/__pycache__/decorator.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pydantic/__pycache__/env_settings.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pydantic/__pycache__/error_wrappers.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pydantic/__pycache__/errors.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pydantic/__pycache__/fields.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pydantic/__pycache__/functional_serializers.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pydantic/__pycache__/functional_validators.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pydantic/__pycache__/generics.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pydantic/__pycache__/json.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pydantic/__pycache__/json_schema.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pydantic/__pycache__/main.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pydantic/__pycache__/mypy.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pydantic/__pycache__/networks.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pydantic/__pycache__/parse.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pydantic/__pycache__/root_model.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pydantic/__pycache__/schema.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pydantic/__pycache__/tools.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pydantic/__pycache__/type_adapter.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pydantic/__pycache__/types.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pydantic/__pycache__/typing.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pydantic/__pycache__/utils.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pydantic/__pycache__/validate_call_decorator.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pydantic/__pycache__/validators.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pydantic/__pycache__/version.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pydantic/__pycache__/warnings.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pydantic/_internal/__init__.py create mode 100644 .deps_siteforge_v812/pydantic/_internal/__pycache__/__init__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pydantic/_internal/__pycache__/_config.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pydantic/_internal/__pycache__/_core_metadata.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pydantic/_internal/__pycache__/_core_utils.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pydantic/_internal/__pycache__/_dataclasses.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pydantic/_internal/__pycache__/_decorators.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pydantic/_internal/__pycache__/_decorators_v1.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pydantic/_internal/__pycache__/_discriminated_union.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pydantic/_internal/__pycache__/_docs_extraction.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pydantic/_internal/__pycache__/_fields.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pydantic/_internal/__pycache__/_forward_ref.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pydantic/_internal/__pycache__/_generate_schema.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pydantic/_internal/__pycache__/_generics.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pydantic/_internal/__pycache__/_git.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pydantic/_internal/__pycache__/_import_utils.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pydantic/_internal/__pycache__/_internal_dataclass.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pydantic/_internal/__pycache__/_known_annotated_metadata.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pydantic/_internal/__pycache__/_mock_val_ser.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pydantic/_internal/__pycache__/_model_construction.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pydantic/_internal/__pycache__/_namespace_utils.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pydantic/_internal/__pycache__/_repr.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pydantic/_internal/__pycache__/_schema_gather.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pydantic/_internal/__pycache__/_schema_generation_shared.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pydantic/_internal/__pycache__/_serializers.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pydantic/_internal/__pycache__/_signature.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pydantic/_internal/__pycache__/_typing_extra.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pydantic/_internal/__pycache__/_utils.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pydantic/_internal/__pycache__/_validate_call.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pydantic/_internal/__pycache__/_validators.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pydantic/_internal/_config.py create mode 100644 .deps_siteforge_v812/pydantic/_internal/_core_metadata.py create mode 100644 .deps_siteforge_v812/pydantic/_internal/_core_utils.py create mode 100644 .deps_siteforge_v812/pydantic/_internal/_dataclasses.py create mode 100644 .deps_siteforge_v812/pydantic/_internal/_decorators.py create mode 100644 .deps_siteforge_v812/pydantic/_internal/_decorators_v1.py create mode 100644 .deps_siteforge_v812/pydantic/_internal/_discriminated_union.py create mode 100644 .deps_siteforge_v812/pydantic/_internal/_docs_extraction.py create mode 100644 .deps_siteforge_v812/pydantic/_internal/_fields.py create mode 100644 .deps_siteforge_v812/pydantic/_internal/_forward_ref.py create mode 100644 .deps_siteforge_v812/pydantic/_internal/_generate_schema.py create mode 100644 .deps_siteforge_v812/pydantic/_internal/_generics.py create mode 100644 .deps_siteforge_v812/pydantic/_internal/_git.py create mode 100644 .deps_siteforge_v812/pydantic/_internal/_import_utils.py create mode 100644 .deps_siteforge_v812/pydantic/_internal/_internal_dataclass.py create mode 100644 .deps_siteforge_v812/pydantic/_internal/_known_annotated_metadata.py create mode 100644 .deps_siteforge_v812/pydantic/_internal/_mock_val_ser.py create mode 100644 .deps_siteforge_v812/pydantic/_internal/_model_construction.py create mode 100644 .deps_siteforge_v812/pydantic/_internal/_namespace_utils.py create mode 100644 .deps_siteforge_v812/pydantic/_internal/_repr.py create mode 100644 .deps_siteforge_v812/pydantic/_internal/_schema_gather.py create mode 100644 .deps_siteforge_v812/pydantic/_internal/_schema_generation_shared.py create mode 100644 .deps_siteforge_v812/pydantic/_internal/_serializers.py create mode 100644 .deps_siteforge_v812/pydantic/_internal/_signature.py create mode 100644 .deps_siteforge_v812/pydantic/_internal/_typing_extra.py create mode 100644 .deps_siteforge_v812/pydantic/_internal/_utils.py create mode 100644 .deps_siteforge_v812/pydantic/_internal/_validate_call.py create mode 100644 .deps_siteforge_v812/pydantic/_internal/_validators.py create mode 100644 .deps_siteforge_v812/pydantic/_migration.py create mode 100644 .deps_siteforge_v812/pydantic/alias_generators.py create mode 100644 .deps_siteforge_v812/pydantic/aliases.py create mode 100644 .deps_siteforge_v812/pydantic/annotated_handlers.py create mode 100644 .deps_siteforge_v812/pydantic/class_validators.py create mode 100644 .deps_siteforge_v812/pydantic/color.py create mode 100644 .deps_siteforge_v812/pydantic/config.py create mode 100644 .deps_siteforge_v812/pydantic/dataclasses.py create mode 100644 .deps_siteforge_v812/pydantic/datetime_parse.py create mode 100644 .deps_siteforge_v812/pydantic/decorator.py create mode 100644 .deps_siteforge_v812/pydantic/deprecated/__init__.py create mode 100644 .deps_siteforge_v812/pydantic/deprecated/__pycache__/__init__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pydantic/deprecated/__pycache__/class_validators.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pydantic/deprecated/__pycache__/config.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pydantic/deprecated/__pycache__/copy_internals.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pydantic/deprecated/__pycache__/decorator.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pydantic/deprecated/__pycache__/json.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pydantic/deprecated/__pycache__/parse.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pydantic/deprecated/__pycache__/tools.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pydantic/deprecated/class_validators.py create mode 100644 .deps_siteforge_v812/pydantic/deprecated/config.py create mode 100644 .deps_siteforge_v812/pydantic/deprecated/copy_internals.py create mode 100644 .deps_siteforge_v812/pydantic/deprecated/decorator.py create mode 100644 .deps_siteforge_v812/pydantic/deprecated/json.py create mode 100644 .deps_siteforge_v812/pydantic/deprecated/parse.py create mode 100644 .deps_siteforge_v812/pydantic/deprecated/tools.py create mode 100644 .deps_siteforge_v812/pydantic/env_settings.py create mode 100644 .deps_siteforge_v812/pydantic/error_wrappers.py create mode 100644 .deps_siteforge_v812/pydantic/errors.py create mode 100644 .deps_siteforge_v812/pydantic/experimental/__init__.py create mode 100644 .deps_siteforge_v812/pydantic/experimental/__pycache__/__init__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pydantic/experimental/__pycache__/arguments_schema.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pydantic/experimental/__pycache__/missing_sentinel.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pydantic/experimental/__pycache__/pipeline.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pydantic/experimental/arguments_schema.py create mode 100644 .deps_siteforge_v812/pydantic/experimental/missing_sentinel.py create mode 100644 .deps_siteforge_v812/pydantic/experimental/pipeline.py create mode 100644 .deps_siteforge_v812/pydantic/fields.py create mode 100644 .deps_siteforge_v812/pydantic/functional_serializers.py create mode 100644 .deps_siteforge_v812/pydantic/functional_validators.py create mode 100644 .deps_siteforge_v812/pydantic/generics.py create mode 100644 .deps_siteforge_v812/pydantic/json.py create mode 100644 .deps_siteforge_v812/pydantic/json_schema.py create mode 100644 .deps_siteforge_v812/pydantic/main.py create mode 100644 .deps_siteforge_v812/pydantic/mypy.py create mode 100644 .deps_siteforge_v812/pydantic/networks.py create mode 100644 .deps_siteforge_v812/pydantic/parse.py create mode 100644 .deps_siteforge_v812/pydantic/plugin/__init__.py create mode 100644 .deps_siteforge_v812/pydantic/plugin/__pycache__/__init__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pydantic/plugin/__pycache__/_loader.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pydantic/plugin/__pycache__/_schema_validator.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pydantic/plugin/_loader.py create mode 100644 .deps_siteforge_v812/pydantic/plugin/_schema_validator.py create mode 100644 .deps_siteforge_v812/pydantic/py.typed create mode 100644 .deps_siteforge_v812/pydantic/root_model.py create mode 100644 .deps_siteforge_v812/pydantic/schema.py create mode 100644 .deps_siteforge_v812/pydantic/tools.py create mode 100644 .deps_siteforge_v812/pydantic/type_adapter.py create mode 100644 .deps_siteforge_v812/pydantic/types.py create mode 100644 .deps_siteforge_v812/pydantic/typing.py create mode 100644 .deps_siteforge_v812/pydantic/utils.py create mode 100644 .deps_siteforge_v812/pydantic/v1/__init__.py create mode 100644 .deps_siteforge_v812/pydantic/v1/__pycache__/__init__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pydantic/v1/__pycache__/_hypothesis_plugin.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pydantic/v1/__pycache__/annotated_types.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pydantic/v1/__pycache__/class_validators.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pydantic/v1/__pycache__/color.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pydantic/v1/__pycache__/config.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pydantic/v1/__pycache__/dataclasses.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pydantic/v1/__pycache__/datetime_parse.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pydantic/v1/__pycache__/decorator.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pydantic/v1/__pycache__/env_settings.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pydantic/v1/__pycache__/error_wrappers.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pydantic/v1/__pycache__/errors.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pydantic/v1/__pycache__/fields.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pydantic/v1/__pycache__/generics.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pydantic/v1/__pycache__/json.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pydantic/v1/__pycache__/main.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pydantic/v1/__pycache__/mypy.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pydantic/v1/__pycache__/networks.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pydantic/v1/__pycache__/parse.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pydantic/v1/__pycache__/schema.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pydantic/v1/__pycache__/tools.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pydantic/v1/__pycache__/types.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pydantic/v1/__pycache__/typing.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pydantic/v1/__pycache__/utils.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pydantic/v1/__pycache__/validators.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pydantic/v1/__pycache__/version.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pydantic/v1/_hypothesis_plugin.py create mode 100644 .deps_siteforge_v812/pydantic/v1/annotated_types.py create mode 100644 .deps_siteforge_v812/pydantic/v1/class_validators.py create mode 100644 .deps_siteforge_v812/pydantic/v1/color.py create mode 100644 .deps_siteforge_v812/pydantic/v1/config.py create mode 100644 .deps_siteforge_v812/pydantic/v1/dataclasses.py create mode 100644 .deps_siteforge_v812/pydantic/v1/datetime_parse.py create mode 100644 .deps_siteforge_v812/pydantic/v1/decorator.py create mode 100644 .deps_siteforge_v812/pydantic/v1/env_settings.py create mode 100644 .deps_siteforge_v812/pydantic/v1/error_wrappers.py create mode 100644 .deps_siteforge_v812/pydantic/v1/errors.py create mode 100644 .deps_siteforge_v812/pydantic/v1/fields.py create mode 100644 .deps_siteforge_v812/pydantic/v1/generics.py create mode 100644 .deps_siteforge_v812/pydantic/v1/json.py create mode 100644 .deps_siteforge_v812/pydantic/v1/main.py create mode 100644 .deps_siteforge_v812/pydantic/v1/mypy.py create mode 100644 .deps_siteforge_v812/pydantic/v1/networks.py create mode 100644 .deps_siteforge_v812/pydantic/v1/parse.py create mode 100644 .deps_siteforge_v812/pydantic/v1/py.typed create mode 100644 .deps_siteforge_v812/pydantic/v1/schema.py create mode 100644 .deps_siteforge_v812/pydantic/v1/tools.py create mode 100644 .deps_siteforge_v812/pydantic/v1/types.py create mode 100644 .deps_siteforge_v812/pydantic/v1/typing.py create mode 100644 .deps_siteforge_v812/pydantic/v1/utils.py create mode 100644 .deps_siteforge_v812/pydantic/v1/validators.py create mode 100644 .deps_siteforge_v812/pydantic/v1/version.py create mode 100644 .deps_siteforge_v812/pydantic/validate_call_decorator.py create mode 100644 .deps_siteforge_v812/pydantic/validators.py create mode 100644 .deps_siteforge_v812/pydantic/version.py create mode 100644 .deps_siteforge_v812/pydantic/warnings.py create mode 100644 .deps_siteforge_v812/pydantic_core-2.46.5.dist-info/INSTALLER create mode 100644 .deps_siteforge_v812/pydantic_core-2.46.5.dist-info/METADATA create mode 100644 .deps_siteforge_v812/pydantic_core-2.46.5.dist-info/RECORD create mode 100644 .deps_siteforge_v812/pydantic_core-2.46.5.dist-info/REQUESTED create mode 100644 .deps_siteforge_v812/pydantic_core-2.46.5.dist-info/WHEEL create mode 100644 .deps_siteforge_v812/pydantic_core-2.46.5.dist-info/licenses/LICENSE create mode 100644 .deps_siteforge_v812/pydantic_core-2.46.5.dist-info/sboms/pydantic-core.cyclonedx.json create mode 100644 .deps_siteforge_v812/pydantic_core/__init__.py create mode 100644 .deps_siteforge_v812/pydantic_core/__pycache__/__init__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pydantic_core/__pycache__/core_schema.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pydantic_core/_pydantic_core.cp312-win_amd64.pyd create mode 100644 .deps_siteforge_v812/pydantic_core/_pydantic_core.pyi create mode 100644 .deps_siteforge_v812/pydantic_core/core_schema.py create mode 100644 .deps_siteforge_v812/pydantic_core/py.typed create mode 100644 .deps_siteforge_v812/pygments-2.20.0.dist-info/INSTALLER create mode 100644 .deps_siteforge_v812/pygments-2.20.0.dist-info/METADATA create mode 100644 .deps_siteforge_v812/pygments-2.20.0.dist-info/RECORD create mode 100644 .deps_siteforge_v812/pygments-2.20.0.dist-info/REQUESTED create mode 100644 .deps_siteforge_v812/pygments-2.20.0.dist-info/WHEEL create mode 100644 .deps_siteforge_v812/pygments-2.20.0.dist-info/entry_points.txt create mode 100644 .deps_siteforge_v812/pygments-2.20.0.dist-info/licenses/AUTHORS create mode 100644 .deps_siteforge_v812/pygments-2.20.0.dist-info/licenses/LICENSE create mode 100644 .deps_siteforge_v812/pygments/__init__.py create mode 100644 .deps_siteforge_v812/pygments/__main__.py create mode 100644 .deps_siteforge_v812/pygments/__pycache__/__init__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/__pycache__/__main__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/__pycache__/cmdline.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/__pycache__/console.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/__pycache__/filter.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/__pycache__/formatter.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/__pycache__/lexer.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/__pycache__/modeline.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/__pycache__/plugin.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/__pycache__/regexopt.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/__pycache__/scanner.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/__pycache__/sphinxext.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/__pycache__/style.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/__pycache__/token.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/__pycache__/unistring.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/__pycache__/util.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/cmdline.py create mode 100644 .deps_siteforge_v812/pygments/console.py create mode 100644 .deps_siteforge_v812/pygments/filter.py create mode 100644 .deps_siteforge_v812/pygments/filters/__init__.py create mode 100644 .deps_siteforge_v812/pygments/filters/__pycache__/__init__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/formatter.py create mode 100644 .deps_siteforge_v812/pygments/formatters/__init__.py create mode 100644 .deps_siteforge_v812/pygments/formatters/__pycache__/__init__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/formatters/__pycache__/_mapping.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/formatters/__pycache__/bbcode.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/formatters/__pycache__/groff.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/formatters/__pycache__/html.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/formatters/__pycache__/img.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/formatters/__pycache__/irc.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/formatters/__pycache__/latex.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/formatters/__pycache__/other.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/formatters/__pycache__/pangomarkup.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/formatters/__pycache__/rtf.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/formatters/__pycache__/svg.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/formatters/__pycache__/terminal.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/formatters/__pycache__/terminal256.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/formatters/_mapping.py create mode 100644 .deps_siteforge_v812/pygments/formatters/bbcode.py create mode 100644 .deps_siteforge_v812/pygments/formatters/groff.py create mode 100644 .deps_siteforge_v812/pygments/formatters/html.py create mode 100644 .deps_siteforge_v812/pygments/formatters/img.py create mode 100644 .deps_siteforge_v812/pygments/formatters/irc.py create mode 100644 .deps_siteforge_v812/pygments/formatters/latex.py create mode 100644 .deps_siteforge_v812/pygments/formatters/other.py create mode 100644 .deps_siteforge_v812/pygments/formatters/pangomarkup.py create mode 100644 .deps_siteforge_v812/pygments/formatters/rtf.py create mode 100644 .deps_siteforge_v812/pygments/formatters/svg.py create mode 100644 .deps_siteforge_v812/pygments/formatters/terminal.py create mode 100644 .deps_siteforge_v812/pygments/formatters/terminal256.py create mode 100644 .deps_siteforge_v812/pygments/lexer.py create mode 100644 .deps_siteforge_v812/pygments/lexers/__init__.py create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/__init__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/_ada_builtins.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/_asy_builtins.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/_cl_builtins.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/_cocoa_builtins.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/_csound_builtins.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/_css_builtins.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/_googlesql_builtins.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/_julia_builtins.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/_lasso_builtins.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/_lilypond_builtins.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/_lua_builtins.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/_luau_builtins.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/_mapping.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/_mql_builtins.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/_mysql_builtins.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/_openedge_builtins.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/_php_builtins.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/_postgres_builtins.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/_qlik_builtins.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/_scheme_builtins.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/_scilab_builtins.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/_sourcemod_builtins.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/_sql_builtins.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/_stan_builtins.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/_stata_builtins.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/_tsql_builtins.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/_usd_builtins.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/_vbscript_builtins.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/_vim_builtins.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/actionscript.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/ada.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/agile.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/algebra.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/ambient.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/amdgpu.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/ampl.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/apdlexer.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/apl.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/archetype.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/arrow.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/arturo.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/asc.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/asm.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/asn1.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/automation.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/bare.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/basic.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/bdd.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/berry.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/bibtex.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/blueprint.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/boa.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/bqn.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/business.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/c_cpp.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/c_like.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/capnproto.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/carbon.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/cddl.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/chapel.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/clean.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/codeql.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/comal.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/compiled.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/configs.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/console.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/cplint.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/crystal.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/csound.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/css.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/d.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/dalvik.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/data.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/dax.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/devicetree.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/diff.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/dns.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/dotnet.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/dsls.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/dylan.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/ecl.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/eiffel.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/elm.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/elpi.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/email.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/erlang.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/esoteric.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/ezhil.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/factor.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/fantom.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/felix.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/fift.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/floscript.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/forth.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/fortran.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/foxpro.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/freefem.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/func.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/functional.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/futhark.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/gcodelexer.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/gdscript.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/gleam.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/go.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/grammar_notation.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/graph.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/graphics.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/graphql.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/graphviz.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/gsql.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/hare.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/haskell.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/haxe.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/hdl.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/hexdump.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/html.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/idl.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/igor.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/inferno.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/installers.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/int_fiction.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/iolang.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/j.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/javascript.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/jmespath.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/jslt.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/json5.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/jsonnet.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/jsx.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/julia.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/jvm.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/kuin.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/kusto.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/ldap.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/lean.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/lilypond.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/lisp.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/macaulay2.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/make.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/maple.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/markup.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/math.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/matlab.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/maxima.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/meson.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/mime.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/minecraft.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/mips.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/ml.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/modeling.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/modula2.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/mojo.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/monte.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/mosel.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/ncl.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/nimrod.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/nit.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/nix.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/numbair.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/oberon.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/objective.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/ooc.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/openscad.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/other.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/parasail.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/parsers.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/pascal.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/pawn.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/pddl.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/perl.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/phix.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/php.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/pointless.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/pony.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/praat.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/procfile.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/prolog.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/promql.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/prql.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/ptx.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/python.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/q.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/qlik.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/qvt.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/r.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/rdf.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/rebol.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/rego.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/rell.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/resource.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/ride.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/rita.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/rnc.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/roboconf.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/robotframework.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/ruby.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/rust.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/sas.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/savi.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/scdoc.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/scripting.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/sgf.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/shell.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/sieve.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/slash.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/smalltalk.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/smithy.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/smv.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/snobol.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/solidity.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/soong.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/sophia.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/special.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/spice.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/sql.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/srcinfo.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/stata.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/supercollider.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/tablegen.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/tact.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/tal.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/tcl.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/teal.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/templates.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/teraterm.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/testing.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/text.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/textedit.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/textfmts.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/theorem.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/thingsdb.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/tlb.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/tls.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/tnt.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/trafficscript.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/typoscript.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/typst.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/ul4.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/unicon.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/urbi.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/usd.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/varnish.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/verification.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/verifpal.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/vip.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/vyper.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/web.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/webassembly.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/webidl.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/webmisc.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/wgsl.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/whiley.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/wowtoc.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/wren.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/x10.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/xorg.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/yang.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/yara.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/__pycache__/zig.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/lexers/_ada_builtins.py create mode 100644 .deps_siteforge_v812/pygments/lexers/_asy_builtins.py create mode 100644 .deps_siteforge_v812/pygments/lexers/_cl_builtins.py create mode 100644 .deps_siteforge_v812/pygments/lexers/_cocoa_builtins.py create mode 100644 .deps_siteforge_v812/pygments/lexers/_csound_builtins.py create mode 100644 .deps_siteforge_v812/pygments/lexers/_css_builtins.py create mode 100644 .deps_siteforge_v812/pygments/lexers/_googlesql_builtins.py create mode 100644 .deps_siteforge_v812/pygments/lexers/_julia_builtins.py create mode 100644 .deps_siteforge_v812/pygments/lexers/_lasso_builtins.py create mode 100644 .deps_siteforge_v812/pygments/lexers/_lilypond_builtins.py create mode 100644 .deps_siteforge_v812/pygments/lexers/_lua_builtins.py create mode 100644 .deps_siteforge_v812/pygments/lexers/_luau_builtins.py create mode 100644 .deps_siteforge_v812/pygments/lexers/_mapping.py create mode 100644 .deps_siteforge_v812/pygments/lexers/_mql_builtins.py create mode 100644 .deps_siteforge_v812/pygments/lexers/_mysql_builtins.py create mode 100644 .deps_siteforge_v812/pygments/lexers/_openedge_builtins.py create mode 100644 .deps_siteforge_v812/pygments/lexers/_php_builtins.py create mode 100644 .deps_siteforge_v812/pygments/lexers/_postgres_builtins.py create mode 100644 .deps_siteforge_v812/pygments/lexers/_qlik_builtins.py create mode 100644 .deps_siteforge_v812/pygments/lexers/_scheme_builtins.py create mode 100644 .deps_siteforge_v812/pygments/lexers/_scilab_builtins.py create mode 100644 .deps_siteforge_v812/pygments/lexers/_sourcemod_builtins.py create mode 100644 .deps_siteforge_v812/pygments/lexers/_sql_builtins.py create mode 100644 .deps_siteforge_v812/pygments/lexers/_stan_builtins.py create mode 100644 .deps_siteforge_v812/pygments/lexers/_stata_builtins.py create mode 100644 .deps_siteforge_v812/pygments/lexers/_tsql_builtins.py create mode 100644 .deps_siteforge_v812/pygments/lexers/_usd_builtins.py create mode 100644 .deps_siteforge_v812/pygments/lexers/_vbscript_builtins.py create mode 100644 .deps_siteforge_v812/pygments/lexers/_vim_builtins.py create mode 100644 .deps_siteforge_v812/pygments/lexers/actionscript.py create mode 100644 .deps_siteforge_v812/pygments/lexers/ada.py create mode 100644 .deps_siteforge_v812/pygments/lexers/agile.py create mode 100644 .deps_siteforge_v812/pygments/lexers/algebra.py create mode 100644 .deps_siteforge_v812/pygments/lexers/ambient.py create mode 100644 .deps_siteforge_v812/pygments/lexers/amdgpu.py create mode 100644 .deps_siteforge_v812/pygments/lexers/ampl.py create mode 100644 .deps_siteforge_v812/pygments/lexers/apdlexer.py create mode 100644 .deps_siteforge_v812/pygments/lexers/apl.py create mode 100644 .deps_siteforge_v812/pygments/lexers/archetype.py create mode 100644 .deps_siteforge_v812/pygments/lexers/arrow.py create mode 100644 .deps_siteforge_v812/pygments/lexers/arturo.py create mode 100644 .deps_siteforge_v812/pygments/lexers/asc.py create mode 100644 .deps_siteforge_v812/pygments/lexers/asm.py create mode 100644 .deps_siteforge_v812/pygments/lexers/asn1.py create mode 100644 .deps_siteforge_v812/pygments/lexers/automation.py create mode 100644 .deps_siteforge_v812/pygments/lexers/bare.py create mode 100644 .deps_siteforge_v812/pygments/lexers/basic.py create mode 100644 .deps_siteforge_v812/pygments/lexers/bdd.py create mode 100644 .deps_siteforge_v812/pygments/lexers/berry.py create mode 100644 .deps_siteforge_v812/pygments/lexers/bibtex.py create mode 100644 .deps_siteforge_v812/pygments/lexers/blueprint.py create mode 100644 .deps_siteforge_v812/pygments/lexers/boa.py create mode 100644 .deps_siteforge_v812/pygments/lexers/bqn.py create mode 100644 .deps_siteforge_v812/pygments/lexers/business.py create mode 100644 .deps_siteforge_v812/pygments/lexers/c_cpp.py create mode 100644 .deps_siteforge_v812/pygments/lexers/c_like.py create mode 100644 .deps_siteforge_v812/pygments/lexers/capnproto.py create mode 100644 .deps_siteforge_v812/pygments/lexers/carbon.py create mode 100644 .deps_siteforge_v812/pygments/lexers/cddl.py create mode 100644 .deps_siteforge_v812/pygments/lexers/chapel.py create mode 100644 .deps_siteforge_v812/pygments/lexers/clean.py create mode 100644 .deps_siteforge_v812/pygments/lexers/codeql.py create mode 100644 .deps_siteforge_v812/pygments/lexers/comal.py create mode 100644 .deps_siteforge_v812/pygments/lexers/compiled.py create mode 100644 .deps_siteforge_v812/pygments/lexers/configs.py create mode 100644 .deps_siteforge_v812/pygments/lexers/console.py create mode 100644 .deps_siteforge_v812/pygments/lexers/cplint.py create mode 100644 .deps_siteforge_v812/pygments/lexers/crystal.py create mode 100644 .deps_siteforge_v812/pygments/lexers/csound.py create mode 100644 .deps_siteforge_v812/pygments/lexers/css.py create mode 100644 .deps_siteforge_v812/pygments/lexers/d.py create mode 100644 .deps_siteforge_v812/pygments/lexers/dalvik.py create mode 100644 .deps_siteforge_v812/pygments/lexers/data.py create mode 100644 .deps_siteforge_v812/pygments/lexers/dax.py create mode 100644 .deps_siteforge_v812/pygments/lexers/devicetree.py create mode 100644 .deps_siteforge_v812/pygments/lexers/diff.py create mode 100644 .deps_siteforge_v812/pygments/lexers/dns.py create mode 100644 .deps_siteforge_v812/pygments/lexers/dotnet.py create mode 100644 .deps_siteforge_v812/pygments/lexers/dsls.py create mode 100644 .deps_siteforge_v812/pygments/lexers/dylan.py create mode 100644 .deps_siteforge_v812/pygments/lexers/ecl.py create mode 100644 .deps_siteforge_v812/pygments/lexers/eiffel.py create mode 100644 .deps_siteforge_v812/pygments/lexers/elm.py create mode 100644 .deps_siteforge_v812/pygments/lexers/elpi.py create mode 100644 .deps_siteforge_v812/pygments/lexers/email.py create mode 100644 .deps_siteforge_v812/pygments/lexers/erlang.py create mode 100644 .deps_siteforge_v812/pygments/lexers/esoteric.py create mode 100644 .deps_siteforge_v812/pygments/lexers/ezhil.py create mode 100644 .deps_siteforge_v812/pygments/lexers/factor.py create mode 100644 .deps_siteforge_v812/pygments/lexers/fantom.py create mode 100644 .deps_siteforge_v812/pygments/lexers/felix.py create mode 100644 .deps_siteforge_v812/pygments/lexers/fift.py create mode 100644 .deps_siteforge_v812/pygments/lexers/floscript.py create mode 100644 .deps_siteforge_v812/pygments/lexers/forth.py create mode 100644 .deps_siteforge_v812/pygments/lexers/fortran.py create mode 100644 .deps_siteforge_v812/pygments/lexers/foxpro.py create mode 100644 .deps_siteforge_v812/pygments/lexers/freefem.py create mode 100644 .deps_siteforge_v812/pygments/lexers/func.py create mode 100644 .deps_siteforge_v812/pygments/lexers/functional.py create mode 100644 .deps_siteforge_v812/pygments/lexers/futhark.py create mode 100644 .deps_siteforge_v812/pygments/lexers/gcodelexer.py create mode 100644 .deps_siteforge_v812/pygments/lexers/gdscript.py create mode 100644 .deps_siteforge_v812/pygments/lexers/gleam.py create mode 100644 .deps_siteforge_v812/pygments/lexers/go.py create mode 100644 .deps_siteforge_v812/pygments/lexers/grammar_notation.py create mode 100644 .deps_siteforge_v812/pygments/lexers/graph.py create mode 100644 .deps_siteforge_v812/pygments/lexers/graphics.py create mode 100644 .deps_siteforge_v812/pygments/lexers/graphql.py create mode 100644 .deps_siteforge_v812/pygments/lexers/graphviz.py create mode 100644 .deps_siteforge_v812/pygments/lexers/gsql.py create mode 100644 .deps_siteforge_v812/pygments/lexers/hare.py create mode 100644 .deps_siteforge_v812/pygments/lexers/haskell.py create mode 100644 .deps_siteforge_v812/pygments/lexers/haxe.py create mode 100644 .deps_siteforge_v812/pygments/lexers/hdl.py create mode 100644 .deps_siteforge_v812/pygments/lexers/hexdump.py create mode 100644 .deps_siteforge_v812/pygments/lexers/html.py create mode 100644 .deps_siteforge_v812/pygments/lexers/idl.py create mode 100644 .deps_siteforge_v812/pygments/lexers/igor.py create mode 100644 .deps_siteforge_v812/pygments/lexers/inferno.py create mode 100644 .deps_siteforge_v812/pygments/lexers/installers.py create mode 100644 .deps_siteforge_v812/pygments/lexers/int_fiction.py create mode 100644 .deps_siteforge_v812/pygments/lexers/iolang.py create mode 100644 .deps_siteforge_v812/pygments/lexers/j.py create mode 100644 .deps_siteforge_v812/pygments/lexers/javascript.py create mode 100644 .deps_siteforge_v812/pygments/lexers/jmespath.py create mode 100644 .deps_siteforge_v812/pygments/lexers/jslt.py create mode 100644 .deps_siteforge_v812/pygments/lexers/json5.py create mode 100644 .deps_siteforge_v812/pygments/lexers/jsonnet.py create mode 100644 .deps_siteforge_v812/pygments/lexers/jsx.py create mode 100644 .deps_siteforge_v812/pygments/lexers/julia.py create mode 100644 .deps_siteforge_v812/pygments/lexers/jvm.py create mode 100644 .deps_siteforge_v812/pygments/lexers/kuin.py create mode 100644 .deps_siteforge_v812/pygments/lexers/kusto.py create mode 100644 .deps_siteforge_v812/pygments/lexers/ldap.py create mode 100644 .deps_siteforge_v812/pygments/lexers/lean.py create mode 100644 .deps_siteforge_v812/pygments/lexers/lilypond.py create mode 100644 .deps_siteforge_v812/pygments/lexers/lisp.py create mode 100644 .deps_siteforge_v812/pygments/lexers/macaulay2.py create mode 100644 .deps_siteforge_v812/pygments/lexers/make.py create mode 100644 .deps_siteforge_v812/pygments/lexers/maple.py create mode 100644 .deps_siteforge_v812/pygments/lexers/markup.py create mode 100644 .deps_siteforge_v812/pygments/lexers/math.py create mode 100644 .deps_siteforge_v812/pygments/lexers/matlab.py create mode 100644 .deps_siteforge_v812/pygments/lexers/maxima.py create mode 100644 .deps_siteforge_v812/pygments/lexers/meson.py create mode 100644 .deps_siteforge_v812/pygments/lexers/mime.py create mode 100644 .deps_siteforge_v812/pygments/lexers/minecraft.py create mode 100644 .deps_siteforge_v812/pygments/lexers/mips.py create mode 100644 .deps_siteforge_v812/pygments/lexers/ml.py create mode 100644 .deps_siteforge_v812/pygments/lexers/modeling.py create mode 100644 .deps_siteforge_v812/pygments/lexers/modula2.py create mode 100644 .deps_siteforge_v812/pygments/lexers/mojo.py create mode 100644 .deps_siteforge_v812/pygments/lexers/monte.py create mode 100644 .deps_siteforge_v812/pygments/lexers/mosel.py create mode 100644 .deps_siteforge_v812/pygments/lexers/ncl.py create mode 100644 .deps_siteforge_v812/pygments/lexers/nimrod.py create mode 100644 .deps_siteforge_v812/pygments/lexers/nit.py create mode 100644 .deps_siteforge_v812/pygments/lexers/nix.py create mode 100644 .deps_siteforge_v812/pygments/lexers/numbair.py create mode 100644 .deps_siteforge_v812/pygments/lexers/oberon.py create mode 100644 .deps_siteforge_v812/pygments/lexers/objective.py create mode 100644 .deps_siteforge_v812/pygments/lexers/ooc.py create mode 100644 .deps_siteforge_v812/pygments/lexers/openscad.py create mode 100644 .deps_siteforge_v812/pygments/lexers/other.py create mode 100644 .deps_siteforge_v812/pygments/lexers/parasail.py create mode 100644 .deps_siteforge_v812/pygments/lexers/parsers.py create mode 100644 .deps_siteforge_v812/pygments/lexers/pascal.py create mode 100644 .deps_siteforge_v812/pygments/lexers/pawn.py create mode 100644 .deps_siteforge_v812/pygments/lexers/pddl.py create mode 100644 .deps_siteforge_v812/pygments/lexers/perl.py create mode 100644 .deps_siteforge_v812/pygments/lexers/phix.py create mode 100644 .deps_siteforge_v812/pygments/lexers/php.py create mode 100644 .deps_siteforge_v812/pygments/lexers/pointless.py create mode 100644 .deps_siteforge_v812/pygments/lexers/pony.py create mode 100644 .deps_siteforge_v812/pygments/lexers/praat.py create mode 100644 .deps_siteforge_v812/pygments/lexers/procfile.py create mode 100644 .deps_siteforge_v812/pygments/lexers/prolog.py create mode 100644 .deps_siteforge_v812/pygments/lexers/promql.py create mode 100644 .deps_siteforge_v812/pygments/lexers/prql.py create mode 100644 .deps_siteforge_v812/pygments/lexers/ptx.py create mode 100644 .deps_siteforge_v812/pygments/lexers/python.py create mode 100644 .deps_siteforge_v812/pygments/lexers/q.py create mode 100644 .deps_siteforge_v812/pygments/lexers/qlik.py create mode 100644 .deps_siteforge_v812/pygments/lexers/qvt.py create mode 100644 .deps_siteforge_v812/pygments/lexers/r.py create mode 100644 .deps_siteforge_v812/pygments/lexers/rdf.py create mode 100644 .deps_siteforge_v812/pygments/lexers/rebol.py create mode 100644 .deps_siteforge_v812/pygments/lexers/rego.py create mode 100644 .deps_siteforge_v812/pygments/lexers/rell.py create mode 100644 .deps_siteforge_v812/pygments/lexers/resource.py create mode 100644 .deps_siteforge_v812/pygments/lexers/ride.py create mode 100644 .deps_siteforge_v812/pygments/lexers/rita.py create mode 100644 .deps_siteforge_v812/pygments/lexers/rnc.py create mode 100644 .deps_siteforge_v812/pygments/lexers/roboconf.py create mode 100644 .deps_siteforge_v812/pygments/lexers/robotframework.py create mode 100644 .deps_siteforge_v812/pygments/lexers/ruby.py create mode 100644 .deps_siteforge_v812/pygments/lexers/rust.py create mode 100644 .deps_siteforge_v812/pygments/lexers/sas.py create mode 100644 .deps_siteforge_v812/pygments/lexers/savi.py create mode 100644 .deps_siteforge_v812/pygments/lexers/scdoc.py create mode 100644 .deps_siteforge_v812/pygments/lexers/scripting.py create mode 100644 .deps_siteforge_v812/pygments/lexers/sgf.py create mode 100644 .deps_siteforge_v812/pygments/lexers/shell.py create mode 100644 .deps_siteforge_v812/pygments/lexers/sieve.py create mode 100644 .deps_siteforge_v812/pygments/lexers/slash.py create mode 100644 .deps_siteforge_v812/pygments/lexers/smalltalk.py create mode 100644 .deps_siteforge_v812/pygments/lexers/smithy.py create mode 100644 .deps_siteforge_v812/pygments/lexers/smv.py create mode 100644 .deps_siteforge_v812/pygments/lexers/snobol.py create mode 100644 .deps_siteforge_v812/pygments/lexers/solidity.py create mode 100644 .deps_siteforge_v812/pygments/lexers/soong.py create mode 100644 .deps_siteforge_v812/pygments/lexers/sophia.py create mode 100644 .deps_siteforge_v812/pygments/lexers/special.py create mode 100644 .deps_siteforge_v812/pygments/lexers/spice.py create mode 100644 .deps_siteforge_v812/pygments/lexers/sql.py create mode 100644 .deps_siteforge_v812/pygments/lexers/srcinfo.py create mode 100644 .deps_siteforge_v812/pygments/lexers/stata.py create mode 100644 .deps_siteforge_v812/pygments/lexers/supercollider.py create mode 100644 .deps_siteforge_v812/pygments/lexers/tablegen.py create mode 100644 .deps_siteforge_v812/pygments/lexers/tact.py create mode 100644 .deps_siteforge_v812/pygments/lexers/tal.py create mode 100644 .deps_siteforge_v812/pygments/lexers/tcl.py create mode 100644 .deps_siteforge_v812/pygments/lexers/teal.py create mode 100644 .deps_siteforge_v812/pygments/lexers/templates.py create mode 100644 .deps_siteforge_v812/pygments/lexers/teraterm.py create mode 100644 .deps_siteforge_v812/pygments/lexers/testing.py create mode 100644 .deps_siteforge_v812/pygments/lexers/text.py create mode 100644 .deps_siteforge_v812/pygments/lexers/textedit.py create mode 100644 .deps_siteforge_v812/pygments/lexers/textfmts.py create mode 100644 .deps_siteforge_v812/pygments/lexers/theorem.py create mode 100644 .deps_siteforge_v812/pygments/lexers/thingsdb.py create mode 100644 .deps_siteforge_v812/pygments/lexers/tlb.py create mode 100644 .deps_siteforge_v812/pygments/lexers/tls.py create mode 100644 .deps_siteforge_v812/pygments/lexers/tnt.py create mode 100644 .deps_siteforge_v812/pygments/lexers/trafficscript.py create mode 100644 .deps_siteforge_v812/pygments/lexers/typoscript.py create mode 100644 .deps_siteforge_v812/pygments/lexers/typst.py create mode 100644 .deps_siteforge_v812/pygments/lexers/ul4.py create mode 100644 .deps_siteforge_v812/pygments/lexers/unicon.py create mode 100644 .deps_siteforge_v812/pygments/lexers/urbi.py create mode 100644 .deps_siteforge_v812/pygments/lexers/usd.py create mode 100644 .deps_siteforge_v812/pygments/lexers/varnish.py create mode 100644 .deps_siteforge_v812/pygments/lexers/verification.py create mode 100644 .deps_siteforge_v812/pygments/lexers/verifpal.py create mode 100644 .deps_siteforge_v812/pygments/lexers/vip.py create mode 100644 .deps_siteforge_v812/pygments/lexers/vyper.py create mode 100644 .deps_siteforge_v812/pygments/lexers/web.py create mode 100644 .deps_siteforge_v812/pygments/lexers/webassembly.py create mode 100644 .deps_siteforge_v812/pygments/lexers/webidl.py create mode 100644 .deps_siteforge_v812/pygments/lexers/webmisc.py create mode 100644 .deps_siteforge_v812/pygments/lexers/wgsl.py create mode 100644 .deps_siteforge_v812/pygments/lexers/whiley.py create mode 100644 .deps_siteforge_v812/pygments/lexers/wowtoc.py create mode 100644 .deps_siteforge_v812/pygments/lexers/wren.py create mode 100644 .deps_siteforge_v812/pygments/lexers/x10.py create mode 100644 .deps_siteforge_v812/pygments/lexers/xorg.py create mode 100644 .deps_siteforge_v812/pygments/lexers/yang.py create mode 100644 .deps_siteforge_v812/pygments/lexers/yara.py create mode 100644 .deps_siteforge_v812/pygments/lexers/zig.py create mode 100644 .deps_siteforge_v812/pygments/modeline.py create mode 100644 .deps_siteforge_v812/pygments/plugin.py create mode 100644 .deps_siteforge_v812/pygments/regexopt.py create mode 100644 .deps_siteforge_v812/pygments/scanner.py create mode 100644 .deps_siteforge_v812/pygments/sphinxext.py create mode 100644 .deps_siteforge_v812/pygments/style.py create mode 100644 .deps_siteforge_v812/pygments/styles/__init__.py create mode 100644 .deps_siteforge_v812/pygments/styles/__pycache__/__init__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/styles/__pycache__/_mapping.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/styles/__pycache__/abap.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/styles/__pycache__/algol.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/styles/__pycache__/algol_nu.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/styles/__pycache__/arduino.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/styles/__pycache__/autumn.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/styles/__pycache__/borland.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/styles/__pycache__/bw.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/styles/__pycache__/coffee.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/styles/__pycache__/colorful.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/styles/__pycache__/default.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/styles/__pycache__/dracula.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/styles/__pycache__/emacs.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/styles/__pycache__/friendly.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/styles/__pycache__/friendly_grayscale.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/styles/__pycache__/fruity.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/styles/__pycache__/gh_dark.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/styles/__pycache__/gruvbox.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/styles/__pycache__/igor.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/styles/__pycache__/inkpot.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/styles/__pycache__/lightbulb.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/styles/__pycache__/lilypond.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/styles/__pycache__/lovelace.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/styles/__pycache__/manni.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/styles/__pycache__/material.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/styles/__pycache__/monokai.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/styles/__pycache__/murphy.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/styles/__pycache__/native.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/styles/__pycache__/nord.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/styles/__pycache__/onedark.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/styles/__pycache__/paraiso_dark.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/styles/__pycache__/paraiso_light.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/styles/__pycache__/pastie.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/styles/__pycache__/perldoc.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/styles/__pycache__/rainbow_dash.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/styles/__pycache__/rrt.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/styles/__pycache__/sas.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/styles/__pycache__/solarized.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/styles/__pycache__/staroffice.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/styles/__pycache__/stata_dark.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/styles/__pycache__/stata_light.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/styles/__pycache__/tango.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/styles/__pycache__/trac.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/styles/__pycache__/vim.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/styles/__pycache__/vs.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/styles/__pycache__/xcode.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/styles/__pycache__/zenburn.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pygments/styles/_mapping.py create mode 100644 .deps_siteforge_v812/pygments/styles/abap.py create mode 100644 .deps_siteforge_v812/pygments/styles/algol.py create mode 100644 .deps_siteforge_v812/pygments/styles/algol_nu.py create mode 100644 .deps_siteforge_v812/pygments/styles/arduino.py create mode 100644 .deps_siteforge_v812/pygments/styles/autumn.py create mode 100644 .deps_siteforge_v812/pygments/styles/borland.py create mode 100644 .deps_siteforge_v812/pygments/styles/bw.py create mode 100644 .deps_siteforge_v812/pygments/styles/coffee.py create mode 100644 .deps_siteforge_v812/pygments/styles/colorful.py create mode 100644 .deps_siteforge_v812/pygments/styles/default.py create mode 100644 .deps_siteforge_v812/pygments/styles/dracula.py create mode 100644 .deps_siteforge_v812/pygments/styles/emacs.py create mode 100644 .deps_siteforge_v812/pygments/styles/friendly.py create mode 100644 .deps_siteforge_v812/pygments/styles/friendly_grayscale.py create mode 100644 .deps_siteforge_v812/pygments/styles/fruity.py create mode 100644 .deps_siteforge_v812/pygments/styles/gh_dark.py create mode 100644 .deps_siteforge_v812/pygments/styles/gruvbox.py create mode 100644 .deps_siteforge_v812/pygments/styles/igor.py create mode 100644 .deps_siteforge_v812/pygments/styles/inkpot.py create mode 100644 .deps_siteforge_v812/pygments/styles/lightbulb.py create mode 100644 .deps_siteforge_v812/pygments/styles/lilypond.py create mode 100644 .deps_siteforge_v812/pygments/styles/lovelace.py create mode 100644 .deps_siteforge_v812/pygments/styles/manni.py create mode 100644 .deps_siteforge_v812/pygments/styles/material.py create mode 100644 .deps_siteforge_v812/pygments/styles/monokai.py create mode 100644 .deps_siteforge_v812/pygments/styles/murphy.py create mode 100644 .deps_siteforge_v812/pygments/styles/native.py create mode 100644 .deps_siteforge_v812/pygments/styles/nord.py create mode 100644 .deps_siteforge_v812/pygments/styles/onedark.py create mode 100644 .deps_siteforge_v812/pygments/styles/paraiso_dark.py create mode 100644 .deps_siteforge_v812/pygments/styles/paraiso_light.py create mode 100644 .deps_siteforge_v812/pygments/styles/pastie.py create mode 100644 .deps_siteforge_v812/pygments/styles/perldoc.py create mode 100644 .deps_siteforge_v812/pygments/styles/rainbow_dash.py create mode 100644 .deps_siteforge_v812/pygments/styles/rrt.py create mode 100644 .deps_siteforge_v812/pygments/styles/sas.py create mode 100644 .deps_siteforge_v812/pygments/styles/solarized.py create mode 100644 .deps_siteforge_v812/pygments/styles/staroffice.py create mode 100644 .deps_siteforge_v812/pygments/styles/stata_dark.py create mode 100644 .deps_siteforge_v812/pygments/styles/stata_light.py create mode 100644 .deps_siteforge_v812/pygments/styles/tango.py create mode 100644 .deps_siteforge_v812/pygments/styles/trac.py create mode 100644 .deps_siteforge_v812/pygments/styles/vim.py create mode 100644 .deps_siteforge_v812/pygments/styles/vs.py create mode 100644 .deps_siteforge_v812/pygments/styles/xcode.py create mode 100644 .deps_siteforge_v812/pygments/styles/zenburn.py create mode 100644 .deps_siteforge_v812/pygments/token.py create mode 100644 .deps_siteforge_v812/pygments/unistring.py create mode 100644 .deps_siteforge_v812/pygments/util.py create mode 100644 .deps_siteforge_v812/pymysql-1.1.2.dist-info/INSTALLER create mode 100644 .deps_siteforge_v812/pymysql-1.1.2.dist-info/METADATA create mode 100644 .deps_siteforge_v812/pymysql-1.1.2.dist-info/RECORD create mode 100644 .deps_siteforge_v812/pymysql-1.1.2.dist-info/REQUESTED create mode 100644 .deps_siteforge_v812/pymysql-1.1.2.dist-info/WHEEL create mode 100644 .deps_siteforge_v812/pymysql-1.1.2.dist-info/licenses/LICENSE create mode 100644 .deps_siteforge_v812/pymysql-1.1.2.dist-info/top_level.txt create mode 100644 .deps_siteforge_v812/pymysql/__init__.py create mode 100644 .deps_siteforge_v812/pymysql/__pycache__/__init__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pymysql/__pycache__/_auth.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pymysql/__pycache__/charset.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pymysql/__pycache__/connections.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pymysql/__pycache__/converters.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pymysql/__pycache__/cursors.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pymysql/__pycache__/err.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pymysql/__pycache__/optionfile.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pymysql/__pycache__/protocol.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pymysql/__pycache__/times.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pymysql/_auth.py create mode 100644 .deps_siteforge_v812/pymysql/charset.py create mode 100644 .deps_siteforge_v812/pymysql/connections.py create mode 100644 .deps_siteforge_v812/pymysql/constants/CLIENT.py create mode 100644 .deps_siteforge_v812/pymysql/constants/COMMAND.py create mode 100644 .deps_siteforge_v812/pymysql/constants/CR.py create mode 100644 .deps_siteforge_v812/pymysql/constants/ER.py create mode 100644 .deps_siteforge_v812/pymysql/constants/FIELD_TYPE.py create mode 100644 .deps_siteforge_v812/pymysql/constants/FLAG.py create mode 100644 .deps_siteforge_v812/pymysql/constants/SERVER_STATUS.py create mode 100644 .deps_siteforge_v812/pymysql/constants/__init__.py create mode 100644 .deps_siteforge_v812/pymysql/constants/__pycache__/CLIENT.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pymysql/constants/__pycache__/COMMAND.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pymysql/constants/__pycache__/CR.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pymysql/constants/__pycache__/ER.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pymysql/constants/__pycache__/FIELD_TYPE.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pymysql/constants/__pycache__/FLAG.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pymysql/constants/__pycache__/SERVER_STATUS.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pymysql/constants/__pycache__/__init__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pymysql/converters.py create mode 100644 .deps_siteforge_v812/pymysql/cursors.py create mode 100644 .deps_siteforge_v812/pymysql/err.py create mode 100644 .deps_siteforge_v812/pymysql/optionfile.py create mode 100644 .deps_siteforge_v812/pymysql/protocol.py create mode 100644 .deps_siteforge_v812/pymysql/times.py create mode 100644 .deps_siteforge_v812/pytest-9.0.2.dist-info/INSTALLER create mode 100644 .deps_siteforge_v812/pytest-9.0.2.dist-info/METADATA create mode 100644 .deps_siteforge_v812/pytest-9.0.2.dist-info/RECORD create mode 100644 .deps_siteforge_v812/pytest-9.0.2.dist-info/REQUESTED create mode 100644 .deps_siteforge_v812/pytest-9.0.2.dist-info/WHEEL create mode 100644 .deps_siteforge_v812/pytest-9.0.2.dist-info/entry_points.txt create mode 100644 .deps_siteforge_v812/pytest-9.0.2.dist-info/licenses/LICENSE create mode 100644 .deps_siteforge_v812/pytest-9.0.2.dist-info/top_level.txt create mode 100644 .deps_siteforge_v812/pytest/__init__.py create mode 100644 .deps_siteforge_v812/pytest/__main__.py create mode 100644 .deps_siteforge_v812/pytest/__pycache__/__init__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pytest/__pycache__/__main__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/pytest/py.typed create mode 100644 .deps_siteforge_v812/python_multipart-0.0.29.dist-info/INSTALLER create mode 100644 .deps_siteforge_v812/python_multipart-0.0.29.dist-info/METADATA create mode 100644 .deps_siteforge_v812/python_multipart-0.0.29.dist-info/RECORD create mode 100644 .deps_siteforge_v812/python_multipart-0.0.29.dist-info/REQUESTED create mode 100644 .deps_siteforge_v812/python_multipart-0.0.29.dist-info/WHEEL create mode 100644 .deps_siteforge_v812/python_multipart-0.0.29.dist-info/licenses/LICENSE.txt create mode 100644 .deps_siteforge_v812/python_multipart/__init__.py create mode 100644 .deps_siteforge_v812/python_multipart/__pycache__/__init__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/python_multipart/__pycache__/decoders.cpython-312.pyc create mode 100644 .deps_siteforge_v812/python_multipart/__pycache__/exceptions.cpython-312.pyc create mode 100644 .deps_siteforge_v812/python_multipart/__pycache__/multipart.cpython-312.pyc create mode 100644 .deps_siteforge_v812/python_multipart/decoders.py create mode 100644 .deps_siteforge_v812/python_multipart/exceptions.py create mode 100644 .deps_siteforge_v812/python_multipart/multipart.py create mode 100644 .deps_siteforge_v812/python_multipart/py.typed create mode 100644 .deps_siteforge_v812/pyyaml-6.0.3.dist-info/INSTALLER create mode 100644 .deps_siteforge_v812/pyyaml-6.0.3.dist-info/METADATA create mode 100644 .deps_siteforge_v812/pyyaml-6.0.3.dist-info/RECORD create mode 100644 .deps_siteforge_v812/pyyaml-6.0.3.dist-info/REQUESTED create mode 100644 .deps_siteforge_v812/pyyaml-6.0.3.dist-info/WHEEL create mode 100644 .deps_siteforge_v812/pyyaml-6.0.3.dist-info/licenses/LICENSE create mode 100644 .deps_siteforge_v812/pyyaml-6.0.3.dist-info/top_level.txt create mode 100644 .deps_siteforge_v812/six-1.17.0.dist-info/INSTALLER create mode 100644 .deps_siteforge_v812/six-1.17.0.dist-info/LICENSE create mode 100644 .deps_siteforge_v812/six-1.17.0.dist-info/METADATA create mode 100644 .deps_siteforge_v812/six-1.17.0.dist-info/RECORD create mode 100644 .deps_siteforge_v812/six-1.17.0.dist-info/REQUESTED create mode 100644 .deps_siteforge_v812/six-1.17.0.dist-info/WHEEL create mode 100644 .deps_siteforge_v812/six-1.17.0.dist-info/top_level.txt create mode 100644 .deps_siteforge_v812/six.py create mode 100644 .deps_siteforge_v812/soupsieve-2.8.4.dist-info/INSTALLER create mode 100644 .deps_siteforge_v812/soupsieve-2.8.4.dist-info/METADATA create mode 100644 .deps_siteforge_v812/soupsieve-2.8.4.dist-info/RECORD create mode 100644 .deps_siteforge_v812/soupsieve-2.8.4.dist-info/REQUESTED create mode 100644 .deps_siteforge_v812/soupsieve-2.8.4.dist-info/WHEEL create mode 100644 .deps_siteforge_v812/soupsieve-2.8.4.dist-info/licenses/LICENSE.md create mode 100644 .deps_siteforge_v812/soupsieve/__init__.py create mode 100644 .deps_siteforge_v812/soupsieve/__meta__.py create mode 100644 .deps_siteforge_v812/soupsieve/__pycache__/__init__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/soupsieve/__pycache__/__meta__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/soupsieve/__pycache__/css_match.cpython-312.pyc create mode 100644 .deps_siteforge_v812/soupsieve/__pycache__/css_parser.cpython-312.pyc create mode 100644 .deps_siteforge_v812/soupsieve/__pycache__/css_types.cpython-312.pyc create mode 100644 .deps_siteforge_v812/soupsieve/__pycache__/pretty.cpython-312.pyc create mode 100644 .deps_siteforge_v812/soupsieve/__pycache__/util.cpython-312.pyc create mode 100644 .deps_siteforge_v812/soupsieve/css_match.py create mode 100644 .deps_siteforge_v812/soupsieve/css_parser.py create mode 100644 .deps_siteforge_v812/soupsieve/css_types.py create mode 100644 .deps_siteforge_v812/soupsieve/pretty.py create mode 100644 .deps_siteforge_v812/soupsieve/py.typed create mode 100644 .deps_siteforge_v812/soupsieve/util.py create mode 100644 .deps_siteforge_v812/starlette-0.50.0.dist-info/INSTALLER create mode 100644 .deps_siteforge_v812/starlette-0.50.0.dist-info/METADATA create mode 100644 .deps_siteforge_v812/starlette-0.50.0.dist-info/RECORD create mode 100644 .deps_siteforge_v812/starlette-0.50.0.dist-info/REQUESTED create mode 100644 .deps_siteforge_v812/starlette-0.50.0.dist-info/WHEEL create mode 100644 .deps_siteforge_v812/starlette-0.50.0.dist-info/licenses/LICENSE.md create mode 100644 .deps_siteforge_v812/starlette/__init__.py create mode 100644 .deps_siteforge_v812/starlette/__pycache__/__init__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/starlette/__pycache__/_exception_handler.cpython-312.pyc create mode 100644 .deps_siteforge_v812/starlette/__pycache__/_utils.cpython-312.pyc create mode 100644 .deps_siteforge_v812/starlette/__pycache__/applications.cpython-312.pyc create mode 100644 .deps_siteforge_v812/starlette/__pycache__/authentication.cpython-312.pyc create mode 100644 .deps_siteforge_v812/starlette/__pycache__/background.cpython-312.pyc create mode 100644 .deps_siteforge_v812/starlette/__pycache__/concurrency.cpython-312.pyc create mode 100644 .deps_siteforge_v812/starlette/__pycache__/config.cpython-312.pyc create mode 100644 .deps_siteforge_v812/starlette/__pycache__/convertors.cpython-312.pyc create mode 100644 .deps_siteforge_v812/starlette/__pycache__/datastructures.cpython-312.pyc create mode 100644 .deps_siteforge_v812/starlette/__pycache__/endpoints.cpython-312.pyc create mode 100644 .deps_siteforge_v812/starlette/__pycache__/exceptions.cpython-312.pyc create mode 100644 .deps_siteforge_v812/starlette/__pycache__/formparsers.cpython-312.pyc create mode 100644 .deps_siteforge_v812/starlette/__pycache__/requests.cpython-312.pyc create mode 100644 .deps_siteforge_v812/starlette/__pycache__/responses.cpython-312.pyc create mode 100644 .deps_siteforge_v812/starlette/__pycache__/routing.cpython-312.pyc create mode 100644 .deps_siteforge_v812/starlette/__pycache__/schemas.cpython-312.pyc create mode 100644 .deps_siteforge_v812/starlette/__pycache__/staticfiles.cpython-312.pyc create mode 100644 .deps_siteforge_v812/starlette/__pycache__/status.cpython-312.pyc create mode 100644 .deps_siteforge_v812/starlette/__pycache__/templating.cpython-312.pyc create mode 100644 .deps_siteforge_v812/starlette/__pycache__/testclient.cpython-312.pyc create mode 100644 .deps_siteforge_v812/starlette/__pycache__/types.cpython-312.pyc create mode 100644 .deps_siteforge_v812/starlette/__pycache__/websockets.cpython-312.pyc create mode 100644 .deps_siteforge_v812/starlette/_exception_handler.py create mode 100644 .deps_siteforge_v812/starlette/_utils.py create mode 100644 .deps_siteforge_v812/starlette/applications.py create mode 100644 .deps_siteforge_v812/starlette/authentication.py create mode 100644 .deps_siteforge_v812/starlette/background.py create mode 100644 .deps_siteforge_v812/starlette/concurrency.py create mode 100644 .deps_siteforge_v812/starlette/config.py create mode 100644 .deps_siteforge_v812/starlette/convertors.py create mode 100644 .deps_siteforge_v812/starlette/datastructures.py create mode 100644 .deps_siteforge_v812/starlette/endpoints.py create mode 100644 .deps_siteforge_v812/starlette/exceptions.py create mode 100644 .deps_siteforge_v812/starlette/formparsers.py create mode 100644 .deps_siteforge_v812/starlette/middleware/__init__.py create mode 100644 .deps_siteforge_v812/starlette/middleware/__pycache__/__init__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/starlette/middleware/__pycache__/authentication.cpython-312.pyc create mode 100644 .deps_siteforge_v812/starlette/middleware/__pycache__/base.cpython-312.pyc create mode 100644 .deps_siteforge_v812/starlette/middleware/__pycache__/cors.cpython-312.pyc create mode 100644 .deps_siteforge_v812/starlette/middleware/__pycache__/errors.cpython-312.pyc create mode 100644 .deps_siteforge_v812/starlette/middleware/__pycache__/exceptions.cpython-312.pyc create mode 100644 .deps_siteforge_v812/starlette/middleware/__pycache__/gzip.cpython-312.pyc create mode 100644 .deps_siteforge_v812/starlette/middleware/__pycache__/httpsredirect.cpython-312.pyc create mode 100644 .deps_siteforge_v812/starlette/middleware/__pycache__/sessions.cpython-312.pyc create mode 100644 .deps_siteforge_v812/starlette/middleware/__pycache__/trustedhost.cpython-312.pyc create mode 100644 .deps_siteforge_v812/starlette/middleware/__pycache__/wsgi.cpython-312.pyc create mode 100644 .deps_siteforge_v812/starlette/middleware/authentication.py create mode 100644 .deps_siteforge_v812/starlette/middleware/base.py create mode 100644 .deps_siteforge_v812/starlette/middleware/cors.py create mode 100644 .deps_siteforge_v812/starlette/middleware/errors.py create mode 100644 .deps_siteforge_v812/starlette/middleware/exceptions.py create mode 100644 .deps_siteforge_v812/starlette/middleware/gzip.py create mode 100644 .deps_siteforge_v812/starlette/middleware/httpsredirect.py create mode 100644 .deps_siteforge_v812/starlette/middleware/sessions.py create mode 100644 .deps_siteforge_v812/starlette/middleware/trustedhost.py create mode 100644 .deps_siteforge_v812/starlette/middleware/wsgi.py create mode 100644 .deps_siteforge_v812/starlette/py.typed create mode 100644 .deps_siteforge_v812/starlette/requests.py create mode 100644 .deps_siteforge_v812/starlette/responses.py create mode 100644 .deps_siteforge_v812/starlette/routing.py create mode 100644 .deps_siteforge_v812/starlette/schemas.py create mode 100644 .deps_siteforge_v812/starlette/staticfiles.py create mode 100644 .deps_siteforge_v812/starlette/status.py create mode 100644 .deps_siteforge_v812/starlette/templating.py create mode 100644 .deps_siteforge_v812/starlette/testclient.py create mode 100644 .deps_siteforge_v812/starlette/types.py create mode 100644 .deps_siteforge_v812/starlette/websockets.py create mode 100644 .deps_siteforge_v812/typing_extensions-4.16.0.dist-info/INSTALLER create mode 100644 .deps_siteforge_v812/typing_extensions-4.16.0.dist-info/METADATA create mode 100644 .deps_siteforge_v812/typing_extensions-4.16.0.dist-info/RECORD create mode 100644 .deps_siteforge_v812/typing_extensions-4.16.0.dist-info/REQUESTED create mode 100644 .deps_siteforge_v812/typing_extensions-4.16.0.dist-info/WHEEL create mode 100644 .deps_siteforge_v812/typing_extensions-4.16.0.dist-info/licenses/LICENSE create mode 100644 .deps_siteforge_v812/typing_extensions.py create mode 100644 .deps_siteforge_v812/typing_inspection-0.4.4.dist-info/INSTALLER create mode 100644 .deps_siteforge_v812/typing_inspection-0.4.4.dist-info/METADATA create mode 100644 .deps_siteforge_v812/typing_inspection-0.4.4.dist-info/RECORD create mode 100644 .deps_siteforge_v812/typing_inspection-0.4.4.dist-info/REQUESTED create mode 100644 .deps_siteforge_v812/typing_inspection-0.4.4.dist-info/WHEEL create mode 100644 .deps_siteforge_v812/typing_inspection-0.4.4.dist-info/licenses/LICENSE create mode 100644 .deps_siteforge_v812/typing_inspection/__init__.py create mode 100644 .deps_siteforge_v812/typing_inspection/__pycache__/__init__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/typing_inspection/__pycache__/introspection.cpython-312.pyc create mode 100644 .deps_siteforge_v812/typing_inspection/__pycache__/typing_objects.cpython-312.pyc create mode 100644 .deps_siteforge_v812/typing_inspection/introspection.py create mode 100644 .deps_siteforge_v812/typing_inspection/py.typed create mode 100644 .deps_siteforge_v812/typing_inspection/typing_objects.py create mode 100644 .deps_siteforge_v812/typing_inspection/typing_objects.pyi create mode 100644 .deps_siteforge_v812/uvicorn-0.48.0.dist-info/INSTALLER create mode 100644 .deps_siteforge_v812/uvicorn-0.48.0.dist-info/METADATA create mode 100644 .deps_siteforge_v812/uvicorn-0.48.0.dist-info/RECORD create mode 100644 .deps_siteforge_v812/uvicorn-0.48.0.dist-info/REQUESTED create mode 100644 .deps_siteforge_v812/uvicorn-0.48.0.dist-info/WHEEL create mode 100644 .deps_siteforge_v812/uvicorn-0.48.0.dist-info/entry_points.txt create mode 100644 .deps_siteforge_v812/uvicorn-0.48.0.dist-info/licenses/LICENSE.md create mode 100644 .deps_siteforge_v812/uvicorn/__init__.py create mode 100644 .deps_siteforge_v812/uvicorn/__main__.py create mode 100644 .deps_siteforge_v812/uvicorn/__pycache__/__init__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/uvicorn/__pycache__/__main__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/uvicorn/__pycache__/_compat.cpython-312.pyc create mode 100644 .deps_siteforge_v812/uvicorn/__pycache__/_subprocess.cpython-312.pyc create mode 100644 .deps_siteforge_v812/uvicorn/__pycache__/_types.cpython-312.pyc create mode 100644 .deps_siteforge_v812/uvicorn/__pycache__/config.cpython-312.pyc create mode 100644 .deps_siteforge_v812/uvicorn/__pycache__/importer.cpython-312.pyc create mode 100644 .deps_siteforge_v812/uvicorn/__pycache__/logging.cpython-312.pyc create mode 100644 .deps_siteforge_v812/uvicorn/__pycache__/main.cpython-312.pyc create mode 100644 .deps_siteforge_v812/uvicorn/__pycache__/server.cpython-312.pyc create mode 100644 .deps_siteforge_v812/uvicorn/__pycache__/workers.cpython-312.pyc create mode 100644 .deps_siteforge_v812/uvicorn/_compat.py create mode 100644 .deps_siteforge_v812/uvicorn/_subprocess.py create mode 100644 .deps_siteforge_v812/uvicorn/_types.py create mode 100644 .deps_siteforge_v812/uvicorn/config.py create mode 100644 .deps_siteforge_v812/uvicorn/importer.py create mode 100644 .deps_siteforge_v812/uvicorn/lifespan/__init__.py create mode 100644 .deps_siteforge_v812/uvicorn/lifespan/__pycache__/__init__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/uvicorn/lifespan/__pycache__/off.cpython-312.pyc create mode 100644 .deps_siteforge_v812/uvicorn/lifespan/__pycache__/on.cpython-312.pyc create mode 100644 .deps_siteforge_v812/uvicorn/lifespan/off.py create mode 100644 .deps_siteforge_v812/uvicorn/lifespan/on.py create mode 100644 .deps_siteforge_v812/uvicorn/logging.py create mode 100644 .deps_siteforge_v812/uvicorn/loops/__init__.py create mode 100644 .deps_siteforge_v812/uvicorn/loops/__pycache__/__init__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/uvicorn/loops/__pycache__/asyncio.cpython-312.pyc create mode 100644 .deps_siteforge_v812/uvicorn/loops/__pycache__/auto.cpython-312.pyc create mode 100644 .deps_siteforge_v812/uvicorn/loops/__pycache__/uvloop.cpython-312.pyc create mode 100644 .deps_siteforge_v812/uvicorn/loops/asyncio.py create mode 100644 .deps_siteforge_v812/uvicorn/loops/auto.py create mode 100644 .deps_siteforge_v812/uvicorn/loops/uvloop.py create mode 100644 .deps_siteforge_v812/uvicorn/main.py create mode 100644 .deps_siteforge_v812/uvicorn/middleware/__init__.py create mode 100644 .deps_siteforge_v812/uvicorn/middleware/__pycache__/__init__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/uvicorn/middleware/__pycache__/asgi2.cpython-312.pyc create mode 100644 .deps_siteforge_v812/uvicorn/middleware/__pycache__/message_logger.cpython-312.pyc create mode 100644 .deps_siteforge_v812/uvicorn/middleware/__pycache__/proxy_headers.cpython-312.pyc create mode 100644 .deps_siteforge_v812/uvicorn/middleware/__pycache__/wsgi.cpython-312.pyc create mode 100644 .deps_siteforge_v812/uvicorn/middleware/asgi2.py create mode 100644 .deps_siteforge_v812/uvicorn/middleware/message_logger.py create mode 100644 .deps_siteforge_v812/uvicorn/middleware/proxy_headers.py create mode 100644 .deps_siteforge_v812/uvicorn/middleware/wsgi.py create mode 100644 .deps_siteforge_v812/uvicorn/protocols/__init__.py create mode 100644 .deps_siteforge_v812/uvicorn/protocols/__pycache__/__init__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/uvicorn/protocols/__pycache__/utils.cpython-312.pyc create mode 100644 .deps_siteforge_v812/uvicorn/protocols/http/__init__.py create mode 100644 .deps_siteforge_v812/uvicorn/protocols/http/__pycache__/__init__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/uvicorn/protocols/http/__pycache__/auto.cpython-312.pyc create mode 100644 .deps_siteforge_v812/uvicorn/protocols/http/__pycache__/flow_control.cpython-312.pyc create mode 100644 .deps_siteforge_v812/uvicorn/protocols/http/__pycache__/h11_impl.cpython-312.pyc create mode 100644 .deps_siteforge_v812/uvicorn/protocols/http/__pycache__/httptools_impl.cpython-312.pyc create mode 100644 .deps_siteforge_v812/uvicorn/protocols/http/auto.py create mode 100644 .deps_siteforge_v812/uvicorn/protocols/http/flow_control.py create mode 100644 .deps_siteforge_v812/uvicorn/protocols/http/h11_impl.py create mode 100644 .deps_siteforge_v812/uvicorn/protocols/http/httptools_impl.py create mode 100644 .deps_siteforge_v812/uvicorn/protocols/utils.py create mode 100644 .deps_siteforge_v812/uvicorn/protocols/websockets/__init__.py create mode 100644 .deps_siteforge_v812/uvicorn/protocols/websockets/__pycache__/__init__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/uvicorn/protocols/websockets/__pycache__/auto.cpython-312.pyc create mode 100644 .deps_siteforge_v812/uvicorn/protocols/websockets/__pycache__/websockets_impl.cpython-312.pyc create mode 100644 .deps_siteforge_v812/uvicorn/protocols/websockets/__pycache__/websockets_sansio_impl.cpython-312.pyc create mode 100644 .deps_siteforge_v812/uvicorn/protocols/websockets/__pycache__/wsproto_impl.cpython-312.pyc create mode 100644 .deps_siteforge_v812/uvicorn/protocols/websockets/auto.py create mode 100644 .deps_siteforge_v812/uvicorn/protocols/websockets/websockets_impl.py create mode 100644 .deps_siteforge_v812/uvicorn/protocols/websockets/websockets_sansio_impl.py create mode 100644 .deps_siteforge_v812/uvicorn/protocols/websockets/wsproto_impl.py create mode 100644 .deps_siteforge_v812/uvicorn/py.typed create mode 100644 .deps_siteforge_v812/uvicorn/server.py create mode 100644 .deps_siteforge_v812/uvicorn/supervisors/__init__.py create mode 100644 .deps_siteforge_v812/uvicorn/supervisors/__pycache__/__init__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/uvicorn/supervisors/__pycache__/basereload.cpython-312.pyc create mode 100644 .deps_siteforge_v812/uvicorn/supervisors/__pycache__/multiprocess.cpython-312.pyc create mode 100644 .deps_siteforge_v812/uvicorn/supervisors/__pycache__/statreload.cpython-312.pyc create mode 100644 .deps_siteforge_v812/uvicorn/supervisors/__pycache__/watchfilesreload.cpython-312.pyc create mode 100644 .deps_siteforge_v812/uvicorn/supervisors/basereload.py create mode 100644 .deps_siteforge_v812/uvicorn/supervisors/multiprocess.py create mode 100644 .deps_siteforge_v812/uvicorn/supervisors/statreload.py create mode 100644 .deps_siteforge_v812/uvicorn/supervisors/watchfilesreload.py create mode 100644 .deps_siteforge_v812/uvicorn/workers.py create mode 100644 .deps_siteforge_v812/webencodings-0.5.1.dist-info/DESCRIPTION.rst create mode 100644 .deps_siteforge_v812/webencodings-0.5.1.dist-info/INSTALLER create mode 100644 .deps_siteforge_v812/webencodings-0.5.1.dist-info/METADATA create mode 100644 .deps_siteforge_v812/webencodings-0.5.1.dist-info/RECORD create mode 100644 .deps_siteforge_v812/webencodings-0.5.1.dist-info/REQUESTED create mode 100644 .deps_siteforge_v812/webencodings-0.5.1.dist-info/WHEEL create mode 100644 .deps_siteforge_v812/webencodings-0.5.1.dist-info/metadata.json create mode 100644 .deps_siteforge_v812/webencodings-0.5.1.dist-info/top_level.txt create mode 100644 .deps_siteforge_v812/webencodings/__init__.py create mode 100644 .deps_siteforge_v812/webencodings/__pycache__/__init__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/webencodings/__pycache__/labels.cpython-312.pyc create mode 100644 .deps_siteforge_v812/webencodings/__pycache__/mklabels.cpython-312.pyc create mode 100644 .deps_siteforge_v812/webencodings/__pycache__/tests.cpython-312.pyc create mode 100644 .deps_siteforge_v812/webencodings/__pycache__/x_user_defined.cpython-312.pyc create mode 100644 .deps_siteforge_v812/webencodings/labels.py create mode 100644 .deps_siteforge_v812/webencodings/mklabels.py create mode 100644 .deps_siteforge_v812/webencodings/tests.py create mode 100644 .deps_siteforge_v812/webencodings/x_user_defined.py create mode 100644 .deps_siteforge_v812/yaml/__init__.py create mode 100644 .deps_siteforge_v812/yaml/__pycache__/__init__.cpython-312.pyc create mode 100644 .deps_siteforge_v812/yaml/__pycache__/composer.cpython-312.pyc create mode 100644 .deps_siteforge_v812/yaml/__pycache__/constructor.cpython-312.pyc create mode 100644 .deps_siteforge_v812/yaml/__pycache__/cyaml.cpython-312.pyc create mode 100644 .deps_siteforge_v812/yaml/__pycache__/dumper.cpython-312.pyc create mode 100644 .deps_siteforge_v812/yaml/__pycache__/emitter.cpython-312.pyc create mode 100644 .deps_siteforge_v812/yaml/__pycache__/error.cpython-312.pyc create mode 100644 .deps_siteforge_v812/yaml/__pycache__/events.cpython-312.pyc create mode 100644 .deps_siteforge_v812/yaml/__pycache__/loader.cpython-312.pyc create mode 100644 .deps_siteforge_v812/yaml/__pycache__/nodes.cpython-312.pyc create mode 100644 .deps_siteforge_v812/yaml/__pycache__/parser.cpython-312.pyc create mode 100644 .deps_siteforge_v812/yaml/__pycache__/reader.cpython-312.pyc create mode 100644 .deps_siteforge_v812/yaml/__pycache__/representer.cpython-312.pyc create mode 100644 .deps_siteforge_v812/yaml/__pycache__/resolver.cpython-312.pyc create mode 100644 .deps_siteforge_v812/yaml/__pycache__/scanner.cpython-312.pyc create mode 100644 .deps_siteforge_v812/yaml/__pycache__/serializer.cpython-312.pyc create mode 100644 .deps_siteforge_v812/yaml/__pycache__/tokens.cpython-312.pyc create mode 100644 .deps_siteforge_v812/yaml/_yaml.cp312-win_amd64.pyd create mode 100644 .deps_siteforge_v812/yaml/composer.py create mode 100644 .deps_siteforge_v812/yaml/constructor.py create mode 100644 .deps_siteforge_v812/yaml/cyaml.py create mode 100644 .deps_siteforge_v812/yaml/dumper.py create mode 100644 .deps_siteforge_v812/yaml/emitter.py create mode 100644 .deps_siteforge_v812/yaml/error.py create mode 100644 .deps_siteforge_v812/yaml/events.py create mode 100644 .deps_siteforge_v812/yaml/loader.py create mode 100644 .deps_siteforge_v812/yaml/nodes.py create mode 100644 .deps_siteforge_v812/yaml/parser.py create mode 100644 .deps_siteforge_v812/yaml/reader.py create mode 100644 .deps_siteforge_v812/yaml/representer.py create mode 100644 .deps_siteforge_v812/yaml/resolver.py create mode 100644 .deps_siteforge_v812/yaml/scanner.py create mode 100644 .deps_siteforge_v812/yaml/serializer.py create mode 100644 .deps_siteforge_v812/yaml/tokens.py diff --git a/.deps_siteforge_v812/__pycache__/py.cpython-312.pyc b/.deps_siteforge_v812/__pycache__/py.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..efbd8a96626ae9a6cc4f7b9ba6d65203367230bd GIT binary patch literal 492 zcmZ`$ze@u#82xhB>(wh2bx=e^#i>J!SX7iE6h)|rlPMent?1%61{>M#eH?V?q-EhYe zBX7UxrFkT?eybG=Uo_(uKkQ^^QY4JUtYa%iebJECx0Wd_@{V*cK5dIOBN#J%C28`6 z%SlWVLAWR?90o{JQI^s(CjOc_hFQ98M{&>&370m;ei&kW1MjL2pL;u7-Z3YPd!?1l z%?+8>w)?;x)Ep?5?g>d_`G2%pu7D6H2@tDVjo_rm6hr%us`TMCLak(u&vhi#_c zOZ^A%iW=TbgwRI@(DDGBXISdN(jzSNVBswdbBpKZMYfy0-0#E8{p|cmRr@ggl%2ZT M>p62>n~kfKFJ-KN&;S4c literal 0 HcmV?d00001 diff --git a/.deps_siteforge_v812/__pycache__/six.cpython-312.pyc b/.deps_siteforge_v812/__pycache__/six.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2096e68de37f042fcaa498c197d4ac880df986d2 GIT binary patch literal 41340 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

I* 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 J$Tz|Le*wJRZ!G`- literal 0 HcmV?d00001 diff --git a/.deps_siteforge_v812/__pycache__/typing_extensions.cpython-312.pyc b/.deps_siteforge_v812/__pycache__/typing_extensions.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8df6adabcfc18bf3571bba66285e7205b07d298f GIT binary patch literal 166538 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 literal 0 HcmV?d00001 diff --git a/.deps_siteforge_v812/_pytest/__pycache__/_argcomplete.cpython-312.pyc b/.deps_siteforge_v812/_pytest/__pycache__/_argcomplete.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bc600aa557a308f82396769063c9248ad2d8d762 GIT binary patch literal 4825 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 DjC)!f literal 0 HcmV?d00001 diff --git a/.deps_siteforge_v812/_pytest/__pycache__/_version.cpython-312.pyc b/.deps_siteforge_v812/_pytest/__pycache__/_version.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..03e141763bbadb0c4c16e8a1148b9a2385aac72e GIT binary patch literal 789 zcmYLHzi-n(6h6m}e>8EEKqze$sTp9%kfdt)p`r>2r2!(EwxS6VmZeh~AHkJlTegcx zryv%_!a~_9CRq6kSeRH+WpPhJio{g0RCHtFo!hjh^Y^~{?%lgj@BC9$ClHK3KaRG) z#}N9>7ULnDlhbcNKB6Y-AOn5oG-PlZM>sEU#0=hwb$N~F5O4vwpa~ozJP}Boh%aI@ zaMFmMMNI-#G|p8`wgsYqrf4z|$%G*-LvF<6h)JL+7i=P8GH8hlmSoIOUvpYQOB$09 zRc+D`A*Qr+Pb{PgB28fYig>>1*cj8y zEDLT?8_zeYxbnDKc~V<@M8%-zns&#F4zOpUylDrN_XCfr&#F&1YwK&c(cDt{9gdEIKc4q(P0e`T0 zAb%=iAmPu3n+DJ*hO@?ZEh|=NJ{1D~CRBniJ sVIn(3*)TOdMAM_`xdEE%C*F0!8^r;Fnc1t4a`^$u_tp1`cO7i~2QW+4SO5S3 literal 0 HcmV?d00001 diff --git a/.deps_siteforge_v812/_pytest/__pycache__/cacheprovider.cpython-312.pyc b/.deps_siteforge_v812/_pytest/__pycache__/cacheprovider.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5cd428149886780e3cb3e30fa3fc6ece75b239ae GIT binary patch literal 31619 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 nQ`!bVNI?7@1P>B)_3FUW_-X4` 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 DL+dO2 literal 0 HcmV?d00001 diff --git a/.deps_siteforge_v812/_pytest/__pycache__/debugging.cpython-312.pyc b/.deps_siteforge_v812/_pytest/__pycache__/debugging.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c922ac59963c36648adf2ff4f682550413277188 GIT binary patch literal 18278 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

VHDKh#irmwj_WW$~?Z!j_YZNjg_6u zEo#F%CyPxKb!#nW0HsO{kavylG~6lOqy=a>X@HfpX!+??;v36PVy2>~Rg{3YHEO_iz1h(eoCm|O8HtT-Ca4)w*2O=4{pGndf2a zxT@0Iu73H-mos8V-dS0auSi?3x~{k~V)H|X&|$|#ky&^8Yj9Y2>aXovuw|P1Z@c^d z)h_Zq4$KXtw_S73pUXCES@LYn`|8q4TAfW@OWgK#7#c%Ei2Uk zx>Cp49G3>(8kpUN6DeE<5geBW-x^GBT@vbO=6X%VlF)w7kJ#BofB(!esMd6gLyl-#jE11XPlqrVbxO|d&;pJ*?R6YPH-&6yKwNjn|=VJ zCkwYLus5Zzd&;S zu_%_5fLRFTSeY68R))=?4_D4dQRqY8UAEr*?((&T?+)1YvvC+Q)vv_D7jREC5tdC` zEnxYCWYH(YR#;f*C$tj*Hyz2MAzX?7n`&M`qIurH6&p|*+yM~9KcRA3TTKL&>1A$u zwGD@$UeW=s$wF{oED|kJN9blm#tbeCqNfV2VloEC6rBQ>p?BqP(8N&C46^+>b)$qN zwYrGR4EL#>v-N#kQJJe~%T}}@j=Ul;bM&60GTn1^)0It`j_pg19iYsnjvKAlTW2fo zx~uZl9l7dW1g0-+$yRU5cs4EjT64bctgn0i^{g+D5d-&})#;-Y!L!u0d&#*6-Emd` zk8RH1mGyVc+8B_A*%y|5HM$2r%P$5jP@b{hD&s zUD@ic`GdGBlks$$t>$^S@-t%R=a3(9@X|=E&)2rtb?!16LW!*B%en@Ejs^)pp77-? zB&Du+$E7UMt@4M!DTbIX;^WE;l7OQ`=d~fS5JAOm!^1?^uSE*CqQEbOTvN*1)W@Ry zFZ^iyM5!ID!z94}kT3#@0x;s9SeX->vtskL4^)9-HKy-z~&Dq8emJcZasiN8b8 zg(I$;uS9bRbChL zq9szml}m;WY+@dMirpBQr2o$a{Q5-j3LPG6zTtQ@s*6vxKXhhT5vYoghS?!H+1uFQ znjJs1C#0?_|CW+(P(oV-TGy5JenfVFj7j2ZKBKIl2PmX>>gSSdb%<9e#LgAsJzAsL zoP|5|;~0_dnA3=~#E;d3N$Ac=RljdzPJtYrD)VU-r=TlADqH7bU%Y(S zv3%Qot{Wu}?IJHdtgGei4>vdPEx+D*z{DoU`tc{(02RxktZDR&$-v{FwbM7@xx-#g5Irz(+02nBBk( z*}jX`JkC&1M^4~k)MnU4m@vHA#1tdXN4$w8Y0YLB4mQ_4hC*WdK5+5?tzkrQE7*Wj z%>emC>vN^|YkFJ9aN8}7ryBWza z2*z`o$j4FYA{mikkM?U~>d!w*Gd2mdV-c+q6=cz$ zeol(T`lJueJg4Pe_Wz=ojP8-Bj24v0X_BDPN3iyqs4we|_Ble{hH3-PSd6OMZ2}GB zy9Cp%)lVa=+3_%Ot-5zdupch0sb7VB^%(msPz!9kAB#M7kPSkjC7IpA=GN%~3aPhY zbKGknaFNZigM>ty!VXo0d%*cV|G<6em-(BFTe^M!N;w zdDywr(Db5}g)X541YT4Vq0BP8^E$&AH8~I2%+!<{ILwUW4`^cw`%?sbW_+sG)i9OJ zZ1NVF2s?(*ZAfQ~o-fRRW-*#Uu+9vMVf-BI8k^9pk{KM4@2$UOgmoq~ygDwJt!*Es z-Qt}PTRvp+?%`NXRDK?-$o6Ec@L+@o@)T9b)UUi@%#p+WU}dbrK|@Y1Z96=muZ!Ex z!GaT^iv(?psP3ZedYh0aHmDd3GI0&YJ235_wC*+9G}?thD7u89$(BvmfsG;=!2hy`V&F5a?Q6EV7=#sB>izO&z^yvU=KJ3CV^q zScn~Fc&(5yqs;_bgn}vCx_4Q|WQQT~;*}H49Yalf10z2`xL(H&mz5J;I`g`og`K!Y z@PG8IFwhZ(ZE4!z0YLHm8ofcZKZ7E*0)HKMpqreg$vU!6Ex4CL?7NeB*q4NWpQ5i( zxZbcRWsGx=W4|91{vna2a!5$S7(M&BOHcNDM_e@MuW1+k

YPv`Lmg#w>3-)X&12UY37Up`TK27=<6(ZL;Oo!|{j@*0(EKU1zO zUMs8?ftvML)bmW}MgLrzovFc=rmS}E%=Gj}wL%!q+1#!zi``jU#jUUwdZEE#aba#; zXq-E{we=yZ!G@}Vo@U_JtVmzKji7X4r#s>_v9oUVtkE&BX2U-lhsTcccyR2O?9jdI zUyVM>PV6R>q49mdE2Vsrj>5Z}TnhryO;ZfFir-C^0w3qcx*5xYrnaoEZdvuVxNt0s zimH`Rj^%$&zPqa>Q)l;6nPb2@2I_NC{cRQW=d_)XJzdEc)Lku+asZXe O1J|iP&ui*Q8PUH|u;1nY literal 0 HcmV?d00001 diff --git a/.deps_siteforge_v812/_pytest/__pycache__/doctest.cpython-312.pyc b/.deps_siteforge_v812/_pytest/__pycache__/doctest.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..69ea7bc38da357771db211782af26574f6cb6272 GIT binary patch literal 32625 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 U7Wj&v8}d*d?NokFF}Ah+|GrzX4gdfE literal 0 HcmV?d00001 diff --git a/.deps_siteforge_v812/_pytest/__pycache__/faulthandler.cpython-312.pyc b/.deps_siteforge_v812/_pytest/__pycache__/faulthandler.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c35b412d164949fc6e42dd21f68a0a1e5ee16655 GIT binary patch literal 5079 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 a*PK9~z+tJ1R5iME*jF3=7YCHRyZ-_2h*l*4 literal 0 HcmV?d00001 diff --git a/.deps_siteforge_v812/_pytest/__pycache__/fixtures.cpython-312.pyc b/.deps_siteforge_v812/_pytest/__pycache__/fixtures.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..76def8ec5a5efe22f20071f68f7f39d9bf49e8d9 GIT binary patch literal 82725 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 ZvR0W|m*D)lJRIMl8p_xoDsW=^{{ji|;Aj8< literal 0 HcmV?d00001 diff --git a/.deps_siteforge_v812/_pytest/__pycache__/helpconfig.cpython-312.pyc b/.deps_siteforge_v812/_pytest/__pycache__/helpconfig.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d0c613ad9efd15e38befc6a649055155b79bddf4 GIT binary patch literal 13111 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 literal 0 HcmV?d00001 diff --git a/.deps_siteforge_v812/_pytest/__pycache__/junitxml.cpython-312.pyc b/.deps_siteforge_v812/_pytest/__pycache__/junitxml.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..19b230b642792487a2fe7ccf77c7403276092dd0 GIT binary patch literal 33293 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ίyKlB=i8ZOx92gdKh{h6JKdKFCT<#g z7e!@;5@nExkID+Ir?h&DVtMqVtW@put?Ok6O!I|%VZRNw+|G`Wr4d+00A_-At14Fm z=N$3+&-6-!ETudui?OXEm)rcO2F(wstDlp3Q z@fA5sv1WG*ztVT$LT;#Sj0|6*$@u(BRPTLaBs@ zOdnevChO0NIaAY*xaJ>lo*!^|Ki~@C{x#P)$2IZz@9XKli{V%)K0uQaV++Uzh@g7GV?ntpuNK3Cm2m%lbu zR)4o)>s;w$AKNT$^Ei)HTE1es4_0}TWozPPYZ7H0Q!tbx(%=~LBUS9E2 zbBU>D$-=`eod{e=Blkih>lc|CdHQZ;uL#B3< hedz^Lf$52*Mysh~sem^X{k(k8Vk-S9M=o3V{~rU+dMN+^ literal 0 HcmV?d00001 diff --git a/.deps_siteforge_v812/_pytest/__pycache__/legacypath.cpython-312.pyc b/.deps_siteforge_v812/_pytest/__pycache__/legacypath.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e6b4051ae17eabaef510a5f7674350ccf80b5c44 GIT binary patch literal 24169 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 literal 0 HcmV?d00001 diff --git a/.deps_siteforge_v812/_pytest/__pycache__/main.cpython-312.pyc b/.deps_siteforge_v812/_pytest/__pycache__/main.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..66d9bf6c1aa00995b969649b5e83108377c44d32 GIT binary patch literal 47282 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 lhOanEa03HZ>n2>(%l$iB*1A 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 K1QGBE`u_n%zw?m* literal 0 HcmV?d00001 diff --git a/.deps_siteforge_v812/_pytest/__pycache__/pastebin.cpython-312.pyc b/.deps_siteforge_v812/_pytest/__pycache__/pastebin.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e9b9ebaa2cca41a47ee482d79673afaaa457d7ab GIT binary patch literal 5867 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 X1_I1^=4p2)Gs=859AYjqbWi^Wds$+h literal 0 HcmV?d00001 diff --git a/.deps_siteforge_v812/_pytest/__pycache__/pathlib.cpython-312.pyc b/.deps_siteforge_v812/_pytest/__pycache__/pathlib.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..caa3cec184eb6cef60019a9a28a9ca2c1e613d5e GIT binary patch literal 41701 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 literal 0 HcmV?d00001 diff --git a/.deps_siteforge_v812/_pytest/__pycache__/pytester.cpython-312.pyc b/.deps_siteforge_v812/_pytest/__pycache__/pytester.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..58b5cb8f81105aa717ecd2d76e78ed48beec1858 GIT binary patch literal 84918 zcmeFa33Oc7c_#Q?ZKw?>?1deLjYN?IxNi^_fdIG@q)1Q;LC^r}D-Wx!%& zbsLZs3$)}AjN}n$J1sC$VmQl$PWp7@Epc~}=_(ayKow>V)zQouPtr4xAUl>OGdSg5tarw&@ zLAW7ELZ={!lBr)DFm;+l3Y+`QofbST{pJB{r~t#cT%9hKr@PaQr=ve}AgeP=iSu-NSX_2zHlEJ@oPpfVTqQ2AGmpjP zcjmKaL1zJb7Iqf0r?=C~o<*HSc)I$F2TD3i*tc$kOFK(hI1}Nr&N3FxLb$xMoP|9I zS9Dgea5ln~os}${gRrmD$HKV?S9Mmga9)4)Kuu>23+MOO4%Btlv2X#xt2$S)aAE)I zf%?vR7WVf02iA11Vd0|wwFB!q*RgPMf5SjyXCn)jAiTbFJqwriZy4Cvxsip-5Z=_e ziG|A%-rTvFg)0!=(z%6&D-qt>xs`=|2yg4$#==$o+Xr@Z?qK2S{+$C&olPuU)Bni8 zuFhR7T#NAT&fP3r*S}|AZ|7bXUe*8TKyzm^3$O0qH_+1A!ou|kw|2I&u)n`;V1MU+ z7G8t!fzAUgycXeuod;QXUH_qh!<~m&xB=lKokv)>vH$46vCd;GydL59&UO~w(0_d3 zMCS<>-q`=xz{$>&q7bZ-2?ovmr#eqbn=Xi*9nxmpPfH!rmgg;)R3$eS^SnbQF zmDoLq-Mb?83`%-ZYBrQ~)+FHFzPDQ56nWv&Q&OuT{?~%L976IxsZ-i-NYRDwpOy|7 z-kigmGtxoBn*d75@B4w$Ji|jeq?X&+{lGMbwKS5_x+7@a(G{%=vVJ*+xOO$JvxmJq zj&~=tcNdgg9>d!<^{v)}7ugpl5%;JX*V)V7ox;0)+Pl6{v;Slzit$ zeNwO|Fw!6P`$8kb!$We|*E1yh!og6uckqI5c$7lEz@VfChxm<30Gina3~xI_YMt)+VR`o66o)zA`x~R2o472KzK-| zh(lq7d4&B`@R^a|U^gYo=ouUcgu5?Nl2c(Bbvbk#Vatg?nBJM22S@R+?(ZE8^i!Zm z37A;2jHA7jEydV}E(hiQp@4+2ts{6f%o1dFJawY2tL0!@%aKFv2N0F>L@;paNN}{Z zx0@;*kSTk|p~2pkp~0Ts3;2 z9JpZfRgZ;yNxN-&pmx5h`BLnAya!~4$da3&|m5fgYDGR)CcHkrJm!~5* zFigX}KR|`kczAfD_qRR~kO$H8i9Fu8pxhM*q1g=FB8BTO2junrz3115LZS5o-Rp-D zc}410kOiB`8MvDTL0oO;S>Lj~sV?acN}D0sC5PmcTyMFP z(xQ~qOUjh8-twrEQWQo7e|CGs*3i%&8tO?nLuhTVD=d!$6Bc^2KT>^iWYD+2)yIZ3 zJ6(R*KS$QQVbabVNOlCYtlFoFq-glqM^ICgKXP7)jXXsK^N)ZY&ijxU#Sb#{5b}+L2t@=2eXF(nR{MZFR_i&e z_SNI_i$jCHh5;V~UEe?uza_s#-htBO?Nnq&_r;-JR7c)Pzw)&~4PFfZ>VpI?k&Ugr zGK0cVUwFtD20Zz~7lTUWg0fFZ>+9+54~Bd_@({q3<>xmg905rh8fKkiL$G(S7rY0Z1MmB!qowK0X@F4Z%;pU{cWyt^JUrYQ2nWs_9qJDBpCM2@Gu%7e5Dv(g-woR? zY~B=!Yz>_Ol%3%XJk!Oiy&F)D716pUm&}4S_osr%Y6E2|2uYw1ij;%?A0qHz&{x+ld23h$VRgM37stdtWkOK? z>ZQiZrI=7Q^{xKwbLe3;G$yJgi8Qrb>U;eunIv=4WU;i%TM?be7zoIhFl3Q_wa>d$ zh#|aFzlQEzq2Psq;9xl9Z}RmG5$pnLF7#dw0hB#40$6R`Y{j8T!7>aH$>sCOE!1Qcpm-XrF7gSy?#Ex~C~g3#jLa`S z`|Hn6U7pXVxR+ZK&#jxtSajtsxN2jr+IiQiMR)Fk+ZS{D7To@r+dsQ{-o5EQ0byy( zQo3l*Su%-^T|c#2tTwp~84{Fg7eNj#ad{8!H-wxNg0v|8qW*_Xu{J1n#c;Y zX@1M1jv5f7)j(OJd+>IuKQ3h=d4=Hryu!sADZ#w zzWT$8A4@i&poGOOIjosk3{fORXF?QYnyEjb6*VEQ#}ZHIBVh(5n6Sw~(4HVp&JPXs z`*ZMeG?Z|IG$*37t9P(xC}Cq24JKS@fI29NeEb-egFP6xprpQ)ce8v!jmW(eG7W_i zHcHqlfkQZSqAiip+V*(+>7z&G$0^<#8t(56C!B{4v>!j&)^(!!n0%hz+lZ1Ml>83R zt-<~t88ZRZ1*pgpg`9dX@|M_odV~E^C}HZ65_am{!N5Q;M0m@mUVL=2lE)z8`?OJt zAdk=|xLp(Oxv~~qr7>6O^p$s9tKRpPFL?biuRrcxJ9&`(+7$C{n%f%p?wmMyFRSRK z!O2084O34{OW$mH%k(YBZO5Cgcu2$E|E>jAk6 zM+EVKm``2R#!U_gH-FP~L)3{xy~`z0B>?37FmYV$>Pxfws`)VzP#a|R3qerh$IWBr zR^cT}x9OtLZ90oQvBFs-6RkR5F}>)p;4Q6rpPnXyRe0R0=5~Q{Lx<_Pjae^a(;Bl@n17v5JBb$W42bUzF!w5+jUXOz+2QwZ3W)i z@z#O2XpQ0RL-M{Zngq$2jvfIio~8Gc8v3$;5?t3stjc1aMxm&&f)K{)#xKd;SD=RV z=U9f?w$!Hx{^b<3>l;F@FC)U`B-EJ*W`k3%ejQ2@iD-_3H$e_Vxcv8Pg>DKZKuO6QTv1Z}rdQywW{gb))B2 z|HsSJj^_%zV#Gxa-xmbW7;H)+NB@~El_ybx`~uy+gqyLA@^9e9Kd*Inki)W}bq%+A zwAS@}EPt5_a8k>fd%*Ml3u@S(pyu)v-B=sE)P|msK_+GMeQvx7|MBoeJg0&|2;)K{ zL0`?L&D*xgL44zP$88Aql3Pe(;kPq7B4L3{djM22aRM8a=ca@RQQu(jN>>l)Qa4Q($X6NRpRj^vhG3SK z+hpq0`vAy|_&+m{zky6cUj*&IAo_kz!9q@LET=Y}vueV&WGZm%PUIKA?*$J3X=WZLz|(i39JhZitsQ-?}_gHJ3BD@!R=#yuV-hTg@|9VYY~|gktKM`S7hv=m~5e7Yc#)MA%9COf6JWo#{=IUph9yfE>|d6 zwUmd@loi6coPryU>yFpf&(?m+f7?IT7GKj8FL~r{_O7L3A-nA353Pc?;p2DKw|$%_ zZT|U3E+O0d3wGVD$3LE5(RbhJ`FQ@Jopde+m&b{#?$%b>b`L)(YXq z4urnzakqMG-z~LKxXRs{Z~tzCgTmX~t;M$QnJskB*yqKoA5`o-=r{k+>u#&H|FFhQ z;q~seUCtjq;z9V&o$mc*_CNPp=w7-n3$K3UH{&zn5BgAWq@W-2yr;t>5LQ18Zg-sz zx3g!JSoso?fNS_IJmh-1v2Q9hHQ3YbLxaI}zM=DdSmV^^S${^t-ql4MW>;6j+0`{L zB#l58-j#57bv-i@=;tvE$uPb`uAr=M;dWbKycAD;RCl!DlW+Phh!-z*V7NbqA1{LorJ-- z2!TF0hy*8<)X+yjR06G`k}DMfQSA`a4bTzjdLdunJUDwFWFg?wSbkJYs1bbKzoAZp z4ov3iFayVy$WX-k^6wzc5Ahcw!QVCEBd6dln0g}aDvw&qIqQn|+NE$+;xY-q45Ct~ z+nK1;%0;D6$J$uwNo*K>%(|82i4Z=CV?vjf^Qgd9b0{$ET}picXEeC-ZQP7&JHZch z9pSF7NUk=3jq1Dqg;G0kgCMA=a>DW(nYeHA&JpuyCbPS;aN?-_0r<4|36K>L1M=@8r-U0~bfy6AjgU|s!QRkC^bUa& zBzu0dvVhWnC){*bK`lfrB+zQ1d^{rJQ}Ncbl%cu9y0y`bYR%w>-ToTC7~4-XT(tT=vYD-Ad{DKSi6mD1q1mb! ze@pN#}8yV%F5W85@NC(0{N{CmJ;5nPb zYQrPv`+K_?VhqXu7S&w9Ux>Q$n(&^tJnm|^etD{DCTC{jt^C=%Z0^j2rzNH7NrOJj9)0P#|$b1 zhDSB*rs7dEVMz&AA6Qv!Bg0Z4986dlbZKaVpgLi@1hV`>C<$S(ITDc741$RZvT@iQtCd5T|=EQ}G!;3kDR1e=eJhNvmZ|2AZNv8tO(zF_iuz zdQU68{Fih~LGJ&K-qGzI{z63JJuiGZAgMJ<=+v5YKFBwNx*8 zktzrMRfIRWqgj5xOiOs=f8}uH1L9>^SHqC7LJ0V{O#rA=X1k><2()^rYX*_VSOB`^ zAaD{h)obeR2kLA($ElK_43w%YBQQ=N(LTOFS!Cg$Eh5}r2XB(gTc?yl3vn>eeUTw! zmf3kGfAT?`H5|AAi3Ac*hJz%g=;t_FS%N?^J2V3`je;dogTT#9;5LE47Xfev)*uNZ zPYPWcX>vipFKuE=^b2|LOEOOgJs?Y-caOuefL3FOa>cavB<3;A?f^LuvBNGHSy|eX z78OzhF&!_NP<&OqAxxTy7E8U-Ei1a!!59csRm<_F(iF1j;zYU`x@KB-<*Ue*%}T!| z;h?c$D@?-4f@Inkl7B!+7?Vc5hgI?#CrWL6Kq7Sn8w#e9wIxQEHu7XH)FD)k3?OJE zK!n zPHPEY*9+3N19}f3)P`@+!5`TKhj(gp-d=UTV(o-w-d?)cuztewqH}RgBOdmjT0}>S zxNH+0IgmS5u3M;RiB+_`z5hpT@rsiZmb(f+kRFOju9yf_R^TW^sZguX|?Hycp5EMv;PT!q$w?iwRGxS zkh33dA_f{It%(?T5tG7;035+crezPuM5dj~9W#M1DPVjF*hGadF@Y};BYO`J;S6jj z8!2!m7$8{T42i)QjI%EFAgd|lZ-m{5vlj-AT(-d1`palI13XRvDQu;ceG1ZuW;$Uk zZ9>M>xyX3%w7#;jxZ zH>9I;S&&Pi`dfdhspTR=KGtxtnnKqC!#2zQriH&3g+XKbjI`-dZ$15s zy|BOngGr*62=U=CM;NR!l<63=O3tJ&p0fyH&ALl+jafm{yT@#=n!aXH`w0RR5tdiq zA0;c}%=Qk(Y0DQ-M#9nFe5~zQb4Sa;gqvByx1D6aBBq8tiLCbH#&-#^A>nF2u76=6 zem>&q9vbPFKpSA!Kri+PL^8OIvd^Cd+hxc^VT=scit9ccL{#_4#7xdMOv6B+Lp6u(yUdc)0zSutL+W}#$rtYmY%V9SK_-K>JelB(C& z-dsBqc(rlz$VBT@-M#WvGvPNPw<2>@bJa7S|8e=Y>y9b&R0mmUHC=C-$$uxW?p}WJ z^xB#5Y{z{5rhDGf>9(0BV1@D91S?wuOFRiemB4PUUtFMz)Z*Nwz(~Lvv({N2o>8u zDuTjd>e-p_%*ELYbHTZ%q6K^BvmgDaT<{cf(m})bXuF1CutQ71Fb3=*JWNoCSzM>g zVl%LXB^|dqCekd16siz-!B{SerfbZ+8^rP!MUH9n!OCM( zRvxry+y<*X+pEIYXocZ+C7QJd@CM0E*(F)iVK?29QlE#T>iT5E`ebYGh~%eC9$G1U z5$ie#w3{L(5_h8nkUvX!80LiyR*NuNWAi}1Nl7v|UGsfPN9?NnGrAFkC#+0cDE}$K zAsQzlad2^ZEdz6p3XS9^!MO4kd*g)AB)(B$`<~Y~lmAA^t&$&m*G(RnXr2f!dh%Y1 zOh%?V;vOtpMK=y#KRo@+)Uicx=~rA!8A4G587kG!T4t`!jzn{|Mzgm~SQqV?FS#e( zvzBi;f5!=}+->K~#%a^^#_5``T%2{r3O3K%x7@S4CpunwYVxV6k$7hLU3+GZ-^_VrOuji#n14*sme*=>TS+VL`_*8a55y0$#~pNx5>qk%JZ) z&9bdgoX}*W#-}ntygH2^-jPpIx35KV3|f{@v-*vOTMcv8`RZ+P&-PgLw&};F#2faP z?a}IOG0*m>eLLvM?ToE^C2tM*Woo&4(d=Wra1MFi5SVp&+I>@FrnfYj5*WUl=({m< zs0`RRO+QFX%OGbW)>xWSDrlFgF*CS{wBL@INkf4&n?dDNVB%WSQLE|11Ud!FIEhnO zd(u#;Iv$7NP>?LnLmr(@#R@@djrD69HT;~Y?+bt!0{ujnWV_UgTILwOgjF8Cw2xV^ z!f5G$YAlk22uq+EXJo6UH6_732L zH8n+QQ#@SIUG~iRB+=3jyF?odO9LYY4ASq$ml08Dav+B=kdMg1L|{pMmDi+GV& zHshE*^{vj^oeK@SV-36SNb!c2cYH02*@ZVUuV+qo-_5RuWp(}r=-0Cg@8y;)=5CuC zof}vZ#8z<)?m26g&E~wUCBdAT^^sk0dnPV_>5=K27xuEW+wVB$#^2ue_T{%PMzfDE zn@yC`l$ix7LU!RVgZNjVUuI=bn>ewDD3sD=K>2FSv()ckM7X{&Y1KtPK4_U)QJP$U zEFN6SzoG)*1L41JDPN)j;87t>DTH_!YLgK7g%CVUYafK*L9%8u0Y%<}(L61JFag6j z1SOS-cn@`ua}kk5M0`pYzC!66@Fm0ZzS`HHz4`3y$b9A2xTiH%xpmrnC-2Wn{Cn5Q*rZw01rW!lhJavCiE#?oNvMi$ydD-7$AS_B@{BQWq*z5&8AwW~Wf!91}Y z!+bshA<^ikil*4k4&HDjIW~fXB2`I~SAj&>eexVB$=9f*`KT4hB~RXhr!ofW$VclL zmM^|`8yliYt>6cNuUaBjE@-=9OP+T1(i?@f>x&p(;!D!3Tk@9l{0i1H&aYJJHt%DW zuwCt1RYDdr9VX&3t#m|RJFxDwpqw=O&}2xz(ym$BdV+5Q_%@9!CFDFzJCrPt6c`Cz z^QY!Fi8T-e@tC>Cj3KwQgP?>D5Ft^NqCM^(y1+-DtwWLg(_}`=EO>p2D7nc8#TbFV zOrl_!A^7{~BY8dD*y4~uo5_cRl3a`ws`Qqn_R%Nwmd_I!V_FOpzQ#?G2c?)$q&Nwp zj8QYlAao1gvN^-n1KctebMmy6VbT5Q&&;NL=R^ykUT`}vL>BW3Z|u0fWBPI&{CH{I zO!r$&^QGIO`P-tU+ZOXnZXCOQY^LUJ{;I{C{2QfTDP7F=-pIP1H66N}U31@CawGDU zNHn`STDS8~%^h#Fra7M7JQYb=HoPcrsfZB$l^I*qvnpy=NNrSCyEI~~6R#dLKUE$k z)xg8R|7bHfy@7{Hj#g1$Gfqe0AQ(q!AmiL|o8z7>G2iCtjjx!en!jp``ZmWrTcY+YKvP>7LHcrTy(K+8dX#m6B#vVz2%MP+ z1K|AGP7;EPluPZBaT|6#S|Rmg(2T9B7M_sp#B*mScAXNzPX$-8gjVqBER#rbhbGnPPpLyW?&ngdzqRka zpXXi3tBvK=#`9K9*x$7mEV}a-^NJVqOL701!|F(5ctBmiaDFucMBoEKrgR_ul<==q zQ*nu0H1P#Q7!(iCd%?^{j^rkT0loVRsx3F7TP!M>u)O4)bY9;$$*s>I?l^*+WFOs_ zj^+}DG;I*@jTVN!=siS!k_Y10H|9Hpt|w0jWPo{=rHT|Xv&>ZO{#ktZ5k8C*tNun- zSZ*d2@_nj7uU3tJhmcZqq*C!S^7Lv2|0S(_t9dm(@oMF?@MY{IqrRyg)BtPY@ zm1w!BHaHpwMbV2j_YA zv!zlkZzB!{01zXe#mpsarQgs@nu{b2Mw<|l;Ur2~R9Hxdv%C}o4*9Psr{AL+>DRaz z=wk}~zjXUAbR%^HU*1T+#wj3@7BgfZ*HDPz20w*h0$UgrZ16HXk;t z9Y*B`#be?JW{>rhxLCeni3c8S<$?Q-%w;P@Emw=yie;O{+A1!)^R3O|@;bY zIhU+9>*o8OYW7@m3(m$5Gx0`AiqKL%OO;{W!BXMLQY~jvs8A>=LzR$?T`2P{dCIKC z_wx#uta#F@TeFlwp)A2u%;I2n@2p+Up?AB~3jgI;l@Y_7gg;TNg9GOHe7ul zElOfF@Fj065vph_?)+kH{HtaNBw#0wz26WzKtussl%z&0IVah|z#N8aZo|E_BrxMs zw2rU=wTx;W^3|&*smQVyF0W*8VboLDO~VrSxtU7`dH%rvkx;N_q(8|!RzEBPNfL53 z9D>RJ2&`ye1xXdeT28%;qR>IGSdbK}P4GgSsVisEK;vqeXXqmA-eGQ<tFlk|6h0-9P>-dtY*e#L6F@n`Y0Ak3#28P3|ro33x z6`fAh7kMb+_Q#|pk>MQ7*TdXLL7Wxx4JbndGuBWq;9?y_SyFG$C<j@H8+O z_FWm0FHuF{0S2{%|0(L2bLU9bcJ3T&V{q_tuRJtJw!1KLmKmgle9{Qb8h{^qGtl1~ zVV$B^9)Ltrt={et01phO(?Ausa|tz4Tai2jCu2yf!M*|>P3OV`!(FU8tnSKdA8drl zcIz^fplC4jwFFxSw%;Pq3y~pr-b7VY`nNYsz7G^wm;WOG2T-~laEB%HHhv|t)q=Ws zSB^s5#yo_f@a~Htz?*=9;VLCN&lEe)$o%;pK`&u2KyMk4!qkCidBg1I-j?QFN1~P^ zoLNV-b`jdEA+$G!0N>H6s}fbzH*VNmti6LL8os~t;bteF#ugCyPkTdE^R4VtqoETR z^3bV4Sl3+~3MX>$<`~=ygZs1Vo?vnqyeMs!5NXth}7id9nVbW zS&aQ@Xy_o*-BnFQ6_f9M!32p`TZV;Do}6 zo@?wja=;pU_vfs|Mt2&zaui#Q(Bu4az_t;;m7Wq{g9{54p^Nfi7jkf^VO^1m6eOHr zS5gX1OM(=nMpRP3?nda+q+_F)|GLKjIOTx<94uXiT0JGacvRg>&ZntECWA-$Z|Ihd z+B067E#dI)gN?Ve+VgeyaZ2(8ZV5Z^G(*j*T#0>s1YgTX>2?e^W)(<_4o7Z`;*aEN zqodhiIxr(bL&%lnNES!wz18dI%-Rjp{`p6haFIgpJxUk7B@?Y*K1eP;NV8c#=E#@gm{wE3%fG_|^m08O_LQFCQK+1_N1hoVK zq#V8`+|MeXmR`Sj^WyXwsCm{@-K(md>3HMmTTd_4ZHv`yi&t%r`gR~@=YwO47K_SW z&%BvA(;fA$nZ<5pb7td`IjzWs<|Z589SA%~42Yg3oSky1eNR+&;4}tOQ#X>y7AQIR zKqaK3$fckj1ZmZ<2dD>fK8|?EgCgN0PJPL6h8pO1vI&EmLIZ~{g747CGf*boa63s9 ziZs$4loY~Vmyv4_h(=&OjPgE4xOvhPVPZ#Tbu z8$t1rI%OR~T<=IB+Eb6JQV>u9#t4~hXxz9NJECxL+LCg5f{Fxap&Fp}K3C22s&4Xa zT<5Fi*(pDi97D=amMi-)e$KGf2_!)tb2Ql%8UZIDk9M)9$wvsud;vpZWCJo;N>vaT z@oK={m=v*&ppDeTOplqJzu;LN^Q?wNP3G;)xMy3`zHOxmgN6tmN5M&)fhGZlx&x9_ zSjT9CgpyGoXb9OgL~lQU8uHhi#U z8EgulC}0>FKrn99Fb{~s;ULe!kaLs@;Im1{_PB1(vt65?gXpsQ#|B^FNL+emrJ3{FM@J7^yN9tr|`lrI#NPoE~2Yxg`g3!XJG z&zjjSci}msbk#y>W304sHXJYAGG%{vciVj7{_i|_``GO1xz?#L98(m$+8Oh%zVqai zIaav;-Ry!T2fV6%nwiI9*{kLrdB?LiYTx_ua-QHlAbz}@D`Y<^GIR4k zE8JIU|L&gheRa0>ZbB~VMHC@hk%-n}_T~;vD?_xLkPfM+lXTn#=EOPX;#wI{hNPi^ z=SS#glrq4eS^c)i``R+o49gN&vS`ap2x5j_`010>g$tZNl!#!UN!-I$A`sLhi+~SU zEf?;Z8Zw~`;F$yDNE_UAh_RyjIe%H3*}X?oBmt-UZ$}kH-+#r)nD9m_)0yPr6D z1DI-s^)z`T*DHv2ew`}u4Z3}kZn_eJs8TkV2^ZTS$fZ6qVIV$KDf$I6u9WEpudhNV zhOeI&-m@H9vJ_iu?q`-RS@8tM$SYgQppczH4hlI1co%e02qONh`@@qk1(!An-l9ePFZ9xX9scFDThLEwNuG*gH@L^>!Sj^| zT&}>~!1Tk#JN6V>*-n3A?=7Pu6t;Wtd3%!06Mc_AahaqSAh(k|bLb3^&cHs5=>bz~ zK{o7CHw;=6&=i4bVAX9k~pv~BE27N{hUmz zM3x`Ldzw)xSGv_f+4^ZW5S!`vOHCG`XQ{3$x|hW_mAb-)UngDD<(8@EqK4GmKJ{Cp z)J6F?eQWjFV0VtGJ&AQE*w{?4b^?K~ikg2r$T}G1jE!8_uxjjjr6^sP0}8!}meL-R z;`^>r^?k<$C!#!v@1Zz;5ueHdgcITbTMw9E5s3X7*GT>b;v)@dAO&DarxobCP&J=- zqXavCeD`xExr6zlFYDc!Z8x{T)IV^i_RrRTXZ??=?lv8r%9(0@#TKjC_AxU?`$F+5 z@oSD%H2tV<@-W@LOV#VA+YtSHnr=*S`USjW4AePZ&%KC=lsveeSEy3Nr~e$bCoa!R zdtcZ~n`Dnp9$j!(#@v<5RLgrctKN9z)*}lwTVgd^=4-aW{L+{85t(1oeoyyHu1VK| zy*Oqs#(vLxb{GF$60?^~Z+U&!&0Tlx^&I<5q8#+?GgPOt7Lc9HI)Rxdtn-l@0Q52+fp^fgrOLkqQyyZ{qm8?Wc7b!w^{wX8kD9RRnabG);-WS5v*t! z3N%ABh69X}m30J_YE>FPz-GhY0`95vN4WDj-ccC zRJlr_QMzffM3G~%SyHs3-O5~ko|;{ST40LQultt!wmW98d(r%oebRnCXTtT$sT*gn zpS`s)R#3;_tDV8DJVFJf&=~|g41%svmTqLp9TPqBkqz2#4xJ3Z^d|FN#`q1_0r*H? zqEAD1iRIbUX$e~4i^2@KyJZ*Bu|tlm&%?$(sO{^-0ckjt!06LLm9^i)80;M*nV}Jv^8lTVTY>=Lk)+4?#xW4b4anO1A0dH+RX1yd< z*G_2zO4}$E1#QwsW6N0V)6yoiZj-e6d7*PNlt6Tp*%s+6JIZXU^lLcC%+k3{Dw4LV zwPRm(Njvb>cIliHkai;X9SEJ5nh@GKTIBC$8qokPNsMRwANq3Q`bez%P zM=1K2(3w*a`(?r#30KQecv|l|)zNyWJ!vt+fY1dyC){;(0s7taiC0yGHFoq41i?JF zT^)utUuwT?PGzQ&F;^lh`2+b`6!V*e#E7zco}4*x?Dgw};Yek!8KzQw2u-s!^7FVG z*GQvTcd}AT-)^s>B&>V*nqSaqry)^gZ~FJ?)F|WD!9t{_PD;7bnTL~17UgSY!q6Pf z7*a26M<;s{bPHWbuc>dyam4Ox0Iw{Kdn4t5?3$w~nEev=M<_>H7#<#H_T5IHjiww6DxpiH9T` z33lO76`^mv`b&SR{eimM`p8ru<&vyn@Cy7w4r|n~{#0A9ju*>ms~)e0;Oj2qElu|Q zz*LUpR7s_8Q}fcFYUxZ$pRWE+IGk6;dWFU>8POu0)oSzPYva z_=%2F3CqFb$J!FE)2G@_;>-Pqj<&&1>Qh}U$B!OAc`6}p@R!L1h0H~zO!Fs^O-@|Z z-7&P61&$CDf6@#gk&qca5x-AgeUCn{4O}8yCpe^IRBnc18pMdIL@u)=p$N^KBvIR? z;ygG+pfmSEFg8&H1$wH2w6Epgry?D&KZ6>NJZX}UBjJE#Tsb=}ql-(HV8cQ(&o1<$ zJOq;%H#yybhc#3M5_g=g1Dh!lz(YH6nVAtMY;?>Geh^Ix;pHeb@HX9c5(35Psfw{T zwLD=$gENr#MK~N{JRz+Eba5V!1m=+(wV4{VeiNe}BEcDi>Dj7V9_HBe#mx8J<#BiA zl38$M&fCi&YjWq|h?RF-rHEfBs*e@bFBENz6>Xesy`NWdW7qXv+;o1TmHFs{JD<|E z3#FT4rJLrqyj>eD-4ri90AUCTqc(3vz z+vd*xs3TgwC0-7XLG$*K`&H|r_H`62p58~9!+aj!*qtvqCLPgS|67}9!wc)1W9ysa zYxm9DTki9isfO8t3CFyBz4AMnTRq!GFE+iOiz8lc^j`0Mxe%HSF^gaT&Egl zyz_Ryn$ueBWw9$eveDn#MPV#i;&KTB58`^5P3OK20QHbe3TEBBjsTg=;Uuzj<4(`G zx#4;lie~~ieyks?55wKG&2@^v<_jo`NdOe+AP9?OYS2NM{slos7Sci740Gaqa#@h} zyctxUH-M(?LyRTh58!LG{mI-Oaqd!!@+HJQs1)P6QY-Z%o(9J{ByJy9yXHC?;hEW(v5+Nk#_3LN#1D?k-^-AZi%&>Z-H|?O7r(F6sL`wzi z=vOD7hCW{l_HhjQMuvSC`iIU#9SKb3K4h+#+Hm~R6c~v zJ19doiMyH!HW`Q6S$pKy1)!i6N*hW52NX9l%#0(YA*3MRGjebMr@XR^30^vs~%=|*laD(HtZD8L*UN0aw5B@@Mx6u$e(u?@Tllc|E*eA1%4LcqsFGG9pt zhF>KxRGcou%c_P`|04}~J-QmO2rQ+z30|X?1?T|1ZI zBtd}&r9NasQ2Knzc^9C`P&!=a^;&*5S2uLwq z*ovLVWvBmM#6;8l4o6D0o?jXHzf*@3ISaRF^;@w1N%QrmqV`i0SiQ@pFDI|4+ZJk? zVl_?k_DA3oWkT^`uJtQ2(dLVyv-!+e6Zn+|jx++!xWNwTHex;)`V znvCb|#>2O?S`w#~h93rpVohg91ZN$vFoItoWv=5WM*)uKtgxE8rD$X_U`-^Xb3~Vx zd)5xv>eIMEy*G3n&bV{Tna(X;pTX}w$_7V_ForYoEdYD+h6#)~ z$;TEBY}vq4Olv%rYSN7-#Ky?RVa33?k#GkccBAYrfvE>08SxXm2c0ar4%>Q0eT@+} zn_XDG*q$6r7ww`c&Q`=T2#^<8T}3fwkk6}?mUA$~#tEZjG^v@Soe~WD{xwC0dWeBV zzK<^w*7MW^;)}>Lif})FD8=;&LMb2E8NZ z$~X?8n}mZkbq*<7MI4W3*fqhl09X-T#OKUgk^BefZ)HW`jpBz+)jh01njXO*VH7_T%c?Z5zK9`T?gcY}mH z9|*BiQ@JIbm7VjD5W(>C1YNv0NjeDf!FP^sxE|$m!J$FEk+xQ6!!qznh^(7FW}D^U z!SM8rr>{T#@>!U(O<3={vL+t6ms=Vw+Z4~;9CdGg&y_V5y7BqzpP%WT_pV`XcUsD zb4Z&!oiqL9%;jiV07P6~j+0`>U;@RsKvUkL? zcihQ~XYZb9hX2Ty4o)7Vvp#3G%+*e`&%3wZD=eEn{qn%X0j=Z%cf5aA{wL+}iUaY2 z11KFIQK5yem)$ITwPLP*;^@2^QZH)J*8bb z;7)0NpyIUQ5WAib;#(D|hSY{eo}T<$(PD(xNeySq8FHbCg4K#RUS(^eDk?-#&x}AE z!Y%KSRN4Bc*GXwFKd~aY#cky49kM!XZv^4WZHv{a@Wpc-?H0t6O69WYNwoyOOvd<0 zHN}dYuBp6Dys29HCdHOHxiTHBOK=LU=H#ArkZz6Nb~OOs=s}E{meZ#lb3)^>f#KW)h8j^J!;vo3V)a8~E$Yu#H#RmJ5@G@R_rR-IfEb(iJz8QcG;h**fw!yp*dXyz zUlq}6M@tEql3gT<`~2kRr@Lpi%ofHy8>03NE4gWWz`5yF55-Bk!$A^RM24SWJD3AB z0U^~1g@F$gh}6bgnIwGOX<~C&ovwzNq{6KlpOPDOAzh?nP|HHtNev#x3CsP#+ls@S zAE48c1QTvbqka=9(1;=9>mAfzTtWyNNc7jUlh4lDCZ3IZ*25v_%HAT{4WB$5gJTnz zK@NfbGDj||{S_X%+6aLE74%hdPP~_VzKDggM-Q1153u@c^okry?V1eg1Eh;&C-n;6 zJZbZeX1!@UO zIKpXX;|2{}Ke4hZpzCZr5BYA|zGKsmDw{I>=+!|(?|~f1e~EhUiP^`g_sG}MN`N9x zhXK^dhfcpYQ5Cw3u61<*fW8e#O~=m{V^ekeNxs>NmK)wMIHBN2tTOw0w)`njNwcN* z0v`qWU6ctn(*dT0K^=rA9V7IEw5}3Nx-)n zU#`|aWmG3qQDO5sG$JyhagHZu^A)T^pwqNj%ve$)+z68x6(zqUr6UDFL1KOD3PI5V z-N@jDdOnGQvOk&DA`D#PJH*{mu;PjQ<9arzT$1uY_4Or?KBP;{m-QoG+Za)S<{C%^ zo|cNz$)ExBWiQA$-e}NL*bn04#HlaPtsK(W{~3$yhCpa&U^kI74=TBtwgaJY!=T*3 z5u6`_VsZ6~7OwWK@~!5%DWgf=VX!TfVI1^FT7KmdGZM>xiOF$WK{bCxsOAR%yCk8v z9L4KMUdl|MQ4biVK&?sx1<3(_+_Nca-^7tHzSb^6!oSQEc>1$U0no)dApc@>0G~c| z5+MFx^ejOAKV=#q1r6zc(|Mql`74?T$j&$u*iVL~K6NU9mP2Yb7v%p0cuj(Ta>;xt zsMIx*pEM7Yw4aQzcXsEze(_fZhv-F8BgEQlP~F)-~9g9lp5E2Sltb8Ahu|!;Dh(KXfS7 z3JnnX$>vy#9|MhYpxjor&Qr-?tLdCJZ6#q`jn2I)+zeGF8SfL1%{W2rOFF-hsgX#l zl<=Sp6i-`>sELX!nsa)TEQx8!R8n&RVx&+T*QEE8G_qua1~xBOaA!~KA`R}&>pLe} z?~{g`Yoh!yyZ^14S?Nvx-E^cUjUMt5ms=5dkU7bi7;zEV3F|wgO}$jD11!pLJQN(J z#glZR^`VjT{)Y&qXvJUInX0semI>`+I!=%LUy&68ry11cGNtn$EC^ATVl7V{_cY02 z$a8U|E5=C`8)(A`%{Lp(Kf_q5Ytr}86D!GInUWavgiNw~gT~5-^lYp$b6z?$d1%@a zbC=;LPP^l!jLD1_9gCjq3HwUsfO;H%;_`C{{PRaB-Q3i5hv{^NDdTI3d_8G=F$6dP z7roYfDR9Bax3QVX`wAsLX-yVMzjsm^K1nAVL`b z?x72?6d_v@$`msrsc@Ar`UFRhf*Qwhv&c`Ki8L_szr`(Kg$v#8i}FtpPR6Gh1DA$J z0UJi1n7{cZ#y~kg1VVxbj39;*hGJ8@mTT5y-?};fmF!vH<^v4 zT-5$uL<$sZgwIo|!Nb=CtlGw1ttKwf3;uwBr;t zIQK}sB<=L1ht)E)URPPCQ$xcohtNlRcT+AL%uPni7XBLOK5eQ~P%%HOba{9B@6xpg zIr&m5fuC<;+R`=dFtnqZzcb#^4u`5yMs4BUNFNu;F=ofeIE|y?cSZi81t%|a?8D?D z3d6k`9tSlKg$6?uxJ2re4aA2+AAI_f#_$`~FgobjxB@XLP4a4Z0?U!Q+#xynT+r)M z4-x@B2!TJDdqG;d&Ye?SY7#Dd=$a<)MQ=qmscUFgkm~_(PB$F)!2S>FORyHO;Ddh# zswg-Zt|&+ptoVjXmfyp`MCz3$w`-N4~-5$V{j>y z$VAa0)Prl3BMzlNUv;FenySQ(z$5P^9Q(6gM5bDQz(v6CqD|bU+QvmrinB7Zp=Oo* zGD=Oj$rm*Aobn~OH<1~0NehFjxD8PsBrl!TkArVgh;=xu!cEn3gMjy5xjq^QhjD7`7>-4xw? zDDFKx;a+rkqpnIQZlmRo#dA+a-6!Gw{Pkluk1dpKh?Q+vDBBq;+j-|`tnA=~>wbCF z>t}DCT_|54D__4*zAIL~E4sVm$K|IdjxFZ-7V_$FxL(v<7tO0%ENPrxyHL^?D`}j| zT-dNLwqakiq-FBRl2xd9T!eQ5M$W=Pm-D7`;?QDI%}j5sXgxy3wX=@7wpjhnSaH+D z;rpHtm(s=PdEktrJJ@ySxjoikPcn-sPJuTk!9R`S;uzkNb};_)o|D zr|11o+;iu?bbRvo^wHVcZ~1Tg??};;kKc7ap-|pYciCcQ@j_-*EVF7pvqn)RFBGhf z6|A0hFQC@-P-Qnw?5AvwOdgqb&-Bc@8{l_i;@JDS<%_wc(?_TGME&ha=h1MQ5aXp? z8i&@80W=!Zq+85&OA?&wET$6}Ul#dttCJ1-K^mWRVP7eJrh`e-E$wL$y;--X1*$fu zv*j|>T=QRbL6ppJTchxxl9kq7vW!bxZkh#k%pBHs1@*ySRqFMYP0bs%vVyzG2xOtl z58%{W;!L`=15<#SfQG?7a`o=YHGxajlTdrJDvIZE`Z@r_eJThw=J2Sd$%GRoo{I1A zOB)3rpxoG$5#1#J>@|p1l(h*8#iUC3P8ik~!ZUg`9?V5iWA${h3te&>dw3N8Q2PBwJg((IIcuS;5=iU zrLlBc-#5DMXE<*~FJ`dCRUD5FXjz4zruj%;Eba({0Dl-?j!j#TVG@Xs; zm|53DGhC5|5FWDtGnn2otNBY71v8{C1*VCq7chgi>oA>V=v!7bZv`{h0y*ebgP!fP zAO}@i)!%!`INufMI>-D>!=fRS9v83)Z`{1WZP5uc)pKlt6p~*>Gc8=P$1R|{jjZ|u zT)LCvgpP< z(RvV4r!tex(0t}o`A;d7gZLFw*@dnp`iV_vU0`Z>yF^p^%gBID>FoTe)-OdSEI2cC zA*VK$12?1boHaN#G`BcuuHSQer!!u6-*nGNarfF!o60O$_{ zX}1b(PJ*QuiA=HZ0QGbCrDPt$4l0A8Fk2-JYa;`H$aF@qywPC-ic~M<17;wmn27lF z^95L82D>gp!GI__R~oDmsS)n@+?2wVGX>bEs+o$G8ND@p zgYsCWhNTfONcwYH{p9}#VL1aK#TzoQ5WE&V9(KQ2?$mcQ6qG+#sGzc2|BIID8h{L#nSK%w|-^@uZ=BO ze{$~+fP<%5;0NrF1eDkNA)Vyu6@UMVr6h+9q~_^(q?Q?NgJT;k)gqC5cnCwyl;!~( zKQMR!5>J92Zd|E-3CSoYW*T|M^q@+s86>&EwIqmCp*xtn%%hH#|qIP~_dO^sIGFCS)G2sOcEuqvQ!*HkMO~$Fc5W{fzBCy3)GB5^g zyqZ#WnyKTbWk*hIX~uA2fuWuX8Z;VJ@h~O`XJqIzK08=9=r~QqpaX1|A{i()`N&wF z$usadJBo$*+a;^t5t42&8xPPidM3b$q`PO<^a*%nB#Y55^n?*Q;b>7WgOTU#aN1O$ z%+|l=O^6hqP)%b}qp)rXq3s+@^GS+Ny!`&Sy9O(EPW>xerd{#8+GtMwOaM2WPw1(b z2$MM=9TB)1boolN0S!i9U@*#H458FZ5--4d=Ii3`+90KVfFezo)x(Xlxf`cv!i6(r z;|yO?IVl%E+ZwK$amFOMpHrMBo5sy>lWY!DE6|(_u1~LSXi%2826B$N`Yd6z-oXY1 z#tkrnslZK_3El|X!lYIQe6X5|R|S$z63Ekn3=Wv5L6#($)F4p{`F^%1P6AtYQNrg|3(k6AujufSWry8?~pp&RrAW+HcH>=Y%x8^Nybu-xC>jkoNgb(}On4EHYL*B6B^x@eQ4 zdcesGE_i3uXvK7FpsEo;I-LpB^&50LtOX}sq&*KDUL4bCj?^5}iFV#pY7SN`ZH{4$ zMf_~JTo7IW$8kWIa80=37t@T>NbHg(W?}*+7w;DqQ=ppF{c^>j>Kib8qOTWo5pQdaK6dIQmQAL z_+5ej4Dbe`1CeLIZH&s(aboi7Yuw!Z-xyo6T`eFmH1FAruH>e@0Vm@YaQjs9K%rbO zz>tD;GjNkF!LcJ`e;`H_t%91DVjT<<8N%@J|2?R>bYSK+=STHp>G}B}>+yg;dB*!C z)rjxOKrXzWjYN||Q7}?M`-W=J@<3vOSvO+cNxwAd=v~QLap{xH?X{5t#>b;*xng_* zU-8mGgThDXJEK;Vl8R81i^Fa~Wh43(BFGrxrAigq>ky3N3b!A*3=_P28Yz|L5Fn*hiP{^Gd&FqF?|+J&`_ zpV|`YLWR23iAPTb%k#|+Up4E_--5u36T*ZzVFG>>pLeTs5pJDLe#<8tQx`dmyuOoG zjXW6ahwPR+L(rBVwOAld?%#omoiwHM!VBNLW@Bx6I6ayFrm{$=PwXuxX(x$MQm#pH z7v3;u!MwL%%7qs)eaTMFr7f9(&_x)QFqzXD;1GEg-AJy?z4~6ertHEc$HI|)YJ;&c zLnU<|-W#x6A?jO^jAIA~h&b4U%&_bDe!lm{;p>N|!wZ$$VwKzGE-!3965D79J;KBN7%Qd`3E2AAilesB|Rv<)s3IFl<=^+EllG?6-8 zZDB$aqZvn2UWfkZr#jQR^>euM%PIev$(r*h`7O___<#i&IW2EQN;nuHqC~!c#7Xd{ z3|d;(gZgIebiK-*Xtc}MDBs^kzPz*+3X%H^c{9rW9MzCu92Lwj;wdOHQLOa8IzeGh zq)gDC{1;sQeEmpjoLB)Oiu?|1Q;+pm!qH32UKqw?iJaa+Z1d}tASLVShJ_){%KCe% z2!pGasWyA*#+kP;BpD&rrfb z25gC(6(~X3I?Q{xp5T;`txnAcS%{4RCt+DhtbgJt6MAXYQJD?{AtD;isY`^<_bA^v z%t&N|HMTw|yQ!DSfy^h@grBYxu*nw(aXE4q>@_ia&Ah$t{ahTw{xYdd3cM4}B~yc= z4DK{Cy$kO0m>VZOEmZ7@RqTmZJi1VEFjjGJ-hF7%UC1NnD;nb!8x|^d#42{oyLXbq z`htm;f3##|J}UlHpgW8sGxKS$FZ8_e(v9=(P4{vOZ(HKthTE2zjjx%fo4;n8wZyy) zb64hb_fE9j^W;u#{?gUyjW3KZ7MH#5z3F|m_`2;MZDLOG2Lf(zZ&gx0vH!)xm}uE~ zuUuRxToWr?^A1iE+1#>NRPnm!rYBn85-)0c<}GKkfbvw;=U zU{w{te^%0e+@h2Iy4e?qXKSAe^-8()+{sZaW24jf4H*-A(TZ=1Ul!pR!}>6vmdty* z1AaZM>HKHtrGNgSm+ot)zW%`E4CH(2#r<=H*Q)Q+ zry6tgHK-}{r`j%f4S86sW%7 zKW=*p?9O<`=QF0n7Ylgkim2=Qkq5*hx;a>1iP{4FsrDtE<%#_5sre)4eQFKBZv2af zMzt+|MIp<%_zkdJsz2EnNdOJ$yquLc6{30kC38P$b zCJi^bh34KMnrE!_SNCdbx;%b*Rcq+`hP$f=H;HeRjP~ zCS;mPHWtlnoBtjcXl0A{A@s3VXTJIlh7W99~D)@9mYhUJ`t!N4@9n(R-fxS+m$SB8p^*~+B~{3S=vDpp&?~OY~0_N_EZ%# zr9I)|*2$W-k@AZT=Nraa;x#KSwI*s-4Ckd=TrjX&)t;zoPgc!4bNA5t;j&5CUJW!Q z0!`zAmaBo#b6d`AnF!QD-(Y0Nc%X6m9S8x%U}tDf+&?ECnsYS>$55qAHsZY)JReL} zw~aM_q3e~dWJmYqyS~2tE8CMRw@$3wl~}n8c)C#GkQY=@W!;b$W?X6Z zxn1-Yl<% z*VU4mnkBq^Xy2OaVZ7bV zctjB+hv15QW1@L_Uag3uGAe%nbC}dRwVvS-(M$nj zPUdH%0w`+$cJMb6D~xT13<0%La!rDX?7ovde+=n9`3(+bc`NHLb0O)+Lo6hi z2GtW~ZHcnBvGQbD*N~sSbyl8PIdaDjidrYj<_#~ID4Ulkn>Y4Qylm~|%0$`Pcwp@$ zWL%G(d2Hmd9~8GwmUZ%-&O}+~*c0)xb(dQcW$WUBbytJ6@!2ac@BI2>UwQ2Ew)m`j zlEHi9{(EJY%aF%%3&bAhp8B7NIANz5H~cgj%wJi<~ z*xdeKXc%>L-@}-k?tf2W95;W)fH?Dvf$6h%cZf2xhvGLlG!7we;FLHt zMtskA5Bc9JsTo?w6YRoIJUJ1HBw$LR?u}5_Bpg;2y28~%A(*Kut;NZ;xPr3J$#BC& zxD`$)M+aU$_0p+{xyurBmrcxFo0z-ywc6y|dz0a55YEaRtg+~svU>HiYrVkmD04M9%EIp@GyZ6yNqiV))Wo*7$SR^(g&t%cZAQJrDmAP&1?`WL$hHVAH8{B z#MJi{!@2#)8G0pq`k>Ey{Mhjm&V5m^*%z#F zrHaTM$4sEC^4jcuVFGV!FVHt~0&`xfIZozpDkwU=u!g9bSTWiD2v77cG1$cEZUByj zHvL21{SyW<@tMbTl%fL{=}_x--2J{i`=_d11@+fk+9q07BwAKnZW(V`lPp`CXjwDz zp%>PVxIeWi-m+%Mk3HdfN%gtf z-VT|yXl@lGw$|27O!_xsPwbPW~cXvxtddk7z<#QgYCT-rmpI%O)qGV2%jB zFrabr+k~1pmISfqqq@RCE@5Je|8HspXP~PL>JwUg2B3q&v19gX%VHI)gG;AUR&&aH zr&mj*5CegIQLD$zT1Kb<=%{KY*vfjHi~UX%ZD_}-udqjqk&2nVzd~U~D@n`2z95Vc zqD^!|K5VzFqYU*h%U~lbWp#u7a%`3W6dhW4*kDxUy9;CfLUtOkVASX{P>B8FWyQqG z5OJt{vG9A8gGpj(vH&xBEl0g=l+4I0&EbA8UdM2cxJoL-jrP7|c+QaaR(TD)h=j_X zJ9XyN$lb|M3oM0xvM8;&Dr!Nv6g=P_erWjKcx2_}2QJ_J+U6^LS00R)?n?T14Si@j zjm!BVbK-79KESCAt`l=H%LhICEsl>EMfkqCqGdv)ErgLR^;IqE7(O$MbnMXo<|AG9 zc8DDnjv3P>8%NDC%L|(uYZo} zTX-R^oG58Zlr)Vl6{tNsmL}BpjXabLwZ{Fe(rJ)j`xUtRz`$O18ZQhm5m-wH-iKeA zhm-+p$;t%-HEBe>1znos7@U*dv^p6}qE}zA1u|uGGZTgTLaA3qFaU~IUG~y?z~Vu*;4Eye2XamQjkgz&6OfMB9bmkBb~@yhT=N)^x4)tkEF&Dx*Zl zOg3^;gv@lfi;beiGaEST#JWr)PDpZ0FTtD{nFy2zpMdo;dS5|{lb8`!D~NjZl*3%c zZo~Wr-eSA;W39FtiBsRwwq`zxKZ52a*aW+;X$6EY@`_baXS@HzaSFmgxN%%i|E53X zZidYNk1^a7D6vSt&N%h{Qhur(-3z)*X#iQ8*^25G+%PF4odlB(#uwBw6&WQvOrhPp z`aZrUYJ{FcC*^(N6b8F77 z0S{;NegsHT1YT{JNAb}eL)*s#?UN0Q2nY4>bkFWo%$yMFbnz3}!!`bnC4S28}xxW5!&`K5wimSW{R+)-@<- znVFtZRA>;5RQt%Wt~!WDfSwi6DBh5|P^@F6$u)sVNM!Gv=DY)u=a?8f`g(Q3E-*-M zv1CnCRoZV!$Q?BWBz}f+?tlm^OV>|T2&tx9(-z84s6_cx1NAHm7-pZM z-fRkuMyH-dt0_Lz3ut;pfEj$qKF)@3@@_R^ne3)i$Y|f5J*UJBfdi(T^<*Ju#ly%3 zlo$+8m5!88)O02Azo_eK=YmU>U#|aReRAQtWaphj8zusci9q9MYXT#?YSu(mXQHZe z?1AyBB~VHVRmxZ%fR@N)RsAP}AlRyJC#R`gk!2{xy10)P@u)%vq1e@}2f0|{w;Ia; z-P&n$jK*BM`~XT;7xd?ixd32m4)&rsV<5B7xkrQ5i2C!|Z(%O1*_9XdkPnO<7lvsh zMs8M)UHk-Wu@00=!-fZa1C;E^W!B;Y=c7Tzz{t~<-sh5< z54!h(!Wi^M^DcM{swE$3XZnr~UW^{NI?aHx8J%-f5d@c5kS0y1e+Y92o-Yp{gI|q% z5I3A$LbiJ(N@~ikg1o99!aMqLNdzV#NEIqI7<<35`Jcj5hp{$!Z5`LQ3Y3dozupxp zdG6qugH*&w1{;RFSL^C8ZaKf@;`Z~~VZ>v2$%Uqi?dRKHoHyG4^2wJ@jtySEKRI`8 zyk=c8a3>_AvmqHBx)&~?u&h;74|#@eKT~kMC_KDjqP!)6|3xiVYg;bfcK)`ZJBLnw z{GO}DRU_Jk6XV5mGHze5ZQ%1W_q-jhod`z~;mBxJJiPT%WX@vah(A>$y_nbRq*3psDCh* ztSi@=ZVPTsYrjM)%X$d*0*bWcifa4aXT0iU1Z0qXKhG9<=CWa6)!(Qy)&eb;`U;F^ z`CqlnQQhtmAHCK|(5wYcd)e>#2dteW*IrXY7SVhVZ*T;ir zg1PpLkJjeCe=B2Nz)%-sga-)(-{*@4o(Y^TJmY@41UZrL%et((79A}uaOHl-cJTzP zS^oR1_5NEwj~4Ntx!0ktS-)rKDfW{wPH)jqnQuXv=3ryi=i;aN#Rr>bes(<cPFy zr7h0>rtIGs6?3MS{gHsJZj%dxXt&u6>5CodJErGvryrPaK0VlF#_YcX7S_;iam>EO z<{KX=GVjBqT2}83+-|=$7#J-6NWnpBc4l2xD=yD^fS=}!5DcS*0L(>?054c1Tos4P z(Zenv0PiiUN0TdA zD?HflR9ouCr;fwgV08LvP)M!Jnrs(UALSnSD5xM-==cfPeaa|oJ`{)yCe7~M>+jvZ zV`I;bof|f91Fxseu*2Uj`b36#WTpI}q+J;qWoVa8>mAi0;O&tlT;+Vc`M!<2AxCo# z95#K#7*)7$RbAAUNaNvCjvVQW9)N5Yw5shmn1VfXlpnC=x+*_`EmGepquP$|=Mfre z5>6Ujc?FX1hxZ`)CBCZQL`}$J=h5FP#aUzHSZ-8mTG`u?fqoJL0KT+@QAUm)J`W}n zkDb^rLK??ta5kH9Vo-&e;U@6V_MOm~DLNY*HM%&3L<=Jk#!evQ0$2%l8;>0oUs-q! zKD+h`JJHaZ&S)C=e^dR1u>b)h7%HI4nb!xaQnxgw8ki2W!4b^Lu5HH#cJvKE@2$`D zq=&zow?7I#=i`V07;4pIW?N%QFz0B~offgl6`e#4whQ;vCPePR!b_RW;~+jxJCEu) z2uhpzZa7$+mEm9!2O2rV*;|o)sj`4 z81*NM+lM^4?p^g44;_S|y^&=}f3y6!&~mZueA|m~`ab7+ap<`R&pbH1V|3}zgX6^= zV|y+&eR=K|=U#5Ul6PtD_@eFc9lPKI^4!t0N0XJU;7*oTQ^j&JTs2uaYqF{a|I-Do ziiRndRuLT9NJn86&wcpJhvT7E4uzt#MHAu1M7VK0+)U-OJJQfSy94aea1}g3R?a$i z@a)0o55r5O&kM_>wG9_L&UcJH2=MdpdawpiH~6D`cFSZSs3PyJa4o*iJyF~N2X4VI zD?YN1)t)m}I(GjT9(?7&%lpP29G}1OO6g>2`MJ4g=RV&)QQDd)ZB3T84Xw|0?>)`c zwFx=S5vSDT#Em)eV!RPV3Gw32oBgLSV*Wf(zQyhR z^T@i+%}cb`i%T|_`ChM&FuvN42j7a6ZK?HsYw@ZrLG3RV_z}NS6WG$~{dQUH=7ss+ zp6|t*-(KKme4)gbXq#6Te0v4ce#fnCDa!v&P}@>f@SU=Jr2S5Z60bayBQWtEBdtjUpyN2BqM)$!6bKMbtJ5Ws_* z{p6j0th~x~u6mL8i|$34$auDvv~?>Y@5k(qx@q~Q-Loy`pMT`Ipce7{hmn%zMqyiS zfSS4bZ{kKjn_V&f0NVrSQN2#qp#x==`QUYaatYGG*K?>;Tq(xl4$+&{vv`HHh}8Q| zL}tQ{*kd6V2mxECk)xnSmK%pP1@-9P3qQaE#*q!aK)USjqd{y@w!$J0`mO5~kOruP z+7dp6EYT*MOfX+9nK)^fQ~~xxGzHKk(=h0`_m^w|a@3)B4)NntXP&wsIw50uGdV~^ z0@V&15c$`&1My-`Ph+tK0tQ24|_C@e0{)b!HGrHk$hUom5A5aV(FFj$ z|0vIThwWTd^IFxSmZO{miL!W~`Z=20V03U9%;YWX{2<&K*30N-yNCZj<)SEgd5*6% z51A&(x-(X2cgWwrh6&CC3VjH`F{pH4$_J@^U#!R;E~{dFJC675J+SW-xlh*k?T?sj zDGa7eFbsx>YB?OZ?Y1Lspbsbkl^56%0yMyQ0$jZ}+8xn%VJ6A~1o;=atLk(i<}4l6W{>Rq?B4ORj;ocCw8vewU~<9Iv8O;LRIHftdTFWOE0+2zYDQY)fXyrcZ05{{ zbg`?dIUNL4U?S9%2sHuoIXaLC%|p(iDwyoUCN)wX58XC;GU0IIYpk@WudLN-kFvDy ziAo8#u;R$piaL~O}K^U_4~(q!}UWbKM%NjGdymQ)S<;UMCn(Jf=W zm-5D*iU*g&{vRl?*qPW!^Vrd$STeLK?q8+AGS2ZyS_Hp=>&F;!LSQpZec14?0gKA8 z0V;Z9)Mt<0gA|=-UFL{=62cJ&t)ZDC7v~(SRs@8YBfH=EcE0^K#&ixy%|T22X%`PG zcE;?;Y3v-$8c_g@nO79s z%9&@&V`iRwA81klKcNPC%)7X2f&SYlIOSQoBnCT0$4|{)x+Jm(tmTaDMIXa(j?FTs zy9`lzqxVVJdgGxeRvJZq}$rC6tEPyZwXIZ*kJL(z&1=M<1{V`)-L;yqw?VM%LHC6qfwf{&} z|Cw{^Q^*w1j6zpg!^o+zImyxm(3~$SePMCDdfs?Z=cV$mRZpzgoLI3rx#FJ0!h7I! zAzso6H!D{|urf57KOSn6TV3OU+a`h0Y(ER$NlT)%W%SWx>B5Q9rHRs|mj;rhD~Iy0 zhi4Dxi=x5av1OOHjfd|6&cYvXXi6=6dDTnMlUfL)np9-^ z>0(McEB&;)6-;^t0X~J}B{mynqhiPUyG*z*t3<)THhAuIG>%U&i#IECzi+o3GX5qD zA#T)hH&5Y&>-dQ5d;LE%;FsPEuUDq&K`uiufnCR_oBfLZU+^;SR^d;q)@oC}izHU^ z$5noXJdHfchmRvoRiI?0$$Bbr@BA9Fs`8LOww3 zc-g~9@^Lp%FI#fMw0qwVBg_mFm7WEUE$yV z33HVBBu#I0aY%JB8nrXh)z!seavm?n!r9Znk<_POU|!aj3gbRwEC^Z8Gq)p^ng1;ivdijNx%J)qkJCw;B8u zgC8*XA%hfys|@~{fhYvL&6pUo5jUUzz@Ok&DE$Dvf-$;Y&>I*uGnmVukpb;F=qp)+ zU5rsrQ&cZS6HRoMbSeqxM;O@nL=iy=myH}#b=dlS#y-t}$0z+&2C^M~j@fTyu%5v( z){nsu-st}$1pmQx!yV z@)k-&ImNg)wZ&bpEu32G&eIl5h4T;%`?cGr%DpJ4(623@DlgR*Pqme5OWth@;6YuH zRx(vvpq0E^*{KDm9&?uqsaUICnn1E9RxW{JX4M{$4LAozn1o>LIsZ+v-l27ira# z;Nk>&sYItKx{6W;#=wa-LA$tlS|gzDA?)N*1NBlwxuJRFT|&=def9*14yGZqz2vgKqMBv}Jf>lY5K%hPPf@AZ^CKOYcncx3n4l zzPr{P&{qGXE~r(gGY1B;bzuba%C>zK^}#l*?-6fQ1K`sK+gDz!F$*y6E#@gxF);{n z_f!)i&I8Nm`4rcns|RV+b;=+kJbD3cK(Z+(xC6(sS^Toj#7gc3mAd}ek>g#aic<)T zj{YE$f}>v@d9e_hHN}(u4eFDO&ybQ(BULMlrAuW`d6XICVh)%!hegHQX?RG>x@-pk zI7H-5m7kRaKQmH!bLlTQaI!x8pl0LObKzBO89uzT``WeG*VX~EqSNmkHoD!rMdG{& zEK*oLAg9vsx6{V^z53x(?PP!&%v!UDc0&UjR!~nkb)(rm;PN69pje=w6EzWOq#(L? z^YK=Bpm*&%Dz7MEcVwkvN_5Tm#%c$CYNwcR(EHF_+rT0CCc0U_8$VKh`gH-J$F@kq^N(n02Q%;jbN8IPRZK zu0qY(nvo^X!~4%wf7$S+q<_|gpMK!STHo+5lySaI4j8Y1vOtM9i=FhKBL38|WSV6< zZr%E~*?L*~8K<<&>ma{_xaMbEQ`loRy5IE|YKx|?7o1$U+*ALA&^G87bz`llRBJ`F z3U>E>k7&{As1JYhqBSt`=zAm|fAjDch9vEO(b{M}?iEC5!BC^`k-}(Qv;cpL`U1$M z@EM;u9UmcJHgLi1NI5%N{7l{>!5sI4xF5>CZ>EgaM?-iXjy6QYD5(U0OYj%K=FER^ z*ISDFWzoiH8UB`|KIQmZajK%dNk&@iN1G1AFCxz4wz3%kWuQ(C9-T7zLe({lDVT{l zAn-|nGm2yc1b5^x*!R@he+uk50Yj2|hl4%D77z^=lCf)uKTM7sx%#h0SXL+ z0ciWzIxI7jvIjywksaIu!@kS`+}}^*8uA>7wl2f1%kJG$W!qApa*ZP;JC{|)WwDP) z&r?CM2QhW+pJA#01s((J0`^3Y`Y1!5sq=wT%BNQSa!xjL-7pva4h~Hv%AKsJ#j;;G z+1fVbd&95nK4D^)EVyyn;l%{iWnS9u!pG@xWltXUq8!wtOSuyL}V`m?G{?{ie+7lJ+L-5v8F;skuW?X_i1-VDs!=iz3 z)3|aqD|T#8yEnFT{S$qA4`DUn4l7QzIZ_2k%F@DER zMOT1(Gfwj04Fs8eUy-wrSmXQF<4%@pfHJasKheJP4gW4ReYj+s7ssNkLI7c@?k5?Z zbNZN#$rS>~ihUw+%ek=QDa5Ui6hIZ07Bl+`Azgryt8E|ymXBzj!&=kK>X1mcWhpk{ zZ4WzO#a8=(1B0y36ZHX_o0mHu+fR@j5qJX1x}0x3`vmc4?HVAsa4Ik4b$8^Z#uXuQ zvs{D$`;MJ}4?6B3LMwpI{Q>(=_FEtKTYU-zq_m!W*!fL63tS&l@J-J(fa`@!b%BzJ zWwVs`NdNxnh88$5Vhzsz&N1rlVs8&3f6&2s1|1AzKd@UinwKn@3x}5FwJ`QmQFHE* z7akdQ!wkd7!cVSvsc>xJSoDp^V!}2zpWQri=+z~ce4prIb@z#$- zM;?mLSrM=79`;U#%g+^@Ef{HjA;>6Dy(5c16}nO4s>tDrkXa&PsY->4(iaDq- zWI9|fu|*HJY#?>KGH{9=+aB(k!vMpl8r_a&%Dw=vaT9ZK^oSw2NZ^lTMZkt zT;Q&?SieDg(5Mf)9G+#s?ml*s^c!~p1;CNPVC)MHnv(>?&dircdKibA$o|8}_7HXx z-3z@9C%r+g<2Lj%xQ*dMs6Ri94#bnv1Uy&xHj%zt5zB38iE29 z<_n7k1_inff3vS#%mI*-IoF711XXI%F=l6)f3Z0MhO$ln9NIO5SoNXI#XL(bLd4R6 zAU4&Z17XuwhQ5PfQ~$_&(k)UM{o_ar`sZ2vQPxN~3{CH|zk_(J+1dD98XP+WT-ZT8 zsju#3Cs5R*G#%Pjf2I7=x>w7_e(ejDUn&2Y{=0Yrt%dJ%cszx;(4{8TMcIUNx6=Ox zuf@XIrS4`CbC4JyeG|2;Y-NvnU-rM`Pq5Hr+v;Rw^&9>*(|e8kgiPgMjkMpEL0Zcf z{U&mDVoVFoIlw6_km2%Hc9o$fbyvz1&2o&W0uI6f~P;7n7|hBG&(K z81CX;1=j*Vm(X4#uqCpVSsAIV5a(&^d`FKRop1fd=}ZhG!N=E0(8m2#h~n{CmP=TP z$0^xRLUEvH4EUUU(4y|h4q-4X|Hh;s4IXfAo{IQ3%)D@S*8h=HjEt%*aQ9zwPSrT)R0hSppChsn$*}!a8{01@ zSq*F$jI@4s?%0ag=79iz!@o^UEaWCv##pq@EHjQp(%jg!Jg@p3qfl$l>;0X7?I?86 z+rQfIQ^%m)yR8fzY{Xx3)MZx08fE_;qYkHaK^^{bqfV;yKBLZBZrIOUZvJf#FZ!Dt zs8mOf!!Uj;+TyoctC?t~#7th<OXNtT+8; zpBj_Mt>0kb@33$#hFoL}^|8#-Vzo2Zj?|bjwOKO;J`~;zEQ7DA$?9fk4;9q@ z#HAG!s+nV~zC56ih)}C|Deh)amky>$u2W$yds-GX*=Sp?fZd`Zlp&W*2;_oWWx~Wk z(&T_&**W0Ql+&^gpzWYkPit`VniuuLc_{{sQMzRcKI$Vyd)gn>P8aa#HSB);qxfBj z-$jGkuv@<$_8CY@WL*G~vCVM7azf!?fE2Y}k9u$;&AOubwvQjU3pLW2Ed+}Y-qQuo zu99OM@-px1TXW||@(cPoOD8lDJX*u$q@di&0tZc9>BfSu|OF! zZZdLG?A<$X0$gpSk8Zg00Qmi=MZj=*-Mw4(9N`+UGQ7vjsEhqDvj*#gkRxU>BL|T- z!UL7s_1f{#C!rMICm%uT4h0!uM`07cacVs{^!>)?EtUTfKAMs7xWOD2<%k@3#7 z`lE$Zf*xqz9TMYx8R>YUHlK(sOw`aId&x%;h1c)krv4fJv{+gCr}*<98Sqdk zJT3kA`15W4l%pt*aP2_{|N6(ckqW9IX?B{Tt10%Sb|J)J(jZDAtexara7#tcS$$Dq zXrzM1M~TWNoL0dD3^lR=aQq+0UT_E;nf9gdK_pCK)lRCzIgLL-nuA~`u}d1R1!X?( z<=bAmZKA0=(bPR2y5nl;ta$yhulX)N@F#(Zph0vR`+NUL|eeHHhwE25W;`i0*p-I|nqgME6+jw~1)G}9DO}g7v(>bX%?{0*R5UiIO>tcWAp_(g{;uIQ- zUZ;W3uy!0vI)wGyT4IdlhYKRob;}N6uB_&fSgj`ZBC1ZfJlqC*;7eQ0Jee!x9GbGF zYiNfYvZWAGIJdw|USZb5?{fmxAyWt1g2&uh3ID8-_23bV)fPZ(?XZ`Kz_Jd3?#Wi)S=6oSVQUiVmTl*nFUIm>Jvr|Ka&A1G^*6(;a<2{e*YT zKH+_K$*Z0(_+Rl~D!UX-w%sw_yiz(#{|Uwh6y|k57<_sGf=n4yIJ=NoopXuJe)>Ze z7(rL(7$uW|P@-hz<%2)*da_S+NMN7nzK>N}Mv5=$fy%CN0&%*;ihAW7M1hZUhBFsj zkRbdiPCCG@iXA^oR-iv1rx7ZIc_1$GBXtgz@}Q+CBd35IZ$!0RqV#CqVBV|w=GntO zM-E!*I_5LU*E(COIx}>Z{Oq1VP0j%yynO!*8YZO?w) zpvEy0)OD&5sy%0Cn#W*iQU^yEn!OGo%fJ89xk>0TJwO8JXIvp~GWa=ze`fG62nc-> zLg*Lx7DWgN4MYi_B7}qnDo`|#th1znbT?z9e4RS{J^3h(V7jlO3NQ;zF zlnke0CjTUJZNJV*_Cut@Bnuk+6WBWqWs@JY$MV7!SWJg(re)MS4lf@ybK~$!=^0x* zHaA|mXxeT{;okg-+O9-x*VxI+eev3^WbKC6qgR%E^Wb>+*BmmxA62zx@8#XE?UKFx zp6~69-@og-5095WIN91U42Nk-XlUm9ixvRm%FvKZkz?EAwiW0r6GYB z;P)cvB zv2X_Om%G$KNY3Oh8?$jnv9`q%kO;MOGuh8lzp?cxv z-fL}xtC8kIcizbcY-C_{vVI$S0FPeb-4^T7A0URiU&WtT zF|?|58QU1z#&G6Dg3wm7A@Fxv4zUti1$@*oMIXn=vrEaP7Ad(=BgV)%7P<>EWxW^B z)JLfh__%q$#J;~?yskaoen=NRq{1HXmEf+r59&r3OW3X)T!Jn#3H}d9oR1$$Qbflf zf?5!RkMn`x6Q1YKW%!x$K&PO9zm77{5hx?(u&1UhxP6IdESNF?k-^ z05Tb!#F5UGNhd5?4>BNAT_=Z6CmvNVMUcur+1Gmrwk34(%2Ndgz)X&>_=V+@i@ zqekTJ=yUjbnRV;f(L;TwP|v+jh_*OOKxEzn*4<3GL`t>e8x{Z&l<{CJ19M%j(c{f#IR_|o5PK%5^9Nzu#9$G}%~A$;Fu0S!T?k;(9=%0H><4-4 zVFpwq)Ts-suVNq?WCFXFMKN=sO!-$Try{HMd6)@!-_=jwTd=F)cHi)M-M*g`y4=lw zm7M{A;d8$-hk^&DUJ`j~BYGxjN-3N}nfhBE+M5d97W- zx$*+yt=C)~*IWy4K!$np-}6+v=cio= z_+EZmelC><+r92tX%|D|k=%&pNyc7xrI9gWHv+gfbY$FJ_l_seUBw5p(~LoJk7XCT z?{!ZW&%*A($Zc4;C9?G)6}hezzoYSvkt3tnT3CP0U2WV%3_619UZZud5hYYN4eJ7F znxCv%5O`rsB|oDp5`Z^&jYw3nzq>kONz*NR^-m#AU0%r6=Buche+l6*235{@$B6 zZ#*;KoA+}*uOVoE|8)QAFDgQRFs0kX1PI&T0Jw>4R7JL6r(B_ts-^^nMOSP{Rf%KC zO*iDK%yAm9QdI<$;XgZ*ymw1mv*pjzRdu(GVLQ9u@@(DC*(!|BEGb5QdGZo=15EV5 zZO%KMKIa2mqBx|Oys3u^e$%ye&!_q<)?MGUv90@_?$A*8uX_`Jv1J-UoHaf0F{$Hw zA*ky6_@gHFEDSh*(RW>J(f4rR6AA`x27)J=B_-R$Wf~-Go3OnM0Jcn6ks?$B%Sw7h zUQwvf^^MYZJ9w8(euhoy+U(SAaYc=UC>4nuXCgJqTsw+Ts7#><-R*?_OjtsOI6Y&C zu^gCTi0xPjuQ@>gK%O%l7u&D^(Z!yj#j4+=mfyf3fx|~6!vrHlT&&kAF)ci6TGtjF z8`tZxMzHC2B4!!xM&%tzWC%D*O~-?|RJ<6#mSH<&0rj2sq<-;S?Sl}Luy*Fu*|Trd zE(F2bCN*pC`j+X|W^f~@1x|2+nq(f+6R*#oIla96T3BE;to^hQm zlkkI^2$te}qIQ->`e6D3{KFX#tEk~?;4TC+C+d8RHAYjf*_Z`->PJuM4QzTL+!VU) z*xne`eamVRLx($tg{F8BOidazsXj;ihHio>q&jJO6EjIhv7_CURx&2UCt<8Cc2je) zlalhj3?whIk%yQV05Kh_*G=L%-h3EqHV!enj*?7OgvtZ0@?fiUh&=$xgWaxPr1owoL#@i#x6#+p z`qVvPt8(s3b!~7{9R_0;Da8$Cq%~@MKm6VBx_j@!*64ZY8s1c2YK+FgaQSH|xH7KqSoc&6J<|WT*A;%6vFLa-A<#?kQT%cy z48=0Rv`M^Jn)W?xsQW&4pE^Bl^d(>4V>=eT98xk4ET+jMJm9k?Nf^W@=1k;>(J>jp z2=zpRFt9%M6$!aUS$~BI9Rw5^rCzsvi&c<%eXa=~4b_Ce?FbaJlDf)D?u0pO5mrm% zLg#*;AiUg$gjH1{Gr{h1&ch!NKcs}I>SV*Wn=U?2#-N|wvf*za+M*x`_t9Jb=8$ms z5qkL%GJZ#|w1H85EqF@hcCjoRYp0}w+!h(ip?q&U&rlEQDYgp?^)egxDAaAT}hQow#qfSbLlnCB%S2m$QC`_?aZCI z-@o^Hlth8)o@A|=wdR1%*|qmR`|Rhx|M&h&m&-2T`iFn_ao;zbI^jRk59MMhf}mSW zn+4&jAPRm#)QS3j-GJV&*HPHeZ}1!NH1-<@OnwuKGa+pDn_1Z0ZyB)qtt@Oo*ygvf zuoYpu-_F7|gdKhd3)>NP`cV&*=|I@!cd@XcKgXZLzUTJ4+0*0q;OXqo9mw#iNA!UmHJEBv&>(HXHI|lK!v}8<#8ii>91sA55iUc zDi+Q~c!hrj3+Ey1^?O-3AK_|$H47X1SNd17a##6R;aSkXYM{nn!_o>7uJzZla8dv2 zfjWO33l}3?@2_X!lKzH)HU2d$T#9g`zmbK@5N`4}v2b~R^MKFqW8sSamVveYwJcnT z@H+oG7Oq0L)!)j(D-d4qU(do`gg5v%uy8fP8~qzucqPJ{{F_*K6~deSn_0L9;Vu3x zEL_{ab)e1P#=@%+-sa!N!gc-I2X^>(uyB3<&VgP2T`b(t|G>a*|85pugYX{z9u{sy zxZU5*!c7Qw_&Zp*xqt6Kr@xbhef|3e_WSpWBe z-iq)e{zq824dF-qkFxN#{>KI$_dm|U+Y$Er{Vcqr|HQx({wG*?XaC88FZsX3!n+U- z_yam2R4th?Cm!g3avbBrCx}Nc3*tke+P;(m5VYra4Ls~0U^$N==kf3A<+#DnP~T6qyh;dl7*cAF@R#vd z*7u2$sy)SSrNzqgj37Sz9UZ+B8dT!s5(bqLmH6+PcuD@qi9uGkM^Lv%Lj!2X-)Y}a zLLo!n-z#D5sh07S(of7!yyJc;K;_U4!HW&q_WJ^dB&Y4J&n8{ z<@^5U>ARR|XHepvFaXwVxCMWS9Y9O)T)DkP0Gc_X3dxnXZ`Q1nXY2E9YQ zJVWof!JcS%XfWdYDSfCxmv#gP2Zy3T7TJa0miD1@gVB&gNn)rsc&AYo z_Xkh)hY+^y3k`;(V04J`Sq~t;oWR38-f}oNJRBZ8O<9hIzI-k;*hBAHhJ#Wx9PCHX zcr+M2gQsEl;0XQf2}bBkrrz)%OSc>fQ_+;w+BI|xO_Bx?c6C2~bZ?-2|K9e42fFrA zVsH2Y(n1k>d2mQ%0Z(tZA8!YuQm`jU=5_5#a&(M{YP02u4N*dsr&f1UVds_U_x={&?W%?(Y5gY3@4GeSB~C zbySaP5WxGvXq2kQe`C=5gAr=GxqWD`H_STx%+S!;@W3#2o%v``im(yQ={XbXIU5K^ zhDT_=AkE&%d#)qYi(fhNuVXY?EZyCSO3SfwALUSkFwcfV0qSbJW;q^;M0nMm_!b(D zem@?f@|}VGVNnbXvS|FyqnFPO4r4e%VqhRBosC>auKfo(I`(#h^?wegVG?M?`1rR&)BY^XxGqSNJ}*y5B2x<$a^DO@W4_9Kug`?e?Z`> zFe>zArm`#o!(c^&XgsYKO>dYL=%Bk>Zi}cFt*3RO?LP9_k=KE|&ilyg5_3ei=(&%a zxyUJqd6_v!gogaCv=j3WKr4V18j7TiST2#6jruaM#y30?v#klx4+?JG*FSWsIT9V| z4|yrK7lVk&H5l>s4oP10vI_eKhQxD7S3zKdA#IF~42RMdc~z#3XF~nM=^XjnY|YTu zyYZ>mrlV2_KqM6LqS({;rkAbXJHEo+(ms;*C@oO{D?Uo)b=+rp3P5I%8tdlFkeE?= z2Un{=naLV0f6Oz6h3_>8VN*)0zE?m1Lp?A(UCyp+%V1i zwDHu?P=9Bvr-K*fjYK6B;pMQ)J2*7hy!&|jfdk&2Gg!Ml=-o(@cYwk8hql=P*1S`iSE$A@Dsz~;pQkNSt#1P>Gb;6 zSZrhDiT?1ZC-~Ss5x^5;*1~^c*0E7VOaPx8V%vlCFup~9kw3ugl8`dmpFjNU;gq!~ z<;uU6TQPMu?pc#^?O$*Rro7vN*<@bG5ljxArv!OYxy2LKlxy#Tjb$()gHz2=re!#u zHA5F=@DwiSbvey)hNYjVp=x*vQmzB620x=3ETX1D+Ikqf1AtSh30HhZ0kFc@m3C-TDRdp6p4MgB!6z8o1R6|fJC-=+25p~dC!q{10dqon93@Np za7&xdo+midpif(|&RCbE&9ng_8=;|IuhdQX3i)inF3T_+zV;&0DWr|yFVc=1CO}@< zTwZM=uXZl4HIdhP(-qHa{V1<<%$%}Tj9KTb6$xv_RPQTiubquskIX#s=E>_P6V@Z| zn`f;@J~asW`*hMHC}+u-P!F;(IfTHy_5cOjyes5KPilF1FIwc52LA@DCZk8|(I*Vj zHDSG-ek$0HeZQd%3%|eL8(fMrdIx|DhKB)TfxZAx4g=>PI1(NN!~#%|LW5Y{(&=*p z2t+8F&^4b{u9tUdfB%SgFccCa-sq6`6o81Ac5y;c`R55RD7ErNhC@B!-Y~)CLA-}; zlmTXf(%O5#bg_?oVF_yeS%ZDvy%&N&kV0+V2QoXP4&Po^*W^91uBr88gZKQr`);*E;I{lP)D5ws{ZV)b8@QAIBxyNAPgHV=T?4HD{Q?qN7BhxUAl z(xyw`nWq5nmri5Jd?coF(G=@S-|%&3_9j)H2hQu6IkApL!!_A_|Q4l z%`~sVgBWPG-7~=EIh~cw=Lsg;SpG!lBVz^Y^ll8WB`S@8eu<{7L~9TX zUji11mCL~58yJAQZB-Z^su2Ozqo)XfY(LyDRtv74`d`l3G(PAans7dLs zZ-E5)w)#tn)d?f!9>ECNM_8n_;2j77iNvZQ6!b~#LLn*oBwIoRr2(y; z%YOX6;FAnpc}W~F@e-P`cJYTzhZdU(XhDL~H1%~~7aE*t!_cWd$%{T>^IkfO5Ev4e z%7FY45Wq6TBM652n{t=VxmPCKD=+O&`L@jY_9lFL=X_ls__~tr zBMD#EbY#LX@z_+)Uq&M?wWV5PUUUAxn{2IV503{eCwh2ADn&kMEr?U z$wzzQkMu4G`qR2E>+s0^vTo5}bmiPOm~D1o-cHXiKPweHM|G?(-ph5h=Nfx5d5tQr zMg2fPMbyO{ShRYZFsjc+dnuNAcmr)r;A>k*W_gU;A;b_`{*%pw0M5yvKJ^1V>Q8!b@#t4A@ z0g`J-8j-Fb-`n_$JcJv-xLI)LPn=BTth}^uL2on_q>3u94owavi|WQ4pIQZL{=B=0 zPm}3QH;i%j%A|W^+`2L4aR1`ALvWUVCg@ECKXK2?5i zB0{-n4l}54ya&`5b!Y3bSiTH`BJ*9jj6N0iD}~Z7punog>Zm$&qjw=m`Y zC<7ePjnMnqYT#P_c4iH*x9Iy+_^;)p+GywnO*ooDAcbNEOb)3NIj`%}4$kjjOf6{~ zaShhAIdZNS&?aq(aDH6c9tMUUj?!cTU4uL5BCy3mm&*#^tJf%~cUfFSN$6R?!qP*os zPO^O0ZG*lb2a~g;BIPN%nlqU*=V?rM8fOk9J)6c%snV*s(zZltTdJgbW;L*ovTcik zzRW&m#w(tpF&CyM$Nw@1kd_FWeoY8G4+x=Qe`7+gPSjntj_Nv8X@D}Rjsr!C9X!VO zvGZrpnZWj`-+^7TekUAF!KalWV3ZQ@YlR9T?o-5j8u;R%ZwV$u^Lafn2{$3|+q#2- zj)AU|9wNfE3XbZ=Rp9eGx~n=6(|JTp{~N=3-F01;)P}dg9Mk*O5-Xl@(-_o<5C-M- zUgBWEwwEsgz)_0?}T4m65=_#zh|9!^h#{1 zZh8a8@q5-^vRU!fqEa33J_GuqLC*n}Rp|_dQ$EQ-lk^gbVz&epTtEe15YdpZcT^@Fo8!jKe3DTgD;L2Z{MIc#hpCr2#YQu> z3vH{3A-@W9JP!()@JpG1$~N_!E^1RgGpa%?sS#LS7MKP+Rc*LbqHR@uS>YEtO86ggvD9uZD zX(c{^e!pnAXpELCX?+z+NP9+CC}Cny0rNF}+n|&y3ejpMbySTVHHxMfqL(thm-K)| zv?@Iy?apY|N~JXIsnu7?jn*phq78KsMEkc5qT>yxGB0#Ov`)!^QE5=Z+LK04$*r`4 z#t=JY2Xwjsp}6B-yv=0QaJhyBjy9$v9`LAaHwCb^PUZjfW{T6XdtUY z=YjtdDJ(NHIB`JVL?pc%@z$qx4V_Y$K3m9lJ>bL;H>OSPBpTx@n2>6TIuH^e(6^B& zW9GAkozCGg3VxloU?*hgxdo&-cu-L)RY{eqs3anbBw`7sU4*zp)->E72Cgpcp*RB= z;pzNvL}nv`RWB2_(iTcK60}ddh|8!XM8L?j$z41oX#*ksA$^Eec7%2SAZ`lkR+@E# zCTcrc)_7XgT6Em+jUvhd}+nh zW8bPw6<1E{CSyNySzI{_Rf4DBwp+lrk58>ZwOx%24_%XRqx4yK%RKnvMMvWgJsdy! zNc@pU;{}hUDyn~GGUVo=h6N>4wz-m)L`lmH3YJ zIDx3ELP+rIpj)OFbl z>t(_35S^k+v|Tp&oshBFp;6!(aWuHo_M;=9Mh}NCga>12>xOO|uHt+-K)i8uWgCemy!D{S39~@z& z(;_?-!W+cNCGHS=h4K#%MHow-EmI&k0}>cr5r~t5OoS8?yO*Fh)VKK4EgoWh3-Co? zWZ9){P73h4s2kNi>jVN$yZy53IOsZtlRa>hOA0w|>O~)6Nu$jScA+oMqlxm;L;F}C z)dT1edz#o04Zbd5OeDDHbQXsv1SNrq8zYeBLN0-(FF_5iAvu<-4Tz6_9O{KaWHr(l zMdc|VB=_asq`P*^m?~cpFK--+zIb8$0tlxY1-NmvA+sm`HT!9o+)rH0`D=BP^nJ8V zdXsL{LDIXpW%f(j(9<7@uN+{{Bx!+E~M`U^tfvduZ4v6^ca$^{C6`O%J z?BM6f(3mFVDQLXVr1tUIPBWgP@DVonTu@0H9PJrkK9P{N$s6*W_A$0l&`W!b80;rL z5zC?!-o%yyTR~pAM@ekK=Yda6?2;m=^mU!|9v+!|ZmUxBSbj!#DUr|}6(WQ~Vj_6} z1Mi$Ev|X?j1vSuR2qv`oG!zUg=+Z^3H=0??E(M{y0G>tw=n}uM=CZO2B|qY=N-pi0 z4e5-k$R*y%(ef%GWqm78#-atOv;68)kwPtnpm6q@bg0ldqg;X!##rgY0B%zM2!TL( z=I{mJOB(|Sb=?82))Ta%?OY|rz+@$O)TdQN4UOu0^)fkDppawIw~;LQal5XUeh-hd zk;E{W%SD<-bgVjK{85!Gv=_8+g$QA8BEiV# zO3HK%cIl51Oj}5q2n9-iLBDbWyAK^Y@@U}j?qdh{9y^|PK(4PyQq$(p1p@yPNmyhA zQAX1uY4f>Z5f~mCK~tEeE@=Y+f+b5HpM=V?5IK(oU?<=#6-|g!ozor3oQ5$&%8@g+ zEmgJhmHun}-x`cNo5nh>9GEf?!Cn9c(+BRADFi&;gvUGQsY`h3X6xH-ZutI|?`=ue zcg%YBB4cjhRoA3z&Qp`X|C;W1H_Wvk|DgSNvZj01)17kXjoJBfQKvL2LSeRCh*b`z zAWeCM_%$N1!D`U4t^%S=Elr!a$dcZakuui!_!9Ptij?2y>v}9KN6(VAJ88cg+j4jk#tw>aM z$fw7jj=NXKt*dcQ<>bA1cKqy%L*qkZ1|WJ9k*SVX4qQ8sWEpt3X2LNQ2JS?uSB29g zn8}J+D3A_X4QH^5jibh_9cs$xP-B$Xl}#r)reY54?}FD%-fRT zBQb^`qUmSSO+pK4=0GYN4T75pFe>kK63wu*&evdyx8_az9IU~^_)Pa8{?FtX}^q&)D4rSVeuS6r# z52$#eYVVMX*a7?SDm5Zjm@y5tXqXiUkxO_N>(-M8W@16fTtRK3p!TDJy0MN_e(}|< zlUv6+=G~=JHLsax-F5TDrB}~So}VkOOBC157?Z`#Gr>gh+6m*w`71syu1S?tzT&** zobH}2S@Us0Y08=V;-T?FQw1M7t04L)tcMW9mP3+{D{jCjLIbBvzx7PqT6HJ5pgGRK z#S;iDWgcalx5{qESROhi)>Hul3Xm^lHeii_O)zTc1?!oEJqSIJhcD^>h!EEJdlYe* zZuA<6ecqMJ7{7KX%t8r`q+d%qS&QCFo!pEfz}qWcIlbZaEw64#7B`JM$BbjWsl1}A zEt4(Dyj5cs5M>jOOg$QR*Tk(gtVh7G=f9gV$ctT&xTHbOh4%z1qvrM5rKmsmp@W`Lk@8#h+6^qr9BYf^9*5UAn@gL z!G1Z#5(q$ZBM^}O8sA7;!8{*^L~=wT0zmp9-F{3r+62L(4-v;zBG@NUM@s*RZiE1` z7BS|l^l#}W-To{7B45SrvhbUE|viuXX%%vAQ7*<`A_o#QgCxLsv5)$;mLt$OgM zUKl;Pbc-a<(MV##z@etKcVIX=(oQ@WP*Bp}A&c}=y8S)fmbLnyD2{GNX+{vt_zk&J zC^GGYDj&2O`5^2xP!zGKNC?43rzZ%>hR8?f)H&#W15pkHmef_==@b%ECEx8}=o>-8 zJ48L6M$rxg(&#sV$Ts3>#?yqS1thl_Pb)}n3!XNp!CLXOkJuVKX~)r#b}l9vlCpE5 ziRfb+yWC~?sa_(mMr98ninU8Rj5J6Bt2D+t(@+fzNdb}&1h`_Nf^sncN6k}sLpel* z&;&&XVZz0rk*rqFw=^wC-CcxtA%bz|&$(L@?$-GFz3)bo?n80wAr5=wu45He_f#)_ zE`dFejA(R)XgDV@%=+{p2! zL_5*-z~E3Mjhm-F0H==R(wzzfMoqb`%w=frVi@)0F9;7P%oNm9wEfXzDyBQvp zF?8jbm0gxNkag_Z-|bGiABk~YgE9pdk}K*^ssdG*jM)NIRmF|8(l6*F*gtCOFnw7lKrOc@(2N_=L!vJI0Y1~& zV3R(_Q~ED-BSZrx6J+et!>RbRgLNnWA=OYiSk>IhMJk}59v}lU-ko4o54f zcQ8dcjOAe(FCzG`)*S9RM^(a6H5Hw8)XW&)bYFMhG|bj-Cv;-f%cF0c zO{~~D>u&p`2>jC-`;EQHqP7Xce17><`%61M$UpenV{^4T6SX^UMv}Gd??&Ub2jk9z zxAIHw9CNB6{&b5fX@OcSzcR)=?~BG9BJT{sA{$YmjfsT3Y(zS{fi%#D#z*5%5TZ31 zJcX?mYUdJ3`Xfs^^9HiSU}d2JF5a1J&_*abrxF-7NR%EBE6* zCG(CvSs2$d>sW~hfVew*NL>|gBTr~AqYsPAub!PeJ6BYn zD5{?+|EOr=SSN<~#b?H!nR;s0z53(g@&&7qUo=%T*^Fhh`qdpb+HY?9OSqW5P8{eE4}r=&r!kS`Q(S||`ag?x@=t`lOlu~jsW`;uKx$cAxe!nTP1G~iNH zEe-1{b3aAFbcu$rpa=y|lg6n!kFw|2V68GEq)pj#X5-0WzE)zsLP(p9JBL|J_}sEf zp(3DCu)>%Dl{LBn^tBOn$O5JNh^w;f>QfaEwFJ?qdPl{7_8WsX$TtK?HhFUd(i3ExA}6jGami>c5(7&K%3+lmC}M+)^G;0E zvPjx;Di{et0z+b~&>(DnslA-5DdnP`tWIeIqSIzMnrID9%^IL_Dm{ar5klR71Zfx7 zBC^QcMgk0N7ndVnMDdYVaJwWBBQJXO!sG?u+ugJ7)>MAs*a5JG^Gc@*UMaa>k;K zC39Qjp7mp|8xqA0GplBceQ(#j)ACl!yQ^kfJ5%LV zuk5_GbEf;vC$2y7-7n2GcO;rSJ}B=m7YU-(v zoU0c$3I(ML+l9P>G4p&;)zs4;6*XexbmqNyc>M6x>JOdO^tq;6?!qzGo#+y=vH{Y7 zP+}`ogdg`Qt_8v)bFmHCM7C`Ez?@iqWo&kGDz*m`$ODKDM@`vEx}b}Ys~6#-StCWV zf)e8NXJ|BCw2WG^ZGPBh+)AxREu+@#+uW!b+CO9-V&-`-+5njBnuZTjjVhQd2N2J) z1p?#|Ilk=D0N$wi*`|yV%^4+nvzG|F1GBmb+`DX{PX*^%1+vvA+eSNTj{>RS*QgEK zhCEhEi!oNVKGL39c16myZS$^UWQ9Hz<{MpHkOs{&Tc5hM@)Wu&EydTYeYIM=63R%= zS{rN`2eD;ftLw|M2}^lQ^U8wMEE_O%vaH`W4uYM7?Z~Tq@h)w0jyf=pvX&$2!zkko zn=+4N(cxi5bl6~5w6)R}B3U6(>jo$EB)$lhaIPsuOjo|U{1DNM9|PN1TPU~o)+5{OOFP{?Ke-=UAtCdj1! z>AX%^#@?YCXL2PBe@x3lw7e;wcKz9+Mk6H+Y2OyNO}Or6C-c~5rQo^43U}Ul|HO&guIa! zVu_&-(>Bgu#1_T-%Z$B|ey|>--sQdUBvk|YZKMkjkycZWSs((V?zqcdD18sF#7Z;w z!6n*XKR_9guj9r9xR5kl?V9X*ZBw#f&6q9aF5z5n+*7$_S1TtgVQC5(XVqlYYo=uG zYW#8+e&DW|TK&qJYimBpX`U$7<(p=mn^RWji}rE* zoV7k-t)H{5PgvLA*!O4M?{Asi(LHN@_?El)#b;i4W}b{{S10mTPe*Umyn8U7w>p`3 zH0eBgixk5)CY&2l63nJ)BWq4Ln`fOZw<>C0Ir8!mm{xDvIk&0vgH4@N-4G*JPw#*I z;Hw9xcihk=%39y=n6O}vn=4wAD8epTk}CD4Dr-~T##Gg6ve2E`5U*SdoM6Ev6gMsk zmf}3za`Qel36)L1WKyH&IyZN2)pdceWvk>*X&R6~fyo@M8)eo$rVq4=v;-_iwi-x^ zlRY=kBYyU&l!cb2@pQ1!KvD)PAF4mQVCAz9 zip6&2cQ%9~n`~E863#|1D;rlfq(L-hY`1d%j2fTQ0|7FA#duy9RkwZAZTqiNH@zU5 zvmpmSSIU%jYR|8$rZ!7zp;FqY#ha)_uJXpc+Dj7gY;?HNAEMIdGZtEG!x7{Gxq8je8x#6^ah_=&8ipT1~UWl*G9PQ20xHgnQ_r7+j) zgRIeep0q_FC?@q;F7K1y-+!R1yA9e91JI1YSB6KJKr#^Uws>K;)x&BDefB}I1-7JQ zx*CL%4fB0MBsgIXOhm?vy|vGAbR_aq;uppm4lvh6%mR>PW~dHtEGb*CcX)PhUF=w$ z&%Pf5Z8GdXi?+Z@p4*XUWQ)L|0Pk&d4=N4%9u~H;ZU;kUe0J3VR+~NcPY*MP55wF; z0r^_;4xNiKuQ0H>mGvXfhhfLg(x~5Hg$R2>MG?e%fSA+4XkCQYg=rL%DQy$;CIMq! z()L&6?EO&r=-<&ToeMqbqc_R)SLu58fuQ1(pocde{Yu_t_$L`Zq=|JjRpbG=)+EL8 z@v2LAMuvMexgr#q%9zft>;h&xMgVLS%tq1};p}h6@ewGyTwntt6;cM9x*?~Xa*(GI zRh4!!W6hp(5?O09BLng$Ched!GuOK2-bx%9Bb{b0#Gw9iiWI6x(l%z1Pae(i4wLRm z1l!Y=&;=~0{t=E)Y-P1k9h{}}?(EyNi#r(@fOCe^AqkR4Wj*p~)L`ZM9fYPsRKw;T zTayOd(>mC7;EO=H$*`G1)D|?6DUZW{Kp=W5LK~vO6yhu)4=;zNA^(a5=aj;2{6#** z4Lp6jkXJI7Tb;ZG%N&bc<>Tzlhu+_^UC+y%P>xBZse1A|pt?mvSK-5-FlRoyUGy*^RBey+MLQQbDX?O?L{(AfT2s0u+3vSiL( z{ehd9G_JxqX9X-MrZ>l(6|>I9Te+pY^OCu%7A%4-7rd{M@~JH^T^w^RX?H4T^G)N8 zr(iz4vz&^o7#m!$7+h702J|Hc0X24*sK!nhd%U=FeCKC^)s&lZI8o0J94nx_w4^ww zp}2tYEqi&vf_FZT&;);}es_uRUWsjAjp4oO-TFPN3_n=q#E&1eY;Ip|`192i{`1y7 zYdR|oKdLCgj~}%*?^|K`@d^{dJt#oEfE&`KTZF)kS!b$>9Lx>__7O(W%E)o85=dWU0t$VM*%aFt82rj48XOjE6#k{1QqzmC+|`VO+q43 zIo;wO1in~A1P+pnb|SlaDZCO*^@ zx?(zGe0h5Kx2V0mgy}9$cWmMRbv@dqbWr*k%(5a6OJoTor(_ zsKX+o3avCO51^^Pfc)R;oU=A2tc|nQW)^NvSes|9Ekr9j$B|Z(u-3@5a>uN7>%6;Q z{QUScNw+s{_1;4~nS<{>uYca~#q&?|IP!x23x$)a)!++;as4I3xZ#p+#30(n^karz z*hF4-GNXY1ju970+(&56vMZY!El^&NddQzeNGX~{%Nth35iSJeh6a0ZB|7DpJV6La z2%d`$0SAMlJGN|^nLkU_vk`{_z#8OXgF+I|C!3G}VdNJfGf*3RYUnIr1?Jtu!+aUG zL_xk}CNH03uQJC8 zNDg6i5yD2{TL})e!1aQAAp%$DC}|k(_<$55ibV5jeYyWBxontCuG+Y6bAzTpMScY` zS^;I5jHQIcoTbf5yXm*ctOQs$E;W&$xd5_Rw|EAD-`1;gf@R}NKQF>&{aL3*%8zH* z4igWbb3exo&$=@5fK*ptB}N(l$ikhgtLi!Pu7WWQ1Jnhwz8u`J!dD7&QAP%;5_fdU zRW(DHBhne%Wc`Fod?BaHE|5RPtY9T}fDf?m-q1(0*SLF82M9E5L0@p=`BY)W)h|tc ziIf@2<4`R2G|fD5bJMJ6&%bx(rrcF=_Zo@{-0b}j_6`QY(o0S-n_ATd^ZU){Wgx^Nr?{}MJOIYJ3| zm7W*(kMEBcZ3HS;Vjpt?hb!Nt#%@DwxqZwH1rcXS+_7T%NW5knWMKap4y!tKK-z4s zOh~*bVcm2qABLjuSl_bFt=*kiyF1>pC!X7$%57BGr+H;yFoKb2+x#=ZVv~(b&;s=$ zJ`~$#{}-6f0;boXtFnS=Ww-v90@Oedlp-0dWKXH5#^Wj-FV?9DRVRuz@*0k^e0_ki05rYg_fXAyEH5⊃w&CgjBe-Y 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

I38`~g-?+e9!jj7tE>j#Z~ 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 b##{Yg>wm2A*W;+C*i|)K^Fs{}S#kd#$E^R1 literal 0 HcmV?d00001 diff --git a/.deps_siteforge_v812/_pytest/__pycache__/python_api.cpython-312.pyc b/.deps_siteforge_v812/_pytest/__pycache__/python_api.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..50c01026758a4e9998753837b002961ccf252744 GIT binary patch literal 35349 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 TGxL^Zx0!Fb?=IloY`*?q!3>Gr literal 0 HcmV?d00001 diff --git a/.deps_siteforge_v812/_pytest/__pycache__/raises.cpython-312.pyc b/.deps_siteforge_v812/_pytest/__pycache__/raises.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..445a9d6bfd324e8160c9ee606b4ed93f8dea1804 GIT binary patch literal 59994 zcmdqKd3apcbthOWP^c=PuoPB;WMLyv*hqjo2!aFwaFw`%S|CzD0k24c!a}_&aH)Vc z?N-O2twdB}x1d=13@7@gsg}~FW6gv;@i#-qS)8=LZ(b3QQK~RAbdNGU?)(FZ^2BC3 zGxIy=zO4!cl5)56^~|I6aO=JM?mhS3bI&>VoO93p_qn-l3$DNa^N-H|(SNd7{&)JJ zT`rNZK4@@QEN@sOi)0XWU0@x z2eR=jYs@vCGmyj2vd7%xo&gW1T}XQeyqwNKI(HzK({7~m2J$%VLE1Oq2HvC{Fffig}Pj+KvB3{-Hs zXl&JZhU!LYxrF;(rX9Sa@sK#7zp5f3F@gDsN!d(W7Xp|12vp3 zL%MdL)@rd?!ZlL)dsfk#19kXaD^-Noh1W@|-miQQeM{FsJwIPf&);A3o?U&~Fj0Uy z%20<^7JrK`*Xnu1mjN!%cpBvLs!&?>dp5PSMt)Y4@~nxU)rOl*`C9l{UCOi8aQ68Z zMLFiXwC*L8E_(rG{n&=_wt+ToeFM_%1MS>;=lL_{FGN}jYCUrP-14^ro5O`tBmOnL zWy`Uc|4F@4^D9~JshO!tIQA)@b48KKP2 zJXEGNS*U(2x@Mm|IduU~^YqT*CrL}|ySAirJQN){mvoMt3y-`sqS82!JaM5=>jNTi zZ&GXV zF7$??p;J#yj)cZe^@qnVoVqZ2p(Tp(4@XRvyYvR-({$jA! z%c0RoIMRCIN-|3%<#yC@fGW$z?V4rDYw?#pKoTG_5F?bPD`Pw4R=knd)2$J=2xSTc z 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 literal 0 HcmV?d00001 diff --git a/.deps_siteforge_v812/_pytest/__pycache__/recwarn.cpython-312.pyc b/.deps_siteforge_v812/_pytest/__pycache__/recwarn.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9e69cbd55ac0ca1b985bb72e4778a7f7fb6949c4 GIT binary patch literal 16057 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 literal 0 HcmV?d00001 diff --git a/.deps_siteforge_v812/_pytest/__pycache__/reports.cpython-312.pyc b/.deps_siteforge_v812/_pytest/__pycache__/reports.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3cf313f3df869ce5e7b328dd177db81870fd74b9 GIT binary patch literal 26979 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 IoRJCoFJ9o1(EtDd literal 0 HcmV?d00001 diff --git a/.deps_siteforge_v812/_pytest/__pycache__/stash.cpython-312.pyc b/.deps_siteforge_v812/_pytest/__pycache__/stash.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1878a9a79dbc8071346af474670de10818e4a347 GIT binary patch literal 4368 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 literal 0 HcmV?d00001 diff --git a/.deps_siteforge_v812/_pytest/__pycache__/subtests.cpython-312.pyc b/.deps_siteforge_v812/_pytest/__pycache__/subtests.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0d24581f639fde05216246f73998b307ae148d18 GIT binary patch literal 17713 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 NM2$CpOgK!l{XbarrA`0< literal 0 HcmV?d00001 diff --git a/.deps_siteforge_v812/_pytest/__pycache__/terminal.cpython-312.pyc b/.deps_siteforge_v812/_pytest/__pycache__/terminal.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bfb0f4b77870acbb5edcbce7463667c90ac14368 GIT binary patch literal 84179 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 literal 0 HcmV?d00001 diff --git a/.deps_siteforge_v812/_pytest/__pycache__/terminalprogress.cpython-312.pyc b/.deps_siteforge_v812/_pytest/__pycache__/terminalprogress.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..92a9ec137bca8452c6d42682f9a9a5b72d77f627 GIT binary patch literal 1141 zcmZ`&O-vI(6rS1LZikXWIS7axN|4w+6vZk8Lre@BJd}ozib=DkS=ga$**|BuCM7k2 z#Dhm~T=Ap^E+i&gy?FA{3I?1=Vq!cPFJNP0JUP4DP0_^N?7Z)NZ|1%E-psy>$76s! z-`{>LZwLT<@tpAC0|1*V3~T}ws89z6EI{aCPUi|7v%Jn5LP793f#FCY;^T-e8qq@3 z$0Eb1fFR%ij;PTcv<-vYYE12TB5b!R7CNe&)cMWxBuG0I)3jX0)hyG=F<+dv%w?^_ z5L&UURn4$^O6JrKFiGk_aR7*tUp^NuH5WmsEAdahF;f@GCRn zfQg08Ukq%58Yuhp-$S|8j_oP?fPtXedL=PO6Rb;qGfAsuRLI|%pQVCY zF&5YQ!l>P9b=nSlEtradB}C%^V<@Il!i2_3*ewPW6*Wh3-74iQheomaNF$a>c{YJ~ zO`(Y3lIE}q5>Le?|D-)GI5{c?NgVGClF8(6dHRNY&%wlzGgl@jC*)hUeOqxAIcqH` zx}3*`E!&!%b``=3q{mB{t7~i59a+~FWxMLGSf*TL7CY|ce>IKTRhkH5#b6s1g2{PS zx0ij+LpH2~&#BSF)Knuib(orIq-KuLK$GVrasBQI0w__BUf4n}x}SF+B*$Ja?v;1T zd*-hBW?_FkTc2BaKfds(=h92G-!t}9JVITEXt03>579^ijchH~Gub*C`G96mB4FY^ ztS84V~{=-0T+9A@c{Py;G!@Gn>^t9PkrcT{R61q9>V|t literal 0 HcmV?d00001 diff --git a/.deps_siteforge_v812/_pytest/__pycache__/threadexception.cpython-312.pyc b/.deps_siteforge_v812/_pytest/__pycache__/threadexception.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e75954ff77c2b6cf5ae6b04bae492c1e43d03681 GIT binary patch literal 5753 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 literal 0 HcmV?d00001 diff --git a/.deps_siteforge_v812/_pytest/__pycache__/timing.cpython-312.pyc b/.deps_siteforge_v812/_pytest/__pycache__/timing.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..27f8b8b55993861a82f91084dad9f170ebf17a78 GIT binary patch literal 4783 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 literal 0 HcmV?d00001 diff --git a/.deps_siteforge_v812/_pytest/__pycache__/tmpdir.cpython-312.pyc b/.deps_siteforge_v812/_pytest/__pycache__/tmpdir.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d21bd63981aecd21f7255e32e73d0c39889fd38d GIT binary patch literal 12756 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 literal 0 HcmV?d00001 diff --git a/.deps_siteforge_v812/_pytest/__pycache__/tracemalloc.cpython-312.pyc b/.deps_siteforge_v812/_pytest/__pycache__/tracemalloc.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a356fff2ec4fc658dd92abb11a3784463dfa5aa1 GIT binary patch literal 986 zcmZuw&1(}u6o0d`*=Dybv9TIIKo%`R#3X7_+agN+00mK@inJ^&o9r|n?(Qrz6Jla2 z5fnV?sRt1h4^{;K2M?aSC?2dUg5XKK1nWV1a<*HwpzrYJy*I!2esA8)ydEAlK+3mQ z9~uS*_!2w6MM(^_vP~L`;DU=h@X;JX;Hs{2Tb)zdIG_3&!}2H^bb^2iN7QM+D=5ib z)2h_6Hd=WhjYa4}54DjFuHu3R3#f}4sSW6LvkTAHYb+>T<<^*s8w3AADGmMK==1f^ zp9}~LCq==NvP~pu9>YGVG>d! zCQaIEIs9*HMPr5#vy(CBDKmX4k@aAn7W1iSa6{E-@SGn~CeAQMSu`pM?4+2z28wwi zwL$Aaq{%wIBV@jobj#xVh!jzdhLSBg4_kV;$vht4s2R)Z)l*Z}6;2qp#t%$R9<`>! z@U$Zw>%5$xXI&yw~$@_59M_clyEJ$>jlEB62$y z+xOd#vQM`^-u`6gyIkS!P+x=WzNP(3yWV6b-lQi!8&>aBUqyOmIg`Ch?w#mkq>uG6 zPH8`X%k`$p&u=|HLiZqs! FkzZ^i4s-wj literal 0 HcmV?d00001 diff --git a/.deps_siteforge_v812/_pytest/__pycache__/unittest.cpython-312.pyc b/.deps_siteforge_v812/_pytest/__pycache__/unittest.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..47c091c35d081b67ee1282f36f132d9c86010188 GIT binary patch literal 26465 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 literal 0 HcmV?d00001 diff --git a/.deps_siteforge_v812/_pytest/__pycache__/warnings.cpython-312.pyc b/.deps_siteforge_v812/_pytest/__pycache__/warnings.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4ffc73e79f532c9916babf7b817a2d2f835983cd GIT binary patch literal 6783 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 literal 0 HcmV?d00001 diff --git a/.deps_siteforge_v812/_pytest/_argcomplete.py b/.deps_siteforge_v812/_pytest/_argcomplete.py new file mode 100644 index 0000000..59426ef --- /dev/null +++ b/.deps_siteforge_v812/_pytest/_argcomplete.py @@ -0,0 +1,117 @@ +"""Allow bash-completion for argparse with argcomplete if installed. + +Needs argcomplete>=0.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/.deps_siteforge_v812/_pytest/_code/__init__.py b/.deps_siteforge_v812/_pytest/_code/__init__.py new file mode 100644 index 0000000..7f67a2e --- /dev/null +++ b/.deps_siteforge_v812/_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/.deps_siteforge_v812/_pytest/_code/__pycache__/__init__.cpython-312.pyc b/.deps_siteforge_v812/_pytest/_code/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d9a694cc4c00329de7ea994b070e3a2da9e4c41b GIT binary patch literal 686 zcmYk3L2J}N6vyA}W|K{_*?N!)in{b5EVx~UX6aWwNeBqO(#?lQI>fxsXC}6OVQd z_BT(>@?+A3T*^}0e0@s(<(yq?z`JPgQz9zUvM*;PCa%r0Tnojt;)yuo$p_5!UMgOg z>ToXftj;qjq^b1}uM`b0gFUI$60ZZSR{Wz`4I}qWxloB1`>Ka4DA!2eNMyu3f(ngz zMtma!l-J|6#$zlk6ULf=vDrc|lweGC@v?D@_Z2_eYfY`erD@~VcDS{)1C1%qbH={I z1vaIf!`13h9p~9m zT-n!%8Gc2rr_3xhWh|4KW^A)sHvMNsnJ#kiT&?3M^8)n*<-AJ>IfnZuuyF$GE!=Hk dqlFtS+-~7k3!@h9v~csc8-$(Dp1O`5e*utgwS52p literal 0 HcmV?d00001 diff --git a/.deps_siteforge_v812/_pytest/_code/__pycache__/code.cpython-312.pyc b/.deps_siteforge_v812/_pytest/_code/__pycache__/code.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9aea3df22d7d083596e0d0b6a1b919f096bbc312 GIT binary patch literal 67886 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≤!DAm_g$GtSf4UC_`xrz9pQ^ z%SwPp!h7hN1~Vm`&%@AI6wVf?;q)&katkOcf_u1L!0)tWI%hf;HL#&2IZ?DHYVN^1 z;+ICP(OgohfYz3qWxx$uTSZK3%b;|=A57 z`Nx#l3fLxxEb#Pc6>vbh1oci_6BA|Lg+7%m9sTh1IE05!yLHC8VzNQoYt2T)Ud)@zn}2%onR{kaO~HjDGmp+bId2AM)$N)2 z(!#b_{nn(fdDb@X`6ejP;@QCb-WU@MetIuYa94tBYOkXzw9U0Gh;vLvU2Cg{x{^wNtvKbNv72^ zqUe<=`zk{+eNf);`H8{q8ypO)6vvqn37R1qUmen-@zIxQN@EkEqB1%7t1>d7AoK;e zok6>ABH5~T`MuxT`LU;D_S{=NF=q`eK*y)|@&#|Tfmq>xvGx|3J}k2LuCabt zE#jWR-5^*E=qcWXo#Su5r;tW&S}tlR6;XYK$dhN)|?8g*1I;OHdhDpH@*zmi44 z;jK{-sdAzw+;hKUXHcKGYV5)~!diS?2y0LuF=Q4BXaH#6qpdDm-+lA$-Mb?;OefOW zY>t?kIRy~05CI=CW4fh8N2@~z=KJssd4z5ka7*QklB{teRR}TEsd2w79ir(fPfmnW z`587ET#btM0)?NwM+Ho`vvxU%c|gr6;d^Db}=gDSUn3 z^({BNH>4X+|71hV)fcn%eR|I&xN8X>7bTsAw_POAvX>`4WnbM#Ah}!-Y1Q|(2_Lq3 z_n6E-7De1M<{m1`=ibu@{HFMf5k|rhQ$=q*3o|7dd}jtJM`VJ0m~PovHjV2lESqGY zC#b*4azcllax9zZh-H@^mE*W%j{F_^0!?6pgo0(}&bGxsZEOnYsKs%5W z0n>6nRF-ncDY^~OZJut!xB9a()YM=X1ASNG8gf1vaflqP$R7T1F~ zUcpYpaZO7S&+@qKTHN-#(9L{8RNh#u>y$SL;Y*a-#5FmgtBH_Os>xcfq@$3HlsIid z&m-cBxy)LdEZfF@@7PQ3TJijgO+rD*>_{Tt_v(SWmQt(d?uI<;5pktLDW0o>{sP5w z@eCU>n*G{^{fsX0E{)??09o`RTuo4GpAm*d$@GSEz@o)D)KL0Y%7c9y1iVnybY|MO zAtpaV}s1a>lzqcbC?2}c?Yyc~eW#n2FxH#yR#2s{}tc_a|(G{${tw*OCJ2LDqGYxu58 zp~)2z=(RU2)21+CJM6lEefR62$}%xkbvGAqmye{>`Qvj~`! z`A$PQ2U_IZR8K?pR0|4HT+M(lof-QFjq^qtXIjXQgU?8N2yw4ot2VND`?h#d1jT14 z8ezG_H>;)~#`;6^IgtZvE+$Rog1H-n$tyFDo0Oq4H3=Cq^_og?iNldRzA^cNjXL!7UzgI%K^f16588L z42_aV$th(DpOXJ?3P2Z}X@3KapaBPZ`zi;AyXZ4TTIpKThN(RyxJ7I3IRt0P?5TG; zZsoPywOI07?m310q6?44omH2foR{Bz>LX|UC(c3^F@NkMr;oF3h#pffjW+vtC>Ns$ z{-Y!Yz3EXj;_;?u*R(Zid8ix~b-qsLL zr;Tg+NlzBE{`byYIkVJsvuPW$CS4UVSQxMWVC&VbAM{-9S#H}KZ`*t0i#OYj z;q7e?6h$sSf9d&zr}d_@b)`lqDnTpUUe)AKLod)ky0C4FcWIVo>WqMHVcq_3@g`%o z5nn zQ5#iW19doi6(WR6+xkDF8?I0{Y(Zk|{$SUZ8D+bKcVl`y;a?vuqT=P7g7Ds+>^-A65SQ{gV96tE(C3H0OAbL56xy0*TJ9{ z`vPN#!X`TdSv}dVWMf+dOMUXBwzDxr4MyB)z|9P@0G5dR35KGtB@_stVE)w7>hqsM z6#%xi97l*x1)>G5z$VJYnz|31M8_O}B^u-S24SDBGat>^iD)`{ z2w1crK&uI>1w)s8UC{XEPT(9Ia~EYI^TGG{C;X)H0j7y>FgPv`4hK1K;RtTZ)-&oK zJ06DN<`Jn1HBPGqB(7eZLqG%~o4}#x!6X3$`UT zQPedjqNJ1wbzw{HB;OijQV58+wJD<1<30Ln8~^IPD#EqL-aTnQG`Fp{~Q3-2SAE+EApK%j<&Ic;KQFzOQJui5=! z(Pf>e!)%1W80zt{XqL$Md0GPIhY2es}a*SGKfJ&=h z2DxCBi=tNa4oF}k8KOBaVOEC3H|P*AO%TB02&mF7Q%Stk<*0z!W?5{*a7T0L;jeXV&Z=qmyg$Vi=qFQrT)5;K(K5=sXY*`&a*A2vlP3o!|~0l+uW z!d69KX=T#NlsJ;I0A zrq%HKD4Vfd_lo<)8SCvbXwUqf>%zgA-kC|T+B{_>AY3l0ix<@`7d6L=ny(ct?*HKN ztB)_Y_r%+KmfIhRw?C3-?Y*)2uX;Z0i4`4z6tlQuxwtW2+_*5bxFJ#8IdkBXqLSIk zi(j1k;&N$QytFM*)IPKKV^{G{eND3mlchBa8{hg;vc~t04di-lGdN=To^Kq!aCpA{ zBj=ixN};@Er3y$8!$klX{vI`sv}j{WZl=IweGvVwXZrTx0!m8QXry{P)Oi$`=My6@ z1<2@wIIQ?l*t{H>y^Vz2LYsx|Qod4yd}-$L&yWOFWOzUYSVa3ypcBaq*0-q8? z`1uu!%UXNKxoO3UUt-Y~{9`VKnuM;6knSbxnvz``SMutt-a7?tD^~m>QDNmuE`@AD ziEqVDA%{@X!BP>Q)^j(X;tq=7bfzr(7|S$i*#{H&lx3^(wDlh?7Hj*8v&h<9b%k1`DpW*r60gm(0wGj;!42mu%~*B0Th zEWd?k`9I*6%H>WRLh`?%sQ;aAgki|UNyJMqfBmQ7?v9=;XW=iX)e4=E^FygJ#kdYC z*QfBz-SQ2Nh2Q{=RZlKKpUQA6%c&-|b#Us`18@TF!pQJc+Mk~cf3iZ168|fFiQulo zT6Ei8I&YqTez7j$?wGOu!Yeo%t~r)A-rDxqt(M1=Ma2YxFXVnw1nS_emYLi;c`h>V zz2o#iE@F4w$uC;5;`grE>daXYtk#@Q?^O#_g+s05KRQ(4}3{(X43ZV092Uw&$i_l;gd7wiQ2;EZoKpTf5Rhm8gT++A8@XgLhE$E| zx3GtDyT zs)T^0SlTF6A*MvyBvs?LG~E+CByG-)S^X?+k=CHBGHI(+gEGn~g|uyTIu^fO@*!;n z1Uz|C?K`H-R8qH8_eRb@R4fxQR3Uy*hF*ziNGR3oCT0#@@oq<zdUjp{7KTQ0htt5*@>Nz(%ulf1`>hBKtfA1Jg?x{Cld=cRRm{}^axo$n4(OJ z_e=MI01pcJX;k40^T;cKR5@d@SGxbDKC5T5+^KjH-4j07i)H$9E*| z_>S=u{OPr{TG_07Vyc_G7np0()Ca`*uwrX)C43HW>B|Q zKbmR1K3YYp{{UL$Y-Nw9MFT`^or4ND=asOzn^D5albP!tUCX4P)cWzs@B|p(zI~j` zVp83VUdbp)+4{R^tni(|K{V&2t|6VBK1E@IabdcX(dGZwS^Lb+yuae@o5mr z)(zDuz5BPoxm4?_*}`bcjCs=8Y9@4A>tJ|{vJkjZoQGm`8O#Ls-;4DF(oWRuJ2BBE7eqz1s*09sKGR|3v_%;#cK3@fHAu0y<8115O)5ozXOsO zV6$pY*(6z~&Eyf5w4bVz^;z$_#8X;?A7!-Pp6%q80EF#~5V=nXhHih?g7D9{5V8tuI7NR>p}0 zM2PkxUfxV8HqnhXjLcagkKQhYh-6W`^u@*fOPdm<+en;J3USKq%KC+ai%(v8JW;uB*7h-6ejHq`>WWu& zEt!|s9f+?xaI5OkWOdi91O9Jkx6KbMZMvniYr>bcOWRXOTS>p28nLC+U4=$n5{ZU= zm3AAG8V@TS6Spchh_^f-Ju64ehhLVCb|M{Br0H>L^%k%rXlq*PL?LC`t?TXoUaWISaXqUEBN)W}JSxZpbx~7rhn1+@}ZF&w) z2Ek~DJKIe3sI!j)9!`6U43;o1kFvkmA$%VsO)EK4n$<_@i$l~w#6o^m_yyb>cxE>( zV1c*9Y%RRj`UOs5hBR(twFXeeZ}#V&d@iGQ-5?(&B?bJ?ABP1w5$jwyCdlYZx6pQ7r8R#?=pNX453i% z`tQ;|j7{Ge+1kkhzj_A|tTOJPjd&CBWOfky2oB`2D^j4d$I zjaQ{v0d+&`jC8Z*$6k`%&#$rsAvk4x0$OjJ&!4ptVSLlQlwV`Pk7(x0_i`pIVzPS6s?pR*k?KRDdmJf2T<|fvxo7wl~5%RtWyWyB^ z&1dX$BswMLh`u2zmq+Am9c7OpaG>8~X!P*WhSzK?h&lbh zu^>xVvi^U_`Al|3Itt8i1Ls(?I)=z$)d{>mlMX}LobTjfOmq7ptvwlwo-uM5!vh+H zd@oYAqFpJmCnfqAS{TXoh2fz)xKsWi8Vz!at&M*`n6C{+Lz$6)b?>TqL3Bssja*WM2?GDH9&1(-tF52bCBkGj4e!(rCI4>9X+c z&(Ls?%c7RBrn&IhX(dwy1bR`lw>8&~GYeG`knkrB+JpD!^DE3U9kKTed5v#|egL<2 zf?@)hoCyQZC$KQ zl(xs*?d+rw250&rCBEl*HFa|W8ht781b1g_k&mIO_iHoQ#O%DREM)|yX%O*dq*FvF z&^q`#)LEu|jfrI$MZlqO0mYG_Jq`_E%wV}24_wqiJq@@`bQdreWjUmBxV)Bp!UY2% z@J~UC$kd;q93*5bfa@is$U>QVG*WEX(O;&{Boj7t=1Nhn9llA|ubA^Vlcu#s%rUkt-wc%UtpOiiEodI?JxYZ=Abu zZg%o(ugq8oiGbsuuT9TbKF;$fD8+pE@|P}s=@xU__0HY}%X_XXu0++kSk;DD=|3q_!YR<(%cCbb$3@ZNRLy1xfyFx=@Y)Ndd|0N- z3Lo_3$+V443sREEfD=&dlo1?xrbG#XJFROET3eehD&1X0159mHRmAflElC%hO?PL! ze3u5A;5M_v!FV=YUVCZn!q9`%0BIhv?t2U$)pwtyms))$wwed-Be{u!#0w?S1+_S^ zj$lJ&m@mq>jl{n^Ys6F3bVOj)(ttFC&t+XkJZ&RYKx0oK#(FAD>IQ(EeM#nPnotu; zgXt~N90iX)g5*)F+HO_pZ;E_I{rtb;V%S+f_$trR`p!#P+2Mc;u5wf?jskSLcEli( zpQPKD==K6~rQFA`GlBCHcmf`9NJgx{3E7FaGR_@Vj9axFY2cu+v+@bL(*8CiweiZ0OUM4Ua2t#b;Lp2nt`BD(cpGQ- z!`7S7!G(s8Jk80PhRa{P^u^_xE&%4`nvLI`*=f|&&(klff}eSVp(%%Y2Sx)> zgdr>+s50*oCK=+k0jl?vtd_5yD72x5Fl#<*y0D2#B24n`p4sngnhuYghY16!(o(0U zs$)bqRdEP5hLiHdcqkBRHJ-^@LzUUdK8Nnc_rd|?5R}ZXK0~??nCaTu*}0Rj53+(7 z^F!VmLT%K=qa49QM;L&p(UvN4s4*KcJ%Xq8^TV(XtJ|%s0lhT(KE*S3Cg%FD^L4C% zkzi)KF>P5aDz5<-=&A?R!-_WQmKSvn&Zf{hH?6*)EG;N7Z0y{gPC(PFm)nrys{9M{?_q zAH4EMugqI67hEb>_(Hs_b-8Tg&9aR@*>pG8Qe1M^oKsT-QG8|1az%T*0+>PPy+XUU zU`4R!7u+imTs6tk+GJ5_(p#A)#cOzfek`EAp8EW}`eO*CO1usN43?ASj` z8s9L|OQsy^{!W@=QB7I$b#NtHt&#t@mri?5`@h1BmIJ5Z`GLew>iJy0lZeA3w3bM) zuE0_b6SQ?h!=~eDWLy6#bc>?l!M!eY%Yn{Nz%LJ-(d!Rk@xrN>`lXW$U=i$9StMY) z0F*Q1Gm=kc>ou{YOlvE>4*Mfr$EXEZqhTyixcwTD$oPwYh0>9@e~9Fntfk$)hzF6q zoU5ZKJRx$e!?zDPr^Y9tzlCpOOGejp210O`5g@HD+GAJ=yCEVc)n!JUr%ZAH^fha# zqGL#B`B9h3e~ppm6#Ov8ZB`Ihtd@Kdr#hFe2FCT8pEloE&8s45$`U!CV7uyk9R^NwGJ|WwxH~6Rl!rK! zY#a$0(Ke~h1DuL$%_Zvty~e$3@HCyK4iE3pNCBIG&#v>7XbLyg6pB>ac*;}}rWc#j zX?;5Ri%6PsLcfXlXfPCqIeEUKg~1InPXT##o>peHy9=+o!OM0qsZ0;?b#)PR>K3{*UX8kSK2H5WM!*rA0(pY)h;!{gc z+~|lMJ09P29F7{jMNoOD@+I9>F?Y-2^bP0Dt|wqCej5kP4#ny>-|}o>R^r~6%Xho1 zZEu|6yO>R7FhE#mSXnP<)+z*#H9p0= zcwjKw@_8_O^%TyAwvq|(+x(gnx^L!}UO*>hLE{HZc*7lnQ)lm6P(56rw=DkO!b->l z<%9%J5W1$|s*AL-l7FQA+$bLGH>3M^9Curx$ns}M$SwEnpnkGH> znfR7#+H##fBGbr>5L?L@tp7&nAX?zZp%EbV+O|X3n6`Xa5m;HkYSr>}n4ONOivmiM zH5+XPsxcc=D`iVEv$oieLTJy(@CYda`xRVR?L=C>euDW~duCc)nQ91P%P5he!cR8T z@TGx04s;4_LjKdV#KUYm$=YVAw9ngWiOeDo9Vd_OKiaWn>o#A{nQ(X_)ZNuJ5ts;d z^3At%Ts}?D9qW*yYZr8{HT$8H8NJIDFO`kkaL1-P^^bFKhIWp^C|ia!gx8$64Q?#e z9`j+_AMQTM`V^^vr~(y5j483H`1{y+!Ar7%6t+a3`U87LOnjxKE6of?q>ICXiejFr=WyA`k zMaPy5wT#0wX&fJ33!HAPI?wc7w3c`l3~khXZf(XmJqM%?L>PG*ptT3iITfVmxpy;7 zuI`gqKN#)KV}xXe_^zJrrO3{`tHCZBLD(h+T+iS)LJuOfC)r2?)g~JW%y61J1PQen z!7v1%mwyM?uao`I=Byg9e2F%3+TP^X>1Kpk7G$Cm*=p^XZ`YZzVPVsvT^%N(u0HsY zr5&6x{BJdFyG7?L?Yh1(VcQjRZBG`ghw`Socs74w^J4FJyK%bWk~v}97;|+(u)@8- zb38S+ZqJSVKiQmcJsGn-scy?h@g1@{{u^pvBh|lyZX@)_$kG;iY@^#a#SPI-qFXm^ zU|4UYS3bIJ!tFlvxiu?gN_#(3)d?m|$V2*v_xZN-JSpuM8#;0I@86=L*;juXA&dll zM%H5QXIcnk?VP|n^jVow`B$_8sToR&NUl;3H3zjw4U;sTdDB!QenuHBd^%8A*DFE( zilhR%4x{cw2155zh5Mh~yZ>nap(A~RM~)31K5}e7&raQ|eBgfRG0=B<$$%<^v>lL1 z9Co1pK!1OKYmv-&0Bm505uiIW?Kp8YIAeglChkC_n=!GXzO=!026~y*c`!cH_!cKP zXMq*P*$b!z=P|H2Vl<@N8)>5YXOxWwXwQ3zheRpRB@wDuF`qP9SOV*oNWSVel}OVQ z{5&G3{0e>VX}Yo1cY#8*1n*ZF7NvBb{3@lpO;vx5LZ>OjCI~eGSbb(;@|%>1Eozo0 zowCZ%0-YolDc9hjKM4541Ul-O>+&RhX$uucwhQtux;;X-y>vTAZ<)dyV+Z{`J!0ka z6-cx!p-$Z8DG83`7wJZkz%v; zDe+EyPs~y>Z%bHu?i6fJST-l!o-ZFsx_t?Y4-{)fRkCUAiaihBJ)J_O?-%(9-6_No zmlTsDcuQAuDP$A84QeWH*$SlsqX#}xT@->d&q6%<8dmZvt(#G*w|FIwLQcU`&Et@` z;+HPOk>xuMhNrjP6tYpa+yd+NJ4G8-toX%Ok%?mP4w>u-DVZn^=fD@WxiJStk7?x1_E6d+~dH7mI+Xd^FNb{2#`jKkte9t)#;OIPw)ut2C-v*Kbww@_BK zQpkcukiPL^!CG^yyIV}LcaNd_yesR3hL&XYnw9)2Yw;ZqTp!>U->mLj$)%7@aJR46 zDdZ4JYgh6pYzsPH`9ux0Wb*xw^pyX#koRnIz!d{~UN5Q>( zish4+&D%;$xsda~HYAto9Q@|*k8&|KBYEi>Vn0eRXw@OU`qwjh=SHtML z0l#aeDqAS2n7m5b14m#^mcoT#Zn{heR z$rK{F8Fk(;{4x%X`Q?`YC28YMA!ez{^HJ4MWpHK8O ze4+5YvMXiZt9W}b?ro2=#N5S$`X&D_HU49>)E(+mE0~znLW7l7A>U9erx{LM{m_Mv9=-puYaJ@+34Mlrw z)*o9a{Nr4a!ZtI7^C>1{I#S25>39+W(&;7)GW+@*p$Sxd;GEX*m~wS$kvfp-tj)~5 zv(w5**(6ZbG!W{5F?|;rwag&{D;8otax|r!bmKD|H(iQ1Xz_*T{s6UuP~Me0aNA0!MVXiansBJIKrG!td(ezsTb`ltb6_w0uRzQhifk8MaM|8 zWMt{jPKi~z8O(X$XUqW`IW{xYUy+?U#<@;gDVVR1mxQ#L96ThOj8;0B>DG+1;dfi~ zXD?|G<2al);LO_oR#85QhSOn3WB&6Y`0)mpoJkj>h~}q%GZAAWJ$@6nl#>xHfY-f{ ziK?nhG^!~Jlal7*PG%b^8v;ysBV_}&&fH$C(rjY#+Cj<}saQ4OGH3_(?yEG^6Zp8I z!~WHsNl!8Sqc*O6@A)gwFE?(BH*UM$A8Xw9w~c$^?%LVhd9cO70B^kMX}n$2yl6|* zte-ph35BjV+^X3Pe_zVMMb9jpiB)xd zlO^R)e&ZVOUd}B4zei7gR&PD1rcy4|Qxbp89y7nT4YSZB*@A8WIwOTPO<#RF?}2@w zsc51q$_22NC)uw#ROC55?ok^=HF=SCMN-J|OUCX89?5V;$u~Kn9__}|usi#>{bvG& z-?tFMwKXS22fnidt!1((7a-{D+j(O!x&uRlbeI*AIVFIecp)*eSmR?{-ig zXQ;{m-pjR=kwHu=z#onc%7OAX4tPsdlbn#weHg`G7ywR&yYWHPoP8fEEJ{s6@*I^j zk6S8_@_-#pobFbq4)t+Rli&;iJ{CE1uM3||6{trzD{r($$W*nI6H0PJfkAMNp*_VL zyomS^c`u{>P%j0WqHim^@@i}<{qoxcr9VVDz@|!sLJxGt*)F`WcJT|}?MymL-l?A- ze!DI1Y@B%vZ)SGQ4_w>0`0@wQtI;Ca?+soVTzWdu*n>{Zs&4c3rk^y#%3BlVM{YTfCSBecC)@Uh^gD@$ zCjAqp2+$m1seWkydSdaP69UZhKxgQlTGhJ^m>k z7#3)?lhlbA0~zOjLzZ<>2z#(t-$oQ_Ui+%(&jC|b8%v?X4&WhtB}>Y2$+7MF7e#EbT& z+NHY1{CM%^8OKk(l^|Rc5L~e!#mm>;a<-*B70K$RImb#aJY}wc@mj0`VLh#?>Cile zDnF1`{U)bX_X*zsBj7Z6Q_RIR^c}gkArKQ#<$}T%v}Wc^Z@MgW(k{Tc(Ued@C{TY3 z2K!k)xuRA=;>m?Jv~F6^{YA#Cf%JrelkA#@pnP>IWAHkghmWu;?Q9#9&X5T~6Z36& zq>bjjEqq5Kjqufh2UEXf>oWwG=;z34n<0cL@sX5saPZV5xX%zN!{uwa{t0>lJ?gt% z5p&P_b-)OB`amxe&oN~QjYAKaCMj3UZKO~=ZYc*5%w+pB8d5aMMMR}ZH-VKdJZ^_L5kLz1;g2R<9^LrPM zFBM*WdMWh&iEr***fO&(=Gl>i%YZ`{4!!yKPs^%+Qk&aVCQl9!&{e@R;U5zAx{$%nEHZD6gC5`t!6mAXDd;zoHO<>6D$o_kQ9`bBP42%%9O~!W^KS zv4cLw*xC#&VPKtAD&5e`=CO_d#{uOM=ToNhYnbzHnCjDa23cKL-5AJ+p-KQyKjq+{ zpYpK-(ltaz3vhzn%XWgWYK$_-3XL?bLUD|eRnpG!x2O7N(!FpxtPIcYo6PHdPvbJftwmV+iJzD@7 z`Q^MzdDw=kYRPx_tR4H%{4;TH19r1b+xf}I3xQju?bpL_>6M zmYcW6o44Mq*mZr+Y%T=#mn$w2LxP`+w6Hee>6qDfyJGF)rlsN^RDHkdh8SPpdt+;& z;_;csljYS4JD~A{_N(oQ@~tzE(aBm*!RO@J>usPQJcnao3*;Bgp1zr1{jrP0LBw2u zY^juqF{-K(V-Jv{fm#fBxz5_V4<6yEbQE!TX%!ss}C-5;y&NK`*^ z%iU{`gHsavZBRohb1>)+LMlT~>L_w?wPLp1t3N0_&8Ifn`c$McZOM=0o?KW%?1D`J z>OogJPUo~=;~%K07|)%_o{Um40VgPYenFgB06I?`NQ2J z?ioTu!dMu_^-pY4=vXa=I8??^YPb{jP)#n3@{}trT~>D&xfpwphV85AuuE%GK|Q`G zXae>Ezr3NYsF}_OWj6oXaHex&@6-I6o))Jq>8S+kYx2(RPN!8v*ED~<*uNDItibdP z(@GY$ySL%3!Pg&DED>g>_9*B&5ZIvW za(>m8D^yVqs3L1$aZ2WM`88?dwAKYPp58*}#l!X{MIj|0xQ-PQBYEQ~CLe-Qb8+rIo`iV&ckD`itQDw# z)C%V+kfssQF)cnvHL|?IOiq>h;pv>H?mMjx|Kq46G+>$aIWsZf2Wo5cTDKa3C^D7( zaTsnu*2N4Q$jLS$9V$Y|_{yr|M(x}!9cQ{9y8d($ET50(tlXyg(wY ztaM^O88^H5;9>P;>VqGDjSvkn=73o)i{#*bU0I4v^@ zA0MLg*2hQ=$4@0dqqKT}`=|I3Y8i%+L_lHUtia{7gG)fY#|$w|=}D9;Z{5sTb4TdWkrZ+?^M1;KRgJNx?!|)Zh4Gf|Sk;bM zJ28yQ=UZd0`k1XAAh^1A)=nT_iTU(iBi{c~aM%xt$@0pX$5t$+!qQJlE9ZOPI=5Wf z94~E-m9#9H7S}H}!m4>)SF&sS;%Kb<2=n}SB60M^*pb25;K`L-bKxnG;D5=ATPUwv zXj|xxm31zaUw`cSj_W|n2 z*sKU<>;M|tNup%N=mTP7#N;Cw3kVK|TbeG>_Jy3N8Em{+9c0&7!qJ?U!Qzu&kLDOj zER1rzkHMsiSm5#n9n?36HM&AYyX0qdw1r^Au1h0^kbP>5y7U2SI`>qD?N)1J+$+&s z&f+&>!l?`|wS;L~keG$U$7F+OJ;kWfm97Iu5a(?Y zyAf(@=F_60C1pNa#Z&6 zmcmKcv^DrF%4*6=h{x9+Lww8X_|Rw7)u5bzvAUX3S573EUvnAFXIcTWX;ysmb+BRE zR@Yn|ThPfJ>f1CSMrP4mSQ6PsP@ElMtb^d>lAiWhX#@a23}b0*VA`&~8sc|H_U@Zx zTWR(I_gMv#7tt(BL!Y|kbhnl=sY%KV0~*;z(Af-I%e1RY`6I+}B0{DRk!&2)|1O1Y zQ=CNEaM&NKF|L#K80hS?`m}fv&*l7EU$y ztH{G%%?e66hMwq+l!K8Dkb{o|`G!I#<&b*(U<$_bp&=RaBpj8*_ny;Kk2AO#xp$F? zIFD2tlO9T;7io9-9i#(~;pt}~SFUS)uj5L`jF{ZE8|UNTXm{)F@~XFsK_C>kE{wfv zT3G)*>tg>+-+B@X9lUVxTgRaG{M4IAe&#B@2iu-C^Zr}48*h0wed618-TR~JA6EY) zH{m-vd+2t<*4t$*x2r&bFdkM_SF)=9^5IK|v1u*%FFmnbwIN=$0j^@oE8r@ov1INr zc8kW=<%X^ChOO6aiH6=;ByHfy5>=b#?4ToRH{IH_?=Sj)Qu}Y7`H4UFCK5q__Cu<8zPCipgEO ze>DF8Rd(gUZCvMl7Y6|lAOI311zr*_QQ{%$v~0_^6y1kq`j8yQautabBwMm9+DrNn zBh{S5X-QidfsHew8@Fbr?U9(6j? z-|u^i#i6C#?vQUE-oAbN&hLHiJAXL%=6$J#I};6ezN^nRY??XnSll}o{py5!K3sXJ zVxnRu+dD2Ryo10rwZ;II=Do<<$ zAvvc#SSGUceY@yrHmpkL+luDS(iP$TI%7htA7tz@=7&<6BMeZnKeJ-2*-hVRcq4lP z=i$ShK_xaOL@*Hnk(iLqukocHSZ^*?n(<%wAlL;Y>F)w#RLOfYReL7NO(E)an+MdfI z-PX|TvgVLO%X_H2Z+`9Yt%dxW zg)FnxI21X+Z?SZdgdo3J(NCUcY%!gdv_IAlq!$P+H9a4YHv%mh9=mtB3ocNQ0iuUGE=!8DsbVZj2m}UyC$0PFb1u=J2F1T zputw1@iP|C>e@0Dj0LIH7Gf+6m?&j{vjE?{5vHBe+uV)w!ODyqca#~a&3G8|(rvJh zv2v|uS;o&8Y-HAFDi{m0q!43amQ=|Y0LYRkI3XEd2(_$)ejV;8v9dY?AH`Bj=ms)Y z&UgHXWm}_Y=_c*2do!MO?)&CL?HM=jcnJ++3aUXT!-!>{(!K7wZ1ocNc0IGM&D~Bq zjuZEJ-|{T#-oCI;U+wM{U?qdR3)%vXG zA51%jUo7Sg%;8;lwkhZa__zd+ICA6z?${ggSj@NvYhODw=D1eTFjX~M(LUyw^9C-4 z#zT{iS#JaFGK(qZRH!u(YE9BhPrG9C6n3e_=D8Pz(*6!Q3bE1)BrHck*IKB0%nA7# z*B`hk2H9uqN68}_hnk4w@-z^+QAfe~TL| zsL+ulTg=V`j*@dtFjLh!wd$`OmpA;C_uZ=L^Y3n*DR3)gPC2|mrAu?dQ2fAd!hBs< zOdUNP=^Gs0OKD8HA%p2Xk}uvdomu=hs#<8c+4!6dT~buv{7C2Kq-CmYo(y|H%w#jGk%Rn##gkgtIWMT z8`j-h7Rq$D8f?i^v?P(Am!3U=Fux?}?)W&Y;Bg*6OBOz~8H3iU%T68zj*!bB31ap& zG-*9}L%BnS`{S4o8mHb^FjkI5?SpIGP`biGe7UL(?u>@uudgyjBz70}{MY&8+XzPL z1*K3srR?On;P7Fn4Jo?hkmbAMxsTH?#(~>6$gzcXiRFX*^2Rsl(#YAZJdlv z?Vdg`)3o-gcb!_Rg|nF)G=MifJA9iKLLRRZdS%FW9wczuF|=*73x_weYyo{1cMe6H zSN^~YiMei=UNd{esd~l3uA!)J&Jc>sYW9yo&zuIImzda7>!zev@(btR*eh#M0D7gh z zbr8H$Dk3qdt;Dz(lnE0ZHH7Vxl65Ee)D2F=PZ5C81Kd`%7_JpGX+r5@bn*d%kJy5{ z@qusB8(4JjEKeYp>NROZ*D+Lu<@apdM3$O~%BrTD$ ziWV(~lb4?T62jkIWZ7Cu*X}V5K2@Br1h}(G+=Vz&U(yei0aSYoB9%9{9{^eUH~{q% zYMpUqS~zeIsKd5KNQ%(a!*U`nBGy^6l*1qnFmP8F98+lD!gDfaNMTYs!`3e+)ABa5 zIWOU82;HhKvA&*H2R8N~**JlQfYJVI#y2x~RwW1vk8#;`%>QT;*_*163Z&MO2iy2{NmVlJr9 znSG7*z7rC%#0}&c2X9=BTaH9W7)c>*zF^$TZhiF>-fG@ zpd}G#nQEP-wq|Yox$%)iX!%^Y>XK)|GwFEUpNo3ns}fQbaSt^;eOj~?R17uR^^@za zny#>NO{qX*^BcAy(8FU3J!B$E%d6Nm5d?zmd`ZneE1~Q0Yb`yzwsdUS+vZ(?1qLw!3UOL4<9&d7lRM^ZQX`nQ~LuL7CeUCf946A>_V~IF2+c^BZ2_`T`Lunk9rq zGE3%>xe)UPOkjiq8a_(AaOTki)a^?LU z-Wb(>6>XVnPqnT~w66O_bp80Av8`jzL!*%gzsb#o4}-9i5= zR|9Qess`!>KJ?uV3`rMCx4y(t*uvmG26OC124VI^8_9oQ!gzGP-ZE&%}w3~ZB^3IqnN zMffCRBtRrt&O#uHY@h$X?pvV4n1 z7FARu`xW^(fQL1PNHDLM{o&JAi!cYIa(6*7G2TQhVTQ^5qCr!)4eTSSFo`=wqsj#^ zQeQGH3gE#;_G$x4R2A7bS)(4m+g1MP~1VgU|#EF7!Az3gnTv(os0mCq0Sf8wX|Fp+b3VPu#jtVktd zW4y{&e$L=E2JC~P$zV|tH=>;)snD&N^VAl4eF8toW2>=c+e7EP6ei4lO$T0crt6&B*3bq{mgiiBc_H!=v8j!N z+HZyVE(`#3&~5d-Aa9&MIiB;NwIH{mo%*RIb|r)8?*{EKH{CTFRe^sDPyZ*FR*!L& zD;YgVz8#~Zc6dR07CMw-1rEYl6l{a&7GlFW4w@x_EQ<8Xl68>=et~4;qeGCygVP-o zw-s?i*&$FYy!1dyVF(_m$_2|XgI2k}1f|Zkd1ZN~wQgoeKAJJyjOX_?uPe#0vmnD- zGlNyO7<1;L6G_BjG3QJLcec%d`ulMafuP((tFj-^TGSCR*u0VtRqfeyh_6|N%WFIF zh8XkcH02O&`UVES`=6%3oqQTnhP0k+>DTjt6ty|uGKeRj=K+i%enH7l<{5%XauJst zFpl>>Ep?(vXNdeMK})zZVps6$Q_8Hfm>b6181jQ~*W(%(JRM6rVL%^71oE0v%>XCD zTT%>8g)|ZZZDR1J45(R~t~esQgK3RKQ6){W3OOkX0pjmbV+e*% zWU`5&m7#P6wZmb|-~8zoY0`A)B#f~XKEQE6lLkp32p62Ds)`7@s@_+K#!eozdT)KY zjhjUXHG_a3r1`2Zj=VZD7pR&q5Xc5guCD2=@;A$A(WB~;cfvba{d&-fekTYL6j+aC zT15opi~6oq-5rU#J7%IAFCUw!-8AOER$DjOoviH`^UpOcgZ_DI`&eMEZTVQmwT5bXTeTwrOvyehLP~k6?bQ@fD+uK`R4+F1Nu+t1XM-P7Ab{6l?t>a0&P>_f4Itm`@a9udhR6T~Y<{Wyo0YDpO0P=n&ZvrAz~As)MQ zYT{H9^yv+6Z~5Mq%R5pVb|g0J$T}Tic#m@U6#qaSLX_Qkdn&WcLYa@OL+Nqk15RHC zn^+T0(cE!lu4;MG1!_3BQ!R6qE0eC3iVTo-t#+@@4(QeHo`oK#TUA4fIEJ^aD~Gpb zdC*ww)M*`4$WvBlPoJDUp#dNB$QjfNt7w$&PzsYGPbIQlA}dyp2lk>Lcga@^2nn_) ziX40SYlaS^dS<00QtJ9W?UJwWho_~j3OWr#bx0b;9 z9US6)yANam->uu1bwrwx24G&|UV6#l!0h0{XK=xc4&gG9KWjS&b@P&Iz=wLXZo`U7#tt9Q;-)x-zl&&A;yD@O>ZzEn3+H&s2oVaz-0 zUBgt*xF_Bc-ys5`_f0vb)=w>mQ>?4rP8nL-GvKNyU?kgf2<~84*pUll>NB{=dXp@H z^;ur=4H&BnedGSQitsn~XF)7zTPX9oyX2d)TKG}x%8g;R6-kRMMVLDWC)j<+McFFj zC-`9g?0?KGKnmmp!f!p>CxzKhQ1fgDH49q+4To_fcsn;)!AIoT&XT5(J(9y-G~y!G z$yB=$ui=O*nazUD^KJH7GhP97g|q5?ukXtvlDj4kPCRfm&`LB?@m3(4r6|*6*}dZ~ zCB@}AIWM6^)zv~hmCj=DNBkUdj~od74VX7drbl|zeo$DyMF)lAZXA*_;}7wHkw%Mf zSRnFZ3a=hE-a?ilT-X|`4S2E`y~Vf?(*YW60S(6S%O*vFG9pgOcR`I2R+0);XK=1Z z`Me5bzbuhCj}I@F)y} zIWBw!)QA6DNw#8qc>qdqr8pK3;Ty*LXrM5#M;txPn3n8nW%JHQ)#VpY#-QscQ`Xe_ z64o^YofLkEdKjBq z*7nxfH_uM^7bT65=( zTBu~@cptvmaI1y?ed~U>@CzvKeI_QdiIA*hg>df51pa=(3Mr?+%YrNHj;gi9OhtRS zHGBUOPl|bix%qa!aK-^i%+Z&O0OqsJTssf7-~y_i&4!lunNCGuJ4-`It;b-LTvW(~ zlCZTELP_{*V*B1Yi`bo3@?V4fqMAbE5_a6Qd~Vpy50`u`XC>c}QwJ7%Nq&@R(e3gp z$+VW_gg;yQuyq>C&@VN)Cgr~)mJ{6PU1vi08mM*ZLff3Q$$!RWeG3-gPB@CqV8Gzz z@0$iMb7n0eQ!g~Em9$XPBh;TWgPr0-jKm|QtgOJ9Ns8MG@*SR-1;EW5sZIk*>6iku z(DsC-$LEbR%`015dzx3R&1v!;G$~%xvK@dHEOe9CtQvs6Y)ny@(q8C#!ia7E5VmMp z;>1bvF=!}sptWhwQ~g8mNoW{bIj<=;VQgc%&r--Gd5Dg{hSL=g-BmTEgm0RTlNm8U zw8n1A-~a=`YVJo2P@TxJ@jDFM{QM2Z1hf4LVx?e0%=n%dB2YOqXzWIX-sgJyC9*;Q zsao?#C!NXi#?kH9Ji)P{_`X@svbkWTLU1y!kbCJho*G*cP$6kchC0SvnPxH+?;gK9 z9!tSyWNpWczhlPL@%aK5^`}~edns&)`0B4!MpKm?iOPF;a4?E&}u zJ_qA|o$-K&@ftoY+A}Q#d;p)6Jre^{dgb>C7@x7QBg$Ew12xM)k9!~l|g0?RB^RUmBo(p2LxkIqA}?*RXQA+4;?`ncNGhk{|)CNgMZ=t zBFk@7gDS$sQ{$&5mn4HNqr0#9gJUnoUz{wP2`x*7IufCdsTVJwh00Viv^DA9HsjhR zKt1gkJT?NwH+4>em&+mm&yK^to}EKvG4#Yv48#t!+nwrR-5x;v1;{fR0FNA;7es6h zzL1>-y12F{nA{MFcu4>iEV*D7#v(tRbG<kWAsGGYVWHrr%gmh0v&w>4U zr)feD$P2@p{tf}-PM$e-`uP|j95vT-JPZ)X%Sn5BObn!e5s`afaViE+n0$$@=Ga+y zQaB0Akziyzshr}VIAN8ngb`th(Ao+O0l>;R%@mv*?z~4n(1p)}b`SlEf^VEK2A^qG zz82{}l@3jcRX&G~QgH7s#vW!s zS$G8;KSd1sk`L{FbocJZkL=j{2!A|&aOZ)8`~d-w3iK$Lk+5`504wey0-(EFZ8Q$D zEEZEDTAU8!1vD#fcExxUaP%|I^QVy=hr(J7LKzUyNJZKbkv8Q9ITcx-h^!yoKNqQ; z?3sx`bYt`M`pZ2t-J3`Ef9Q!oMg)q6zKXF&FCH2{l=MbNx6TJc7f+6##7gnNL|4Mo zgggaN5Yl9U6oDRVJrJ$6M|cbf7oWpfJU2UGY)AvC*VaFS9T+Nl)AlWZBz)A_a8epXea|7@V{ZSnlm#8_S*kdP3#yzIfp5tpO-NUe3W zg0kI1f~a7QQ{#@a&X~+)l!8#aH0L58u}y}kARc4vV+5VCbor4ZC!VL%XXpiUgrQzM zG@K5>a`$OdKcTmO;F#g!E0~55i)TP0ym5o2?m#aYq_`VISB;xYBbsSs5TrdsrMUED z^}NJ~|ICNd;RIVsUzwoFAUiqjrVGmY(4S>|tC>LyU-er#PY#Nn zOToJ*2YZM5PaMa-W*BXJV>#al=H3wN9>WSntI8$9ODpuo`=OR&tYGd??m@1P1QF|G zX~db}<{&q<#kH!jicel+7p`T%DTD5!7N#-I1i}8eHq|8Xb6`H^&t-htj3Di^bS9PN zw@fyf96~-IMOajAM08(-dkds#MX*QYLq1?W?v;uXBj+dLd$K8~y_it|dzep;G0Vez z(8tP_!LkflijOdba80eMmsrOw47jY)-g}=Jj6Hw)_-2E9D@xFY{vHz(n3t~qtJeC7 zR(@S;m%lcIA8Az|X~FB-((795b#2KM@EBCm7Z*Zldb9Huw{5Sv1a>^4$Q3Cljz!;sNOrHRen~sP1n0_XbeBw z=eS3&y`dra?3~{3(7SJF3>WsePw9F!j?P~N*K$GQj|{Y7Hf_#0qk7q#uU6gHZIt`G z+naSG{l-Sk9T?jlKY!UZ>$+o9M?D>$IX#flYZH3yg5xQ-{)ismJ-KyaUsjVJS*PY$ z!oZVpNkZng{Myo1m~njO-3JoA`({?%|I^23R((0q{#c^vvC&YxE~!1nVk=acpE+V~ zy*C3wtKKVRWmH0+ALWiVs7#Z~XZ7aK92I(dMnk}SEgAXQTAFLY5!9RF`?DHuWba5M8EmI>dQFQP}|_hvQR%zTXXEYnwfxd%(d>*35M ZpWZSTY|1!!U%5l>^X}k+8^2|h|1S?~e>ngE literal 0 HcmV?d00001 diff --git a/.deps_siteforge_v812/_pytest/_code/__pycache__/source.cpython-312.pyc b/.deps_siteforge_v812/_pytest/_code/__pycache__/source.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cd77881a40b4292d2953c509dde2c4061f71c08a GIT binary patch literal 11930 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 UO1VhIBpMt=N7H>TQVc`?2Q{6`kpKVy literal 0 HcmV?d00001 diff --git a/.deps_siteforge_v812/_pytest/_code/code.py b/.deps_siteforge_v812/_pytest/_code/code.py new file mode 100644 index 0000000..add2a49 --- /dev/null +++ b/.deps_siteforge_v812/_pytest/_code/code.py @@ -0,0 +1,1565 @@ +# 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 TypeAlias +from typing import TypeVar + +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 = 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 + # 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" + f" Expected regex: {regexp!r}\n" + f" Actual message: {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) + + +# 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 = 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/.deps_siteforge_v812/_pytest/_code/source.py b/.deps_siteforge_v812/_pytest/_code/source.py new file mode 100644 index 0000000..99c242d --- /dev/null +++ b/.deps_siteforge_v812/_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/.deps_siteforge_v812/_pytest/_io/__init__.py b/.deps_siteforge_v812/_pytest/_io/__init__.py new file mode 100644 index 0000000..b0155b1 --- /dev/null +++ b/.deps_siteforge_v812/_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/.deps_siteforge_v812/_pytest/_io/__pycache__/__init__.cpython-312.pyc b/.deps_siteforge_v812/_pytest/_io/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..17b0a7c6171ab196d95ffc2253860aaea537f517 GIT binary patch literal 377 zcmXw!y-EW?6oqH@CkY8jAqX}$b`kP}K_wCd(Ne4;$}(^n-7y)N-5q8pgrxH+dW3FH< zLpwyW*8g{IjKoV*K-l=$uIh!X4Dc|rBO?Hei(XdS z3A;{(Nm-}e?H;o;tuMIc>@vCMiuFaLnU=a?xxr^Ojs~5B>GUvVO5QO&vBM;0fK#Nl z1u|&?$XHr{rk-S_UX+9*B~DEh7v-Nzcd;#8O6iR3eUs*l>@P~h-~RA+7QS0?o{FY| F#2-e{X+;14 literal 0 HcmV?d00001 diff --git a/.deps_siteforge_v812/_pytest/_io/__pycache__/pprint.cpython-312.pyc b/.deps_siteforge_v812/_pytest/_io/__pycache__/pprint.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3bb41b7e54495f4c40c8f269ae539547e234710e GIT binary patch literal 24387 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 literal 0 HcmV?d00001 diff --git a/.deps_siteforge_v812/_pytest/_io/__pycache__/saferepr.cpython-312.pyc b/.deps_siteforge_v812/_pytest/_io/__pycache__/saferepr.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..40aa6d47a1e31c74ecd95dccd305b797a7f6efac GIT binary patch literal 5823 zcmc&&YitzP6~1?7-w&@}7-JDS81q;j=m4cC z3x+Tt@LU`a)vzjFat*lD8r224TaBo0$UOo%MpVxwqIxxPxTZ94CBG>;@_-LU@Iep1 z>K_Pb0X2{i{G|F%tyP1U+yg5xND2c;J(^JJPVDn}XpA*7*ZF0P2xs8|u&F6#`z-AWa%&kAC z$VVqF1l#mOt9}x7_c5I4Hm>6~N?Ea^qa!NVqM;>iaYPvdJyO&5f@JDh&8E6dhu5es zAZv!9r?H+%Fve#1nKC>skR@**-~IZl)83E08<#?nX*zvuM!9xkHg>CF`$A|(o;rmG zW}d(H=xp7!#}`7|@^mp$J6%7$HJ_QzOij$Z@@~WI^Y1jxZT)%6?B<#8-HLP+d>xCy z(1oX0Uh=bpi;J=Jx+8Gdx-GzdSYfG)_L zGzBQQ0@%A*e5WKwUlVdP2^-E4xriq~2gDM&JZ28-ipNICI7Af156+*@jQj3MD@+-Y zit6tvmt)=WgaiRo>PscHc4<^M49Q}dWL&YdH3x&vM6y!O(V1y2t|#<3KdyEuW8#*J z0CLU=$Hru)Mzql(h#BaR@K8o{RRz}tBMs@QGl@24F-5W!LGp_4kGA>gy61<%4;+3)-Hx$#9<&y1O;BJrYfZA+b32Ysu(RV|!*#VEc7s#C& z;;)(V-tskn;%iyvGNF#x0PA7B{u#T~}(@R_L<6Gx|5wAYlAp z_`@KtO)OM#G?h>_AtWOeDl_E*h(SQBu7}akJ+DcxHCYX%B81p_ZOZOT$O$|YVi4iU z!XUy^6?wua!4r_DrU}u)pw3fHg!Po&WjP3*TpI-6qkYkUqYgLM6Wb2`8M^DWkqm>5 zqdf_vsGCKx0QbqzxiQ=u<2u6Lh`FLV@iiMRs}r$w5QrY>!e{I}!?bx+vXX~zibDAy(NT~K|?^JIgxNWJnWu|*Z{h9HGu~7SPJ_dd< z@4I^^OzK+S?I}b$fB^aH7ejSRkXi1P>BAg^*PxNz=Rehb1H3REU86X0UjBRZh%3eCAso3e()ocMsmr)i>^ za=EIs4I~(}?3#34Mr}i0p**Y|HUdQD!PINJ4eeFUsA$Jw=Ta%d_Am`yp6xT{<+!juH#;hw_N|$#-RUdNDr5zAygGp z9RgOJK&^m`W$;B<(Ne6#zzs?CdFi#F8Qb^R}#U$l&mRU^{U` z0y@Bh&w2e-4JZNU*&P8MV~;Ajfzb%g4FM-h35cxxYKja4LJfnU=z!34V(&SmB1ML; zseF)8-5P?)&7l;tDs%%nCQ?p>>w{3`N%K|N-n7F`Z2ytc)qs=lsK7Zq_jx9sTZyp; zOrS_kANB;4_ruRb;DDbCiFkY4aCLHOawfhIX)X9#?=A%!ohw^p`)qt}-$JDGkG@XM zt>KE+2>mWLZX`GS`^DK{H{5OZi|@Pli?^4d;^-<@g_;qO1APO8Rs5Rfgpy{4p~c!% zt>GwDo&l_t6@=Vv52pmE9@rQ^%su~qMR6Lr6IX=V-6@^d;Qj>Ua9pyKmy5(Qt^h{@ zR+I$a#T+tOQMV}pQw26s1lH*x-7JGi0$GtpnUV%>BPlGE0nUna=oHM=i4($IzT8?d zbigpc4_a`1nU)Im4wL{U+u*>tfk#CLGRTX#%P~kNytokD`Dw8IRr&O)% zy4W62y%SL$M+sS533|x79hbEVdiABX$Yo8jBiIyv=A-Zc-_-x(So{*gcpOD1=|oSDLnZm= zllAL4-^57k@X-S(Rr>p?M&;JljrDx z-l8LLKKgKOEMd6|y@X@+PvHB8r;Z$roq6`OeCo(^r{t5d7v%oq14p=#^Fw}*IFXuv z@A36IeBm*60{UXuH%H(Bw3X67lh)5k_)nx}#p9t}EA@WrS!s6D@SWOrT9+Tanw!eq zAt0`Nm-=bfatqnCxj;6+)o9nD!gFVq#Sq=Q=#MygOPiA)KgsjIxP2>bsQ+?HE$!mV F`45e9U)BHs literal 0 HcmV?d00001 diff --git a/.deps_siteforge_v812/_pytest/_io/__pycache__/terminalwriter.cpython-312.pyc b/.deps_siteforge_v812/_pytest/_io/__pycache__/terminalwriter.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5877d2843ec07622f5db1dada128978229bc2ae2 GIT binary patch literal 11239 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 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/.deps_siteforge_v812/_pytest/_io/saferepr.py b/.deps_siteforge_v812/_pytest/_io/saferepr.py new file mode 100644 index 0000000..cee70e3 --- /dev/null +++ b/.deps_siteforge_v812/_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/.deps_siteforge_v812/_pytest/_io/terminalwriter.py b/.deps_siteforge_v812/_pytest/_io/terminalwriter.py new file mode 100644 index 0000000..9191b4e --- /dev/null +++ b/.deps_siteforge_v812/_pytest/_io/terminalwriter.py @@ -0,0 +1,258 @@ +"""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) + + self.write_raw(msg, flush=flush) + + def write_raw(self, msg: str, *, flush: bool = False) -> None: + 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() + # Would be better to strict=True but that fails some CI jobs. + for indent, new_line in zip(indents, new_lines, strict=False): + 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/.deps_siteforge_v812/_pytest/_io/wcwidth.py b/.deps_siteforge_v812/_pytest/_io/wcwidth.py new file mode 100644 index 0000000..23886ff --- /dev/null +++ b/.deps_siteforge_v812/_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/.deps_siteforge_v812/_pytest/_py/__init__.py b/.deps_siteforge_v812/_pytest/_py/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/.deps_siteforge_v812/_pytest/_py/__pycache__/__init__.cpython-312.pyc b/.deps_siteforge_v812/_pytest/_py/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0980787539641f4ab295fd99a59945a2488d4dae GIT binary patch literal 185 zcmX@j%ge<81h0?Y%mUGmK?FMZ%mNgd&QQsq$>_I|p@<2{`wUX^E5g|-CbT%Us5r*h zz|zt@#<8HlC9xzi#wR~HF()P@HMbz9AhSTXB(W$xwM5q}-PowA%CtBpCo?Igpt2+* zKQAU8NT(K;=mTl}`1s7c%#!$cy@JYH95%W6DWy57c15f}ix`2p7{vI<%*e=C#0+Es E0Ky9~ZU6uP literal 0 HcmV?d00001 diff --git a/.deps_siteforge_v812/_pytest/_py/__pycache__/error.cpython-312.pyc b/.deps_siteforge_v812/_pytest/_py/__pycache__/error.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1d055d41eccb1fbc7b523263276927c572c313aa GIT binary patch literal 4953 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 ASpWb4 literal 0 HcmV?d00001 diff --git a/.deps_siteforge_v812/_pytest/_py/__pycache__/path.cpython-312.pyc b/.deps_siteforge_v812/_pytest/_py/__pycache__/path.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..88a7dfaf0dec781ae3f655c7f4deac7932fab43d GIT binary patch literal 68712 zcmce<3w&GGc_)aA2MH1&K@#Ab6v2l?Qlcp9{jemPBK5K*+LmNnV(c&wFDOwW0r~cP6vlUB&XW zuG5|D|2yZt0A5LM;`Z9QJUI8fAK&@j=P&Z|a!q*tuRpzcqT_v&>2K(V^4NGlxYJ-Z znI=r4smmk^qPbrfFn5^+3S0UuT^9Vc_FD&RT{aeH>(3g*6T~1dn`|Id( zu)oeOC;n#j=MCg{<+HTe2)nvmENn-(psRp|a}X}#tgDR0xezYzDreyWge$r#aui%T6|#Os9oVo?tSY{EdF6ST0t)W#K8rO8VN>WH#+KiB&I~#Ok*&5A<|3v$!RQ ztMM;a(s!+3v0lX1swrDoTpi--)wq>n!^@_wR`z8nzARI}tP&gX(`1-wrzVLBx-@stMf50CI`@(&} zKCHmQ;R^(UJTBCU-}ZK2f4}c&zaQcJo?syCKOG+M1$@W+5@ND_M?+K^1;suo z;2WTHS^m>~p>PNxTVDt%cmUt&TVJTRkClc{e_!Av;_QK-G@ul7uzT;pj=cvEolU`g zosTNP13PykXlVJx#VgcTTVj(Psun!pDPwXS(s% z9}2I+-&Isi>)=Sr-re07=nHpuM+%kBv?@_8RC^9yXH4^+vY7REa_-=VsfT7sd1w+? zr)X!QQ>GKj7v0|z`eBgcPso5#mkbm4mR$Bh(^Ye4N@%@mVPlZ8AN7U&v3ZL&(#U&I28>K33xosd@0E3@^fTxu_0h!M9s{#xV~ z%Jl%f?!KNj;qzSwmwJ`)ENYuPz$u&L?+*tTHB#D)0wVcp>sYF7hzk|trH=3U7tg*p z8A{$|DX63Sg|#-jH!$D}_Z(l`-Uq3@+P;<3JxndmSP|YCmpATCx|SyFOYbqlk09%W zDXi{S~^c%3mVxQfQsO>7?D z{OSvHu4OZ>WpPZxiiCZ|;ttW#f{76Byn&qw6q3+K(||Av^bz350FJ(e4D@uEo;__c zJ%e1MmQfsrqc+hrnw4V;XY=nS!0V_*K}<%oqFHAu!w%(}m>speZWiqrHm4G&{e9j1 zZL2bLF9|t7y`tu5mJ)L!UrVWk#GI2I*b(_inH5#@I!#eq1m#Qhf@#DOE>u!$DYQ7s zsq{^$iSo)NU_3@F=OR|qNLIZmOryr029vZBBV`dyqP4;#+6dH2bz;`2EpsG9t7xh) zY2y)aWt3wPOh-%s>#*sx<%nrmIAS{YyGj@->0D>e36B!e{_4J#Xt7F2_q9xk)Bg5a z(1-o#gXu(t7OR9X%YZkdmZ&8voH9wpSQB*4WjvT?c_TIIFJXp36dQ6=XM3FnsG zH@9$Zlak+xa>j&nYn3r*&h7j;9UegJa)E?MzTuvr=->5of+8sk@YO>RyuFc9Zx2VD zyuMSuK8Ch>dxMf!qLFEKrJUWAuQe1Ll6w478|AhEeH;j-vQexr9F|h~ZFu(`9SZw* zGF&m|(8wSU^1bQk33lVS>_Z zQl4(nKZtGG9Sn3cwAdf$@uw`Pt~r~ZzUGs|yeU?OhuBW}`iK0$lLNqJQN@5id^|+^ z%F7>)kK}awDV;Cuj})npSdH6-?$$z(WwC|SCE%zC@PRN#q47fNd4^>HzNII*(h{X>6tRM<-oZHryy(eY2+FXQh>s zJ1;!{=Ao+%Z!P=IGc%>Dr+3{b-GVQl-flKkExWx#Ky9CXvf1RR``DC~SB*AJnkNg# zHcbxQa5v0X*N*MFZMGCwFVrlHhp!x(uAQ#CayVJDVaodHLS^&ECZV|cW?9v33;zDo zZ3hbYG@qKSDy}a_$%6E(3t9++2;okPK$&uH5+it3^ovT_uTeW_yV;+r@ z#v}6>nhjGM;tg+X{iwcazS0|CH?>v%`i;nZP3^@~Q>WtMx1;!a$7QPCa61PjeHQA) z0RNtIhueJJS=;V3Utd*&_XlowdyegcM%Rvw)(yMmmr8^zAA1}+H_XCA^|EXD^cNTl+(mQ26h=rNT?FLp5?(xz3R?i(Yf`Bvw;n;fs z;W>9fln8P%c(ivUxLGXbkU0t{CH6%4z=)_NK#rQP(Z#|vZ+hL7)5+s=KDJ@$CY*3Y6^ z@l}uL+t@(s&>QwUK&fcb&zNqTvvcaPR3{!9e<djmC*)3k&4QJhL2a5VE zL`20)j(pSCbJ`xVOxA2Nzb7DYZB5%s+x6vU1h2OU6kh3~@OJu`wk&AC*|OM!KnAKp zKr`b}2L6P>L@Cf6L_{sJfry|ZSb7F804IP`DPstwCHSPH9ze7LSuoux$BWRDlCkha zWIS>qI#=2>Q`(etH7D%N968ai?0?ZN6re4v3(}VcuUk zeZ>a54grRF>-&K?dXynnhEd?vXVy%}MZ~4n%o!;!Ao324I!CS}XCtph6ZXo*EupDp zE%^!p{~Q`XY2Bz0R?$8j*jXqhF)AK9zw zPL|o`WrM+L>Om#yBQHGR4k6YW>uN-3Hm4k&eqa;_e0?Fm*okd?3`wNp^y;J6NqRNX zix?i#fb#kp0wG!yFPrAAcAkPh8rMrmrdF>gfTbm~%)o%Orm~;v3-y7f?9s0*^$gpE z=BEKGK|8Y)z9@~8$^jNH{-c!JI8`=Y7e7U{8G= ze7`f+@jH7K3d<(llP6{io3HlVEN!BwTcs7Tj&pnF-6b*WIs1G`75+HptCqy9v-a|v z+uB|Wzp>}dp7@h*x5v9?svdZES+aC9xUSCs%|UNIS@Lp6Q%oj0gz9OHEJ5s}py(K4 zX~>{d4-RYxBkTdxqsa)NjG8-4=dC^FV?f`Y#ljo4My)5*4ai!-UZU`}x?MplJ;(WrRD)<&)4=5q~Jlr^f-|D!h4Y=D%8yipq{+e*}#g%k#8sK#yB39;Jq01@9Z zPnf@EvtcC8Ra*(gR=2a((x}=_l-|I4WHRDbe67>kfGJEtbVM0KC%UFE|{g7&Z z(|(LVI8aZ$(a*iyDxmHbqQ*y5#JUGu5E|cM(?-*|ui;D>IfaPO|HSLRnLZ^%-IC*;FR7U5AMd{qi0!_eWdeWx_}Swz z3vsKXXQMaEYLd>*sqC@U@zs|%UfOu&x%kHKZl4ZKKYzXF&ujm*_mw9mvuB*0cP!?D z+?W;l3p{f!?+ustqlzVCS@V@OL{`UlB`a6V6|9&kSaI88s;s$HSUgcRUNw2_Cxy#m zZB*0gv!@fTn)tfQn=Wm-()Qg4=Sxa2WZ$-#iW*U*vtZ6qb;D70mm+^bMWS41^{2PX zQUCep&pr=QBFBBxSp?c4$Nf(q+f8|o2_S9@TRy$*#QU?*Q4GOLLPwtPazXnl(`%Io zeXq2`YQLVHOYb5py-QjU{efBN$jSP^vXR0MuE6_8%Y+U`){h$P6kg@(5G+61ZbtaW zCIR6e3po^aP~n};woDvi87h`&S~9VTAjQ$^75od)o_pE!vDs;RQn*u9Xlr30h<*Qc zunK7e<)z71XhCD!-c2IL2^q)^A*_P%Fd)l8q%mq7QAIdJw4Rp?++;}ztH|5WG6ud=bQXT0)m<}2CNc)V-(U_I@ zDGzwCv>atPloGYS_b%fuEtNsaZ`8j$l&xYpT6;z8y(%z2R*7X+i0HLFt5Z-1)|li_cCyn=DvyMY>V2`gX3Vbjd_;Joskc<$+5B(}&(W z{LbNI$@bXpTaKdhThDHtv|hAN*^`di_<J&?ba)e1PUiLC zoBGI25$+I$Pv&2QNdY7d{SBwrNixk9`{B3k(`6z z$F)-MKht+2ove$r$+GX1Y!hHk&NG>a!g=ci7*BQw4RN8YL66)vN7%fs{@VZ?N zbplH$+zpn;E)kft(W6>WP>JQ6|5WYoqCrirF-7QNTpdnGf+N+D1rB;{YCG*SX zN}Fd&o3E^&UUzkC(zPjJ-^9mBU6)8orL#HkA^-|2O$&rkt11~N&kq_`kF?Siyuc4= zpfE+Gocb<=s1g;huoE#)ZZB8xn8Wu_Azqk3(&jjqGhf{tvwmo=SUj0lBx8*vJOIOu zH-A9ZQ$6g_lpbD!L{c>Mn?c^I821UHPBI<OY)CU-kmolc` zqjGG7+>WFTX37>E4)`T`xw7y_NE+IJ*BR5_I*JyGmc~yei`FEZYd*3U{j8vPuApwF zpf0}l^43dRr#&X9v^&q|oXt7!I_m@jC5Gh0=C(xIZS)S)DPu2?Hq}k!jy(R3e*pI za8{^iiSm`s3T2}CnJ|hN=n8_nL5G3#FkTpKg5*q?CB2W34DJEC2;Cck3MEUBhVA75 zM$wl(QTlUa`A7VdSHMTs{GWm4C%(}>>+q6J9T*am9Z6Sh9NL2oG3&g;b>`8<+u&cJ z*V3P&mFO*lq$Il2dV7pie1SKxl|tXsI!n44478%Rv_1r29O=FFzj1KZQ9EDYCc0ws z$z(x&yltjn8ICYV!I^!$x5%hIgr4CM?zCX0HEfp@ahU=%*z%&qZ&j77ZT>7Ho*VGp zr-9iDz<6_vbZErvlN-@Zmv|Vst^lRoBfxg8P}?Qt-8`{NbRafgbmEuG@9HY>7ea|w z!;_2fJ74$Pjo(o0RRB+y2fv}>tNt!#s=g&+5y~qSza+X*dKqf*;BUEDjMxgX1n)|* z6z?jr4DV{O9PcG!1>QAcCEi|AJMXF$tMRTApAnZJcfDAHUk#!c@1@W>vig^aHqq8= z6KnCcQLICJlUR>;GgC8PE-poQg}4mw7N%allBt)sicl@@S|vU!E=Rrx#4kg&T2-kYI9zeQYc zJU>KSEBmkOLGh@#0cC6zd&G^XXPYRBoA7tL=odF5{vmM-%6k~&^&oz=iCgh&2l~Ga zzuNJ4JN|Zvz2ZaoyHk7^Uw4UZc<=V_5s!(-#T_r()S1|exIVF6MU;^T;WN(_ompya3hhtbyqFAHjWjv(fsuJkVaKBW8oCHx)`pVZ}e#(y}+ zBo2yC>EfOpu{J-K%3%uJkAtxgdG@iRC;UBOZxHNNXz+_(93_6K7pw+vIOq+AT8aJO z^#w$4FbpY)mzxO?E*2UIK|d-N_`er=o=`WUyilovswG(sun#?KkIFBZ$Pn(((VxC! zN6gKZ&#Ki{wF0e%q-1|zi0N%I%sR4JV)99EFyI{=fr$bVG&OtsdcD*xzqc>s^?3*6 z9E2}+cT+37yBlGwV9hx9QrX?zVz39Y+mmPuKSh=zlyV!?Ed3?Ez}AJ+qAvru@8&s( zg6!PY1NfspDh!6r3i=oo&NOS{Mjw2v0q)Mxgnqp(}>ejjB=%g>bV} zqpO34KI1t-42FzH$2t?jDsxPWO>YmBeN<`+(g7#XDZ+o)BdW5Gmjp!-r`h8Bj~Jf4 zFaqfF^@Gr7>x+x-F^PPM(dxv40>KPnU285|PPEooe2*{SJ?iJ{uJ06Es#pcyo*_xX zD)s`;#hMOEBYYK7w!OVxwvw?-I)ec$7o_s{`ap<>Xrb$K4+o`_eSu?lDa8+lTtK9Q zo0i-mX~^H=4fpkn{*~-Y2y34{0H310;1{(ffLugNkNd*B4@%##Zu^IQBOz~(Pm)G3 z6kgcH1pA?8$Xc9{-`5|a)}oL>UkJj?V;CcK%vcYDd_xcS_4j*2*eCrX7$?$-P=}B@ z>p$(ot`8AyrOVaog}H|};2*}$!l>ZTM%no3?FkO*2D3BLK{F5xploUe8`74{Zb7>; z5Dct@l}LER3yu~V%f?#W=FJX`WRrdu%O#bC8heHzOGR>ORw}EP84*c)=xgp%r2ojE z0YkM@x!Pn%SxBJ}di4lPjQLbih(MM%!z48maZDJ zE>zaV*S)@ta(()V%jAI~m=$?TytmCl@uoW#OKH( zM~+1zU5Orb$Kwa#rRa-rXR#uL0wYGPI{a4Mb&z#hq%M#|GQNaz)Q5C1G#M&nlO0U* znPr3pi2&BOSBb-Z9a?s?dP=&C)CHEMz&0SX07gU3aJ*3w#Vr$Nd z{HONgzQ8eX7l_#sI^H*^fP!NHDgiIs{;((wgQr9cwsD5tk4EgupNNg=eXU$+&hz(vF`Ai8je zRy$Ml!s^a^^s;k}mVu;})Ufa;hCk9NymahYu6j|cZJ(aw7`Mgh9vf&kLvNK&w_kIA=-kGJ zC2ay}6R@rRWd!t`qJKX6fNgI_AsLTC@PfH1M7%Pujj5Is+T` zC^us&?4%4s-=tZTyohu2*ND_ZSta#@G}NRZcgBPv@s!WGm(IACzIpWW$xA1v*S)v( zovlgt!wKiZd_;)0Rvs1jNr?H)MnnTD8Zx7cQ-Os-Ho{~93UZNUkCP-0huBEJ-+SCA ziEL~LTKHgMKzK26DO*Vnl|EQ(L-AEBD?Vli`$bBeMjqP-PSSvegZhC};vS}f^P=l# zOh0oLsw-)DYWQu-mECVS-|e~9@cq6Im65~RQ6D-K*;@Ju$^bAhOPhZabW}G|DvF6U zX24+AjUTE17^%{zB``%G6=77{PVzLgnw}FRL&$hE;}R$c-7keLYBNN0O-3ZYQY%ve z@?=ztpC@RFGag8PvLZNQqt;$#iK&*6{hN$;H2eOe+iZ#KZpR)2x|p$bA+i7pO~@WU z&iEE@8VrKOBg8Rd^)|Nx9GF|Nh@BaW80gr@0Ne1SGskv)Dyt_rFc1u+a-c-m4-^oj z)Mk8yE)SW%NZ+Ec^#rPFwsY~(0gACJs4eVcV9uDz0p-X|Zb-rnBpO0eA&|=A7R4a~ zUJQaOKpY~{lX6?ISBf=Yu8Z!V?Q#h@&zSz&Rl1;vM&4X=dCR3OZ-=JWzZJQ*?uT1{ zu=T?NsK-@KJU{+?e0{RCdCYpN%6qYMs`JYFWYy}qs*N*M8?RZDRoljL<|~$5Tt2lN zrZ5%D$Fk==l@t5N_s1J9H(hEm(GLq`T}VBoBP5OO*GCYz+u^ODc}7wKZPzGkP?Hz@ca+MGQ|Uv5z%g#X zAIO>rf~CPDB9Hq{k%hK4#B$UVG`tna2TnCKm|)cWM$yIcsd9{Ad2`aWJYiqH_)H_1 z#QL=yfxGofRQJ0ke2bLqkgc#mXh>jFa5Aoy>p{@aK|?f`1g1wv2>Duc8nqya9;ni? zBk=nab%W4s;QXY$#k2|{X#bVfGn!&S+QIzmccQq1J3z?j1D$Ie75WDXWr{%Pvwi|V z)l$p9WYqEz*h*Xhceo{C=PFZQP|GVH9|CWM22GX25kMi@tTp8v#7AX7@6nN8Qb$U( zm88%16{Fs~kaV?Pd2U8$9*gNf)|V{2n` zD>npS9Dc*eSwnrw)A|HMc)w9shF~%r_vtOk^<7cV=Rp*A|9!EZEc}IgNwRt%xTgOU z98Z9#qTaL`c>FMwG|2=L7>${ml7i*7_5e`1bneSh=CFZ?b#Jc}8~}OZQ%8nRyIDF) zwiZ^%i~=bueFxj0K@y=i4T?DETldkvfKM9H18ZG0c{aFHht8id-QqSLW8rh%abe!= znbrt1ASyuDziTBHkyJ22xf=rXOM=uQ^EwPe2iR#N!d0- zV2x7~<4W#UB5rL3s*yp;(401=q4?q0k=eY~>Dsh`T{V~2dLys(Gj`CvR?uqsL)*r? z4DG!ZsCMLhu3;5mO|Gg+84jx;535-smEH6)A*yKcm`GUN@H~({8iaaDaBgIhvhYe< zGFFtr4x|MHNJ5C^AcX3GZXspnqp1Rt#k0}&)3}@mNw>s+*P<)K3R6t05syV#ddB+h z%2z&O_e-PkS|g*2mnLbL(OC+l&L83{Q-Ox9#FOY@%*|b3HWGL9xIb6H!oUlpJU}hM zU1S{C0l3x?wIX28Oa=R4j><}JmFyVNl#mP3h~_9|3k~*z#uq5e7BXuC@W%dN$d5z} zkeGWUW-XS=W`2c$P_kV>D}cwgLl zAsE{|@2+54hMuH*S;D#OR)J?CXFP|r>tbzWu314uJ}|=zqqhnQC$h)0znSytn7{*^ zUH1c5Te<0ac~RSnyVzF}E8_kjeH7e$0BlP?R-lfnParMhfeNkxF^|0`gESlr#74|` zGFHuf(x|J(8d={2>vXW3NU+l^bCA6R-`73ycGKxcCn zpX4l_R--AP3<*z650W=!nRkBo+1P~Vnqx}IIsR&Ftd z4umjc4h`*FG`#?+%CBDs z^!ppM4vrux)`6ptYZuQtsuzk%Nq2ejRHE_eWYOV-^YCr6HK$@8OXbnCk4|QN=&XTV zwrnh6<{xMc_0Br0uXb> zEfeCm`PO2$$}G1qWJj|6l4001Fspip`k7@aILc&}27w9D@BP7}D;qH(WRlRR6NZ7L z?-OY<=mT+&B}jEZ%Wc`B3Csv!E@x2Fs}@S;o^a3wghPewl;WX`WWE3dH%J7gms)mO zhA)Nu&pnGwN@FR5nvx___@17jmgmUK07+3;Nv~$utK#UqdvG!5gVYR5j@IT%=*pwN z5cAO(1=v8*=&_G#8Cb|*)#HFdGG&}t<%Hl8Z{O!_T;9kec2Kd$>4mhk=yhkFoJ}|E zI3a0Tl=8%&KNM&TLrBINXxKg_vA$tUe#S=v?Knn#3$ucc%0#Lra%dKq&bl<7u{W6B zzn(~xT^PMVyQ75dj@xyng1U1flcl6U)R1&FChU!id8{hu!Hfl}=dnh0mbRK8IRXHt z96X_XO$9e9zi?#|-d?Rq9WK^@3?tb~3?U8O7O!*W7X83p?kUDHATKi1pgGBEN zTSndx$JGVScah9{Y(#q6v;bZ*jPXXQx3FEWmd|(SaT&>wE>Jg#LnF}`c1px^N|~U- zc$(rEC-Q%%&>ngbzmYR0EznDWMk5qbNdC?=?>Wn>9!a=AN%)8KGk~@DIPbQjoC8~BOi}oa(dlrhzCQC0o6U)8nD4tv%KL8V#j_>Wcy5~m4x_M{G zWW&X#siqlcUF;FCr(h_<>~R(x`Rt(Ec*9Zm=CQYrPB&g#mt6Mn`yDry?OAXYo*zCt zJm;#Jfkk0<($zHQT07%f`|ghScE7WGZe7RBx{mj|lItFybv*&gv^PTWEw8_rsCgh! zx;g>V9q_=4Ag{_Vp3AG5$*YOy&gQKElKjTn$;j&uB`RAI?v)AW%6V7eV&)!!4uAti z;s_gDLxZ*`(S7DV;p+lv)khxK83++?Nm(~uVip7VGNv_o9i)}!(x<$#f zWJqcOBp9%f^m5c(j4Gl<-EBVF+S%B9_kjnB661F2T zk-%Hnoj3!Ecy6rB!j0*Ul6u$Xe_da7sm>7xG= zZ9P)s2Wjps%BhA7VJ!SgRMx6V6nM!h5KfUM!%25TtmCG;bfL6ja@~ay`czhRF?T9A zzAjnT6x&Pm{E6`saZj>jS#0-BSJ}L$dg8J1$KoBA_gvbO^sJl~W<0CMgKJJIJ1bkremH;#Zp zktxnEh%dEBB0wV^DbPVQ=1ln(hEzIA=^OD%s~Xkvs+S>fx2n^XL8RZNk_@ZQ#TWgl z#|%pTh9;C$2)R569*8D=F$hw;3A^_TX%LKM6+Z&Zy^dki2)OA-hTDw%pNre}9JQ@B zVXs|`0;_}d3JRmaA_Vi8FlqtC4(|rwzo59xN0~RIMGA&63V|YHE};gLq2aU~{Kf*L zqti3vQOX3Ev@j)&FR8#w3I@X~nHv&t*EkOkj$8mtda!GOKw)b$LL9ylG>Fna#BnwT zS~zvC!H{MPJQ(*eZ9C}!bdc~MP=_inP9nVx@Zq>>rL>lQSt0Qahp6Sc&2J-2Fr;4} zEP@H{%K;h;zw+XILGirPb^ejF zkBkk^I%{q_Os)b_wJW7qkn!jC!zv<{yLbwzNo=c~L4dP$bu_ujh;^$BHclj302z<+ zRiSmDBv+uhHzj8yj84JSlM@V_*OpQ9xgt2`Fk|A(kzRS?2+83BbB-^EK&dOZ4t?V~ zBs4$jtYI>_qN+~88ICMF>;%@K==p%v5j7MXRYP=QF`+8f&qu7XVmPeeFR@r)2 z)nRe0HnMSU^!h}N%vV`2&`l#@G4_K6nKeUu=?-Gk@JmR?Q9`D4oJCpD@{ku9z?I_u zJ>s6r)t9Q@bx%L}{fe2|2V1^JFzjAK8iCyLk*vW6T;U*dOu^ABP)Nzow!IwM~-Rf%*U@ z)WbMJMorUd=1!v6*aW;gPxtr-$pFI3)M9vDD2pc&Q5n8qps14oy9C6Wn$>G6RF=Jk2?ZmhtX^GQU{ zgXu(~OJ1ki61hgyJSh9UYV~$-h50RBCK%ENfV^6zSIA;ypFNDC11pe;gp~7X#M_UV z?N0+?%*zY+5%X1Sfkl3@{tF`Y#|u*ywbOPbuGf1!gA|(vs#2S0}k~B`FAK25Xvk)rrU)&?{CtY zdo%qtszehGG9_vmwlNy$p0pjr07NGlq)|>KN-+X}-zhyuW3z=;6Gpg+hJTy79r;pL zWI2rRpoMTU#7$}Fo`$v34io^|nyxw^$qwcQ=g9MjOxcHhQUJ>}l(JGNRn#HNg|Nt< z=AWeBqTJk10Qpf1v4{|zrz&lb(@m|S7V1TIYGf77<1S=4W4h^ZF{{_fu31;pLP6O? z-gw@EXW4w&A-G#AE&;2;Tl+t9Y*3zuPb1HSecV3j`DXqt;PVK6Gxv5eRQNe~#Zjg{ z6h?!DJH$s~N00hR3&_|vj)GZ4X5!!EMUvYi0QTz`3tBAhci}!oI5G^o&3ilJBv601 zw!mIhUMgrw0TzM`__fw*u0!=+5VYe_QT(#<-b^4TeA@17ADLM$NEzQs>ezt%?MI`KunP(lX%e_t-vEE->(WrGVR>C}S4`wNpco%~4O- z0~Q2?myn{V)=l_IgwkEU!l&XcNJfcRXG(aSeMwUz9Y7bEUt*af$uKxLN?nbAYtk8z zsjTj9|8tlj9Wlsb*3kS!Q5^h(R9;I?wFZeo|P{Yx+luV%O}?-3u|LLm}`T1 zM3s$~U8sodAh~z-d~qp}!WX*6%#0^Ec7VJ`#3cUr(@zRb1?A*rto)|CjELd#UqWx5 zymSkB)i+CtKF$MuT#p7!6t`8Iu3HM)T1?lgbK06M*P8@-x8$^KvRvON;GHH-qs7h^ z2eFrEQ*i#86IiuWizsfYw+I=-NCaTzmp&2{^DvDBiSV#eJ!O+S$M+{(bqTxdM}}%s z9(7qBM$G45mY;V?ofO1QHGjU%Tog`{&i>3Q|VH;XQp zUn+;TYAsY2jc=0qm+o1I+B50Pw0e!YbZk+VC}tPBBz=YQFs6lmI@BW=hJ^Q@4sGYB zIVgS$dFe4`tkOj}GW%8-5j3@S9g|bt!@L}NYie~N0ry6za&;pi)4fYN0U|Q<0ohZO z83)mhpopVD-dH1BWb%gW4hiK_C>qJ%3nN(K0x$%g+4Yh6tJfFuHWC}JvGq{OPSF&* zt2tM<4hjA-^Er0jGs`+nSwj+Oj@W6KIl1hU{)|wH-;3D{k)9R|77B`(V$ITIfj4G@ z;C<}LbI}FhXge=-#&*Jy!Pxp&wt?k`1OGMV&>4aU7X%MwNXjoudb93wgu6 zjIr=-(g1LHsR|`4hyd|9q+Y5DmosEK^rM9OBZDi`9HKq=D)NDilM96S{Fbv@CgDIN zwq@4Q0IRmYbywh2oOopXk;$PewPTOWdRjgzs8$_G&3h`Cjl(+fCv|P~?=9xSA_z4L zOSqpY7*mkP(nH^S=IS#a7Hs)jVyAl;JAM6ZLG6OhN@-+j{v-(j%u#ytnJ6l<{blDCNJe#3QT~%UE zR}jJRuhmFmrPO6?J*0ePd_Sw94@p&a1%~fVy-F9*MyR_%B+8IW$t+rd!mD6ML*Ykp z>Cd^ZDGFLBfkXi8(Kc|e8KprNQg@A#jp<=zeOtYNM>D>MTVF$4D6Kb2ZP=DA@^N$A ze_HH2Mq(?l35XEjwnU^yP-*3AfAHdW+_xHz?~eN-6&*p+i#ZtpUkT=$#MJ?l zaga>gBQ{nJ(-&-ZFu&W!ac^MC!5uI#BX6R&xT6PH7ZGj9Z|(RAg#_9SOrbPKZ5&f6 zOl1uXicngVtwd9HYHk=e^`vrojt>P+l4C(QVhoDXK`H{sJ7ii%hk8S_+CZ(+BTKX7 zwo(ClX_hXUXpvA78L`W(IXU}D3o&oSM+Hl<=RC__+Xz;KJ?U;vx>w*z9oT^u>h%?~ zb4uom;D)$#qIbM^@(383mJY*d7>}$IFr>%fDf{osDn5{3O>@So5YM;c6kgeZK(X z)ZT?=Z91b>Do-hk)L=BH{ltJrD)rpbQfX#J&tSvREoGy&(X7#Ia`KA1tV%?7r&S0Z za@gzmN-#%=zm4svS~Fska$|auM-fYv@hHJe zB^I)FG?M_OH`oux2A%3B{UdPT0ew2@7-aSVxc6YijCfD^q`n{|VT}LGL{sTnlD;jg zLP2O?()U&2;40rL2Gw9?p&|xe@9EPVLKwb92?|kZiBG3L`dm2UN#RqMT>q7amE8D6 zQuxo6UM@v%V|C9uSb9{1zxu958d+~zMZqy7=@AcV9XBwFb3A;O#z!P7tk#XU}O7L=Z{u`aSk!H!jFfu!~& zWga}re0j@bs%U?ag{j{53++2Y2SF|3q5D;0auuHsoDEDKy7=X(FDLU`VwRgQKY$Hs z%T!CO{oJE;8^a^>8y|e{#dltucdiFlb5rh&eO=5J8=lOGZ=H4n@IwWxXvR@F@2ZX$ z;nLLj(6sr|^8oFM`pwtsu9YNeA5NCEB?{VZfqzpyUOoB3m1weXD=~4h@*3t#$|j4) z`^GH4u$v&f3tfC+>V+#kvo&kTp|WMFdAxnT8uz$O9ggjYMb38Ka(E`qXCI2MiTC~9 zw)rZshGHYH?7!vAKfnL%{>kR})3eb0vge-9JDWG=N3RY~9iFu>TPRtQsCgJi$z09D zGxYzi_t)T+EZH5i;Eo9r(0=+!rO8u8yrJTo1s>uJ6@L~YAai}Qt$n-cN858caxFj3 z5%5l91rd_N(8A^Hyg0^HJrSAt{2J$nMMEpeWv@{Q8pB?Jesl)~M8AIlm<&^L8EGpv z<}TvaM+WWR!fK!gs9!bm@H-$|?~=#xhKv(9GczBQj_cQPW29WV@)xx{j&E7)oXJ9c z*-RIq=q~lCrw#J;LtV@8L{Q46b4yl+wMDbUoY9<7dmtAfA(~x5H*02#LWPN9QE%=& z>$NLoqTVY=?YL*^99`-k>U>H$qc+?Hs-o~O39`UFFH&<5v?0lU2{j!iH%W~IhZ<## zs#aX^+#ovQLHwLw038cZWpJk`I|pXhOe70hVz!^T;9hdFO&^W-OgV2M zaA4YZW#6@?_gBBa=2}akr(J0oE4%mcNNh=Jfypr!aw#Iye_J`a?97ao7ZR!!Bl`KIA7rC=XVfq^I zfZQ-#KYEhjTq6KzmJwN^Su%h}VGan;Zvv1fbawcICG zp6YCnPX)a}kg4oiq{)ifi8zW{8Hoh)XViXf58Q>7C^-x*9}KTCR%^})S1Y-7F3ECq z1|t0LV9MX1nD^}?!vOUIMn@!%@3szHM^`FLS@T3CyG;*47!LPakO(&!m4H$w~_c= z^pgu7Y0IYI%ZVxbi4sxDbhR*)eZ%asLs;sUrJMyEo0-;vXYN9=~dLi&oPE~ zucN}qi67hftfp+7%JeIh3llY5zt|15UwQ%A439VhC3=fLf>M7;VBzoafw459UBxbJ zy18;S{NP_b6z_R$4XOA}9{lF^D~Dp)iJ~>?Jtj9D`SZ2)vE2)$u#EUgY15bmSFcXG zU)wUjq;_og{F0?(yKj2R=}wdU@%+i>;`JXEEQ8WvS@Z3DQ&s(3<%*fg6<6A4D<2p? zFx?Z&{RnD{?ej&&6V2nz{5rOux=L=9lr!^U%Z0$0Wgb6Hj-SL;$)RM)3i@3g&z&h* ze&ygy$pcVxEU!BI$Zfa4F|h!|dUBZ5Ar&W&lAI+a#y%t#|ni^hgDJbVnz3UKX&1#%ZFZ^l8r z4^sp1r^(RK8zUJ?x?C)*Y)3E&pw|xFN+RBw>Jbx&@ly?)0KF^Gy|7c~Mkv^PTvE^j z#Te#zGtG}Ev}tJ<2K;{LscDP{E))0~Iwa*_z;QIvfhH}d7tR9izAk0%3vzBHZ3E?G zB2PCd za|7rF$lhSNF5`gsGhbf&Nsi5vf7@is%ZJz=u8~~u8B_aa!xiiIoL8OGV$$_s!u}xV z)1yN5!2;upj5_-pGw_U=cZQe~gY?V}C6krJbONBGlmV1zkQt;?e1Q^PO*hQJMQf%) zQZKZa39zIoBq@>q9}V~#WQNSWpo9U2FwxP;f>!t>hb4k@${7z|K7Hx*OxddGhMBSr zG26L3+_w>vuT8`xs*gH;q+zPpFu>y0kF%UXIhPa#9y52i&-&JiG$ATmCI)D4=*2&M zA?OyKG^0lO=9LA)_35U&q^mw*ujiAiZyfDNpb@wk7O{XJLa10%ZMa_pH8`OOE#ync z9+o+(Rm8g_x>TdQ?_FvHeIm6F&WV*MzlG&jxw)S&KY9W#t|;^g5Uyy5l8TNSAx2s+ zfH>QHe~Te$r`rj=+~}CmmF((e61Fp4I{EShzzCg%@Y8U;794_=kHUp)f!vXt@N%t8 z024yR8QQ=va&F-|;KR6rm#bIm3gi4Vex1x>+inKvws}{3z4Z(qV31G676|E(Q#>H2 zAhlar&e}wNOX)YT0BC5EIfN?C!Cd-Y}GcUsZ3*3uGCL|O{oJtYnQrKye{2@WyR zoHGkYyaRB^+&9SV%`xcEG6r{#?h*nEG1MXtzK`zQN$1^qIqJiPsEN9}ZFLK|A2|sD zG4&cwrKzE5uJ6*viNkWzvC%GZ20$lbj??%{u@ZDQU{ukEC-IV%=onj3uO-fezj}35 z);JsOO9G+*D7&6zE`#al7K1q{Cs8gqx3jKe!A27qs|aePPbkIOA%(VoqvyjuFf9hrX1i1WOPB4HfQm05}muII}BiKGUH=1tXxP z>BJdf4A`+ogs}|#phuzGpN8kXN&*0LAgwO;z&Kr%Cn&r+=?T=Ok{wFQyIk_8N-9Dt zkjsEj(%=Dz89LP215(DQH!@U}ps0Tgg!eJPurL8Go=2?<&`3^+RvCc*-)tm7GQ#IG zNVHWT(e8VXEeI-s-J2_D1SI@B*FZ?DnwKj*Afih-n!vwX4WiL>LM5DZscf)xlKTa0 z(gsK>M8tgnE;{Ce?AQmp8n+yV`i{btI5sY6?t_jZ;K)%n#ChquJQb#p-K3g>REqT& z8K@i;@!}dz{|3$C8#qp(N!rDyDYOkQT*(Fyj5-74v?7#pvda%bV2=Ra@V!4mX~`~1 z+K-nkROWX({}z>CBcV)4nxGVnqeu8ghn*b01yn28^G z%E2In(n#lcCu}f1O;F$hm;d>xp7_#lpHNtX>pv{0B_p5;7y;$vozFj;KNgzon6=mH zOtvss>O<>$TV2gE{!h|<#e8B2&OD_45u(P~3CIOXfY(P* zD!=L|6PfxBzUVP;DsBtSkp~~RNe`4SUwxii*(dBZi?OLMra#N&{*a*^7hv|uJZHMb zQ0XDz53NdvzDvEz><~rj4mxv?9%g7OFe5`&vTaw9`#nTm}P!_SB|*BDYHhcPmd zS9gaI^GrA91dH!7FRDBU+=FaGmnJqNwwhA2(pQ`c!*{JimDgZg2|%(ZYKvxTV;*R@ zYi@1j6rqLp?`ir9uo=xY%DK-9z)AR5x;2p2)^mSn#X5kJ)IvL)SaDpQe4YU1_rpeC zJ+Q!KAie|P=A6gCY~RT7l^2(U;KmxJ+Q7UHFnT~&20uaAi zt0#CNfA{tD_;FbWkD+t4x8DaQ;ytV8c=0BzWr@&Ml}ApK2XJ6=6PmaTfzJaNK+mR- zlv$Uq%K6_9K;=c{}w|yI%lOi$kc9$&aDZuQ}nr@M5B183V*j6I1#K#dJ33 zaqUZDBr@S%%0b036)?Y;veOTm7KR(}pEShMf1&i7>80^EgGz5R1;b z>gLO9E>=!e#t&V7_R_P-^0l~R9x}EK-`Ebm&)D__Cy@I|_%K@vSqMy~a9?xPR8{=c zzUJU#w&ymsvA*SZ%xHE|)n4gx_Xx)9M{CcNU&@keo~!lSdE zW#sn3Q#JX-#UoQkIB#m!vpyw9#;qTEmj0sJRMN~(Up-Eso>i}4PV|fxBRpG&+v}7s zji{F}O5=|a-j6fD0FIz^+8}rshEjQe>F;oT$&8JjW)P$yu;o}69CxioD2=$JPkOEo zcdK`dR?|w<2*rEZ)MrDour8LR*QRg2)XZtgYa5cT9SQpmK8kW%S%b6i#&6-y|BArf zH?{)xM=i1kOL#Wb_(4YFXiPT|XBZTNeC`wf*+}z2IcsqGTA;*wpERmYnKj~loH?sh zoL4fhg=_-)gB+(ynTL;Z`b>P(gAQPm(M=)ldiMY)?$B~ zR>bbly&`l@)YF#*xlx}XH|h?Q^+!c*g_i4uwzi7&g+Zqq>J#p~i$;Gj3j^{IGjzc# zwKEs|P!(J&(;Vv3C}98r)J_j*XBsZ{AeV~2sJR|W`@XnTr;cN*<7@`%LB$&IQ8k{e zk?o^$wkxe8I^T%W)jTMTaxdxzbf6yaVN1|~k?|ZML*^p%To_*Ef%M+=4~SgHsQ_8JcU{Hq*E* z+4xYh^5L-#5`n826xp|z%xnA72xB5q=?C?N?KaboY(?#*=_r%BKgpi%ZaA;9a_Ab4 zsOe-Ah6z5xU}<8=xk=-R-N%CiuwFy=RB2_nVGm+50`f|`s1&pY|MaF}MyMIAFG@dG zox)4-Za^if`#`z*{A(Capk-_|#%Lirx;bJ1j4UJ(zQs^z`2OB`c<;eOvY;$Q9tcVK ziB2H}Is0E|UFoiK(iQX_T3zRm7BoLhpsL2JE-Z;<%{vOm4!yFStNF?9zuRJ4XB|uC zo$_USvraGUi@Dt+5!6+r8ec(VEZqLR=7jSQ@=+BF#~h!2;=p}$6I;i((k-18Ad5ka zZW-SKf_2NRyJ6l_4w~7s6b_wYj@u5Tkd@f;3fgK-*K3#Ua9BRb74S~uj?jW)TBccS zLERGv2Ou|M{gExC0+8VX<>u9(9q+R-X7ayX)C(u87 zjKIFW29Uv7k#~10ZePLAqPuQ^+^>|eeMXeY@?xFWBPh_RxFwfcH?vDBMj$&@TYacfj_17qQS!dXq=ra?=*$H=cIM|RbG+~AiMtd1Q2qfQ+v`I_IpPan^$^l+LDsvzDy!tg-L~Su=bV8s2VCOtz?((L>bA zABMHiYMy|zA*Oc2Y;>+F7aG#N+`tFZHZ<|Y^u#am)~-`$=s>|v-XdRo`8n&7KESZu zw}Kz*D%kqaV&)KG$E-7-vCcrhc9SNEjhcQFI@p*6r0iYg??Kojr`4$9>qfNoUCP8r z(4&z1HxR4R7+<&P*xnyn9y)NNg2S=@5 zGH!)dT;YPNjDlrgA(ByR*=I}*>Q9>5mYS|FEo$3hxxQJzI}KE;3qwMa^jyP#okaoZ zIJkpMJBV<;P%B*Q>ShFItkHygi6)6AWRcL1zUZ+uR@@C5@&8pSvJQJ78BY@3=2cSpz%UfJ9QxN_`tM{8*bKe|ZphvNMa;G=VU;#8;Fi zWL8Zd{KGJTFT3A@{3)s@S`%Zpaesc;`O3LSa`Hx4%rYS}&PSM6GNXO&{FO{y2bZ8-JL!I?W%A9QPAi`mZ1hPEn`HSs2@<>d4~y;p3N%Pg~z}zT>>+Pr5r2 z&JH3ao`lC}S&cR=iS+*ZYbTTLM-t9Q*flZ`&=9n_Tk`M^BR`1zarDohOl&`xbRW9m zJVY?*Nr5Af-+!R3!F0W$t!T$)%LkhTywjkP6)i@~g*%*;#BhIgDdL;_D9Z*~MM%vb zBi{S3qqD6XGC&Za&xGxwp4UcFdK@({IT4|438xjiReHf3+1$?WH3vo}n;wCmNM4DU z0;R-3AMQo=Dvn3hgmjA#>BsQ(@hF9+>BX79J;#xd!9WEWv7i+F!jS$mMobS2S(d+{ z1+fq1U_pTGUP^`nt>dl9qPkc+?xxPk*K>X$a(m_y=lU{})@r??p3OPcusN40rJgP;k#V#jFe8GEaad3O%ht(3$s04Soi^ zS%IVRIZ_U*^c;5y8~GCzHYi6CbCF8izX4yscg!!fz@U#eTU7o^p_YO{rtnveW<%5KLPFg#V&u;SNVNjHmzJ+sudofO708IpcXtUM$LACWyqHpB&X-cCw5TlIK;q zUkPre)9!$W^u@+^A#FS6AdC!$eC&!8V-#c-HS0mKlKL|m^#+u}m8o7h`@$Oxp_^OM zIY)ldoc(b5^oBI#s*Q%Y!-9y4e{ z3SK(l`4_uJKg>#;8E1!l{nLCQkZUqlfL&z#xrD1ZVVC9RC{=wZ%m%gs0XDGeCoRAR zPt~-8aTtUE)CNCXQvhM5lV3JYdz=*0jN1)=4zLSDv(v7YW>Ge3HIXuWap=9L(f}%t zimkVz)oIwe&SR=_a)n$PADhrWKE%p~cnNN`rJa}zl@L=ZA#XC8wis`hxB7{5)h!~M>Mo{8CO~MI~WYDlqEtvEIUjh)U3T&D?N$|6RAD{OQh?C zaps5MH>H0>OB=DVx+8Ab13>=~{@6el$#u)#)^fQP@B5f9?n0F*?xbUp(lOdNr0C0l zRGRHogkp#B%HYFL7X8je48d*$B^aP4xoI-ZZ2EQNYif8P^PfA*Gr4837`<)jQy{>J^ z($#aNn`TNk&6aK(;FKLw5@t3)Z3p_qpimuVm7!1k(|P<<6ddF` z%a}@HJ+erLXa&%K5&&f?EUA21EP@;EXh}xOG*!v;FAcO9{K!kr)QcAi!0sW>-1DyT z$-_5X#0-9;{op(rBPeX2eKQs;2hUywJqAE2%;B;Ec>*gC^Ksf>VRJk-dD?gxv zRaDpr{ur@dAOVM{hCf<;3JyUkHeYo@yrS9~DQPjng<+_`@`vmdUx%_y)bzUWZM~%Z z-t94xVXF#0Q0p+P*GR6dx6X-LVLyp$+oHmlaON8rDI!ra9tCofZu(cl(7dk9NPwSe z8nPrZu=NpbZ>zfFC+L2rj7QU6KT(_U2|rOLVZ<4aazZe_L(I#aXlKx(02c~u28<|wGhGt;@NY8 z>@zZ+s6bQ0ED3;PWe{)$CP&7--|T+z*-IeCgC(3|lrZ9L)h2>=}g%2Hx3rr4ith{Q=ghM>O|)M;%eeiFW0S_E%d! zusbw>O;KyNx}&4k`?Z|BL!y5>mGX^B6pIyj2W2v6c(;-^>O8Sm32T3qJ%=^;a_;49N&gADtKTp?5t1{A%c$mDyj}G92x}K17=L9m8VIk zr?`w*#s*Z%3Jv5!Zn`UuvE_&z&pVA~s4bu#chBI>eQIfX@qd!8 ztlP9g`y*ToZ-p1j%py9m97+5TTb3H$54rkZNh=wXPZh|^lg1uH$?hTudm;CM z;u_=e2PsZd;~0Q94mfHFuSEJ&eLhS(1`EHzLTbf^gZeRDrAi|LU#{$;BDAD0p!NV< z1F)RDvR_za?UgsPHsPrpbt*a|MeROXkK~C@*=>ZsO#k(6>-!$}$)n zNYTJ)po-c{||VR@EVZ?N$Rdit45{mdZ=pWzyHEbwl*}E2@cz zW`Y^gzh%FWiP{KXRv}uY(kM0QV@eH%gDFIUR|QzJznr$RW9rzspl7Elajis8{Z~+O zAg!<)yEgjghyHv|qVCCL z$y14fr@&?+>C{}&>Y1X|(+$a@jqpAN-A7!6$@P2UFcE2DPGxTumEe9&N731b=yu$| zc!1rFix%PPoB4{`i>szqC8{h++FKX5c@2@u?{efO+wi`aM=qT-?G9S2u%{9UYH3FqqP|j|Ag33j#6ri^J zY%V@6VrBv%=DHAYmxRb6HPQ&X3#OuKe;W2YELu`58SYX>n_31Mn3@#kq(Z15b~z6( zj>2x$UikQX3L6BXohxjfq|ZfoY|PN0#DG9#&K|xKExSObYwD2h9P_ma6K?IjOxK=7 zKL>OmuF^W(l~L*eLkfDXOXJ|A0A#SjbWlc?K9#?Br| zGV&uT@pl9m*YU?l#mw7)$U}{Wc}Q2h5km#{NX!TXprd;yaIkR7Yij8Y8hU-QO%&j< zC!NzqIDG64(*UN5_7Yhq=mfok+KzL-GjfK)oBG0AdI$3ka%{u}Z{m~leqGOSO<8Oa z*RwkYx4hCb?yr5NXH2UblD@J>w{@SRyc1A7OjJK@bX#szk$~N}(Sp$9yYQUhz zN#rYw`pU+A6(_BOTI}0a$8()0JD)u;s3xZ=d(L$r5YD7KKtbOxEWK8+GFq^5ykPaX zdkt9SFjdHVX2lfvrNtMD5QUPguW>A-S|IrDk7%bH*-#F^{{)M5(uO!Vo>`OD2_Jh} zipjPLLGv#j;EO6YDu2jYVi0{PLBHyLI z^GpU9FQI~bJ;qaxX!kivZ9 zi3M>_R%l4iSYgb4(R13we(b`YN&BijS*M%|8L&I!GocH+vj_fk)dSk@{Lgrck=~NF zcP4qn2h_~-uEGcum-g#EqgM0XYE5|#eOY+PZ|tLUha7yQ8$*S~iqnu>JAqAtQ$&Xp zSUNZ_lB+{unWbeA^C8Pct2|=;20F(QhE$)mj_eSW8*qYRJ;^PG!0>)zx{y~~9tI+( zHfgw1EpUnfXa3AnuJ$OYj448Cuyk;;gDLiXnPr2!UU@wB`{h*fuku8X{6twsJNaQs z90UoUe2Ea>d%&g$rOF3E-_cGlod`+dGsQ2^*1GMRWgA9`{0IFj`Q}Tw+oK(yHurhj zlSG%2G!e`i`4E6I6sQhzU!?@SCI$q#_(g>Xz1-Q|E+(D$CZ&n2)s?J%dKdOqcnM75 z7!WnQDt9{($oP+~co$A5$!QfX zz9nrMT5vX@(J_9t2e-AhZtpV}wXx%M8_2C96vpty_*F3-B^n;v zzFm9LC_?nJsrrzP7&_481czOWM}ajiD=ZZF@s9UZzzk%VptE= z)z>$)l^J8|-3P~`dY9y8mzt4HXd9VX@KLc5%xcVa+duBFi`wdN zRGJ+u@<;xJIqM_0Kf>S0E{trP@@5UUzoehFbM6mhuFbJ2zT%Z<%?(c{B&@bJ#8U+4qp3h;u0#;E9t2HI`JSW&uR@pax)Bzy5akxw*0}m8JN{MBQ?(+8MKgDoejnqP^Ki>IymL> zuwW2hj=Cx??s|3qEBi0|qtzRtt_?%{?I~vfQ*8;xG6O^y2k*?NIYjst@bDexhWD$^ zEbiNex_tF_OX@OISFAjeS27HBLE(y*r2{;rgKEmpqnvs3P9w?YokSrrt(mGa4b|E- zXW&AriFEV&+7ri5@^FFvD~Jw=6|l!A2n1-L7-c?FX@cxP5n@e&B?=Moa_cV0PpwEI|IeDKw+ZPAWt@VPA?fgw*_B(Ln!7aWE**E5r#J_l7ng_7AvTE5rw$_GrX9CI&W1Y+FM0Sd(8}PZ zli7oOR}jNw9qBC0laX5~L?k{+MN*Eb(excsFatWsWS1`s{JuhAaUWBD{fs#yW#^-i z_AvJr;8#OYt>fU4j&MrMQE8%>BMT_|&|HW`1Q*JcAze*MR;~a2IRJvoPNkU+%YwFMVc@zmdMjPw(kB zp`YqLBgY-^V~u?Qebk70)<$J)7`u#C--7F;C)w{LBPJcvEswq2>v^N%z&nlU^g#L* z={s8BNT&%B(q2qyC0d=4oKcVx{jBCR8YJ_dI~Ae1?J1PIx1Yo`3$Bofj?n>Uvd1a& z;Ocil57@Q8V!=cB8M4ZKh8~2K`rYp&IN-(`i{$eA6`8c0gK}>9Nrng{%cjwP=y#zs zK5x1(R%V7wn5Ew!OBKUSIR}2zlXq>&Tsf6;S0T0Jdtg(lfu6vzJM{}0N%6N_n@qR0einajNrCtMQ@UE*>CH&)B2W2vtM(7c2lX` z*IDT=rnEU%?kD;Pk`rdH`6)RJVM6`){g0nW9hcIS8q3-2-h1saHo$bv zECbFRT(?GU59?2x{0rq3?I83=ZhdLG`#mJ4WLFQd=-9<(nmt8pN`49i^Hyx&iDMCb z!Y@H?EwM03N0vmvxrkwNxN8?k*S+kd1GfnlK8$ou*kc)GM5e3#SZCMaE>MvjAZ&>- zns^eVX?r+~fSZys0E=KFy@f=`dx$QEy1<^!V@a|T68Qri9Z+xp?nKe-jrp2Ym*C9s2SA+P3!bS?Vg{GBK=1$^(692gSP|Y`A0)IFAiF=)tnZClt3~d5Kk&`Z)o7=~8e`5FLcr#0=eXWX+ha zTY5O5*?e3Qy&B*UB;2tR@9(Q_CYAxAZq;4w2Us4~Wk(2dVln9nR4H!&suF$_FS!ki z5r{NFnqKo(M7hyH7Id`@W}6%U#hL>Kt(E!~>?E`rn}6sodLi)<^Qu z%G?{F?F+ADXkvKfOHOr)0u_Q`q(y5|jylB|le`cJC1~T4Zfw&YF-lX?DY>T9t}MqC zx;f3UlXBOkRIMcX(6^Kl9_5CTpc04-26UG$7~WuXrE9;!F_d#~{37J0e558kp`}sr zOzB0MF(S`ssnS9kvxTZ^l&APdTLgWDgHYmOS~3!iKGvZH%NRGoCH9_l~eSkt&FDj1d<;KNSdqT7oaLgG;iVj4E z)C*EFVZG?9{al~pf~OqxQND@}z|jCGekcr_-U3hYnE@7_FLOMzY;f}wB>KljvL+zR zw|TGG3L(4dvvv}V%fW);+r z2ro8GxJn=u_mtjp1_oO{7Ua|&W_k98I#6G#exp>VUoxL>q(+C>7QY9H6Wj^a=l#~7 zQ4Ml~sy$F#2T|}@WU#U>VMd?=r=h$-@|Gjdh{Pf8I&=hw8Mxc>*#w_CJ-g8^<)ZI~ zwe}e#UijQ?Jt@h9`Cg#dC)0Wzly@*PbHL$32OqoAk?+cZqaY*9c?^~%EqHMeVRqzf zOXBkil2B45E~ETf0LIlgo;W0&VCKT$k-DB-pO1a6^$Ec!#%JNf$>StV&4sjvSEH3U zSmw+e)*m9vonAdsbk$ih>GHF)1dff#Y68B9Z%<_gN!w$=;X#6&7j2c?MQ5D~rocR4 z3m5=O=fRU^EhAgny$FucA>znnyi0WG^f$-Q4XC_U+W(zp75F$}rX(|o+;_wzp2 z-)T%1-<+SbIG<}h**X?n^r3k%;~7Z0akOL4I|h{nw;y+itqOh+rgq^aFQ0UW<9w!S zIxdm2pm+tlRgveE_C&NdXnrNtI0?(6;eG;bVE!T`p3Ev5TIz3#Wp}y1o*D}kZnPp?PGH^D%XI2lbChlfR6l@~5zUm8d=bTH_ zuklONefqua;x1UZRm~e$W!oX;@w;P|411)q3$7$)$ZU6))w57m!0AYC0(TscYyqp2 zG4I~oFa#^442=PWhAR8q+7E)$$Phik@nobJLsKAFD>6^qY_EEY3cO8# zeaB8(PQ+0c{|q3eJ={%!rf@E|vF%L!H>z|Kf$sy9c_sFrRvIy}6i>%AM9jp@w8hJ$ ziC9I|E+?>#is;Bv9gUA^CHhA?*VVw7(M-&qv}@c?bn0^H}7DF2vbZw>@(9Y z&g@*sx4h->o~azFJY7Ago73nFzPQ6|2*!hUL(Oz4$Ltxcj!6<>O?q?AXPnLWCvRCq zbKR3OqB(sC0XChUq4iT)xg(xY=Xh4t&_;xy@s&@T?H1o;X7-tZp@ES{F7Ccme7WYe z^6wpjRBb$S9Ugb)T+e~mf$^N$$mZ*=^3fF&uG;A!XK}!9JfDPySdZcT}-9|?~hxvY6*;A+X*X_eVs^vgiW^MyLI z=*5?;Z#v)H{`TWn9)IJ>@w#o%#oNXU+9J(xQwU)is2S4HYo74dOnM6?s^$_u=B=3U zE`dDIg=T!{EPx)MQWk9y4HCSGeOn#%OiW;9ZaooI0L99&#DB8AU(A}n}@ zW6W6)X=6xI`1BXXG73k+(TwuZ$D$d_pgsc!IQA*CRr0QRs&!hM_}z4}i3Rg9=aH=_ zyeHnC`;rQwe$F%xHD4(DZskjrKM;Osdd)QEgxt<-@l87r;&gb=Rpvu+(w`gA#cbYb zAMSiI*TQpI`6R}?SoCV;E0sSHerEcSX)LfI>Dn*9)sgLoc@u8?NV+{%f$1k!_Qo_8U$Vsfe? zl2a8;1+vbsI=gCQe>6}tq?aXAMU#QT=k+9;D*Df9oyLGyx?l#2R~qawvkf;4_D>*w zwR_KHPn4}0%UH{9K~Y!o8>z$nJK<4$S6A|vY+TO0;WBPop?cF=(CARTx!TcKq56f* z*`(p#GPs*oac|{#wivm$map2hO#OB(PjJPW#th+|<#xb#tvo@8qp?(Yw}dBH;b`K8 z_c)%Q#?Z7}{hrm?l&^X(kEe9LscEtBUX_Z{i+D;eR#W;uc6XIwi%$K%mM3U*ZOK=^ zpT`4=k77@NN<2PV5*QNV%g7OA(fN-*dRAZ~iK38!zu00!%l?Ce2{?I~nl{^M>Crv| z#k_g~hA~fpb5MaN@7Un#jA;Gl&I29nhP3@Q(TmvI{()YjX0O@UQl@HY5r0Pau28mW zBfXbMsEuWOHD31yl6{tD7&?gU2cICvk55UpJVM12_pS1MX8R!(xMOOA_ln$prh`$& zZ%>pJlkIql_Qei*bsm8*Rj3uUluIg|qK*LRC#4LMrVl=;SZtT77V!bx>*QWmab;f+ z_foli1W1S~juAM7woAJ6>{R)4$}1+2Pd5q({0sg3C4pZNASzP)9RR$p?j-(MsPhQC zf4o82?6d{}oiHaLULmm)InhJ_j8aat5%3VmCP1jJWD-zGKWho-DbGm2L|`4|{X3-^ zDAh$N=5vAcaYW)ViVqWboWPR=b`sb{AVh#2pGPURfxvC5fccK4<5u#P$)d9nX2g_6 zVrn?`63r!2bA}Z z1b}65;#Epb5O|-!B!L(K<~xEJO?*T@|3Cn8C{nn@X&mk%F?mP~(#+=(U}8TK+le~} z9H7ce2ow>ZmCOJI3J#w`iLY@pDwUw0)APb+e)zx~M}MKmXtF$4e6sj>{jA0#lua+@ z^rjR2PxnWTUe#sKYRrapvq80?j*lBvfU|m&p@EP4d7%PNI{nAD+!8EQZS}00(^{t~ zg`HY!rMxO7uS&^VqU0@6^2(IFvbbI?1mi}4SqWGKe>__+c;YTSQZBnt8ZWjBj(D+3 zsEq5qLRP$%#}is!$Rm(HtF;QwcrJnt$2}@xE#BeF8Yvw8^5w(tESu#V0y=QMg(q$oTybzcbRXKu(bc?k1xT;ccQ62VaN=@TS)7ocLXmlbIscApU(+ZV|Jfx;=>|ul8 zOXML19UQIwR()e!y;GH_bVfxj46tUI+MTDc?JKSxKBnhQzK04aG2%SL8ECRjI* zFX(J#?x%xvNujumgkUf9MODen=28N5eR`PG(lGrcjR8AmccG8-$A{e`aDm1ri;d5$ zmJ=L_5&CBRu=@07$%^DdP3fmcN}2NePzm!l1Aph~D0|rWq6A$7YEb`a{nUY>E0=-y zew0$DlZd4F34vdNFyHM+ujZUI%z*EtV$_y4{DsVMAjuuxN2lU~D_MeATAx*m%z0oD zi0)aNT(V5lB0fQD_>Xuoi*^D8gU3KfCowy%?Dz?cBDvIw-yPHJ!KBB8Jt0_Fibs&c zRLE(y$m^f6kqKO+`TbM80KS!Np5u+sV1I7K$rX_e&#pTThn6P6O^(mu#csIpbjF|n zg*}6R(%~d4vCg5+5yQBrJfgmy>4!bgq|MFjjOr(Bxlr13x^T20nx__25t*EQ{lQy$ z^K-_N#;==iLvSKzexl^wVeF(A>=S>Sn*4xLMkTRE=jM41RHe4L(2eaH*?4MFmNE0#=tGS z^~Ct zAH2cYZgO7f{|(M~lk?oPC01@N! zKGs+8OJ_KM_>;WA+u}BXw|{JV$icg2ID+wgg&IC5ZWZ{lxXsGD 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/.deps_siteforge_v812/_pytest/_py/path.py b/.deps_siteforge_v812/_pytest/_py/path.py new file mode 100644 index 0000000..b7131b0 --- /dev/null +++ b/.deps_siteforge_v812/_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/.deps_siteforge_v812/_pytest/_version.py b/.deps_siteforge_v812/_pytest/_version.py new file mode 100644 index 0000000..e5c1257 --- /dev/null +++ b/.deps_siteforge_v812/_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 = '9.0.2' +__version_tuple__ = version_tuple = (9, 0, 2) + +__commit_id__ = commit_id = None diff --git a/.deps_siteforge_v812/_pytest/assertion/__init__.py b/.deps_siteforge_v812/_pytest/assertion/__init__.py new file mode 100644 index 0000000..22f3ca8 --- /dev/null +++ b/.deps_siteforge_v812/_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/.deps_siteforge_v812/_pytest/assertion/__pycache__/__init__.cpython-312.pyc b/.deps_siteforge_v812/_pytest/assertion/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..98811c0f9d013a010798260222c007403ffeb114 GIT binary patch literal 10106 zcmd5?TWlQHc|Nl{dm(puTk$GV(&$PpEv;l-e34yS5=rY$Iilj$G@bQwXGku!x15<3 z&CN2Q0s)p-1_b&hpP zOl#m??4vc=mFgbr#+ahEU6jXq)QxHg@3k&=gsE#TGPPav-*#JP9P1Um9q8@6?Xvs& zL|+&Bx+{I_)J=U1TmJB9E}PA8Lz&EQCCfEkOB;#wl%i@zERobyC6UHRDrO`yX~4=PdTBhS z>l&v{J@W6gYFIJ@v2;3P2uvM68;)pcjmL}(AHgs8q4XSH{+D^i$iy>Az)fdgeR()K zcyxI1#Icbh=c)qkhqw$B=s7%1A}yaK;b4X8o2=}z-)`71D6RHnAxX>$bT3n_+x zx26`)u=l{&jK6km3obCj>-5eDRdjw?Z?*kSM|I6P7VaD|1FANWn~Do|=lwf(T6bZ3 zblk6IHd;SZ8C46rI4x;*kvxM=?V#S zO31#Ef#_$>CsfUuroBnJKq6DC0FkiQ^v2>O_NJ_Ymg$R6XA*HuH+}XFOkWC`b3VpR zd0I7@Td}-G&HWvPAB5iN_DC-n@A=VgZ8FP zPHJ%j67Xef#HXR1b=DX>dg#p8UL*gb(X19vOwK7W<-EoxGJ3+8Qvjg*h`lf}~EKLQ($fTZ0G2zX>a+kMG_6?6XggAIfHjVn%HIWF{U< zj-S<1+3{>5yVHnqY-ZVJb~ft7=yw@+U_ef`v8aA0A$EH}J$`oih5KU8k%EI0L(Tep;3(RMD$ z?xsO$sf~HsAG0Qpcd3tg0&ktVaH{OzQf^vPZtYldNzF&4r3U5+J!W2Dv{o>ba%+3J z>C}=>jL8^lTshV}QXP97#8^pQSCrS4{ml>k%_aZlqJQ&}EH#ZwpEdYA-eqi@#i?-~ zGF7mZ(lY!x!i@qOVy{2V*t}#^d_h4nZ0F3F<8HZhrCpWgT}H*e7hE$Ho9hr~%pe~l zo&t0H$m;j^C7Br&D?CHuvFgE|6zs`evnRLF>>$mwI#BiP>{FMntS`9d5iF$xSY?fa z;RFivvWpeunRchY`Yx~^GKtN(!tU6gf)!`9sf142pGj*9A_buxmLXNru@tN+OwxpA zn_mM8Qn9X)#zqCt+3AF?Ov0&)NJ7|*RMnnM$H?LU!={NPVz4TXsZOMI7-vC87|B@t zTx?3S6c*x5mNS7FA||Od`Ia}pb|Us3a=J2?$tf^wIas!u4P;;}$&Aq+CN%gS7+EqF z!dEB>UCf8+&jD-B_8XeIL&@oOPnrmGrACF6#H3g(23j#Co6Nz{2{JidL{LiBI)=@l z15oo=%Hrk$u&c6og}>as1nV&QT`)>8x3KjqE+VEY15kPxzNw6%d}sGOJQ%JnrjCRI zW@7?t!zZTWn(5XJZZ@2SPZ;Jr!%eRh=$SItvK*sxy4gU^3?2*4KsfczLr&OtMuciO z$5HZRtwXs~YR>A@L{Bgk9kI^Ie(`V5vu;otH)_F;O*wS--%TL`8TDFu!gO>&$hI+3LzJL7P<3BZS z4*&Ug7W?)thW6bH?t{f>>wMhE+S;!*{kRF!0v%V?_l}hM4ix(iJP15j?%i6%gFeVte#xyo2A%CIpP(r-zhBC=! zpoU2+qESq-ED;5T*Vr7pC5_x-w_K*IYspCtqr_~}FL=YBLPx&4hFYYS<1s=fN(=0R zHDaeDw`4(tN6>3}xn|@zqL>jVG`8iBM(JjvQL`}`wZsWGXEAm5_E$CUh zc!7RW^9KIJ^<88S<$V?g_19-mH%4k0B#lu7?E;hJId|B@_hUG}_Yi`=otdl?q>;D? z-!A;GB{rl-P*^pgQ;?-bzMP}{Am~~rI1K3X>|D26j92h1X5=dDK=u4!HH!kxif2{f zZ=)3o$TM6c2$&oT1EV2B5;fCf@EFXM%rhCobi>^916V8U=7V^dUMnZz-@p%@j!>~4 zkZCKN$#<<}OT-yS)0`b>aHH+rm*guA<&NHKtiX8(tpIhvB8e@&+ z=U%)d|DeIT_^`~siHRbbE;&}bbtlnK26;6}&{PGO(awS@MQ~D}uJ)jn?#6gIh zDiO>~Glo_@Gi|Fn@KbdaDtnjM*I93-63JAlsqWjhWTE`xxx^+VRlXRUcY$6<70ZDN zt}1px8f9T`{>8I=j=~t>AVndTZ&Z2gJ43R<}^pBCEr8PUNq!mQpmlK4wAkKb+JtD zg5}-iTw2ZK9d<{pT%4?YtwHg3oUKwc)M`9@ZRZS%Z^gZlf{-N zFUjRV%e_GF4f&4yubXZ)6_x!DgDr2Lx_qh>REk06M*D-{wk0>)_!N+YT46s`eGLc| z=Jp(>15Tm7?Os&tMFtE@W@uUqzf@YLOB7q<*Ay!6(|3nzcPp(t;BxclkS?vdi|kzbr&-2KWu z`IVZn`|`G%?GI%iGZ@WHpU*1@j8!H4#a(_|ozqkF!efj8r!mRX*Hs zx4hPch)X?-GgPJ14CtB>)vd}7Vk@5w;zEVnfiR=|LMrTBUz?o(7>G+?+z`74lSJc*cU1XI!l4xVxae4puZgK zDh2zB!M;*(doj5EX4iw@laS8fb~Jt~P&3)^XN`L8|mBQW_@^FUQS}rScOQ*mhB#&~gXgrfb5=cco zs^5K4ArNI!kw#LA=y(@90~Bd>4nB%X<`JYzS?ofV5H&r1My*>x!_G#6~#4v*6B8To7Yl zy+ZtDe?5~^Kww&M-H~qF!3_xq$|c`LxkN6=D1UgXC7PK!0$q?V`<<|fW(={nJ#p6* zi@RQL2MtfbGgB$y*m8y!mHeGq@3^jX%2;jQqsn!SlDQ zw!?&l3f{Uq!6yf2C-dHdH;!*)qfBOV4Fttq^PX82Zj2p=4h&lLja4P)6>=w1EFpa8 zCPAd60cBuR)@_+XaBWp`AU;;XT39C=NtxuCl$cbZ1PW*GckgDX;C~8CE#0LowN0}N+@mU0D6?%%YU7Lzf zjD~4xkt?Pvv?;X+TjEID8Bm5(6Pl`mP+FT6b#SqwrpIGhbRsUKdcLJtR-q(Xv%T%% znpKl2YvR0Z3b2b_loS6IKLl9bJIeb+N5E6I11^8DK+L@pQ-v^DSAx^*P%GkaFpv|7ot95!j1+3wtN zG%*PS_U&8(mrI`p1v5-}CX+~u0{i(`GN*+b_&HiBNll982T1fVk%ZRt<1WQDVW0T4 zVY<<8dJ=?U%J|Zl(rHb)e>@9zTpFNC-~_&1!FuL**n5Tq342WJr|*~57_|obI*93a z&>*H;TF<)rN}c^EU&h~lp-LNM~0lo zOw&u8PiIUy10gYGQW|`b?N}iT^aYB(iy?oW#~cp(%{T?ee2yBTFWCPe_=h519tnGe$eG^E z#0<1ih#vnw?R6MUz7eHWrJ#u7nqM5rbVG}*b43VE98N$q!73j3t7Ne)1AYGH3pAq%l@Y-J`U*~*B(8DFp3IOQJFlru5tXE0=ND*{xGNa@2IT*s zm6mmV0sHu6ApejiypQI0|0aaIl{K}!ef;wAh2hWSwfMsG3|qUt)H6`*8Mrf4+HtVB zE#msH^9_efN8JmAiVc$Ug}F7OJ#&SgJSl-}-v&@#0}N7?M8n zxDP+UKJsrS_=zopE$+W-aRXivQB(MUm{3~&|DN+8i$a)0&x%Z(zkwb;fo4Va(r(|S zo#srU9)@K7Pk{FKgn9xElIl>;@0-3LxsI$#U*dKo2GWd?4H0bJ2cWiG^~JGV!Cj5! zP?m9xa7sUsv zoDv<&N^Ge751H4Cx*7pb6TxjA7lgs-Di#j-jAq8;a3lL+o#gXwwQ zu|1nb{gG<=lR4D;QQzm3`Is%$bXJeVCgPkfO_Xj-FaDDNf(b6j(*1EC&1kc~7DucL zpgfoYcTWD(x|*KPVMJ^;iTvAacE)X^B~C@5uUVOCjYRPEiavSMF6cVUCTC_Efs-y6 z2>WP>HY)V+)PmCkv=yptiVqT^!fsV}sN5p5c45MV_zU4!RgyLR&s%BK3;Y^pkecZ~ zN3-OXBo}+Vg2U zb2VPwa-;pewC&ffeNx}Do4NXz1eBI!Y1dM-Tl$8C5AaKF0d)7;z-E-XM9;207VwFu L literal 0 HcmV?d00001 diff --git a/.deps_siteforge_v812/_pytest/assertion/__pycache__/rewrite.cpython-312.pyc b/.deps_siteforge_v812/_pytest/assertion/__pycache__/rewrite.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6363a0f131d856e1b0144f84cfcbf12f8c4a0a9b GIT binary patch literal 61169 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 J{}@{?{ttn|KYaiI literal 0 HcmV?d00001 diff --git a/.deps_siteforge_v812/_pytest/assertion/__pycache__/truncate.cpython-312.pyc b/.deps_siteforge_v812/_pytest/assertion/__pycache__/truncate.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..526c7eb4cea7912cf8ecb0b3486593b57f3080ba GIT binary patch literal 4189 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 Y>8$%~Qb2tBj-QfWUtrsr0sB1v0T!%J3IG5A literal 0 HcmV?d00001 diff --git a/.deps_siteforge_v812/_pytest/assertion/__pycache__/util.cpython-312.pyc b/.deps_siteforge_v812/_pytest/assertion/__pycache__/util.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..aa1155c6ade9cc8ca7f1cb478e908463a6fe18f9 GIT binary patch literal 23819 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 literal 0 HcmV?d00001 diff --git a/.deps_siteforge_v812/_pytest/assertion/rewrite.py b/.deps_siteforge_v812/_pytest/assertion/rewrite.py new file mode 100644 index 0000000..566549d --- /dev/null +++ b/.deps_siteforge_v812/_pytest/assertion/rewrite.py @@ -0,0 +1,1202 @@ +"""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 + + +if sys.version_info >= (3, 12): + from importlib.resources.abc import TraversableResources +else: + from importlib.abc import TraversableResources +if sys.version_info < (3, 11): + from importlib.readers import FileReader +else: + from importlib.resources.readers import FileReader + + +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() + + def get_resource_reader(self, name: str) -> TraversableResources: + return FileReader(types.SimpleNamespace(path=self._rewritten_names[name])) # type: ignore[arg-type] + + +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, strict=True): + 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 + for item in mod.body: + match item: + case ast.Expr(value=ast.Constant(value=str() as doc)) if ( + expect_docstring + ): + if self.is_rewrite_disabled(doc): + return + expect_docstring = False + case ast.ImportFrom(level=0, module="__future__"): + pass + case _: + 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. + aliases = [ + ast.alias("builtins", "@py_builtins", lineno=lineno, col_offset=0), + ast.alias( + "_pytest.assertion.rewrite", + "@pytest_ar", + lineno=lineno, + col_offset=0, + ), + ] + 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 + match v: + # Check if the left operand is an ast.NamedExpr and the value has already been visited + case ast.Compare( + left=ast.NamedExpr(target=ast.Name(id=target_id)) + ) if target_id in [ + e.id for e in boolop.values[:i] if hasattr(e, "id") + ]: + pytest_temp = self.variable() + self.variables_overwrite[self.scope][target_id] = v.left # type:ignore[assignment] + # mypy's false positive, we're checking that the 'target' attribute exists. + v.left.target.id = pytest_temp # type:ignore[attr-defined] + 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: + match keyword.value: + case ast.Name(id=id) if id in self.variables_overwrite.get( + self.scope, {} + ): + keyword.value = self.variables_overwrite[self.scope][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 + match comp.left: + case ast.Name(id=name_id) if name_id in self.variables_overwrite.get( + self.scope, {} + ): + comp.left = self.variables_overwrite[self.scope][name_id] # type: ignore[assignment] + case ast.NamedExpr(target=ast.Name(id=target_id)): + self.variables_overwrite[self.scope][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, strict=True) + expls: list[ast.expr] = [] + syms: list[ast.expr] = [] + results = [left_res] + for i, op, next_operand in it: + match (next_operand, left_res): + case ( + ast.NamedExpr(target=ast.Name(id=target_id)), + ast.Name(id=name_id), + ) if target_id == name_id: + next_operand.target.id = self.variable() + self.variables_overwrite[self.scope][name_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/.deps_siteforge_v812/_pytest/assertion/truncate.py b/.deps_siteforge_v812/_pytest/assertion/truncate.py new file mode 100644 index 0000000..5820e6e --- /dev/null +++ b/.deps_siteforge_v812/_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.compat import running_on_ci +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 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/.deps_siteforge_v812/_pytest/assertion/util.py b/.deps_siteforge_v812/_pytest/assertion/util.py new file mode 100644 index 0000000..f35d83a --- /dev/null +++ b/.deps_siteforge_v812/_pytest/assertion/util.py @@ -0,0 +1,615 @@ +# 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 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.compat import running_on_ci +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 diff --git a/.deps_siteforge_v812/_pytest/cacheprovider.py b/.deps_siteforge_v812/_pytest/cacheprovider.py new file mode 100644 index 0000000..4383f10 --- /dev/null +++ b/.deps_siteforge_v812/_pytest/cacheprovider.py @@ -0,0 +1,646 @@ +# 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: + """Add command-line options for cache functionality. + + :param parser: Parser object to add command-line options to. + """ + 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: + """Configure cache system and register related plugins. + + Creates the Cache instance and registers the last-failed (LFPlugin) + and new-first (NFPlugin) plugins with the plugin manager. + + :param config: pytest configuration object. + """ + 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: + """Display cache contents when --cache-show is used. + + Shows cached values and directories matching the specified glob pattern + (default: '*'). Displays cache location, cached test results, and + any cached directories created by plugins. + + :param config: pytest configuration object. + :param session: pytest session object. + :returns: Exit code (0 for success). + """ + 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/.deps_siteforge_v812/_pytest/capture.py b/.deps_siteforge_v812/_pytest/capture.py new file mode 100644 index 0000000..6d98676 --- /dev/null +++ b/.deps_siteforge_v812/_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/.deps_siteforge_v812/_pytest/compat.py b/.deps_siteforge_v812/_pytest/compat.py new file mode 100644 index 0000000..72c3d09 --- /dev/null +++ b/.deps_siteforge_v812/_pytest/compat.py @@ -0,0 +1,314 @@ +# mypy: allow-untyped-defs +"""Python version compatibility code and random general utilities.""" + +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 + + +if sys.version_info >= (3, 11): + from typing import assert_never +else: + + 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 + + +def running_on_ci() -> bool: + """Check if we're currently running on a CI system.""" + # Only enable CI mode if one of these env variables is defined and non-empty. + # Note: review `regendoc` tox env in case this list is changed. + env_vars = ["CI", "BUILD_NUMBER"] + return any(os.environ.get(var) for var in env_vars) diff --git a/.deps_siteforge_v812/_pytest/config/__init__.py b/.deps_siteforge_v812/_pytest/config/__init__.py new file mode 100644 index 0000000..6b02e16 --- /dev/null +++ b/.deps_siteforge_v812/_pytest/config/__init__.py @@ -0,0 +1,2197 @@ +# mypy: allow-untyped-defs +"""Command line options, config-file and conftest.py processing.""" + +from __future__ import annotations + +import argparse +import builtins +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 Mapping +from collections.abc import MutableMapping +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 ConfigValue +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.compat import assert_never +from _pytest.config.argparsing import Argument +from _pytest.config.argparsing import FILE_OR_DIR +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 + + __module__ = "pytest" + + +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 print_conftest_import_error(e: ConftestImportFailure, file: TextIO) -> None: + exc_info = ExceptionInfo.from_exception(e.cause) + tw = TerminalWriter(file) + 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) + + +def print_usage_error(e: UsageError, file: TextIO) -> None: + tw = TerminalWriter(file) + for msg in e.args: + tw.line(f"ERROR: {msg}\n", red=True) + + +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. + """ + # Handle a single `--version` argument early to avoid starting up the entire pytest infrastructure. + new_args = sys.argv[1:] if args is None else args + if isinstance(new_args, Sequence) and new_args.count("--version") == 1: + sys.stdout.write(f"pytest {__version__}\n") + return ExitCode.OK + + old_pytest_version = os.environ.get("PYTEST_VERSION") + try: + os.environ["PYTEST_VERSION"] = __version__ + try: + config = _prepareconfig(new_args, plugins) + except ConftestImportFailure as e: + print_conftest_import_error(e, file=sys.stderr) + return ExitCode.USAGE_ERROR + + 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: + print_usage_error(e, file=sys.stderr) + 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", + "setuponly", + "setupplan", + "stepwise", + "unraisableexception", + "threadexception", + "warnings", + "logging", + "reports", + "faulthandler", + "subtests", +) + +builtin_plugins = { + *default_plugins, + "pytester", + "pytester_assertions", + "terminalprogress", +} + + +def get_config( + args: Iterable[str] | None = None, + plugins: Sequence[str | _PluggyPlugin] | None = None, +) -> Config: + # Subsequent calls to main will create a fresh instance. + pluginmanager = PytestPluginManager() + invocation_params = Config.InvocationParams( + args=args or (), + plugins=plugins, + dir=pathlib.Path.cwd(), + ) + config = Config(pluginmanager, invocation_params=invocation_params) + + if invocation_params.args: + # Handle any "-p no:plugin" args. + pluginmanager.consider_preparse(invocation_params.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], + plugins: Sequence[str | _PluggyPlugin] | None = None, +) -> Config: + if 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))) + + initial_config = get_config(args, plugins) + pluginmanager = initial_config.pluginmanager + try: + if plugins: + for plugin in plugins: + if isinstance(plugin, str): + pluginmanager.consider_pluginarg(plugin) + else: + pluginmanager.register(plugin) + config: Config = pluginmanager.hook.pytest_cmdline_parse( + pluginmanager=pluginmanager, args=args + ) + return config + except BaseException: + initial_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) + + +class _DeprecatedInicfgProxy(MutableMapping[str, Any]): + """Compatibility proxy for the deprecated Config.inicfg.""" + + __slots__ = ("_config",) + + def __init__(self, config: Config) -> None: + self._config = config + + def __getitem__(self, key: str) -> Any: + return self._config._inicfg[key].value + + def __setitem__(self, key: str, value: Any) -> None: + self._config._inicfg[key] = ConfigValue(value, origin="override", mode="toml") + + def __delitem__(self, key: str) -> None: + del self._config._inicfg[key] + + def __iter__(self) -> Iterator[str]: + return iter(self._config._inicfg) + + def __len__(self) -> int: + return len(self._config._inicfg) + + +@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`` + configuration 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.""" + + 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: + 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 + """ + + self._parser = Parser( + usage=f"%(prog)s [options] [{FILE_OR_DIR}] [{FILE_OR_DIR}] [...]", + 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._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 inicfg(self) -> _DeprecatedInicfgProxy: + return _DeprecatedInicfgProxy(self) + + @property + def rootpath(self) -> pathlib.Path: + """The path to the :ref:`rootdir `. + + .. 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 --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 show_version_verbose + + # Note that `--version` (single argument) is handled early by `Config.main()`, so the only + # way we are reaching this point is via `--version --version`. + show_version_verbose(self) + elif ( + getattr(self.option, "help", False) or "--help" in args or "-h" in args + ): + self._parser.optparser.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: Mapping[str, Any], args: list[str]) -> 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 _consider_importhook(self) -> 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. + """ + mode = getattr(self.known_args_namespace, "assertmode", "plain") + + disable_autoload = getattr( + self.known_args_namespace, "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.extra_info["config source"] = via + try: + self._parser.parse_known_and_unknown_args( + args, namespace=copy.copy(self.option) + ) + finally: + self._parser.extra_info.pop("config source", None) + + 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 + + @hookimpl(wrapper=True) + def pytest_collection(self) -> Generator[None, object, object]: + # Validate invalid configuration 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_ini_value = self._inicfg.get("minversion", None) + minver = minver_ini_value.value if minver_ini_value is not None else 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: + strict_config = self.getini("strict_config") + if strict_config is None: + strict_config = self.getini("strict") + if strict_config: + raise UsageError(message) + + self.issue_config_time_warning(PytestConfigWarning(message), stacklevel=3) + + def _get_unknown_ini_keys(self) -> set[str]: + known_keys = self._parser._inidict.keys() | self._parser._ini_aliases.keys() + return self._inicfg.keys() - known_keys + + 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) + ) + + 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 + ) + + ns = self._parser.parse_known_args(args, namespace=copy.copy(self.option)) + rootpath, inipath, inicfg, ignored_config_files = determine_setup( + inifile=ns.inifilename, + override_ini=ns.override_ini, + args=ns.file_or_dir, + rootdir_cmd_arg=ns.rootdir or None, + invocation_dir=self.invocation_params.dir, + ) + self._rootpath = rootpath + self._inipath = inipath + self._ignored_config_files = ignored_config_files + 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=[], + ) + + 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() + 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._parser.parse_known_args(args, namespace=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 + + try: + self._parser.parse(args, namespace=self.option) + except PrintHelp: + return + + self.args, self.args_source = self._decide_args( + args=getattr(self.option, FILE_OR_DIR), + pyargs=self.option.pyargs, + testpaths=self.getini("testpaths"), + invocation_dir=self.invocation_params.dir, + rootpath=self.rootpath, + warn=True, + ) + + 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 a configuration 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 the an :ref:`configuration file `. + + If a configuration value is not defined in a + :ref:`configuration 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. + """ + canonical_name = self._parser._ini_aliases.get(name, name) + try: + return self._inicache[canonical_name] + except KeyError: + pass + self._inicache[canonical_name] = val = self._getini(canonical_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): + # If this is an alias, resolve to canonical name. + canonical_name = self._parser._ini_aliases.get(name, name) + + try: + _description, type, default = self._parser._inidict[canonical_name] + except KeyError as e: + raise ValueError(f"unknown configuration value: {name!r}") from e + + # Collect all possible values (canonical name + aliases) from _inicfg. + # Each candidate is (ConfigValue, is_canonical). + candidates = [] + if canonical_name in self._inicfg: + candidates.append((self._inicfg[canonical_name], True)) + for alias, target in self._parser._ini_aliases.items(): + if target == canonical_name and alias in self._inicfg: + candidates.append((self._inicfg[alias], False)) + + if not candidates: + return default + + # Pick the best candidate based on precedence: + # 1. CLI override takes precedence over file, then + # 2. Canonical name takes precedence over alias. + selected = max(candidates, key=lambda x: (x[0].origin == "override", x[1]))[0] + value = selected.value + mode = selected.mode + + if mode == "ini": + # In ini mode, values are always str | list[str]. + assert isinstance(value, (str, list)) + return self._getini_ini(name, canonical_name, type, value, default) + elif mode == "toml": + return self._getini_toml(name, canonical_name, type, value, default) + else: + assert_never(mode) + + def _getini_ini( + self, + name: str, + canonical_name: str, + type: str, + value: str | list[str], + default: Any, + ): + """Handle config values read in INI mode. + + In INI mode, values are stored as str or list[str] only, and coerced + from string based on the registered type. + """ + # 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 (in ini mode) we will get either str or list of + # str values (see load_config_dict_from_file). For example: + # + # ini: + # a_line_list = "tests acceptance" + # + # in this case, we need to split the string to obtain a list of strings. + # + # toml (ini mode): + # 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) + else: + return self._getini_unknown_type(name, type, value) + + def _getini_toml( + self, + name: str, + canonical_name: str, + type: str, + value: object, + default: Any, + ): + """Handle TOML config values with strict type validation and no coercion. + + In TOML mode, values already have native types from TOML parsing. + We validate types match expectations exactly, including list items. + """ + value_type = builtins.type(value).__name__ + if type == "paths": + # Expect a list of strings. + if not isinstance(value, list): + raise TypeError( + f"{self.inipath}: config option '{name}' expects a list for type 'paths', " + f"got {value_type}: {value!r}" + ) + for i, item in enumerate(value): + if not isinstance(item, str): + item_type = builtins.type(item).__name__ + raise TypeError( + f"{self.inipath}: config option '{name}' expects a list of strings, " + f"but item at index {i} is {item_type}: {item!r}" + ) + dp = ( + self.inipath.parent + if self.inipath is not None + else self.invocation_params.dir + ) + return [dp / x for x in value] + elif type in {"args", "linelist"}: + # Expect a list of strings. + if not isinstance(value, list): + raise TypeError( + f"{self.inipath}: config option '{name}' expects a list for type '{type}', " + f"got {value_type}: {value!r}" + ) + for i, item in enumerate(value): + if not isinstance(item, str): + item_type = builtins.type(item).__name__ + raise TypeError( + f"{self.inipath}: config option '{name}' expects a list of strings, " + f"but item at index {i} is {item_type}: {item!r}" + ) + return list(value) + elif type == "bool": + # Expect a boolean. + if not isinstance(value, bool): + raise TypeError( + f"{self.inipath}: config option '{name}' expects a bool, " + f"got {value_type}: {value!r}" + ) + return value + elif type == "int": + # Expect an integer (but not bool, which is a subclass of int). + if not isinstance(value, int) or isinstance(value, bool): + raise TypeError( + f"{self.inipath}: config option '{name}' expects an int, " + f"got {value_type}: {value!r}" + ) + return value + elif type == "float": + # Expect a float or integer only. + if not isinstance(value, (float, int)) or isinstance(value, bool): + raise TypeError( + f"{self.inipath}: config option '{name}' expects a float, " + f"got {value_type}: {value!r}" + ) + return value + elif type == "string": + # Expect a string. + if not isinstance(value, str): + raise TypeError( + f"{self.inipath}: config option '{name}' expects a string, " + f"got {value_type}: {value!r}" + ) + 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 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 type for failed subtests (see :confval:`verbosity_subtests`). + VERBOSITY_SUBTESTS: Final = "subtests" + + _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: + + .. tab:: toml + + .. code-block:: toml + + [tool.pytest] + verbosity_assertions = 2 + + .. tab:: ini + + .. code-block:: 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 config-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 ImportError: + raise + 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: + try: + warnings.filterwarnings(*parse_warning_filter(arg, escape=False)) + except ImportError as e: + warnings.warn( + f"Failed to import filter module '{e.name}': {arg}", PytestConfigWarning + ) + continue + + for arg in cmdline_filters: + try: + warnings.filterwarnings(*parse_warning_filter(arg, escape=True)) + except ImportError as e: + warnings.warn( + f"Failed to import filter module '{e.name}': {arg}", PytestConfigWarning + ) + continue diff --git a/.deps_siteforge_v812/_pytest/config/__pycache__/__init__.cpython-312.pyc b/.deps_siteforge_v812/_pytest/config/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ecb072214acc5e061526ad11df5217c641e81a04 GIT binary patch literal 91737 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%0D6U*6Eng_F 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
YF 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 T#IjcGnsLz~j9&?evY!7Rttoxh literal 0 HcmV?d00001 diff --git a/.deps_siteforge_v812/_pytest/config/__pycache__/compat.cpython-312.pyc b/.deps_siteforge_v812/_pytest/config/__pycache__/compat.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..72feeeff65b3b4c2cdc7d9f7d1ef3ce2b8fb702a GIT binary patch literal 3583 zcmai1U2GKB6~6PgyWStK?KR+FFk`QcSuloTAYc(>7Xk=^K>;ddYcv|~jJ;#e&P?yj zLUy%CPJ+ZPs)GGcLDW`7Y9E4#RQgmN`%;aG)A}ut~qO49(6Kx7U63L))Q4qLa6D)n}o9^{U&h_aLHOecVQMFVxl0@d2eFlyhjqS0=3Xa+b~vEtH-#xNKV=oRM1XQX7aE%H zGX;|3CyD}wY*$f8iM?-##Uo*DTAVVStcdOwWkV6CZJ(v8IHO%t4bjP}1<_DdMRZJY zN)^=~Dzzs^_8&d^`sBD} zos=DU@+~tX>yww&oHc1_)?SF;jOy$?JTr2jSUhA;>e|#KcsFYrlalY{eHqi3)@I<7 z0}F?(f*X-CSv51~XGb?|w-x^Eqd?}#YA{kh^+9RD`BSiawY96-npilsFnaIG{e$;+ ztr2F=DF$fl6jS54czBI)E#WnegtpaSGRYumw0cCC+AbKmOH`6(6q05YCe6W}W8ntz zN>Jh8D<~m_hi{-Dq*~k#boBUjnW|?XvKOiOZlU;>un5h`YPOhFbxWn+_S}L?OnKksAoe`;bNwtiq|z=7Zo$?P1GDQ zlc!WQ9K8U8s%;E9B2}F{HN@#WC~nwFw7@n^g>_AeYeS&DTpU_AHYXRvtb9$aXFw(^ z8#AD$%MKhtQ7zR_z#=e1oXXGmroWKM=tD1)J3Jqjm=cj1{v`$SfI%>ccvnE8sxBAc zn)9}*PrG3>#UHCD6vZIBAxVNO=15Yp`#=3W+z{>oRU3hMo>aRB%KT@cF1ii6ndVi3 z4qhG4xFKL(xMpN_<8=fnwukk(mk1T13zl+#HMAFbhW5e7Mh(%oGbl+Q1v9t4(o9b_ zjB^q|AZz0t3kRPhlFNzYO1yt5)bE?S(G0wHp1B8Ln_Jv1{+96njRwT1uW_3%F%A!F zc?M-o zCNd3e{Nh#s9Zans-1!skE`bFwelN9(ppb?OZ$SFKQit21!V`>kvO^KbDQ1>eVNwB?oxARa zaee6;78^I}7npi4-0V@8N3?K5bzN#>Z~<`V=jZ*~81~@J!vv)x0#-uYnXqprsXS9XsrgsUTmetY;ghad0$ow71=ZXsGt z>{v?dS?%0*_x$4d)vlo@T_ej~Bh@{J{t)_>V_)0479jlte{T^sZC;C#&Yq=2s=6h) zv}NCFSMvVZN72u^-grKE;L+&H;PH>5&$A-o zMytJhff-$F{d`OJV)5S8VsWXh*8`3ePY-~(6#d`^eEj!1P@jVJqk70iH=_oCs-k58 zRN>W?H!_ZC>b5I*CCcRuIi~_BTWSXNuqR2WfCqRkH>Vcp5!~qzlEX;gT|ycG7#kb& z0}zp6O!@=C1lAMB=BUyUQJ$u;EG zhRKfPFSL)e^4T9>sP^ZkJNWRs3bV#h%t6POwQrbz!Ylo$ARR-Uuq4%Ax{@1}Bz$%G zUU#vmm>Ef;{h&t&k$fM?UL@O)V2z|b4blgUjS;>{zF`AGsMf{^J8J<(82C2OFZB3R zP#fZjfb3m7ibzipm^?%236yuDRnvKRM?mx`2SXcQ?)I=em7$1q6p_OX;za~d@SlnS zr-1)Ac=<>7p*-N9-3|CpI)5{~`ED2<6;#c@`$F-+!OzdQByIR#1Pt(?!aL@RM|VTV zeAgYP7hyTXbNdpIHI8AJKa;+%Nb(EP_LOuzC7Zt>@h?g2D@~ literal 0 HcmV?d00001 diff --git a/.deps_siteforge_v812/_pytest/config/__pycache__/exceptions.cpython-312.pyc b/.deps_siteforge_v812/_pytest/config/__pycache__/exceptions.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e0685bb1cb7211191e0a076f78dd83c40153692e GIT binary patch literal 813 zcmZuvPiqu06i;Sm|Jqdx3f5B)58XpsX;G<)AX*fWMT)%)BOz|GyV03ROfuEko3J0C zw|<7w&*8~W;DDfr2l2AF_2S87W>+cr4)6W)esA*L{9ZnEI&EO>$NP^jZ$kjSy2HZi z6@ar{3l0GV6viNdA%qTlv6uLv4*~N%@DNac0%*X3W54?9cz#P*E7_W5>;KrWo;SM9 z@2;i`i!&rmg&~HERNG^1z*7?U=NFX0j^+%`E?9pE7ziN+p+{lpQ!j+M-wlfPo+d-K zqm)qNt7SkuMKU)`8#wke{#zhAa;+%anUvzi2&GJT;G4EXQn@aLp)sGh%;5(j$?@6iHWvxY1~2E0GaMzv4EI7*bA4 zF>ttztl3}75@xF_aZfefpdIJRk2ss~Si5 zx(&C9L1r>#H=*3CD(WVewuuVdp|!J6t5T}G^mL=5d8b<{;1&fEY1 literal 0 HcmV?d00001 diff --git a/.deps_siteforge_v812/_pytest/config/__pycache__/findpaths.cpython-312.pyc b/.deps_siteforge_v812/_pytest/config/__pycache__/findpaths.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..47440730426ebca2503f69eb489dabaa5328ef92 GIT binary patch literal 13254 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
  • liC7tzio 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 Ng+&}zhJfMu{{xmaFi!vg literal 0 HcmV?d00001 diff --git a/.deps_siteforge_v812/anyio/_core/__pycache__/_tasks.cpython-312.pyc b/.deps_siteforge_v812/anyio/_core/__pycache__/_tasks.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..81cff6f2feb86663b055eaf489f0861176e636b8 GIT binary patch literal 7684 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 bQr}kuK;wH-S~}h6PfKq|kNacNq~P`+CJTz& literal 0 HcmV?d00001 diff --git a/.deps_siteforge_v812/anyio/_core/__pycache__/_typedattr.cpython-312.pyc b/.deps_siteforge_v812/anyio/_core/__pycache__/_typedattr.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3f3727384c1edea67e2a25ab0d08a3da95e5726f GIT binary patch literal 3830 zcma)9U1%KF6~6Pc|0}J2B1KAyJ&qN{>q@d}S57UOwyKoWt{pYRb|Gt-$!O=UHTLYx z=H6LJyR3sSqE&DTp->d)52g<#K`(vnOUX;2&^|~tA@bl-m$sBVRgMBSdFnZLepZr8 z=!L!9pEKw0p6{Hqf9mMS5*Yva!@cW&&Jgks+(bhS7SisoVX;n3VoEltN+n6cwQS2( zrKD8Vk}CF!ov0>D$!e;Us-{cnYNnKdbJa}P*=nwolZZ@c%1mxZ0oPJq>{Ic+1NNO8 zq3lw@>@?G>YN<;m-y>#bm6+KLIsCF)oaNvwAD{I!I*Nr)k)SB~Im2;W&+shQ;YHa` zPaC#v%-hsYy=v5ImUFGB`pT5k@Kcu>HTpw?`H70<7`C5w7b&w{!~7Jr1WtoUU`}}+ zv*zm_z2Lex>a~{{bD5vKq~p1txunm~icz<{a-d%@qp}Ji(r!O2)`=IIO-N+oMd05& z{W|sP%+U<3?pSZtspigKr)6(Mt8;2A!5i1`Dd>C_BU33z`9|#&j(;X z4rzA?<{yI>C@D!MDaoc(QcPuyR3uYg&6m_QvMiMn;1SjD-tX~?)NB4ItHHQfuTfUE z4bHWS%MSWPE4#K0p2H^}%Zd-C7ijqg*SrP8(>5+CelX_?6=)1Z$G;1M{Ta-@xK$(%uJd0ZuHk5gLC}^1>A%EfPih47vIOAvfig zd_4gS4h&O{=$Q0wUrTODixQXKea1^ggr?Gxe=eKqFO`V$mZTCd9o@g9w8&5T+QBA~ z1}P?H{erHy&EmT5OL{YlVJyO1YaZ4tu2n700VJB`f%o=%qL}nk5I`6TzG7O;&vFPU zk1tu?0w4j118^ibzLwvu>y~4AdgNYRN92`l&d?`*8u>UzmHHW77yj3Ej#&qudxiDD zf*pdXdGv{x7<+>8AQ197m>#c?`@^Fjls8Y!d^CS|_}p6V{=l>EPyZ^tc6Ph>*-v_h zw|a-~@Q-`H`rGtJxxb8`zu$j&yI;FAa3{Yxa(?rv3%iQ^bk7cvyL)yLWbnx6d6MaT zC$*J6@^Sj82yw_LNhVD{mSzWKXX5GehUf18M8+Le;(sk1<>$)(> z?+6VpQ`-iwNbCsej}i8rz;Jjx!f--2aGV~ml83UINI%M_6GIOPOhO$®O_#CaRt zu^)6)HB$gNHA+II1WlT0Q`vKpf|HD??mJ1-3_vw&CRP*im#IdwnDhJhKh0~*U9?Qf zn$zDkIMv#46)PH|amnHfnA~asb%@lMaPCm8|aOfHc8fh1iw@xgA`O`QD#{!&~ zA*LW(iu8`S_7;s7mmVf*LX(qrB!iCIo-XbkVTuG)gHbGOu{O@NUJ4gsgE~*kc{iLgBEfZ9Uu$04XFvQDZ+^$2S`cRq;RFT;T z9Qm2H8}U0(6ye|}gs|pUzD$*`10lx@yF#{4P6V|tZxDKm>qa|T_yQ#>er9=R{NRARO z_Tvy?gA!@UUI>0d>V)O@6bxwt5d_1OZ$a59%WnXJmdLX523eAdit+osND`hs7Q1~! z3(bG2?!d}&s96kZEVx1oTNnhK0850vYt|x#5Qdsy9z^1vpqs22%&4|uKPf^x7Ofqt z(&{{*1t4u`lOl~wzWMh4H-{$S%_4w14t!M0hF%Rjh4?U(6q*vq2^1DBLI6iZV2n7c zqB(A86x79v3+4yS#~zTOtXSQ)FlJaB^4;5_ts2{}@18btQUf9AI!+B%UeGFrWrM#A z5Coz_U1P2&D^Pg}`V`^d_B%9tr{Y2m*(gwVwy3gEcy&L0o;HHooQWTi<=_Sg5M?6H z*(oGVLWTFBmBdg&iT`h~PXax^0MiQDIh4#C-tHUxr0@7v-|-L5Z1qj7z4EY=boJj} zUMoEQyn}QNd`48DDfIuMxK%iMzyHMM_{(?uU-{$AX4h*Eo<8)`&YcY0f5G#>@K#}f zynlF7`JFVW%ob(#9Go?u51aWbK_fn*MQ#3-D_2Lf;PC2IFb@&Tkd63GtcuA!r)V_8 zem3CS6MU+*fX7S#`-mKCkMI#IMC4*s%(5Xocpj%=oG|hEX($qq>Ai+{0*2i(!<@me zPh+QX!jv17agkAu;}$1m$G;ELD!He=`XJHqnVe1Z>?$PLxr3{n0a*F!ggDt&CxF6N zFNeF!4-(n!)8F1D{fPlQ$~}}}6^KFRkqa5xQ|!g2rBa9gA<(o0jagxDH*L(98HNJG zJnAO_&`=-6rwseiz!rt7_dyP0QJlK1kx@14J!*H$w3@;$fD cu4HzTvh>uWY(auX38qIwqY}g$Ou`BO1+CTRuK)l5 literal 0 HcmV?d00001 diff --git a/.deps_siteforge_v812/anyio/_core/_asyncio_selector_thread.py b/.deps_siteforge_v812/anyio/_core/_asyncio_selector_thread.py new file mode 100644 index 0000000..9f35bae --- /dev/null +++ b/.deps_siteforge_v812/anyio/_core/_asyncio_selector_thread.py @@ -0,0 +1,167 @@ +from __future__ import annotations + +import asyncio +import socket +import threading +from collections.abc import Callable +from selectors import EVENT_READ, EVENT_WRITE, DefaultSelector +from typing import TYPE_CHECKING, Any + +if TYPE_CHECKING: + from _typeshed import FileDescriptorLike + +_selector_lock = threading.Lock() +_selector: Selector | None = None + + +class Selector: + def __init__(self) -> None: + self._thread = threading.Thread(target=self.run, name="AnyIO socket selector") + self._selector = DefaultSelector() + self._send, self._receive = socket.socketpair() + self._send.setblocking(False) + self._receive.setblocking(False) + # This somewhat reduces the amount of memory wasted queueing up data + # for wakeups. With these settings, maximum number of 1-byte sends + # before getting BlockingIOError: + # Linux 4.8: 6 + # macOS (darwin 15.5): 1 + # Windows 10: 525347 + # Windows you're weird. (And on Windows setting SNDBUF to 0 makes send + # blocking, even on non-blocking sockets, so don't do that.) + self._receive.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 1) + self._send.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, 1) + # On Windows this is a TCP socket so this might matter. On other + # platforms this fails b/c AF_UNIX sockets aren't actually TCP. + try: + self._send.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) + except OSError: + pass + + self._selector.register(self._receive, EVENT_READ) + self._closed = False + + def start(self) -> None: + self._thread.start() + threading._register_atexit(self._stop) # type: ignore[attr-defined] + + def _stop(self) -> None: + global _selector + self._closed = True + self._notify_self() + self._send.close() + self._thread.join() + self._selector.unregister(self._receive) + self._receive.close() + self._selector.close() + _selector = None + assert not self._selector.get_map(), ( + "selector still has registered file descriptors after shutdown" + ) + + def _notify_self(self) -> None: + try: + self._send.send(b"\x00") + except BlockingIOError: + pass + + def add_reader(self, fd: FileDescriptorLike, callback: Callable[[], Any]) -> None: + loop = asyncio.get_running_loop() + try: + key = self._selector.get_key(fd) + except KeyError: + self._selector.register(fd, EVENT_READ, {EVENT_READ: (loop, callback)}) + else: + if EVENT_READ in key.data: + raise ValueError( + "this file descriptor is already registered for reading" + ) + + key.data[EVENT_READ] = loop, callback + self._selector.modify(fd, key.events | EVENT_READ, key.data) + + self._notify_self() + + def add_writer(self, fd: FileDescriptorLike, callback: Callable[[], Any]) -> None: + loop = asyncio.get_running_loop() + try: + key = self._selector.get_key(fd) + except KeyError: + self._selector.register(fd, EVENT_WRITE, {EVENT_WRITE: (loop, callback)}) + else: + if EVENT_WRITE in key.data: + raise ValueError( + "this file descriptor is already registered for writing" + ) + + key.data[EVENT_WRITE] = loop, callback + self._selector.modify(fd, key.events | EVENT_WRITE, key.data) + + self._notify_self() + + def remove_reader(self, fd: FileDescriptorLike) -> bool: + try: + key = self._selector.get_key(fd) + except KeyError: + return False + + if new_events := key.events ^ EVENT_READ: + del key.data[EVENT_READ] + self._selector.modify(fd, new_events, key.data) + else: + self._selector.unregister(fd) + + return True + + def remove_writer(self, fd: FileDescriptorLike) -> bool: + try: + key = self._selector.get_key(fd) + except KeyError: + return False + + if new_events := key.events ^ EVENT_WRITE: + del key.data[EVENT_WRITE] + self._selector.modify(fd, new_events, key.data) + else: + self._selector.unregister(fd) + + return True + + def run(self) -> None: + while not self._closed: + for key, events in self._selector.select(): + if key.fileobj is self._receive: + try: + while self._receive.recv(4096): + pass + except BlockingIOError: + pass + + continue + + if events & EVENT_READ: + loop, callback = key.data[EVENT_READ] + self.remove_reader(key.fd) + try: + loop.call_soon_threadsafe(callback) + except RuntimeError: + pass # the loop was already closed + + if events & EVENT_WRITE: + loop, callback = key.data[EVENT_WRITE] + self.remove_writer(key.fd) + try: + loop.call_soon_threadsafe(callback) + except RuntimeError: + pass # the loop was already closed + + +def get_selector() -> Selector: + global _selector + + with _selector_lock: + if _selector is None: + _selector = Selector() + _selector.start() + + return _selector diff --git a/.deps_siteforge_v812/anyio/_core/_contextmanagers.py b/.deps_siteforge_v812/anyio/_core/_contextmanagers.py new file mode 100644 index 0000000..302f32b --- /dev/null +++ b/.deps_siteforge_v812/anyio/_core/_contextmanagers.py @@ -0,0 +1,200 @@ +from __future__ import annotations + +from abc import abstractmethod +from contextlib import AbstractAsyncContextManager, AbstractContextManager +from inspect import isasyncgen, iscoroutine, isgenerator +from types import TracebackType +from typing import Protocol, TypeVar, cast, final + +_T_co = TypeVar("_T_co", covariant=True) +_ExitT_co = TypeVar("_ExitT_co", covariant=True, bound="bool | None") + + +class _SupportsCtxMgr(Protocol[_T_co, _ExitT_co]): + def __contextmanager__(self) -> AbstractContextManager[_T_co, _ExitT_co]: ... + + +class _SupportsAsyncCtxMgr(Protocol[_T_co, _ExitT_co]): + def __asynccontextmanager__( + self, + ) -> AbstractAsyncContextManager[_T_co, _ExitT_co]: ... + + +class ContextManagerMixin: + """ + Mixin class providing context manager functionality via a generator-based + implementation. + + This class allows you to implement a context manager via :meth:`__contextmanager__` + which should return a generator. The mechanics are meant to mirror those of + :func:`@contextmanager `. + + .. note:: Classes using this mix-in are not reentrant as context managers, meaning + that once you enter it, you can't re-enter before first exiting it. + + .. seealso:: :doc:`contextmanagers` + """ + + __cm: AbstractContextManager[object, bool | None] | None = None + + @final + def __enter__(self: _SupportsCtxMgr[_T_co, bool | None]) -> _T_co: + # Needed for mypy to assume self still has the __cm member + assert isinstance(self, ContextManagerMixin) + if self.__cm is not None: + raise RuntimeError( + f"this {self.__class__.__qualname__} has already been entered" + ) + + cm = self.__contextmanager__() + if not isinstance(cm, AbstractContextManager): + if isgenerator(cm): + raise TypeError( + "__contextmanager__() returned a generator object instead of " + "a context manager. Did you forget to add the @contextmanager " + "decorator?" + ) + + raise TypeError( + f"__contextmanager__() did not return a context manager object, " + f"but {cm.__class__!r}" + ) + + if cm is self: + raise TypeError( + f"{self.__class__.__qualname__}.__contextmanager__() returned " + f"self. Did you forget to add the @contextmanager decorator and a " + f"'yield' statement?" + ) + + value = cm.__enter__() + self.__cm = cm + return value + + @final + def __exit__( + self: _SupportsCtxMgr[object, _ExitT_co], + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> _ExitT_co: + # Needed for mypy to assume self still has the __cm member + assert isinstance(self, ContextManagerMixin) + if self.__cm is None: + raise RuntimeError( + f"this {self.__class__.__qualname__} has not been entered yet" + ) + + # Prevent circular references + cm = self.__cm + del self.__cm + + return cast(_ExitT_co, cm.__exit__(exc_type, exc_val, exc_tb)) + + @abstractmethod + def __contextmanager__(self) -> AbstractContextManager[object, bool | None]: + """ + Implement your context manager logic here. + + This method **must** be decorated with + :func:`@contextmanager `. + + .. note:: Remember that the ``yield`` will raise any exception raised in the + enclosed context block, so use a ``finally:`` block to clean up resources! + + :return: a context manager object + """ + + +class AsyncContextManagerMixin: + """ + Mixin class providing async context manager functionality via a generator-based + implementation. + + This class allows you to implement a context manager via + :meth:`__asynccontextmanager__`. The mechanics are meant to mirror those of + :func:`@asynccontextmanager `. + + .. note:: Classes using this mix-in are not reentrant as context managers, meaning + that once you enter it, you can't re-enter before first exiting it. + + .. seealso:: :doc:`contextmanagers` + """ + + __cm: AbstractAsyncContextManager[object, bool | None] | None = None + + @final + async def __aenter__(self: _SupportsAsyncCtxMgr[_T_co, bool | None]) -> _T_co: + # Needed for mypy to assume self still has the __cm member + assert isinstance(self, AsyncContextManagerMixin) + if self.__cm is not None: + raise RuntimeError( + f"this {self.__class__.__qualname__} has already been entered" + ) + + cm = self.__asynccontextmanager__() + if not isinstance(cm, AbstractAsyncContextManager): + if isasyncgen(cm): + raise TypeError( + "__asynccontextmanager__() returned an async generator instead of " + "an async context manager. Did you forget to add the " + "@asynccontextmanager decorator?" + ) + elif iscoroutine(cm): + cm.close() + raise TypeError( + "__asynccontextmanager__() returned a coroutine object instead of " + "an async context manager. Did you forget to add the " + "@asynccontextmanager decorator and a 'yield' statement?" + ) + + raise TypeError( + f"__asynccontextmanager__() did not return an async context manager, " + f"but {cm.__class__!r}" + ) + + if cm is self: + raise TypeError( + f"{self.__class__.__qualname__}.__asynccontextmanager__() returned " + f"self. Did you forget to add the @asynccontextmanager decorator and a " + f"'yield' statement?" + ) + + value = await cm.__aenter__() + self.__cm = cm + return value + + @final + async def __aexit__( + self: _SupportsAsyncCtxMgr[object, _ExitT_co], + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> _ExitT_co: + assert isinstance(self, AsyncContextManagerMixin) + if self.__cm is None: + raise RuntimeError( + f"this {self.__class__.__qualname__} has not been entered yet" + ) + + # Prevent circular references + cm = self.__cm + del self.__cm + + return cast(_ExitT_co, await cm.__aexit__(exc_type, exc_val, exc_tb)) + + @abstractmethod + def __asynccontextmanager__( + self, + ) -> AbstractAsyncContextManager[object, bool | None]: + """ + Implement your async context manager logic here. + + This method **must** be decorated with + :func:`@asynccontextmanager `. + + .. note:: Remember that the ``yield`` will raise any exception raised in the + enclosed context block, so use a ``finally:`` block to clean up resources! + + :return: an async context manager object + """ diff --git a/.deps_siteforge_v812/anyio/_core/_eventloop.py b/.deps_siteforge_v812/anyio/_core/_eventloop.py new file mode 100644 index 0000000..59a69cc --- /dev/null +++ b/.deps_siteforge_v812/anyio/_core/_eventloop.py @@ -0,0 +1,234 @@ +from __future__ import annotations + +import math +import sys +import threading +from collections.abc import Awaitable, Callable, Generator +from contextlib import contextmanager +from contextvars import Token +from importlib import import_module +from typing import TYPE_CHECKING, Any, TypeVar + +from ._exceptions import NoEventLoopError + +if sys.version_info >= (3, 11): + from typing import TypeVarTuple, Unpack +else: + from typing_extensions import TypeVarTuple, Unpack + +sniffio: Any +try: + import sniffio +except ModuleNotFoundError: + sniffio = None + +if TYPE_CHECKING: + from ..abc import AsyncBackend + +# This must be updated when new backends are introduced +BACKENDS = "asyncio", "trio" + +T_Retval = TypeVar("T_Retval") +PosArgsT = TypeVarTuple("PosArgsT") + +threadlocals = threading.local() +loaded_backends: dict[str, type[AsyncBackend]] = {} + + +def run( + func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval]], + *args: Unpack[PosArgsT], + backend: str = "asyncio", + backend_options: dict[str, Any] | None = None, +) -> T_Retval: + """ + Run the given coroutine function in an asynchronous event loop. + + The current thread must not be already running an event loop. + + :param func: a coroutine function + :param args: positional arguments to ``func`` + :param backend: name of the asynchronous event loop implementation – currently + either ``asyncio`` or ``trio`` + :param backend_options: keyword arguments to call the backend ``run()`` + implementation with (documented :ref:`here `) + :return: the return value of the coroutine function + :raises RuntimeError: if an asynchronous event loop is already running in this + thread + :raises LookupError: if the named backend is not found + + """ + if asynclib_name := current_async_library(): + raise RuntimeError(f"Already running {asynclib_name} in this thread") + + try: + async_backend = get_async_backend(backend) + except ImportError as exc: + raise LookupError(f"No such backend: {backend}") from exc + + token = None + if asynclib_name is None: + # Since we're in control of the event loop, we can cache the name of the async + # library + token = set_current_async_library(backend) + + try: + backend_options = backend_options or {} + return async_backend.run(func, args, {}, backend_options) + finally: + reset_current_async_library(token) + + +async def sleep(delay: float) -> None: + """ + Pause the current task for the specified duration. + + :param delay: the duration, in seconds + + """ + return await get_async_backend().sleep(delay) + + +async def sleep_forever() -> None: + """ + Pause the current task until it's cancelled. + + This is a shortcut for ``sleep(math.inf)``. + + .. versionadded:: 3.1 + + """ + await sleep(math.inf) + + +async def sleep_until(deadline: float) -> None: + """ + Pause the current task until the given time. + + :param deadline: the absolute time to wake up at (according to the internal + monotonic clock of the event loop) + + .. versionadded:: 3.1 + + """ + now = current_time() + await sleep(max(deadline - now, 0)) + + +def current_time() -> float: + """ + Return the current value of the event loop's internal clock. + + :return: the clock value (seconds) + :raises NoEventLoopError: if no supported asynchronous event loop is running in the + current thread + + """ + return get_async_backend().current_time() + + +def get_all_backends() -> tuple[str, ...]: + """Return a tuple of the names of all built-in backends.""" + return BACKENDS + + +def get_available_backends() -> tuple[str, ...]: + """ + Test for the availability of built-in backends. + + :return a tuple of the built-in backend names that were successfully imported + + .. versionadded:: 4.12 + + """ + available_backends: list[str] = [] + for backend_name in get_all_backends(): + try: + get_async_backend(backend_name) + except ImportError: + continue + + available_backends.append(backend_name) + + return tuple(available_backends) + + +def get_cancelled_exc_class() -> type[BaseException]: + """ + Return the current async library's cancellation exception class. + + :raises NoEventLoopError: if no supported asynchronous event loop is running in the + current thread + + """ + return get_async_backend().cancelled_exception_class() + + +# +# Private API +# + + +@contextmanager +def claim_worker_thread( + backend_class: type[AsyncBackend], token: object +) -> Generator[Any, None, None]: + from ..lowlevel import EventLoopToken + + threadlocals.current_token = EventLoopToken(backend_class, token) + try: + yield + finally: + del threadlocals.current_token + + +def get_async_backend(asynclib_name: str | None = None) -> type[AsyncBackend]: + if asynclib_name is None: + asynclib_name = current_async_library() + if not asynclib_name: + raise NoEventLoopError( + f"Not currently running on any asynchronous event loop. " + f"Available async backends: {', '.join(get_all_backends())}" + ) + + # We use our own dict instead of sys.modules to get the already imported back-end + # class because the appropriate modules in sys.modules could potentially be only + # partially initialized + try: + return loaded_backends[asynclib_name] + except KeyError: + module = import_module(f"anyio._backends._{asynclib_name}") + loaded_backends[asynclib_name] = module.backend_class + return module.backend_class + + +def current_async_library() -> str | None: + if sniffio is None: + # If sniffio is not installed, we assume we're either running asyncio or nothing + import asyncio + + try: + asyncio.get_running_loop() + return "asyncio" + except RuntimeError: + pass + else: + try: + return sniffio.current_async_library() + except sniffio.AsyncLibraryNotFoundError: + pass + + return None + + +def set_current_async_library(asynclib_name: str | None) -> Token | None: + # no-op if sniffio is not installed + if sniffio is None: + return None + + return sniffio.current_async_library_cvar.set(asynclib_name) + + +def reset_current_async_library(token: Token | None) -> None: + if token is not None: + sniffio.current_async_library_cvar.reset(token) diff --git a/.deps_siteforge_v812/anyio/_core/_exceptions.py b/.deps_siteforge_v812/anyio/_core/_exceptions.py new file mode 100644 index 0000000..3776bed --- /dev/null +++ b/.deps_siteforge_v812/anyio/_core/_exceptions.py @@ -0,0 +1,156 @@ +from __future__ import annotations + +import sys +from collections.abc import Generator +from textwrap import dedent +from typing import Any + +if sys.version_info < (3, 11): + from exceptiongroup import BaseExceptionGroup + + +class BrokenResourceError(Exception): + """ + Raised when trying to use a resource that has been rendered unusable due to external + causes (e.g. a send stream whose peer has disconnected). + """ + + +class BrokenWorkerProcess(Exception): + """ + Raised by :meth:`~anyio.to_process.run_sync` if the worker process terminates abruptly or + otherwise misbehaves. + """ + + +class BrokenWorkerInterpreter(Exception): + """ + Raised by :meth:`~anyio.to_interpreter.run_sync` if an unexpected exception is + raised in the subinterpreter. + """ + + def __init__(self, excinfo: Any): + # This was adapted from concurrent.futures.interpreter.ExecutionFailed + msg = excinfo.formatted + if not msg: + if excinfo.type and excinfo.msg: + msg = f"{excinfo.type.__name__}: {excinfo.msg}" + else: + msg = excinfo.type.__name__ or excinfo.msg + + super().__init__(msg) + self.excinfo = excinfo + + def __str__(self) -> str: + try: + formatted = self.excinfo.errdisplay + except Exception: + return super().__str__() + else: + return dedent( + f""" + {super().__str__()} + + Uncaught in the interpreter: + + {formatted} + """.strip() + ) + + +class BusyResourceError(Exception): + """ + Raised when two tasks are trying to read from or write to the same resource + concurrently. + """ + + def __init__(self, action: str): + super().__init__(f"Another task is already {action} this resource") + + +class ClosedResourceError(Exception): + """Raised when trying to use a resource that has been closed.""" + + +class ConnectionFailed(OSError): + """ + Raised when a connection attempt fails. + + .. note:: This class inherits from :exc:`OSError` for backwards compatibility. + """ + + +def iterate_exceptions( + exception: BaseException, +) -> Generator[BaseException, None, None]: + if isinstance(exception, BaseExceptionGroup): + for exc in exception.exceptions: + yield from iterate_exceptions(exc) + else: + yield exception + + +class DelimiterNotFound(Exception): + """ + Raised during + :meth:`~anyio.streams.buffered.BufferedByteReceiveStream.receive_until` if the + maximum number of bytes has been read without the delimiter being found. + """ + + def __init__(self, max_bytes: int) -> None: + super().__init__( + f"The delimiter was not found among the first {max_bytes} bytes" + ) + + +class EndOfStream(Exception): + """ + Raised when trying to read from a stream that has been closed from the other end. + """ + + +class IncompleteRead(Exception): + """ + Raised during + :meth:`~anyio.streams.buffered.BufferedByteReceiveStream.receive_exactly` or + :meth:`~anyio.streams.buffered.BufferedByteReceiveStream.receive_until` if the + connection is closed before the requested amount of bytes has been read. + """ + + def __init__(self) -> None: + super().__init__( + "The stream was closed before the read operation could be completed" + ) + + +class TypedAttributeLookupError(LookupError): + """ + Raised by :meth:`~anyio.TypedAttributeProvider.extra` when the given typed attribute + is not found and no default value has been given. + """ + + +class WouldBlock(Exception): + """Raised by ``X_nowait`` functions if ``X()`` would block.""" + + +class NoEventLoopError(RuntimeError): + """ + Raised by several functions that require an event loop to be running in the current + thread when there is no running event loop. + + This is also raised by :func:`.from_thread.run` and :func:`.from_thread.run_sync` + if not calling from an AnyIO worker thread, and no ``token`` was passed. + """ + + +class RunFinishedError(RuntimeError): + """ + Raised by :func:`.from_thread.run` and :func:`.from_thread.run_sync` if the event + loop associated with the explicitly passed token has already finished. + """ + + def __init__(self) -> None: + super().__init__( + "The event loop associated with the given token has already finished" + ) diff --git a/.deps_siteforge_v812/anyio/_core/_fileio.py b/.deps_siteforge_v812/anyio/_core/_fileio.py new file mode 100644 index 0000000..3bb8c84 --- /dev/null +++ b/.deps_siteforge_v812/anyio/_core/_fileio.py @@ -0,0 +1,799 @@ +from __future__ import annotations + +import os +import pathlib +import sys +from collections.abc import ( + AsyncIterator, + Callable, + Iterable, + Iterator, + Sequence, +) +from dataclasses import dataclass +from functools import partial +from os import PathLike +from typing import ( + IO, + TYPE_CHECKING, + Any, + AnyStr, + ClassVar, + Final, + Generic, + overload, +) + +from .. import to_thread +from ..abc import AsyncResource + +if TYPE_CHECKING: + from types import ModuleType + + from _typeshed import OpenBinaryMode, OpenTextMode, ReadableBuffer, WriteableBuffer +else: + ReadableBuffer = OpenBinaryMode = OpenTextMode = WriteableBuffer = object + + +class AsyncFile(AsyncResource, Generic[AnyStr]): + """ + An asynchronous file object. + + This class wraps a standard file object and provides async friendly versions of the + following blocking methods (where available on the original file object): + + * read + * read1 + * readline + * readlines + * readinto + * readinto1 + * write + * writelines + * truncate + * seek + * tell + * flush + + All other methods are directly passed through. + + This class supports the asynchronous context manager protocol which closes the + underlying file at the end of the context block. + + This class also supports asynchronous iteration:: + + async with await open_file(...) as f: + async for line in f: + print(line) + """ + + def __init__(self, fp: IO[AnyStr]) -> None: + self._fp: Any = fp + + def __getattr__(self, name: str) -> object: + return getattr(self._fp, name) + + @property + def wrapped(self) -> IO[AnyStr]: + """The wrapped file object.""" + return self._fp + + async def __aiter__(self) -> AsyncIterator[AnyStr]: + while True: + line = await self.readline() + if line: + yield line + else: + break + + async def aclose(self) -> None: + return await to_thread.run_sync(self._fp.close) + + async def read(self, size: int = -1) -> AnyStr: + return await to_thread.run_sync(self._fp.read, size) + + async def read1(self: AsyncFile[bytes], size: int = -1) -> bytes: + return await to_thread.run_sync(self._fp.read1, size) + + async def readline(self) -> AnyStr: + return await to_thread.run_sync(self._fp.readline) + + async def readlines(self) -> list[AnyStr]: + return await to_thread.run_sync(self._fp.readlines) + + async def readinto(self: AsyncFile[bytes], b: WriteableBuffer) -> int: + return await to_thread.run_sync(self._fp.readinto, b) + + async def readinto1(self: AsyncFile[bytes], b: WriteableBuffer) -> int: + return await to_thread.run_sync(self._fp.readinto1, b) + + @overload + async def write(self: AsyncFile[bytes], b: ReadableBuffer) -> int: ... + + @overload + async def write(self: AsyncFile[str], b: str) -> int: ... + + async def write(self, b: ReadableBuffer | str) -> int: + return await to_thread.run_sync(self._fp.write, b) + + @overload + async def writelines( + self: AsyncFile[bytes], lines: Iterable[ReadableBuffer] + ) -> None: ... + + @overload + async def writelines(self: AsyncFile[str], lines: Iterable[str]) -> None: ... + + async def writelines(self, lines: Iterable[ReadableBuffer] | Iterable[str]) -> None: + return await to_thread.run_sync(self._fp.writelines, lines) + + async def truncate(self, size: int | None = None) -> int: + return await to_thread.run_sync(self._fp.truncate, size) + + async def seek(self, offset: int, whence: int | None = os.SEEK_SET) -> int: + return await to_thread.run_sync(self._fp.seek, offset, whence) + + async def tell(self) -> int: + return await to_thread.run_sync(self._fp.tell) + + async def flush(self) -> None: + return await to_thread.run_sync(self._fp.flush) + + +@overload +async def open_file( + file: str | PathLike[str] | int, + mode: OpenBinaryMode, + buffering: int = ..., + encoding: str | None = ..., + errors: str | None = ..., + newline: str | None = ..., + closefd: bool = ..., + opener: Callable[[str, int], int] | None = ..., +) -> AsyncFile[bytes]: ... + + +@overload +async def open_file( + file: str | PathLike[str] | int, + mode: OpenTextMode = ..., + buffering: int = ..., + encoding: str | None = ..., + errors: str | None = ..., + newline: str | None = ..., + closefd: bool = ..., + opener: Callable[[str, int], int] | None = ..., +) -> AsyncFile[str]: ... + + +async def open_file( + file: str | PathLike[str] | int, + mode: str = "r", + buffering: int = -1, + encoding: str | None = None, + errors: str | None = None, + newline: str | None = None, + closefd: bool = True, + opener: Callable[[str, int], int] | None = None, +) -> AsyncFile[Any]: + """ + Open a file asynchronously. + + The arguments are exactly the same as for the builtin :func:`open`. + + :return: an asynchronous file object + + """ + fp = await to_thread.run_sync( + open, file, mode, buffering, encoding, errors, newline, closefd, opener + ) + return AsyncFile(fp) + + +def wrap_file(file: IO[AnyStr]) -> AsyncFile[AnyStr]: + """ + Wrap an existing file as an asynchronous file. + + :param file: an existing file-like object + :return: an asynchronous file object + + """ + return AsyncFile(file) + + +@dataclass(eq=False) +class _PathIterator(AsyncIterator["Path"]): + iterator: Iterator[PathLike[str]] + + async def __anext__(self) -> Path: + nextval = await to_thread.run_sync( + next, self.iterator, None, abandon_on_cancel=True + ) + if nextval is None: + raise StopAsyncIteration from None + + return Path(nextval) + + +class Path: + """ + An asynchronous version of :class:`pathlib.Path`. + + This class cannot be substituted for :class:`pathlib.Path` or + :class:`pathlib.PurePath`, but it is compatible with the :class:`os.PathLike` + interface. + + It implements the Python 3.10 version of :class:`pathlib.Path` interface, except for + the deprecated :meth:`~pathlib.Path.link_to` method. + + Some methods may be unavailable or have limited functionality, based on the Python + version: + + * :meth:`~pathlib.Path.copy` (available on Python 3.14 or later) + * :meth:`~pathlib.Path.copy_into` (available on Python 3.14 or later) + * :meth:`~pathlib.Path.from_uri` (available on Python 3.13 or later) + * :meth:`~pathlib.PurePath.full_match` (available on Python 3.13 or later) + * :attr:`~pathlib.Path.info` (available on Python 3.14 or later) + * :meth:`~pathlib.Path.is_junction` (available on Python 3.12 or later) + * :meth:`~pathlib.PurePath.match` (the ``case_sensitive`` parameter is only + available on Python 3.13 or later) + * :meth:`~pathlib.Path.move` (available on Python 3.14 or later) + * :meth:`~pathlib.Path.move_into` (available on Python 3.14 or later) + * :meth:`~pathlib.PurePath.relative_to` (the ``walk_up`` parameter is only available + on Python 3.12 or later) + * :meth:`~pathlib.Path.walk` (available on Python 3.12 or later) + + Any methods that do disk I/O need to be awaited on. These methods are: + + * :meth:`~pathlib.Path.absolute` + * :meth:`~pathlib.Path.chmod` + * :meth:`~pathlib.Path.cwd` + * :meth:`~pathlib.Path.exists` + * :meth:`~pathlib.Path.expanduser` + * :meth:`~pathlib.Path.group` + * :meth:`~pathlib.Path.hardlink_to` + * :meth:`~pathlib.Path.home` + * :meth:`~pathlib.Path.is_block_device` + * :meth:`~pathlib.Path.is_char_device` + * :meth:`~pathlib.Path.is_dir` + * :meth:`~pathlib.Path.is_fifo` + * :meth:`~pathlib.Path.is_file` + * :meth:`~pathlib.Path.is_junction` + * :meth:`~pathlib.Path.is_mount` + * :meth:`~pathlib.Path.is_socket` + * :meth:`~pathlib.Path.is_symlink` + * :meth:`~pathlib.Path.lchmod` + * :meth:`~pathlib.Path.lstat` + * :meth:`~pathlib.Path.mkdir` + * :meth:`~pathlib.Path.open` + * :meth:`~pathlib.Path.owner` + * :meth:`~pathlib.Path.read_bytes` + * :meth:`~pathlib.Path.read_text` + * :meth:`~pathlib.Path.readlink` + * :meth:`~pathlib.Path.rename` + * :meth:`~pathlib.Path.replace` + * :meth:`~pathlib.Path.resolve` + * :meth:`~pathlib.Path.rmdir` + * :meth:`~pathlib.Path.samefile` + * :meth:`~pathlib.Path.stat` + * :meth:`~pathlib.Path.symlink_to` + * :meth:`~pathlib.Path.touch` + * :meth:`~pathlib.Path.unlink` + * :meth:`~pathlib.Path.walk` + * :meth:`~pathlib.Path.write_bytes` + * :meth:`~pathlib.Path.write_text` + + Additionally, the following methods return an async iterator yielding + :class:`~.Path` objects: + + * :meth:`~pathlib.Path.glob` + * :meth:`~pathlib.Path.iterdir` + * :meth:`~pathlib.Path.rglob` + """ + + __slots__ = "_path", "__weakref__" + + __weakref__: Any + + def __init__(self, *args: str | PathLike[str]) -> None: + self._path: Final[pathlib.Path] = pathlib.Path(*args) + + def __fspath__(self) -> str: + return self._path.__fspath__() + + if sys.version_info >= (3, 15): + + def __vfspath__(self) -> str: + return self._path.__vfspath__() + + def __str__(self) -> str: + return self._path.__str__() + + def __repr__(self) -> str: + return f"{self.__class__.__name__}({self.as_posix()!r})" + + def __bytes__(self) -> bytes: + return self._path.__bytes__() + + def __hash__(self) -> int: + return self._path.__hash__() + + def __eq__(self, other: object) -> bool: + target = other._path if isinstance(other, Path) else other + return self._path.__eq__(target) + + def __lt__(self, other: pathlib.PurePath | Path) -> bool: + target = other._path if isinstance(other, Path) else other + return self._path.__lt__(target) + + def __le__(self, other: pathlib.PurePath | Path) -> bool: + target = other._path if isinstance(other, Path) else other + return self._path.__le__(target) + + def __gt__(self, other: pathlib.PurePath | Path) -> bool: + target = other._path if isinstance(other, Path) else other + return self._path.__gt__(target) + + def __ge__(self, other: pathlib.PurePath | Path) -> bool: + target = other._path if isinstance(other, Path) else other + return self._path.__ge__(target) + + def __truediv__(self, other: str | PathLike[str]) -> Path: + return Path(self._path / other) + + def __rtruediv__(self, other: str | PathLike[str]) -> Path: + return Path(other) / self + + @property + def parts(self) -> tuple[str, ...]: + return self._path.parts + + @property + def drive(self) -> str: + return self._path.drive + + @property + def root(self) -> str: + return self._path.root + + @property + def anchor(self) -> str: + return self._path.anchor + + @property + def parents(self) -> Sequence[Path]: + return tuple(Path(p) for p in self._path.parents) + + @property + def parent(self) -> Path: + return Path(self._path.parent) + + @property + def name(self) -> str: + return self._path.name + + @property + def suffix(self) -> str: + return self._path.suffix + + @property + def suffixes(self) -> list[str]: + return self._path.suffixes + + @property + def stem(self) -> str: + return self._path.stem + + async def absolute(self) -> Path: + path = await to_thread.run_sync(self._path.absolute) + return Path(path) + + def as_posix(self) -> str: + return self._path.as_posix() + + def as_uri(self) -> str: + return self._path.as_uri() + + if sys.version_info >= (3, 13): + parser: ClassVar[ModuleType] = pathlib.Path.parser + + @classmethod + def from_uri(cls, uri: str) -> Path: + return Path(pathlib.Path.from_uri(uri)) + + def full_match( + self, path_pattern: str, *, case_sensitive: bool | None = None + ) -> bool: + return self._path.full_match(path_pattern, case_sensitive=case_sensitive) + + def match( + self, path_pattern: str, *, case_sensitive: bool | None = None + ) -> bool: + return self._path.match(path_pattern, case_sensitive=case_sensitive) + else: + + def match(self, path_pattern: str) -> bool: + return self._path.match(path_pattern) + + if sys.version_info >= (3, 14): + + @property + def info(self) -> Any: # TODO: add return type annotation when Typeshed gets it + return self._path.info + + async def copy( + self, + target: str | os.PathLike[str], + *, + follow_symlinks: bool = True, + preserve_metadata: bool = False, + ) -> Path: + func = partial( + self._path.copy, + follow_symlinks=follow_symlinks, + preserve_metadata=preserve_metadata, + ) + return Path(await to_thread.run_sync(func, pathlib.Path(target))) + + async def copy_into( + self, + target_dir: str | os.PathLike[str], + *, + follow_symlinks: bool = True, + preserve_metadata: bool = False, + ) -> Path: + func = partial( + self._path.copy_into, + follow_symlinks=follow_symlinks, + preserve_metadata=preserve_metadata, + ) + return Path(await to_thread.run_sync(func, pathlib.Path(target_dir))) + + async def move(self, target: str | os.PathLike[str]) -> Path: + # Upstream does not handle anyio.Path properly as a PathLike + target = pathlib.Path(target) + return Path(await to_thread.run_sync(self._path.move, target)) + + async def move_into( + self, + target_dir: str | os.PathLike[str], + ) -> Path: + return Path(await to_thread.run_sync(self._path.move_into, target_dir)) + + def is_relative_to(self, other: str | PathLike[str]) -> bool: + try: + self.relative_to(other) + return True + except ValueError: + return False + + async def chmod(self, mode: int, *, follow_symlinks: bool = True) -> None: + func = partial(os.chmod, follow_symlinks=follow_symlinks) + return await to_thread.run_sync(func, self._path, mode) + + @classmethod + async def cwd(cls) -> Path: + path = await to_thread.run_sync(pathlib.Path.cwd) + return cls(path) + + async def exists(self) -> bool: + return await to_thread.run_sync(self._path.exists, abandon_on_cancel=True) + + async def expanduser(self) -> Path: + return Path( + await to_thread.run_sync(self._path.expanduser, abandon_on_cancel=True) + ) + + if sys.version_info < (3, 12): + # Python 3.11 and earlier + def glob(self, pattern: str) -> AsyncIterator[Path]: + gen = self._path.glob(pattern) + return _PathIterator(gen) + elif (3, 12) <= sys.version_info < (3, 13): + # changed in Python 3.12: + # - The case_sensitive parameter was added. + def glob( + self, + pattern: str, + *, + case_sensitive: bool | None = None, + ) -> AsyncIterator[Path]: + gen = self._path.glob(pattern, case_sensitive=case_sensitive) + return _PathIterator(gen) + elif sys.version_info >= (3, 13): + # Changed in Python 3.13: + # - The recurse_symlinks parameter was added. + # - The pattern parameter accepts a path-like object. + def glob( # type: ignore[misc] # mypy doesn't allow for differing signatures in a conditional block + self, + pattern: str | PathLike[str], + *, + case_sensitive: bool | None = None, + recurse_symlinks: bool = False, + ) -> AsyncIterator[Path]: + gen = self._path.glob( + pattern, # type: ignore[arg-type] + case_sensitive=case_sensitive, + recurse_symlinks=recurse_symlinks, + ) + return _PathIterator(gen) + + async def group(self) -> str: + return await to_thread.run_sync(self._path.group, abandon_on_cancel=True) + + async def hardlink_to( + self, target: str | bytes | PathLike[str] | PathLike[bytes] + ) -> None: + if isinstance(target, Path): + target = target._path + + await to_thread.run_sync(os.link, target, self) + + @classmethod + async def home(cls) -> Path: + home_path = await to_thread.run_sync(pathlib.Path.home) + return cls(home_path) + + def is_absolute(self) -> bool: + return self._path.is_absolute() + + async def is_block_device(self) -> bool: + return await to_thread.run_sync( + self._path.is_block_device, abandon_on_cancel=True + ) + + async def is_char_device(self) -> bool: + return await to_thread.run_sync( + self._path.is_char_device, abandon_on_cancel=True + ) + + async def is_dir(self) -> bool: + return await to_thread.run_sync(self._path.is_dir, abandon_on_cancel=True) + + async def is_fifo(self) -> bool: + return await to_thread.run_sync(self._path.is_fifo, abandon_on_cancel=True) + + async def is_file(self) -> bool: + return await to_thread.run_sync(self._path.is_file, abandon_on_cancel=True) + + if sys.version_info >= (3, 12): + + async def is_junction(self) -> bool: + return await to_thread.run_sync(self._path.is_junction) + + async def is_mount(self) -> bool: + return await to_thread.run_sync( + os.path.ismount, self._path, abandon_on_cancel=True + ) + + if sys.version_info < (3, 15): + + def is_reserved(self) -> bool: + return self._path.is_reserved() + + async def is_socket(self) -> bool: + return await to_thread.run_sync(self._path.is_socket, abandon_on_cancel=True) + + async def is_symlink(self) -> bool: + return await to_thread.run_sync(self._path.is_symlink, abandon_on_cancel=True) + + async def iterdir(self) -> AsyncIterator[Path]: + gen = ( + self._path.iterdir() + if sys.version_info < (3, 13) + else await to_thread.run_sync(self._path.iterdir, abandon_on_cancel=True) + ) + async for path in _PathIterator(gen): + yield path + + def joinpath(self, *args: str | PathLike[str]) -> Path: + return Path(self._path.joinpath(*args)) + + async def lchmod(self, mode: int) -> None: + await to_thread.run_sync(self._path.lchmod, mode) + + async def lstat(self) -> os.stat_result: + return await to_thread.run_sync(self._path.lstat, abandon_on_cancel=True) + + async def mkdir( + self, mode: int = 0o777, parents: bool = False, exist_ok: bool = False + ) -> None: + await to_thread.run_sync(self._path.mkdir, mode, parents, exist_ok) + + @overload + async def open( + self, + mode: OpenBinaryMode, + buffering: int = ..., + encoding: str | None = ..., + errors: str | None = ..., + newline: str | None = ..., + ) -> AsyncFile[bytes]: ... + + @overload + async def open( + self, + mode: OpenTextMode = ..., + buffering: int = ..., + encoding: str | None = ..., + errors: str | None = ..., + newline: str | None = ..., + ) -> AsyncFile[str]: ... + + async def open( + self, + mode: str = "r", + buffering: int = -1, + encoding: str | None = None, + errors: str | None = None, + newline: str | None = None, + ) -> AsyncFile[Any]: + fp = await to_thread.run_sync( + self._path.open, mode, buffering, encoding, errors, newline + ) + return AsyncFile(fp) + + async def owner(self) -> str: + return await to_thread.run_sync(self._path.owner, abandon_on_cancel=True) + + async def read_bytes(self) -> bytes: + return await to_thread.run_sync(self._path.read_bytes) + + async def read_text( + self, encoding: str | None = None, errors: str | None = None + ) -> str: + return await to_thread.run_sync(self._path.read_text, encoding, errors) + + if sys.version_info >= (3, 12): + + def relative_to( + self, *other: str | PathLike[str], walk_up: bool = False + ) -> Path: + # relative_to() should work with any PathLike but it doesn't + others = [pathlib.Path(other) for other in other] + return Path(self._path.relative_to(*others, walk_up=walk_up)) + + else: + + def relative_to(self, *other: str | PathLike[str]) -> Path: + return Path(self._path.relative_to(*other)) + + async def readlink(self) -> Path: + target = await to_thread.run_sync(os.readlink, self._path) + return Path(target) + + async def rename(self, target: str | pathlib.PurePath | Path) -> Path: + if isinstance(target, Path): + target = target._path + + await to_thread.run_sync(self._path.rename, target) + return Path(target) + + async def replace(self, target: str | pathlib.PurePath | Path) -> Path: + if isinstance(target, Path): + target = target._path + + await to_thread.run_sync(self._path.replace, target) + return Path(target) + + async def resolve(self, strict: bool = False) -> Path: + func = partial(self._path.resolve, strict=strict) + return Path(await to_thread.run_sync(func, abandon_on_cancel=True)) + + if sys.version_info < (3, 12): + # Pre Python 3.12 + def rglob(self, pattern: str) -> AsyncIterator[Path]: + gen = self._path.rglob(pattern) + return _PathIterator(gen) + elif (3, 12) <= sys.version_info < (3, 13): + # Changed in Python 3.12: + # - The case_sensitive parameter was added. + def rglob( + self, pattern: str, *, case_sensitive: bool | None = None + ) -> AsyncIterator[Path]: + gen = self._path.rglob(pattern, case_sensitive=case_sensitive) + return _PathIterator(gen) + elif sys.version_info >= (3, 13): + # Changed in Python 3.13: + # - The recurse_symlinks parameter was added. + # - The pattern parameter accepts a path-like object. + def rglob( # type: ignore[misc] # mypy doesn't allow for differing signatures in a conditional block + self, + pattern: str | PathLike[str], + *, + case_sensitive: bool | None = None, + recurse_symlinks: bool = False, + ) -> AsyncIterator[Path]: + gen = self._path.rglob( + pattern, # type: ignore[arg-type] + case_sensitive=case_sensitive, + recurse_symlinks=recurse_symlinks, + ) + return _PathIterator(gen) + + async def rmdir(self) -> None: + await to_thread.run_sync(self._path.rmdir) + + async def samefile(self, other_path: str | PathLike[str]) -> bool: + if isinstance(other_path, Path): + other_path = other_path._path + + return await to_thread.run_sync( + self._path.samefile, other_path, abandon_on_cancel=True + ) + + async def stat(self, *, follow_symlinks: bool = True) -> os.stat_result: + func = partial(os.stat, follow_symlinks=follow_symlinks) + return await to_thread.run_sync(func, self._path, abandon_on_cancel=True) + + async def symlink_to( + self, + target: str | bytes | PathLike[str] | PathLike[bytes], + target_is_directory: bool = False, + ) -> None: + if isinstance(target, Path): + target = target._path + + await to_thread.run_sync(self._path.symlink_to, target, target_is_directory) + + async def touch(self, mode: int = 0o666, exist_ok: bool = True) -> None: + await to_thread.run_sync(self._path.touch, mode, exist_ok) + + async def unlink(self, missing_ok: bool = False) -> None: + try: + await to_thread.run_sync(self._path.unlink) + except FileNotFoundError: + if not missing_ok: + raise + + if sys.version_info >= (3, 12): + + async def walk( + self, + top_down: bool = True, + on_error: Callable[[OSError], object] | None = None, + follow_symlinks: bool = False, + ) -> AsyncIterator[tuple[Path, list[str], list[str]]]: + def get_next_value() -> tuple[pathlib.Path, list[str], list[str]] | None: + try: + return next(gen) + except StopIteration: + return None + + gen = self._path.walk(top_down, on_error, follow_symlinks) + while True: + value = await to_thread.run_sync(get_next_value) + if value is None: + return + + root, dirs, paths = value + yield Path(root), dirs, paths + + def with_name(self, name: str) -> Path: + return Path(self._path.with_name(name)) + + def with_stem(self, stem: str) -> Path: + return Path(self._path.with_name(stem + self._path.suffix)) + + def with_suffix(self, suffix: str) -> Path: + return Path(self._path.with_suffix(suffix)) + + def with_segments(self, *pathsegments: str | PathLike[str]) -> Path: + return Path(*pathsegments) + + async def write_bytes(self, data: bytes) -> int: + return await to_thread.run_sync(self._path.write_bytes, data) + + async def write_text( + self, + data: str, + encoding: str | None = None, + errors: str | None = None, + newline: str | None = None, + ) -> int: + return await to_thread.run_sync( + self._path.write_text, data, encoding, errors, newline + ) + + +PathLike.register(Path) diff --git a/.deps_siteforge_v812/anyio/_core/_resources.py b/.deps_siteforge_v812/anyio/_core/_resources.py new file mode 100644 index 0000000..b9a5344 --- /dev/null +++ b/.deps_siteforge_v812/anyio/_core/_resources.py @@ -0,0 +1,18 @@ +from __future__ import annotations + +from ..abc import AsyncResource +from ._tasks import CancelScope + + +async def aclose_forcefully(resource: AsyncResource) -> None: + """ + Close an asynchronous resource in a cancelled scope. + + Doing this closes the resource without waiting on anything. + + :param resource: the resource to close + + """ + with CancelScope() as scope: + scope.cancel() + await resource.aclose() diff --git a/.deps_siteforge_v812/anyio/_core/_signals.py b/.deps_siteforge_v812/anyio/_core/_signals.py new file mode 100644 index 0000000..e24c79e --- /dev/null +++ b/.deps_siteforge_v812/anyio/_core/_signals.py @@ -0,0 +1,29 @@ +from __future__ import annotations + +from collections.abc import AsyncIterator +from contextlib import AbstractContextManager +from signal import Signals + +from ._eventloop import get_async_backend + + +def open_signal_receiver( + *signals: Signals, +) -> AbstractContextManager[AsyncIterator[Signals]]: + """ + Start receiving operating system signals. + + :param signals: signals to receive (e.g. ``signal.SIGINT``) + :return: an asynchronous context manager for an asynchronous iterator which yields + signal numbers + :raises NoEventLoopError: if no supported asynchronous event loop is running in the + current thread + + .. warning:: Windows does not support signals natively so it is best to avoid + relying on this in cross-platform applications. + + .. warning:: On asyncio, this permanently replaces any previous signal handler for + the given signals, as set via :meth:`~asyncio.loop.add_signal_handler`. + + """ + return get_async_backend().open_signal_receiver(*signals) diff --git a/.deps_siteforge_v812/anyio/_core/_sockets.py b/.deps_siteforge_v812/anyio/_core/_sockets.py new file mode 100644 index 0000000..6c99b3a --- /dev/null +++ b/.deps_siteforge_v812/anyio/_core/_sockets.py @@ -0,0 +1,1003 @@ +from __future__ import annotations + +import errno +import os +import socket +import ssl +import stat +import sys +from collections.abc import Awaitable +from dataclasses import dataclass +from ipaddress import IPv4Address, IPv6Address, ip_address +from os import PathLike, chmod +from socket import AddressFamily, SocketKind +from typing import TYPE_CHECKING, Any, Literal, cast, overload + +from .. import ConnectionFailed, to_thread +from ..abc import ( + ByteStreamConnectable, + ConnectedUDPSocket, + ConnectedUNIXDatagramSocket, + IPAddressType, + IPSockAddrType, + SocketListener, + SocketStream, + UDPSocket, + UNIXDatagramSocket, + UNIXSocketStream, +) +from ..streams.stapled import MultiListener +from ..streams.tls import TLSConnectable, TLSStream +from ._eventloop import get_async_backend +from ._resources import aclose_forcefully +from ._synchronization import Event +from ._tasks import create_task_group, move_on_after + +if TYPE_CHECKING: + from _typeshed import FileDescriptorLike +else: + FileDescriptorLike = object + +if sys.version_info < (3, 11): + from exceptiongroup import ExceptionGroup + +if sys.version_info >= (3, 12): + from typing import override +else: + from typing_extensions import override + +if sys.version_info < (3, 13): + from typing_extensions import deprecated +else: + from warnings import deprecated + +IPPROTO_IPV6 = getattr(socket, "IPPROTO_IPV6", 41) # https://bugs.python.org/issue29515 + +AnyIPAddressFamily = Literal[ + AddressFamily.AF_UNSPEC, AddressFamily.AF_INET, AddressFamily.AF_INET6 +] +IPAddressFamily = Literal[AddressFamily.AF_INET, AddressFamily.AF_INET6] + + +# tls_hostname given +@overload +async def connect_tcp( + remote_host: IPAddressType, + remote_port: int, + *, + local_host: IPAddressType | None = ..., + ssl_context: ssl.SSLContext | None = ..., + tls_standard_compatible: bool = ..., + tls_hostname: str, + happy_eyeballs_delay: float = ..., +) -> TLSStream: ... + + +# ssl_context given +@overload +async def connect_tcp( + remote_host: IPAddressType, + remote_port: int, + *, + local_host: IPAddressType | None = ..., + ssl_context: ssl.SSLContext, + tls_standard_compatible: bool = ..., + tls_hostname: str | None = ..., + happy_eyeballs_delay: float = ..., +) -> TLSStream: ... + + +# tls=True +@overload +async def connect_tcp( + remote_host: IPAddressType, + remote_port: int, + *, + local_host: IPAddressType | None = ..., + tls: Literal[True], + ssl_context: ssl.SSLContext | None = ..., + tls_standard_compatible: bool = ..., + tls_hostname: str | None = ..., + happy_eyeballs_delay: float = ..., +) -> TLSStream: ... + + +# tls=False +@overload +async def connect_tcp( + remote_host: IPAddressType, + remote_port: int, + *, + local_host: IPAddressType | None = ..., + tls: Literal[False], + ssl_context: ssl.SSLContext | None = ..., + tls_standard_compatible: bool = ..., + tls_hostname: str | None = ..., + happy_eyeballs_delay: float = ..., +) -> SocketStream: ... + + +# No TLS arguments +@overload +async def connect_tcp( + remote_host: IPAddressType, + remote_port: int, + *, + local_host: IPAddressType | None = ..., + happy_eyeballs_delay: float = ..., +) -> SocketStream: ... + + +async def connect_tcp( + remote_host: IPAddressType, + remote_port: int, + *, + local_host: IPAddressType | None = None, + tls: bool = False, + ssl_context: ssl.SSLContext | None = None, + tls_standard_compatible: bool = True, + tls_hostname: str | None = None, + happy_eyeballs_delay: float = 0.25, +) -> SocketStream | TLSStream: + """ + Connect to a host using the TCP protocol. + + This function implements the stateless version of the Happy Eyeballs algorithm (RFC + 6555). If ``remote_host`` is a host name that resolves to multiple IP addresses, + each one is tried until one connection attempt succeeds. If the first attempt does + not connected within 250 milliseconds, a second attempt is started using the next + address in the list, and so on. On IPv6 enabled systems, an IPv6 address (if + available) is tried first. + + When the connection has been established, a TLS handshake will be done if either + ``ssl_context`` or ``tls_hostname`` is not ``None``, or if ``tls`` is ``True``. + + :param remote_host: the IP address or host name to connect to + :param remote_port: port on the target host to connect to + :param local_host: the interface address or name to bind the socket to before + connecting + :param tls: ``True`` to do a TLS handshake with the connected stream and return a + :class:`~anyio.streams.tls.TLSStream` instead + :param ssl_context: the SSL context object to use (if omitted, a default context is + created) + :param tls_standard_compatible: If ``True``, performs the TLS shutdown handshake + before closing the stream and requires that the server does this as well. + Otherwise, :exc:`~ssl.SSLEOFError` may be raised during reads from the stream. + Some protocols, such as HTTP, require this option to be ``False``. + See :meth:`~ssl.SSLContext.wrap_socket` for details. + :param tls_hostname: host name to check the server certificate against (defaults to + the value of ``remote_host``) + :param happy_eyeballs_delay: delay (in seconds) before starting the next connection + attempt + :return: a socket stream object if no TLS handshake was done, otherwise a TLS stream + :raises ConnectionFailed: if the connection fails + + """ + # Placed here due to https://github.com/python/mypy/issues/7057 + connected_stream: SocketStream | None = None + + async def try_connect(remote_host: str, event: Event) -> None: + nonlocal connected_stream + try: + stream = await asynclib.connect_tcp(remote_host, remote_port, local_address) + except OSError as exc: + oserrors.append(exc) + return + else: + if connected_stream is None: + connected_stream = stream + tg.cancel_scope.cancel() + else: + await stream.aclose() + finally: + event.set() + + asynclib = get_async_backend() + local_address: IPSockAddrType | None = None + family = socket.AF_UNSPEC + if local_host: + gai_res = await getaddrinfo(str(local_host), None) + family, *_, local_address = gai_res[0] + + target_host = str(remote_host) + try: + addr_obj = ip_address(remote_host) + except ValueError: + addr_obj = None + + if addr_obj is not None: + if isinstance(addr_obj, IPv6Address): + target_addrs = [(socket.AF_INET6, addr_obj.compressed)] + else: + target_addrs = [(socket.AF_INET, addr_obj.compressed)] + else: + # getaddrinfo() will raise an exception if name resolution fails + gai_res = await getaddrinfo( + target_host, remote_port, family=family, type=socket.SOCK_STREAM + ) + + # Organize the list so that the first address is an IPv6 address (if available) + # and the second one is an IPv4 addresses. The rest can be in whatever order. + v6_found = v4_found = False + target_addrs = [] + for af, *_, sa in gai_res: + if af == socket.AF_INET6 and not v6_found: + v6_found = True + target_addrs.insert(0, (af, sa[0])) + elif af == socket.AF_INET and not v4_found and v6_found: + v4_found = True + target_addrs.insert(1, (af, sa[0])) + else: + target_addrs.append((af, sa[0])) + + oserrors: list[OSError] = [] + try: + async with create_task_group() as tg: + for _af, addr in target_addrs: + event = Event() + tg.start_soon(try_connect, addr, event) + with move_on_after(happy_eyeballs_delay): + await event.wait() + + if connected_stream is None: + cause = ( + oserrors[0] + if len(oserrors) == 1 + else ExceptionGroup("multiple connection attempts failed", oserrors) + ) + raise OSError("All connection attempts failed") from cause + finally: + oserrors.clear() + + if tls or tls_hostname or ssl_context: + try: + return await TLSStream.wrap( + connected_stream, + server_side=False, + hostname=tls_hostname or str(remote_host), + ssl_context=ssl_context, + standard_compatible=tls_standard_compatible, + ) + except BaseException: + await aclose_forcefully(connected_stream) + raise + + return connected_stream + + +async def connect_unix(path: str | bytes | PathLike[Any]) -> UNIXSocketStream: + """ + Connect to the given UNIX socket. + + Not available on Windows. + + :param path: path to the socket + :return: a socket stream object + :raises ConnectionFailed: if the connection fails + + """ + path = os.fspath(path) + return await get_async_backend().connect_unix(path) + + +async def create_tcp_listener( + *, + local_host: IPAddressType | None = None, + local_port: int = 0, + family: AnyIPAddressFamily = socket.AddressFamily.AF_UNSPEC, + backlog: int = 65536, + reuse_port: bool = False, +) -> MultiListener[SocketStream]: + """ + Create a TCP socket listener. + + :param local_port: port number to listen on + :param local_host: IP address of the interface to listen on. If omitted, listen on + all IPv4 and IPv6 interfaces. To listen on all interfaces on a specific address + family, use ``0.0.0.0`` for IPv4 or ``::`` for IPv6. + :param family: address family (used if ``local_host`` was omitted) + :param backlog: maximum number of queued incoming connections (up to a maximum of + 2**16, or 65536) + :param reuse_port: ``True`` to allow multiple sockets to bind to the same + address/port (not supported on Windows) + :return: a multi-listener object containing one or more socket listeners + :raises OSError: if there's an error creating a socket, or binding to one or more + interfaces failed + + """ + asynclib = get_async_backend() + backlog = min(backlog, 65536) + local_host = str(local_host) if local_host is not None else None + + def setup_raw_socket( + fam: AddressFamily, + bind_addr: tuple[str, int] | tuple[str, int, int, int], + *, + v6only: bool = True, + ) -> socket.socket: + sock = socket.socket(fam) + try: + sock.setblocking(False) + + if fam == AddressFamily.AF_INET6: + sock.setsockopt(IPPROTO_IPV6, socket.IPV6_V6ONLY, v6only) + + # For Windows, enable exclusive address use. For others, enable address + # reuse. + if sys.platform == "win32": + sock.setsockopt(socket.SOL_SOCKET, socket.SO_EXCLUSIVEADDRUSE, 1) + else: + sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + + if reuse_port: + sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1) + + # Workaround for #554 + if fam == socket.AF_INET6 and "%" in bind_addr[0]: + addr, scope_id = bind_addr[0].split("%", 1) + bind_addr = (addr, bind_addr[1], 0, int(scope_id)) + + sock.bind(bind_addr) + sock.listen(backlog) + except BaseException: + sock.close() + raise + + return sock + + # We passing type=0 on non-Windows platforms as a workaround for a uvloop bug + # where we don't get the correct scope ID for IPv6 link-local addresses when passing + # type=socket.SOCK_STREAM to getaddrinfo(): + # https://github.com/MagicStack/uvloop/issues/539 + gai_res = await getaddrinfo( + local_host, + local_port, + family=family, + type=socket.SOCK_STREAM if sys.platform == "win32" else 0, + flags=socket.AI_PASSIVE | socket.AI_ADDRCONFIG, + ) + + # The set comprehension is here to work around a glibc bug: + # https://sourceware.org/bugzilla/show_bug.cgi?id=14969 + sockaddrs = sorted({res for res in gai_res if res[1] == SocketKind.SOCK_STREAM}) + + # Special case for dual-stack binding on the "any" interface + if ( + local_host is None + and family == AddressFamily.AF_UNSPEC + and socket.has_dualstack_ipv6() + and any(fam == AddressFamily.AF_INET6 for fam, *_ in gai_res) + ): + raw_socket = setup_raw_socket( + AddressFamily.AF_INET6, ("::", local_port), v6only=False + ) + listener = asynclib.create_tcp_listener(raw_socket) + return MultiListener([listener]) + + errors: list[OSError] = [] + try: + for _ in range(len(sockaddrs)): + listeners: list[SocketListener] = [] + bound_ephemeral_port = local_port + try: + for fam, *_, sockaddr in sockaddrs: + sockaddr = sockaddr[0], bound_ephemeral_port, *sockaddr[2:] + raw_socket = setup_raw_socket(fam, sockaddr) + + # Store the assigned port if an ephemeral port was requested, so + # we'll bind to the same port on all interfaces + if local_port == 0 and len(gai_res) > 1: + bound_ephemeral_port = raw_socket.getsockname()[1] + + listeners.append(asynclib.create_tcp_listener(raw_socket)) + except BaseException as exc: + for listener in listeners: + await listener.aclose() + + # If an ephemeral port was requested but binding the assigned port + # failed for another interface, rotate the address list and try again + if ( + isinstance(exc, OSError) + and exc.errno == errno.EADDRINUSE + and local_port == 0 + and bound_ephemeral_port + ): + errors.append(exc) + sockaddrs.append(sockaddrs.pop(0)) + continue + + raise + + return MultiListener(listeners) + + raise OSError( + f"Could not create {len(sockaddrs)} listeners with a consistent port" + ) from ExceptionGroup("Several bind attempts failed", errors) + finally: + del errors # Prevent reference cycles + + +async def create_unix_listener( + path: str | bytes | PathLike[Any], + *, + mode: int | None = None, + backlog: int = 65536, +) -> SocketListener: + """ + Create a UNIX socket listener. + + Not available on Windows. + + :param path: path of the socket + :param mode: permissions to set on the socket + :param backlog: maximum number of queued incoming connections (up to a maximum of + 2**16, or 65536) + :return: a listener object + + .. versionchanged:: 3.0 + If a socket already exists on the file system in the given path, it will be + removed first. + + """ + backlog = min(backlog, 65536) + raw_socket = await setup_unix_local_socket(path, mode, socket.SOCK_STREAM) + try: + raw_socket.listen(backlog) + return get_async_backend().create_unix_listener(raw_socket) + except BaseException: + raw_socket.close() + raise + + +async def create_udp_socket( + family: AnyIPAddressFamily = AddressFamily.AF_UNSPEC, + *, + local_host: IPAddressType | None = None, + local_port: int = 0, + reuse_port: bool = False, +) -> UDPSocket: + """ + Create a UDP socket. + + If ``port`` has been given, the socket will be bound to this port on the local + machine, making this socket suitable for providing UDP based services. + + :param family: address family (``AF_INET`` or ``AF_INET6``) – automatically + determined from ``local_host`` if omitted + :param local_host: IP address or host name of the local interface to bind to + :param local_port: local port to bind to + :param reuse_port: ``True`` to allow multiple sockets to bind to the same + address/port (not supported on Windows) + :return: a UDP socket + + """ + if family is AddressFamily.AF_UNSPEC and not local_host: + raise ValueError('Either "family" or "local_host" must be given') + + if local_host: + gai_res = await getaddrinfo( + str(local_host), + local_port, + family=family, + type=socket.SOCK_DGRAM, + flags=socket.AI_PASSIVE | socket.AI_ADDRCONFIG, + ) + family = cast(AnyIPAddressFamily, gai_res[0][0]) + local_address = gai_res[0][-1] + elif family is AddressFamily.AF_INET6: + local_address = ("::", 0) + else: + local_address = ("0.0.0.0", 0) + + sock = await get_async_backend().create_udp_socket( + family, local_address, None, reuse_port + ) + return cast(UDPSocket, sock) + + +async def create_connected_udp_socket( + remote_host: IPAddressType, + remote_port: int, + *, + family: AnyIPAddressFamily = AddressFamily.AF_UNSPEC, + local_host: IPAddressType | None = None, + local_port: int = 0, + reuse_port: bool = False, +) -> ConnectedUDPSocket: + """ + Create a connected UDP socket. + + Connected UDP sockets can only communicate with the specified remote host/port, an + any packets sent from other sources are dropped. + + :param remote_host: remote host to set as the default target + :param remote_port: port on the remote host to set as the default target + :param family: address family (``AF_INET`` or ``AF_INET6``) – automatically + determined from ``local_host`` or ``remote_host`` if omitted + :param local_host: IP address or host name of the local interface to bind to + :param local_port: local port to bind to + :param reuse_port: ``True`` to allow multiple sockets to bind to the same + address/port (not supported on Windows) + :return: a connected UDP socket + + """ + local_address = None + if local_host: + gai_res = await getaddrinfo( + str(local_host), + local_port, + family=family, + type=socket.SOCK_DGRAM, + flags=socket.AI_PASSIVE | socket.AI_ADDRCONFIG, + ) + family = cast(AnyIPAddressFamily, gai_res[0][0]) + local_address = gai_res[0][-1] + + gai_res = await getaddrinfo( + str(remote_host), remote_port, family=family, type=socket.SOCK_DGRAM + ) + family = cast(AnyIPAddressFamily, gai_res[0][0]) + remote_address = gai_res[0][-1] + + sock = await get_async_backend().create_udp_socket( + family, local_address, remote_address, reuse_port + ) + return cast(ConnectedUDPSocket, sock) + + +async def create_unix_datagram_socket( + *, + local_path: None | str | bytes | PathLike[Any] = None, + local_mode: int | None = None, +) -> UNIXDatagramSocket: + """ + Create a UNIX datagram socket. + + Not available on Windows. + + If ``local_path`` has been given, the socket will be bound to this path, making this + socket suitable for receiving datagrams from other processes. Other processes can + send datagrams to this socket only if ``local_path`` is set. + + If a socket already exists on the file system in the ``local_path``, it will be + removed first. + + :param local_path: the path on which to bind to + :param local_mode: permissions to set on the local socket + :return: a UNIX datagram socket + + """ + raw_socket = await setup_unix_local_socket( + local_path, local_mode, socket.SOCK_DGRAM + ) + return await get_async_backend().create_unix_datagram_socket(raw_socket, None) + + +async def create_connected_unix_datagram_socket( + remote_path: str | bytes | PathLike[Any], + *, + local_path: None | str | bytes | PathLike[Any] = None, + local_mode: int | None = None, +) -> ConnectedUNIXDatagramSocket: + """ + Create a connected UNIX datagram socket. + + Connected datagram sockets can only communicate with the specified remote path. + + If ``local_path`` has been given, the socket will be bound to this path, making + this socket suitable for receiving datagrams from other processes. Other processes + can send datagrams to this socket only if ``local_path`` is set. + + If a socket already exists on the file system in the ``local_path``, it will be + removed first. + + :param remote_path: the path to set as the default target + :param local_path: the path on which to bind to + :param local_mode: permissions to set on the local socket + :return: a connected UNIX datagram socket + + """ + remote_path = os.fspath(remote_path) + raw_socket = await setup_unix_local_socket( + local_path, local_mode, socket.SOCK_DGRAM + ) + return await get_async_backend().create_unix_datagram_socket( + raw_socket, remote_path + ) + + +async def getaddrinfo( + host: bytes | str | None, + port: str | int | None, + *, + family: int | AddressFamily = 0, + type: int | SocketKind = 0, + proto: int = 0, + flags: int = 0, +) -> list[tuple[AddressFamily, SocketKind, int, str, tuple[str, int]]]: + """ + Look up a numeric IP address given a host name. + + Internationalized domain names are translated according to the (non-transitional) + IDNA 2008 standard. + + .. note:: 4-tuple IPv6 socket addresses are automatically converted to 2-tuples of + (host, port), unlike what :func:`socket.getaddrinfo` does. + + :param host: host name + :param port: port number + :param family: socket family (`'AF_INET``, ...) + :param type: socket type (``SOCK_STREAM``, ...) + :param proto: protocol number + :param flags: flags to pass to upstream ``getaddrinfo()`` + :return: list of tuples containing (family, type, proto, canonname, sockaddr) + + .. seealso:: :func:`socket.getaddrinfo` + + """ + # Handle unicode hostnames + if isinstance(host, str): + try: + encoded_host: bytes | None = host.encode("ascii") + except UnicodeEncodeError: + import idna + + encoded_host = idna.encode(host, uts46=True) + else: + encoded_host = host + + gai_res = await get_async_backend().getaddrinfo( + encoded_host, port, family=family, type=type, proto=proto, flags=flags + ) + return [ + (family, type, proto, canonname, convert_ipv6_sockaddr(sockaddr)) + for family, type, proto, canonname, sockaddr in gai_res + # filter out IPv6 results when IPv6 is disabled + if not isinstance(sockaddr[0], int) + ] + + +def getnameinfo(sockaddr: IPSockAddrType, flags: int = 0) -> Awaitable[tuple[str, str]]: + """ + Look up the host name of an IP address. + + :param sockaddr: socket address (e.g. (ipaddress, port) for IPv4) + :param flags: flags to pass to upstream ``getnameinfo()`` + :return: a tuple of (host name, service name) + :raises NoEventLoopError: if no supported asynchronous event loop is running in the + current thread + + .. seealso:: :func:`socket.getnameinfo` + + """ + return get_async_backend().getnameinfo(sockaddr, flags) + + +@deprecated("This function is deprecated; use `wait_readable` instead") +def wait_socket_readable(sock: socket.socket) -> Awaitable[None]: + """ + .. deprecated:: 4.7.0 + Use :func:`wait_readable` instead. + + Wait until the given socket has data to be read. + + .. warning:: Only use this on raw sockets that have not been wrapped by any higher + level constructs like socket streams! + + :param sock: a socket object + :raises ~anyio.ClosedResourceError: if the socket was closed while waiting for the + socket to become readable + :raises ~anyio.BusyResourceError: if another task is already waiting for the socket + to become readable + :raises NoEventLoopError: if no supported asynchronous event loop is running in the + current thread + + """ + return get_async_backend().wait_readable(sock.fileno()) + + +@deprecated("This function is deprecated; use `wait_writable` instead") +def wait_socket_writable(sock: socket.socket) -> Awaitable[None]: + """ + .. deprecated:: 4.7.0 + Use :func:`wait_writable` instead. + + Wait until the given socket can be written to. + + This does **NOT** work on Windows when using the asyncio backend with a proactor + event loop (default on py3.8+). + + .. warning:: Only use this on raw sockets that have not been wrapped by any higher + level constructs like socket streams! + + :param sock: a socket object + :raises ~anyio.ClosedResourceError: if the socket was closed while waiting for the + socket to become writable + :raises ~anyio.BusyResourceError: if another task is already waiting for the socket + to become writable + :raises NoEventLoopError: if no supported asynchronous event loop is running in the + current thread + + """ + return get_async_backend().wait_writable(sock.fileno()) + + +def wait_readable(obj: FileDescriptorLike) -> Awaitable[None]: + """ + Wait until the given object has data to be read. + + On Unix systems, ``obj`` must either be an integer file descriptor, or else an + object with a ``.fileno()`` method which returns an integer file descriptor. Any + kind of file descriptor can be passed, though the exact semantics will depend on + your kernel. For example, this probably won't do anything useful for on-disk files. + + On Windows systems, ``obj`` must either be an integer ``SOCKET`` handle, or else an + object with a ``.fileno()`` method which returns an integer ``SOCKET`` handle. File + descriptors aren't supported, and neither are handles that refer to anything besides + a ``SOCKET``. + + On backends where this functionality is not natively provided (asyncio + ``ProactorEventLoop`` on Windows), it is provided using a separate selector thread + which is set to shut down when the interpreter shuts down. + + .. warning:: Don't use this on raw sockets that have been wrapped by any higher + level constructs like socket streams! + + :param obj: an object with a ``.fileno()`` method or an integer handle + :raises ~anyio.ClosedResourceError: if the object was closed while waiting for the + object to become readable + :raises ~anyio.BusyResourceError: if another task is already waiting for the object + to become readable + :raises NoEventLoopError: if no supported asynchronous event loop is running in the + current thread + + """ + return get_async_backend().wait_readable(obj) + + +def wait_writable(obj: FileDescriptorLike) -> Awaitable[None]: + """ + Wait until the given object can be written to. + + :param obj: an object with a ``.fileno()`` method or an integer handle + :raises ~anyio.ClosedResourceError: if the object was closed while waiting for the + object to become writable + :raises ~anyio.BusyResourceError: if another task is already waiting for the object + to become writable + :raises NoEventLoopError: if no supported asynchronous event loop is running in the + current thread + + .. seealso:: See the documentation of :func:`wait_readable` for the definition of + ``obj`` and notes on backend compatibility. + + .. warning:: Don't use this on raw sockets that have been wrapped by any higher + level constructs like socket streams! + + """ + return get_async_backend().wait_writable(obj) + + +def notify_closing(obj: FileDescriptorLike) -> None: + """ + Call this before closing a file descriptor (on Unix) or socket (on + Windows). This will cause any `wait_readable` or `wait_writable` + calls on the given object to immediately wake up and raise + `~anyio.ClosedResourceError`. + + This doesn't actually close the object – you still have to do that + yourself afterwards. Also, you want to be careful to make sure no + new tasks start waiting on the object in between when you call this + and when it's actually closed. So to close something properly, you + usually want to do these steps in order: + + 1. Explicitly mark the object as closed, so that any new attempts + to use it will abort before they start. + 2. Call `notify_closing` to wake up any already-existing users. + 3. Actually close the object. + + It's also possible to do them in a different order if that's more + convenient, *but only if* you make sure not to have any checkpoints in + between the steps. This way they all happen in a single atomic + step, so other tasks won't be able to tell what order they happened + in anyway. + + :param obj: an object with a ``.fileno()`` method or an integer handle + :raises NoEventLoopError: if no supported asynchronous event loop is running in the + current thread + + """ + get_async_backend().notify_closing(obj) + + +# +# Private API +# + + +def convert_ipv6_sockaddr( + sockaddr: tuple[str, int, int, int] | tuple[str, int], +) -> tuple[str, int]: + """ + Convert a 4-tuple IPv6 socket address to a 2-tuple (address, port) format. + + If the scope ID is nonzero, it is added to the address, separated with ``%``. + Otherwise the flow id and scope id are simply cut off from the tuple. + Any other kinds of socket addresses are returned as-is. + + :param sockaddr: the result of :meth:`~socket.socket.getsockname` + :return: the converted socket address + + """ + # This is more complicated than it should be because of MyPy + if isinstance(sockaddr, tuple) and len(sockaddr) == 4: + host, port, flowinfo, scope_id = sockaddr + if scope_id: + # PyPy (as of v7.3.11) leaves the interface name in the result, so + # we discard it and only get the scope ID from the end + # (https://foss.heptapod.net/pypy/pypy/-/issues/3938) + host = host.split("%")[0] + + # Add scope_id to the address + return f"{host}%{scope_id}", port + else: + return host, port + else: + return sockaddr + + +async def setup_unix_local_socket( + path: None | str | bytes | PathLike[Any], + mode: int | None, + socktype: int, +) -> socket.socket: + """ + Create a UNIX local socket object, deleting the socket at the given path if it + exists. + + Not available on Windows. + + :param path: path of the socket + :param mode: permissions to set on the socket + :param socktype: socket.SOCK_STREAM or socket.SOCK_DGRAM + + """ + path_str: str | None + if path is not None: + path_str = os.fsdecode(path) + + # Linux abstract namespace sockets aren't backed by a concrete file so skip stat call + if not path_str.startswith("\0"): + # Copied from pathlib... + try: + stat_result = os.stat(path) + except OSError as e: + if e.errno not in ( + errno.ENOENT, + errno.ENOTDIR, + errno.EBADF, + errno.ELOOP, + ): + raise + else: + if stat.S_ISSOCK(stat_result.st_mode): + os.unlink(path) + else: + path_str = None + + raw_socket = socket.socket(socket.AF_UNIX, socktype) + raw_socket.setblocking(False) + + if path_str is not None: + try: + await to_thread.run_sync(raw_socket.bind, path_str, abandon_on_cancel=True) + if mode is not None: + await to_thread.run_sync(chmod, path_str, mode, abandon_on_cancel=True) + except BaseException: + raw_socket.close() + raise + + return raw_socket + + +@dataclass +class TCPConnectable(ByteStreamConnectable): + """ + Connects to a TCP server at the given host and port. + + :param host: host name or IP address of the server + :param port: TCP port number of the server + """ + + host: str | IPv4Address | IPv6Address + port: int + + def __post_init__(self) -> None: + if self.port < 1 or self.port > 65535: + raise ValueError("TCP port number out of range") + + @override + async def connect(self) -> SocketStream: + try: + return await connect_tcp(self.host, self.port) + except OSError as exc: + raise ConnectionFailed( + f"error connecting to {self.host}:{self.port}: {exc}" + ) from exc + + +@dataclass +class UNIXConnectable(ByteStreamConnectable): + """ + Connects to a UNIX domain socket at the given path. + + :param path: the file system path of the socket + """ + + path: str | bytes | PathLike[str] | PathLike[bytes] + + @override + async def connect(self) -> UNIXSocketStream: + try: + return await connect_unix(self.path) + except OSError as exc: + raise ConnectionFailed(f"error connecting to {self.path!r}: {exc}") from exc + + +def as_connectable( + remote: ByteStreamConnectable + | tuple[str | IPv4Address | IPv6Address, int] + | str + | bytes + | PathLike[str], + /, + *, + tls: bool = False, + ssl_context: ssl.SSLContext | None = None, + tls_hostname: str | None = None, + tls_standard_compatible: bool = True, +) -> ByteStreamConnectable: + """ + Return a byte stream connectable from the given object. + + If a bytestream connectable is given, it is returned unchanged. + If a tuple of (host, port) is given, a TCP connectable is returned. + If a string or bytes path is given, a UNIX connectable is returned. + + If ``tls=True``, the connectable will be wrapped in a + :class:`~.streams.tls.TLSConnectable`. + + :param remote: a connectable, a tuple of (host, port) or a path to a UNIX socket + :param tls: if ``True``, wrap the plaintext connectable in a + :class:`~.streams.tls.TLSConnectable`, using the provided TLS settings) + :param ssl_context: if ``tls=True``, the SSLContext object to use (if not provided, + a secure default will be created) + :param tls_hostname: if ``tls=True``, host name of the server to use for checking + the server certificate (defaults to the host portion of the address for TCP + connectables) + :param tls_standard_compatible: if ``False`` and ``tls=True``, makes the TLS stream + skip the closing handshake when closing the connection, so it won't raise an + exception if the server does the same + + """ + connectable: TCPConnectable | UNIXConnectable | TLSConnectable + if isinstance(remote, ByteStreamConnectable): + return remote + elif isinstance(remote, tuple) and len(remote) == 2: + connectable = TCPConnectable(*remote) + elif isinstance(remote, (str, bytes, PathLike)): + connectable = UNIXConnectable(remote) + else: + raise TypeError(f"cannot convert {remote!r} to a connectable") + + if tls: + if not tls_hostname and isinstance(connectable, TCPConnectable): + tls_hostname = str(connectable.host) + + connectable = TLSConnectable( + connectable, + ssl_context=ssl_context, + hostname=tls_hostname, + standard_compatible=tls_standard_compatible, + ) + + return connectable diff --git a/.deps_siteforge_v812/anyio/_core/_streams.py b/.deps_siteforge_v812/anyio/_core/_streams.py new file mode 100644 index 0000000..2b9c7df --- /dev/null +++ b/.deps_siteforge_v812/anyio/_core/_streams.py @@ -0,0 +1,52 @@ +from __future__ import annotations + +import math +from typing import TypeVar +from warnings import warn + +from ..streams.memory import ( + MemoryObjectReceiveStream, + MemoryObjectSendStream, + _MemoryObjectStreamState, +) + +T_Item = TypeVar("T_Item") + + +class create_memory_object_stream( + tuple[MemoryObjectSendStream[T_Item], MemoryObjectReceiveStream[T_Item]], +): + """ + Create a memory object stream. + + The stream's item type can be annotated like + :func:`create_memory_object_stream[T_Item]`. + + :param max_buffer_size: number of items held in the buffer until ``send()`` starts + blocking + :param item_type: old way of marking the streams with the right generic type for + static typing (does nothing on AnyIO 4) + + .. deprecated:: 4.0 + Use ``create_memory_object_stream[YourItemType](...)`` instead. + :return: a tuple of (send stream, receive stream) + + """ + + def __new__( # type: ignore[misc] + cls, max_buffer_size: float = 0, item_type: object = None + ) -> tuple[MemoryObjectSendStream[T_Item], MemoryObjectReceiveStream[T_Item]]: + if max_buffer_size != math.inf and not isinstance(max_buffer_size, int): + raise ValueError("max_buffer_size must be either an integer or math.inf") + if max_buffer_size < 0: + raise ValueError("max_buffer_size cannot be negative") + if item_type is not None: + warn( + "The item_type argument has been deprecated in AnyIO 4.0. " + "Use create_memory_object_stream[YourItemType](...) instead.", + DeprecationWarning, + stacklevel=2, + ) + + state = _MemoryObjectStreamState[T_Item](max_buffer_size) + return (MemoryObjectSendStream(state), MemoryObjectReceiveStream(state)) diff --git a/.deps_siteforge_v812/anyio/_core/_subprocesses.py b/.deps_siteforge_v812/anyio/_core/_subprocesses.py new file mode 100644 index 0000000..9796f8b --- /dev/null +++ b/.deps_siteforge_v812/anyio/_core/_subprocesses.py @@ -0,0 +1,196 @@ +from __future__ import annotations + +from collections.abc import AsyncIterable, Iterable, Mapping, Sequence +from io import BytesIO +from os import PathLike +from subprocess import PIPE, CalledProcessError, CompletedProcess +from typing import IO, Any, TypeAlias, cast + +from ..abc import Process +from ._eventloop import get_async_backend +from ._tasks import create_task_group + +StrOrBytesPath: TypeAlias = str | bytes | PathLike[str] | PathLike[bytes] + + +async def run_process( + command: StrOrBytesPath | Sequence[StrOrBytesPath], + *, + input: bytes | None = None, + stdin: int | IO[Any] | None = None, + stdout: int | IO[Any] | None = PIPE, + stderr: int | IO[Any] | None = PIPE, + check: bool = True, + cwd: StrOrBytesPath | None = None, + env: Mapping[str, str] | None = None, + startupinfo: Any = None, + creationflags: int = 0, + start_new_session: bool = False, + pass_fds: Sequence[int] = (), + user: str | int | None = None, + group: str | int | None = None, + extra_groups: Iterable[str | int] | None = None, + umask: int = -1, +) -> CompletedProcess[bytes]: + """ + Run an external command in a subprocess and wait until it completes. + + .. seealso:: :func:`subprocess.run` + + :param command: either a string to pass to the shell, or an iterable of strings + containing the executable name or path and its arguments + :param input: bytes passed to the standard input of the subprocess + :param stdin: one of :data:`subprocess.PIPE`, :data:`subprocess.DEVNULL`, + a file-like object, or `None`; ``input`` overrides this + :param stdout: one of :data:`subprocess.PIPE`, :data:`subprocess.DEVNULL`, + a file-like object, or `None` + :param stderr: one of :data:`subprocess.PIPE`, :data:`subprocess.DEVNULL`, + :data:`subprocess.STDOUT`, a file-like object, or `None` + :param check: if ``True``, raise :exc:`~subprocess.CalledProcessError` if the + process terminates with a return code other than 0 + :param cwd: If not ``None``, change the working directory to this before running the + command + :param env: if not ``None``, this mapping replaces the inherited environment + variables from the parent process + :param startupinfo: an instance of :class:`subprocess.STARTUPINFO` that can be used + to specify process startup parameters (Windows only) + :param creationflags: flags that can be used to control the creation of the + subprocess (see :class:`subprocess.Popen` for the specifics) + :param start_new_session: if ``true`` the setsid() system call will be made in the + child process prior to the execution of the subprocess. (POSIX only) + :param pass_fds: sequence of file descriptors to keep open between the parent and + child processes. (POSIX only) + :param user: effective user to run the process as (Python >= 3.9, POSIX only) + :param group: effective group to run the process as (Python >= 3.9, POSIX only) + :param extra_groups: supplementary groups to set in the subprocess (Python >= 3.9, + POSIX only) + :param umask: if not negative, this umask is applied in the child process before + running the given command (Python >= 3.9, POSIX only) + :return: an object representing the completed process + :raises ~subprocess.CalledProcessError: if ``check`` is ``True`` and the process + exits with a nonzero return code + + """ + + async def drain_stream(stream: AsyncIterable[bytes], index: int) -> None: + buffer = BytesIO() + async for chunk in stream: + buffer.write(chunk) + + stream_contents[index] = buffer.getvalue() + + if stdin is not None and input is not None: + raise ValueError("only one of stdin and input is allowed") + + async with await open_process( + command, + stdin=PIPE if input else stdin, + stdout=stdout, + stderr=stderr, + cwd=cwd, + env=env, + startupinfo=startupinfo, + creationflags=creationflags, + start_new_session=start_new_session, + pass_fds=pass_fds, + user=user, + group=group, + extra_groups=extra_groups, + umask=umask, + ) as process: + stream_contents: list[bytes | None] = [None, None] + async with create_task_group() as tg: + if process.stdout: + tg.start_soon(drain_stream, process.stdout, 0) + + if process.stderr: + tg.start_soon(drain_stream, process.stderr, 1) + + if process.stdin and input: + await process.stdin.send(input) + await process.stdin.aclose() + + await process.wait() + + output, errors = stream_contents + if check and process.returncode != 0: + raise CalledProcessError(cast(int, process.returncode), command, output, errors) + + return CompletedProcess(command, cast(int, process.returncode), output, errors) + + +async def open_process( + command: StrOrBytesPath | Sequence[StrOrBytesPath], + *, + stdin: int | IO[Any] | None = PIPE, + stdout: int | IO[Any] | None = PIPE, + stderr: int | IO[Any] | None = PIPE, + cwd: StrOrBytesPath | None = None, + env: Mapping[str, str] | None = None, + startupinfo: Any = None, + creationflags: int = 0, + start_new_session: bool = False, + pass_fds: Sequence[int] = (), + user: str | int | None = None, + group: str | int | None = None, + extra_groups: Iterable[str | int] | None = None, + umask: int = -1, +) -> Process: + """ + Start an external command in a subprocess. + + .. seealso:: :class:`subprocess.Popen` + + :param command: either a string to pass to the shell, or an iterable of strings + containing the executable name or path and its arguments + :param stdin: one of :data:`subprocess.PIPE`, :data:`subprocess.DEVNULL`, a + file-like object, or ``None`` + :param stdout: one of :data:`subprocess.PIPE`, :data:`subprocess.DEVNULL`, + a file-like object, or ``None`` + :param stderr: one of :data:`subprocess.PIPE`, :data:`subprocess.DEVNULL`, + :data:`subprocess.STDOUT`, a file-like object, or ``None`` + :param cwd: If not ``None``, the working directory is changed before executing + :param env: If env is not ``None``, it must be a mapping that defines the + environment variables for the new process + :param creationflags: flags that can be used to control the creation of the + subprocess (see :class:`subprocess.Popen` for the specifics) + :param startupinfo: an instance of :class:`subprocess.STARTUPINFO` that can be used + to specify process startup parameters (Windows only) + :param start_new_session: if ``true`` the setsid() system call will be made in the + child process prior to the execution of the subprocess. (POSIX only) + :param pass_fds: sequence of file descriptors to keep open between the parent and + child processes. (POSIX only) + :param user: effective user to run the process as (POSIX only) + :param group: effective group to run the process as (POSIX only) + :param extra_groups: supplementary groups to set in the subprocess (POSIX only) + :param umask: if not negative, this umask is applied in the child process before + running the given command (POSIX only) + :return: an asynchronous process object + + """ + kwargs: dict[str, Any] = {} + if user is not None: + kwargs["user"] = user + + if group is not None: + kwargs["group"] = group + + if extra_groups is not None: + kwargs["extra_groups"] = group + + if umask >= 0: + kwargs["umask"] = umask + + return await get_async_backend().open_process( + command, + stdin=stdin, + stdout=stdout, + stderr=stderr, + cwd=cwd, + env=env, + startupinfo=startupinfo, + creationflags=creationflags, + start_new_session=start_new_session, + pass_fds=pass_fds, + **kwargs, + ) diff --git a/.deps_siteforge_v812/anyio/_core/_synchronization.py b/.deps_siteforge_v812/anyio/_core/_synchronization.py new file mode 100644 index 0000000..9c6f9a0 --- /dev/null +++ b/.deps_siteforge_v812/anyio/_core/_synchronization.py @@ -0,0 +1,757 @@ +from __future__ import annotations + +import math +from collections import deque +from collections.abc import Callable +from dataclasses import dataclass +from types import TracebackType +from typing import TypeVar + +from ..lowlevel import checkpoint_if_cancelled +from ._eventloop import get_async_backend +from ._exceptions import BusyResourceError, NoEventLoopError +from ._tasks import CancelScope +from ._testing import TaskInfo, get_current_task + +T = TypeVar("T") + + +@dataclass(frozen=True) +class EventStatistics: + """ + :ivar int tasks_waiting: number of tasks waiting on :meth:`~.Event.wait` + """ + + tasks_waiting: int + + +@dataclass(frozen=True) +class CapacityLimiterStatistics: + """ + :ivar int borrowed_tokens: number of tokens currently borrowed by tasks + :ivar float total_tokens: total number of available tokens + :ivar tuple borrowers: tasks or other objects currently holding tokens borrowed from + this limiter + :ivar int tasks_waiting: number of tasks waiting on + :meth:`~.CapacityLimiter.acquire` or + :meth:`~.CapacityLimiter.acquire_on_behalf_of` + """ + + borrowed_tokens: int + total_tokens: float + borrowers: tuple[object, ...] + tasks_waiting: int + + +@dataclass(frozen=True) +class LockStatistics: + """ + :ivar bool locked: flag indicating if this lock is locked or not + :ivar ~anyio.TaskInfo owner: task currently holding the lock (or ``None`` if the + lock is not held by any task) + :ivar int tasks_waiting: number of tasks waiting on :meth:`~.Lock.acquire` + """ + + locked: bool + owner: TaskInfo | None + tasks_waiting: int + + +@dataclass(frozen=True) +class ConditionStatistics: + """ + :ivar int tasks_waiting: number of tasks blocked on :meth:`~.Condition.wait` + :ivar ~anyio.LockStatistics lock_statistics: statistics of the underlying + :class:`~.Lock` + """ + + tasks_waiting: int + lock_statistics: LockStatistics + + +@dataclass(frozen=True) +class SemaphoreStatistics: + """ + :ivar int tasks_waiting: number of tasks waiting on :meth:`~.Semaphore.acquire` + + """ + + tasks_waiting: int + + +class Event: + def __new__(cls) -> Event: + try: + return get_async_backend().create_event() + except NoEventLoopError: + return EventAdapter() + + def set(self) -> None: + """Set the flag, notifying all listeners.""" + raise NotImplementedError + + def is_set(self) -> bool: + """Return ``True`` if the flag is set, ``False`` if not.""" + raise NotImplementedError + + async def wait(self) -> None: + """ + Wait until the flag has been set. + + If the flag has already been set when this method is called, it returns + immediately. + + """ + raise NotImplementedError + + def statistics(self) -> EventStatistics: + """Return statistics about the current state of this event.""" + raise NotImplementedError + + +class EventAdapter(Event): + _internal_event: Event | None = None + _is_set: bool = False + + def __new__(cls) -> EventAdapter: + return object.__new__(cls) + + @property + def _event(self) -> Event: + if self._internal_event is None: + self._internal_event = get_async_backend().create_event() + if self._is_set: + self._internal_event.set() + + return self._internal_event + + def set(self) -> None: + if self._internal_event is None: + self._is_set = True + else: + self._event.set() + + def is_set(self) -> bool: + if self._internal_event is None: + return self._is_set + + return self._internal_event.is_set() + + async def wait(self) -> None: + await self._event.wait() + + def statistics(self) -> EventStatistics: + if self._internal_event is None: + return EventStatistics(tasks_waiting=0) + + return self._internal_event.statistics() + + +class Lock: + def __new__(cls, *, fast_acquire: bool = False) -> Lock: + try: + return get_async_backend().create_lock(fast_acquire=fast_acquire) + except NoEventLoopError: + return LockAdapter(fast_acquire=fast_acquire) + + async def __aenter__(self) -> None: + await self.acquire() + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + self.release() + + async def acquire(self) -> None: + """Acquire the lock.""" + raise NotImplementedError + + def acquire_nowait(self) -> None: + """ + Acquire the lock, without blocking. + + :raises ~anyio.WouldBlock: if the operation would block + + """ + raise NotImplementedError + + def release(self) -> None: + """Release the lock.""" + raise NotImplementedError + + def locked(self) -> bool: + """Return True if the lock is currently held.""" + raise NotImplementedError + + def statistics(self) -> LockStatistics: + """ + Return statistics about the current state of this lock. + + .. versionadded:: 3.0 + """ + raise NotImplementedError + + +class LockAdapter(Lock): + _internal_lock: Lock | None = None + + def __new__(cls, *, fast_acquire: bool = False) -> LockAdapter: + return object.__new__(cls) + + def __init__(self, *, fast_acquire: bool = False): + self._fast_acquire = fast_acquire + + @property + def _lock(self) -> Lock: + if self._internal_lock is None: + self._internal_lock = get_async_backend().create_lock( + fast_acquire=self._fast_acquire + ) + + return self._internal_lock + + async def __aenter__(self) -> None: + await self._lock.acquire() + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + if self._internal_lock is not None: + self._internal_lock.release() + + async def acquire(self) -> None: + """Acquire the lock.""" + await self._lock.acquire() + + def acquire_nowait(self) -> None: + """ + Acquire the lock, without blocking. + + :raises ~anyio.WouldBlock: if the operation would block + + """ + self._lock.acquire_nowait() + + def release(self) -> None: + """Release the lock.""" + self._lock.release() + + def locked(self) -> bool: + """Return True if the lock is currently held.""" + return self._lock.locked() + + def statistics(self) -> LockStatistics: + """ + Return statistics about the current state of this lock. + + .. versionadded:: 3.0 + + """ + if self._internal_lock is None: + return LockStatistics(False, None, 0) + + return self._internal_lock.statistics() + + +class Condition: + _owner_task: TaskInfo | None = None + + def __init__(self, lock: Lock | None = None): + self._lock = lock or Lock() + self._waiters: deque[Event] = deque() + + async def __aenter__(self) -> None: + await self.acquire() + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + self.release() + + def _check_acquired(self) -> None: + if self._owner_task != get_current_task(): + raise RuntimeError("The current task is not holding the underlying lock") + + async def acquire(self) -> None: + """Acquire the underlying lock.""" + await self._lock.acquire() + self._owner_task = get_current_task() + + def acquire_nowait(self) -> None: + """ + Acquire the underlying lock, without blocking. + + :raises ~anyio.WouldBlock: if the operation would block + + """ + self._lock.acquire_nowait() + self._owner_task = get_current_task() + + def release(self) -> None: + """Release the underlying lock.""" + self._lock.release() + + def locked(self) -> bool: + """Return True if the lock is set.""" + return self._lock.locked() + + def notify(self, n: int = 1) -> None: + """Notify exactly n listeners.""" + self._check_acquired() + for _ in range(n): + try: + event = self._waiters.popleft() + except IndexError: + break + + event.set() + + def notify_all(self) -> None: + """Notify all the listeners.""" + self._check_acquired() + for event in self._waiters: + event.set() + + self._waiters.clear() + + async def wait(self) -> None: + """Wait for a notification.""" + await checkpoint_if_cancelled() + self._check_acquired() + event = Event() + self._waiters.append(event) + self.release() + try: + await event.wait() + except BaseException: + if not event.is_set(): + self._waiters.remove(event) + elif self._waiters: + # This task was notified by could not act on it, so pass + # it on to the next task + self._waiters.popleft().set() + + raise + finally: + with CancelScope(shield=True): + await self.acquire() + + async def wait_for(self, predicate: Callable[[], T]) -> T: + """ + Wait until a predicate becomes true. + + :param predicate: a callable that returns a truthy value when the condition is + met + :return: the result of the predicate + + .. versionadded:: 4.11.0 + + """ + while not (result := predicate()): + await self.wait() + + return result + + def statistics(self) -> ConditionStatistics: + """ + Return statistics about the current state of this condition. + + .. versionadded:: 3.0 + """ + return ConditionStatistics(len(self._waiters), self._lock.statistics()) + + +class Semaphore: + def __new__( + cls, + initial_value: int, + *, + max_value: int | None = None, + fast_acquire: bool = False, + ) -> Semaphore: + try: + return get_async_backend().create_semaphore( + initial_value, max_value=max_value, fast_acquire=fast_acquire + ) + except NoEventLoopError: + return SemaphoreAdapter(initial_value, max_value=max_value) + + def __init__( + self, + initial_value: int, + *, + max_value: int | None = None, + fast_acquire: bool = False, + ): + if not isinstance(initial_value, int): + raise TypeError("initial_value must be an integer") + if initial_value < 0: + raise ValueError("initial_value must be >= 0") + if max_value is not None: + if not isinstance(max_value, int): + raise TypeError("max_value must be an integer or None") + if max_value < initial_value: + raise ValueError( + "max_value must be equal to or higher than initial_value" + ) + + self._fast_acquire = fast_acquire + + async def __aenter__(self) -> Semaphore: + await self.acquire() + return self + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + self.release() + + async def acquire(self) -> None: + """Decrement the semaphore value, blocking if necessary.""" + raise NotImplementedError + + def acquire_nowait(self) -> None: + """ + Acquire the underlying lock, without blocking. + + :raises ~anyio.WouldBlock: if the operation would block + + """ + raise NotImplementedError + + def release(self) -> None: + """Increment the semaphore value.""" + raise NotImplementedError + + @property + def value(self) -> int: + """The current value of the semaphore.""" + raise NotImplementedError + + @property + def max_value(self) -> int | None: + """The maximum value of the semaphore.""" + raise NotImplementedError + + def statistics(self) -> SemaphoreStatistics: + """ + Return statistics about the current state of this semaphore. + + .. versionadded:: 3.0 + """ + raise NotImplementedError + + +class SemaphoreAdapter(Semaphore): + _internal_semaphore: Semaphore | None = None + + def __new__( + cls, + initial_value: int, + *, + max_value: int | None = None, + fast_acquire: bool = False, + ) -> SemaphoreAdapter: + return object.__new__(cls) + + def __init__( + self, + initial_value: int, + *, + max_value: int | None = None, + fast_acquire: bool = False, + ) -> None: + super().__init__(initial_value, max_value=max_value, fast_acquire=fast_acquire) + self._initial_value = initial_value + self._max_value = max_value + + @property + def _semaphore(self) -> Semaphore: + if self._internal_semaphore is None: + self._internal_semaphore = get_async_backend().create_semaphore( + self._initial_value, max_value=self._max_value + ) + + return self._internal_semaphore + + async def acquire(self) -> None: + await self._semaphore.acquire() + + def acquire_nowait(self) -> None: + self._semaphore.acquire_nowait() + + def release(self) -> None: + self._semaphore.release() + + @property + def value(self) -> int: + if self._internal_semaphore is None: + return self._initial_value + + return self._semaphore.value + + @property + def max_value(self) -> int | None: + return self._max_value + + def statistics(self) -> SemaphoreStatistics: + if self._internal_semaphore is None: + return SemaphoreStatistics(tasks_waiting=0) + + return self._semaphore.statistics() + + +class CapacityLimiter: + def __new__(cls, total_tokens: float) -> CapacityLimiter: + try: + return get_async_backend().create_capacity_limiter(total_tokens) + except NoEventLoopError: + return CapacityLimiterAdapter(total_tokens) + + async def __aenter__(self) -> None: + raise NotImplementedError + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + raise NotImplementedError + + @property + def total_tokens(self) -> float: + """ + The total number of tokens available for borrowing. + + This is a read-write property. If the total number of tokens is increased, the + proportionate number of tasks waiting on this limiter will be granted their + tokens. + + .. versionchanged:: 3.0 + The property is now writable. + .. versionchanged:: 4.12 + The value can now be set to 0. + + """ + raise NotImplementedError + + @total_tokens.setter + def total_tokens(self, value: float) -> None: + raise NotImplementedError + + @property + def borrowed_tokens(self) -> int: + """The number of tokens that have currently been borrowed.""" + raise NotImplementedError + + @property + def available_tokens(self) -> float: + """The number of tokens currently available to be borrowed""" + raise NotImplementedError + + def acquire_nowait(self) -> None: + """ + Acquire a token for the current task without waiting for one to become + available. + + :raises ~anyio.WouldBlock: if there are no tokens available for borrowing + + """ + raise NotImplementedError + + def acquire_on_behalf_of_nowait(self, borrower: object) -> None: + """ + Acquire a token without waiting for one to become available. + + :param borrower: the entity borrowing a token + :raises ~anyio.WouldBlock: if there are no tokens available for borrowing + + """ + raise NotImplementedError + + async def acquire(self) -> None: + """ + Acquire a token for the current task, waiting if necessary for one to become + available. + + """ + raise NotImplementedError + + async def acquire_on_behalf_of(self, borrower: object) -> None: + """ + Acquire a token, waiting if necessary for one to become available. + + :param borrower: the entity borrowing a token + + """ + raise NotImplementedError + + def release(self) -> None: + """ + Release the token held by the current task. + + :raises RuntimeError: if the current task has not borrowed a token from this + limiter. + + """ + raise NotImplementedError + + def release_on_behalf_of(self, borrower: object) -> None: + """ + Release the token held by the given borrower. + + :raises RuntimeError: if the borrower has not borrowed a token from this + limiter. + + """ + raise NotImplementedError + + def statistics(self) -> CapacityLimiterStatistics: + """ + Return statistics about the current state of this limiter. + + .. versionadded:: 3.0 + + """ + raise NotImplementedError + + +class CapacityLimiterAdapter(CapacityLimiter): + _internal_limiter: CapacityLimiter | None = None + + def __new__(cls, total_tokens: float) -> CapacityLimiterAdapter: + return object.__new__(cls) + + def __init__(self, total_tokens: float) -> None: + self.total_tokens = total_tokens + + @property + def _limiter(self) -> CapacityLimiter: + if self._internal_limiter is None: + self._internal_limiter = get_async_backend().create_capacity_limiter( + self._total_tokens + ) + + return self._internal_limiter + + async def __aenter__(self) -> None: + await self._limiter.__aenter__() + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + return await self._limiter.__aexit__(exc_type, exc_val, exc_tb) + + @property + def total_tokens(self) -> float: + if self._internal_limiter is None: + return self._total_tokens + + return self._internal_limiter.total_tokens + + @total_tokens.setter + def total_tokens(self, value: float) -> None: + if not isinstance(value, int) and value is not math.inf: + raise TypeError("total_tokens must be an int or math.inf") + elif value < 1: + raise ValueError("total_tokens must be >= 1") + + if self._internal_limiter is None: + self._total_tokens = value + return + + self._limiter.total_tokens = value + + @property + def borrowed_tokens(self) -> int: + if self._internal_limiter is None: + return 0 + + return self._internal_limiter.borrowed_tokens + + @property + def available_tokens(self) -> float: + if self._internal_limiter is None: + return self._total_tokens + + return self._internal_limiter.available_tokens + + def acquire_nowait(self) -> None: + self._limiter.acquire_nowait() + + def acquire_on_behalf_of_nowait(self, borrower: object) -> None: + self._limiter.acquire_on_behalf_of_nowait(borrower) + + async def acquire(self) -> None: + await self._limiter.acquire() + + async def acquire_on_behalf_of(self, borrower: object) -> None: + await self._limiter.acquire_on_behalf_of(borrower) + + def release(self) -> None: + self._limiter.release() + + def release_on_behalf_of(self, borrower: object) -> None: + self._limiter.release_on_behalf_of(borrower) + + def statistics(self) -> CapacityLimiterStatistics: + if self._internal_limiter is None: + return CapacityLimiterStatistics( + borrowed_tokens=0, + total_tokens=self.total_tokens, + borrowers=(), + tasks_waiting=0, + ) + + return self._internal_limiter.statistics() + + +class ResourceGuard: + """ + A context manager for ensuring that a resource is only used by a single task at a + time. + + Entering this context manager while the previous has not exited it yet will trigger + :exc:`BusyResourceError`. + + :param action: the action to guard against (visible in the :exc:`BusyResourceError` + when triggered, e.g. "Another task is already {action} this resource") + + .. versionadded:: 4.1 + """ + + __slots__ = "action", "_guarded" + + def __init__(self, action: str = "using"): + self.action: str = action + self._guarded = False + + def __enter__(self) -> None: + if self._guarded: + raise BusyResourceError(self.action) + + self._guarded = True + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + self._guarded = False diff --git a/.deps_siteforge_v812/anyio/_core/_tasks.py b/.deps_siteforge_v812/anyio/_core/_tasks.py new file mode 100644 index 0000000..0688bfe --- /dev/null +++ b/.deps_siteforge_v812/anyio/_core/_tasks.py @@ -0,0 +1,173 @@ +from __future__ import annotations + +import math +from collections.abc import Generator +from contextlib import contextmanager +from types import TracebackType + +from ..abc._tasks import TaskGroup, TaskStatus +from ._eventloop import get_async_backend + + +class _IgnoredTaskStatus(TaskStatus[object]): + def started(self, value: object = None) -> None: + pass + + +TASK_STATUS_IGNORED = _IgnoredTaskStatus() + + +class CancelScope: + """ + Wraps a unit of work that can be made separately cancellable. + + :param deadline: The time (clock value) when this scope is cancelled automatically + :param shield: ``True`` to shield the cancel scope from external cancellation + :raises NoEventLoopError: if no supported asynchronous event loop is running in the + current thread + """ + + def __new__( + cls, *, deadline: float = math.inf, shield: bool = False + ) -> CancelScope: + return get_async_backend().create_cancel_scope(shield=shield, deadline=deadline) + + def cancel(self, reason: str | None = None) -> None: + """ + Cancel this scope immediately. + + :param reason: a message describing the reason for the cancellation + + """ + raise NotImplementedError + + @property + def deadline(self) -> float: + """ + The time (clock value) when this scope is cancelled automatically. + + Will be ``float('inf')`` if no timeout has been set. + + """ + raise NotImplementedError + + @deadline.setter + def deadline(self, value: float) -> None: + raise NotImplementedError + + @property + def cancel_called(self) -> bool: + """``True`` if :meth:`cancel` has been called.""" + raise NotImplementedError + + @property + def cancelled_caught(self) -> bool: + """ + ``True`` if this scope suppressed a cancellation exception it itself raised. + + This is typically used to check if any work was interrupted, or to see if the + scope was cancelled due to its deadline being reached. The value will, however, + only be ``True`` if the cancellation was triggered by the scope itself (and not + an outer scope). + + """ + raise NotImplementedError + + @property + def shield(self) -> bool: + """ + ``True`` if this scope is shielded from external cancellation. + + While a scope is shielded, it will not receive cancellations from outside. + + """ + raise NotImplementedError + + @shield.setter + def shield(self, value: bool) -> None: + raise NotImplementedError + + def __enter__(self) -> CancelScope: + raise NotImplementedError + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> bool: + raise NotImplementedError + + +@contextmanager +def fail_after( + delay: float | None, shield: bool = False +) -> Generator[CancelScope, None, None]: + """ + Create a context manager which raises a :class:`TimeoutError` if does not finish in + time. + + :param delay: maximum allowed time (in seconds) before raising the exception, or + ``None`` to disable the timeout + :param shield: ``True`` to shield the cancel scope from external cancellation + :return: a context manager that yields a cancel scope + :rtype: :class:`~typing.ContextManager`\\[:class:`~anyio.CancelScope`\\] + :raises NoEventLoopError: if no supported asynchronous event loop is running in the + current thread + + """ + current_time = get_async_backend().current_time + deadline = (current_time() + delay) if delay is not None else math.inf + with get_async_backend().create_cancel_scope( + deadline=deadline, shield=shield + ) as cancel_scope: + yield cancel_scope + + if cancel_scope.cancelled_caught and current_time() >= cancel_scope.deadline: + raise TimeoutError + + +def move_on_after(delay: float | None, shield: bool = False) -> CancelScope: + """ + Create a cancel scope with a deadline that expires after the given delay. + + :param delay: maximum allowed time (in seconds) before exiting the context block, or + ``None`` to disable the timeout + :param shield: ``True`` to shield the cancel scope from external cancellation + :return: a cancel scope + :raises NoEventLoopError: if no supported asynchronous event loop is running in the + current thread + + """ + deadline = ( + (get_async_backend().current_time() + delay) if delay is not None else math.inf + ) + return get_async_backend().create_cancel_scope(deadline=deadline, shield=shield) + + +def current_effective_deadline() -> float: + """ + Return the nearest deadline among all the cancel scopes effective for the current + task. + + :return: a clock value from the event loop's internal clock (or ``float('inf')`` if + there is no deadline in effect, or ``float('-inf')`` if the current scope has + been cancelled) + :rtype: float + :raises NoEventLoopError: if no supported asynchronous event loop is running in the + current thread + + """ + return get_async_backend().current_effective_deadline() + + +def create_task_group() -> TaskGroup: + """ + Create a task group. + + :return: a task group + :raises NoEventLoopError: if no supported asynchronous event loop is running in the + current thread + + """ + return get_async_backend().create_task_group() diff --git a/.deps_siteforge_v812/anyio/_core/_tempfile.py b/.deps_siteforge_v812/anyio/_core/_tempfile.py new file mode 100644 index 0000000..75a09f7 --- /dev/null +++ b/.deps_siteforge_v812/anyio/_core/_tempfile.py @@ -0,0 +1,613 @@ +from __future__ import annotations + +import os +import sys +import tempfile +from collections.abc import Iterable +from io import BytesIO, TextIOWrapper +from types import TracebackType +from typing import ( + TYPE_CHECKING, + Any, + AnyStr, + Generic, + overload, +) + +from .. import to_thread +from .._core._fileio import AsyncFile +from ..lowlevel import checkpoint_if_cancelled + +if TYPE_CHECKING: + from _typeshed import OpenBinaryMode, OpenTextMode, ReadableBuffer, WriteableBuffer + + +class TemporaryFile(Generic[AnyStr]): + """ + An asynchronous temporary file that is automatically created and cleaned up. + + This class provides an asynchronous context manager interface to a temporary file. + The file is created using Python's standard `tempfile.TemporaryFile` function in a + background thread, and is wrapped as an asynchronous file using `AsyncFile`. + + :param mode: The mode in which the file is opened. Defaults to "w+b". + :param buffering: The buffering policy (-1 means the default buffering). + :param encoding: The encoding used to decode or encode the file. Only applicable in + text mode. + :param newline: Controls how universal newlines mode works (only applicable in text + mode). + :param suffix: The suffix for the temporary file name. + :param prefix: The prefix for the temporary file name. + :param dir: The directory in which the temporary file is created. + :param errors: The error handling scheme used for encoding/decoding errors. + """ + + _async_file: AsyncFile[AnyStr] + + @overload + def __init__( + self: TemporaryFile[bytes], + mode: OpenBinaryMode = ..., + buffering: int = ..., + encoding: str | None = ..., + newline: str | None = ..., + suffix: str | None = ..., + prefix: str | None = ..., + dir: str | None = ..., + *, + errors: str | None = ..., + ): ... + @overload + def __init__( + self: TemporaryFile[str], + mode: OpenTextMode, + buffering: int = ..., + encoding: str | None = ..., + newline: str | None = ..., + suffix: str | None = ..., + prefix: str | None = ..., + dir: str | None = ..., + *, + errors: str | None = ..., + ): ... + + def __init__( + self, + mode: OpenTextMode | OpenBinaryMode = "w+b", + buffering: int = -1, + encoding: str | None = None, + newline: str | None = None, + suffix: str | None = None, + prefix: str | None = None, + dir: str | None = None, + *, + errors: str | None = None, + ) -> None: + self.mode = mode + self.buffering = buffering + self.encoding = encoding + self.newline = newline + self.suffix: str | None = suffix + self.prefix: str | None = prefix + self.dir: str | None = dir + self.errors = errors + + async def __aenter__(self) -> AsyncFile[AnyStr]: + fp = await to_thread.run_sync( + lambda: tempfile.TemporaryFile( + self.mode, + self.buffering, + self.encoding, + self.newline, + self.suffix, + self.prefix, + self.dir, + errors=self.errors, + ) + ) + self._async_file = AsyncFile(fp) + return self._async_file + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc_value: BaseException | None, + traceback: TracebackType | None, + ) -> None: + await self._async_file.aclose() + + +class NamedTemporaryFile(Generic[AnyStr]): + """ + An asynchronous named temporary file that is automatically created and cleaned up. + + This class provides an asynchronous context manager for a temporary file with a + visible name in the file system. It uses Python's standard + :func:`~tempfile.NamedTemporaryFile` function and wraps the file object with + :class:`AsyncFile` for asynchronous operations. + + :param mode: The mode in which the file is opened. Defaults to "w+b". + :param buffering: The buffering policy (-1 means the default buffering). + :param encoding: The encoding used to decode or encode the file. Only applicable in + text mode. + :param newline: Controls how universal newlines mode works (only applicable in text + mode). + :param suffix: The suffix for the temporary file name. + :param prefix: The prefix for the temporary file name. + :param dir: The directory in which the temporary file is created. + :param delete: Whether to delete the file when it is closed. + :param errors: The error handling scheme used for encoding/decoding errors. + :param delete_on_close: (Python 3.12+) Whether to delete the file on close. + """ + + _async_file: AsyncFile[AnyStr] + + @overload + def __init__( + self: NamedTemporaryFile[bytes], + mode: OpenBinaryMode = ..., + buffering: int = ..., + encoding: str | None = ..., + newline: str | None = ..., + suffix: str | None = ..., + prefix: str | None = ..., + dir: str | None = ..., + delete: bool = ..., + *, + errors: str | None = ..., + delete_on_close: bool = ..., + ): ... + @overload + def __init__( + self: NamedTemporaryFile[str], + mode: OpenTextMode, + buffering: int = ..., + encoding: str | None = ..., + newline: str | None = ..., + suffix: str | None = ..., + prefix: str | None = ..., + dir: str | None = ..., + delete: bool = ..., + *, + errors: str | None = ..., + delete_on_close: bool = ..., + ): ... + + def __init__( + self, + mode: OpenBinaryMode | OpenTextMode = "w+b", + buffering: int = -1, + encoding: str | None = None, + newline: str | None = None, + suffix: str | None = None, + prefix: str | None = None, + dir: str | None = None, + delete: bool = True, + *, + errors: str | None = None, + delete_on_close: bool = True, + ) -> None: + self._params: dict[str, Any] = { + "mode": mode, + "buffering": buffering, + "encoding": encoding, + "newline": newline, + "suffix": suffix, + "prefix": prefix, + "dir": dir, + "delete": delete, + "errors": errors, + } + if sys.version_info >= (3, 12): + self._params["delete_on_close"] = delete_on_close + + async def __aenter__(self) -> AsyncFile[AnyStr]: + fp = await to_thread.run_sync( + lambda: tempfile.NamedTemporaryFile(**self._params) + ) + self._async_file = AsyncFile(fp) + return self._async_file + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc_value: BaseException | None, + traceback: TracebackType | None, + ) -> None: + await self._async_file.aclose() + + +class SpooledTemporaryFile(AsyncFile[AnyStr]): + """ + An asynchronous spooled temporary file that starts in memory and is spooled to disk. + + This class provides an asynchronous interface to a spooled temporary file, much like + Python's standard :class:`~tempfile.SpooledTemporaryFile`. It supports asynchronous + write operations and provides a method to force a rollover to disk. + + :param max_size: Maximum size in bytes before the file is rolled over to disk. + :param mode: The mode in which the file is opened. Defaults to "w+b". + :param buffering: The buffering policy (-1 means the default buffering). + :param encoding: The encoding used to decode or encode the file (text mode only). + :param newline: Controls how universal newlines mode works (text mode only). + :param suffix: The suffix for the temporary file name. + :param prefix: The prefix for the temporary file name. + :param dir: The directory in which the temporary file is created. + :param errors: The error handling scheme used for encoding/decoding errors. + """ + + _rolled: bool = False + + @overload + def __init__( + self: SpooledTemporaryFile[bytes], + max_size: int = ..., + mode: OpenBinaryMode = ..., + buffering: int = ..., + encoding: str | None = ..., + newline: str | None = ..., + suffix: str | None = ..., + prefix: str | None = ..., + dir: str | None = ..., + *, + errors: str | None = ..., + ): ... + @overload + def __init__( + self: SpooledTemporaryFile[str], + max_size: int = ..., + mode: OpenTextMode = ..., + buffering: int = ..., + encoding: str | None = ..., + newline: str | None = ..., + suffix: str | None = ..., + prefix: str | None = ..., + dir: str | None = ..., + *, + errors: str | None = ..., + ): ... + + def __init__( + self, + max_size: int = 0, + mode: OpenBinaryMode | OpenTextMode = "w+b", + buffering: int = -1, + encoding: str | None = None, + newline: str | None = None, + suffix: str | None = None, + prefix: str | None = None, + dir: str | None = None, + *, + errors: str | None = None, + ) -> None: + self._tempfile_params: dict[str, Any] = { + "mode": mode, + "buffering": buffering, + "encoding": encoding, + "newline": newline, + "suffix": suffix, + "prefix": prefix, + "dir": dir, + "errors": errors, + } + self._max_size = max_size + if "b" in mode: + super().__init__(BytesIO()) # type: ignore[arg-type] + else: + super().__init__( + TextIOWrapper( # type: ignore[arg-type] + BytesIO(), + encoding=encoding, + errors=errors, + newline=newline, + write_through=True, + ) + ) + + async def aclose(self) -> None: + if not self._rolled: + self._fp.close() + return + + await super().aclose() + + async def _check(self) -> None: + if self._rolled or self._fp.tell() <= self._max_size: + return + + await self.rollover() + + async def rollover(self) -> None: + if self._rolled: + return + + self._rolled = True + buffer = self._fp + buffer.seek(0) + self._fp = await to_thread.run_sync( + lambda: tempfile.TemporaryFile(**self._tempfile_params) + ) + await self.write(buffer.read()) + buffer.close() + + @property + def closed(self) -> bool: + return self._fp.closed + + async def read(self, size: int = -1) -> AnyStr: + if not self._rolled: + await checkpoint_if_cancelled() + return self._fp.read(size) + + return await super().read(size) # type: ignore[return-value] + + async def read1(self: SpooledTemporaryFile[bytes], size: int = -1) -> bytes: + if not self._rolled: + await checkpoint_if_cancelled() + return self._fp.read1(size) + + return await super().read1(size) + + async def readline(self) -> AnyStr: + if not self._rolled: + await checkpoint_if_cancelled() + return self._fp.readline() + + return await super().readline() # type: ignore[return-value] + + async def readlines(self) -> list[AnyStr]: + if not self._rolled: + await checkpoint_if_cancelled() + return self._fp.readlines() + + return await super().readlines() # type: ignore[return-value] + + async def readinto(self: SpooledTemporaryFile[bytes], b: WriteableBuffer) -> int: + if not self._rolled: + await checkpoint_if_cancelled() + self._fp.readinto(b) + + return await super().readinto(b) + + async def readinto1(self: SpooledTemporaryFile[bytes], b: WriteableBuffer) -> int: + if not self._rolled: + await checkpoint_if_cancelled() + self._fp.readinto(b) + + return await super().readinto1(b) + + async def seek(self, offset: int, whence: int | None = os.SEEK_SET) -> int: + if not self._rolled: + await checkpoint_if_cancelled() + return self._fp.seek(offset, whence) + + return await super().seek(offset, whence) + + async def tell(self) -> int: + if not self._rolled: + await checkpoint_if_cancelled() + return self._fp.tell() + + return await super().tell() + + async def truncate(self, size: int | None = None) -> int: + if not self._rolled: + await checkpoint_if_cancelled() + return self._fp.truncate(size) + + return await super().truncate(size) + + @overload + async def write(self: SpooledTemporaryFile[bytes], b: ReadableBuffer) -> int: ... + @overload + async def write(self: SpooledTemporaryFile[str], b: str) -> int: ... + + async def write(self, b: ReadableBuffer | str) -> int: + """ + Asynchronously write data to the spooled temporary file. + + If the file has not yet been rolled over, the data is written synchronously, + and a rollover is triggered if the size exceeds the maximum size. + + :param s: The data to write. + :return: The number of bytes written. + :raises RuntimeError: If the underlying file is not initialized. + + """ + if not self._rolled: + await checkpoint_if_cancelled() + result = self._fp.write(b) + await self._check() + return result + + return await super().write(b) # type: ignore[misc] + + @overload + async def writelines( + self: SpooledTemporaryFile[bytes], lines: Iterable[ReadableBuffer] + ) -> None: ... + @overload + async def writelines( + self: SpooledTemporaryFile[str], lines: Iterable[str] + ) -> None: ... + + async def writelines(self, lines: Iterable[str] | Iterable[ReadableBuffer]) -> None: + """ + Asynchronously write a list of lines to the spooled temporary file. + + If the file has not yet been rolled over, the lines are written synchronously, + and a rollover is triggered if the size exceeds the maximum size. + + :param lines: An iterable of lines to write. + :raises RuntimeError: If the underlying file is not initialized. + + """ + if not self._rolled: + await checkpoint_if_cancelled() + result = self._fp.writelines(lines) + await self._check() + return result + + return await super().writelines(lines) # type: ignore[misc] + + +class TemporaryDirectory(Generic[AnyStr]): + """ + An asynchronous temporary directory that is created and cleaned up automatically. + + This class provides an asynchronous context manager for creating a temporary + directory. It wraps Python's standard :class:`~tempfile.TemporaryDirectory` to + perform directory creation and cleanup operations in a background thread. + + :param suffix: Suffix to be added to the temporary directory name. + :param prefix: Prefix to be added to the temporary directory name. + :param dir: The parent directory where the temporary directory is created. + :param ignore_cleanup_errors: Whether to ignore errors during cleanup + :param delete: Whether to delete the directory upon closing (Python 3.12+). + """ + + def __init__( + self, + suffix: AnyStr | None = None, + prefix: AnyStr | None = None, + dir: AnyStr | None = None, + *, + ignore_cleanup_errors: bool = False, + delete: bool = True, + ) -> None: + self.suffix: AnyStr | None = suffix + self.prefix: AnyStr | None = prefix + self.dir: AnyStr | None = dir + self.ignore_cleanup_errors = ignore_cleanup_errors + self.delete = delete + + self._tempdir: tempfile.TemporaryDirectory | None = None + + async def __aenter__(self) -> str: + params: dict[str, Any] = { + "suffix": self.suffix, + "prefix": self.prefix, + "dir": self.dir, + "ignore_cleanup_errors": self.ignore_cleanup_errors, + } + if sys.version_info >= (3, 12): + params["delete"] = self.delete + + self._tempdir = await to_thread.run_sync( + lambda: tempfile.TemporaryDirectory(**params) + ) + return await to_thread.run_sync(self._tempdir.__enter__) + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc_value: BaseException | None, + traceback: TracebackType | None, + ) -> None: + if self._tempdir is not None: + await to_thread.run_sync( + self._tempdir.__exit__, exc_type, exc_value, traceback + ) + + async def cleanup(self) -> None: + if self._tempdir is not None: + await to_thread.run_sync(self._tempdir.cleanup) + + +@overload +async def mkstemp( + suffix: str | None = None, + prefix: str | None = None, + dir: str | None = None, + text: bool = False, +) -> tuple[int, str]: ... + + +@overload +async def mkstemp( + suffix: bytes | None = None, + prefix: bytes | None = None, + dir: bytes | None = None, + text: bool = False, +) -> tuple[int, bytes]: ... + + +async def mkstemp( + suffix: AnyStr | None = None, + prefix: AnyStr | None = None, + dir: AnyStr | None = None, + text: bool = False, +) -> tuple[int, str | bytes]: + """ + Asynchronously create a temporary file and return an OS-level handle and the file + name. + + This function wraps `tempfile.mkstemp` and executes it in a background thread. + + :param suffix: Suffix to be added to the file name. + :param prefix: Prefix to be added to the file name. + :param dir: Directory in which the temporary file is created. + :param text: Whether the file is opened in text mode. + :return: A tuple containing the file descriptor and the file name. + + """ + return await to_thread.run_sync(tempfile.mkstemp, suffix, prefix, dir, text) + + +@overload +async def mkdtemp( + suffix: str | None = None, + prefix: str | None = None, + dir: str | None = None, +) -> str: ... + + +@overload +async def mkdtemp( + suffix: bytes | None = None, + prefix: bytes | None = None, + dir: bytes | None = None, +) -> bytes: ... + + +async def mkdtemp( + suffix: AnyStr | None = None, + prefix: AnyStr | None = None, + dir: AnyStr | None = None, +) -> str | bytes: + """ + Asynchronously create a temporary directory and return its path. + + This function wraps `tempfile.mkdtemp` and executes it in a background thread. + + :param suffix: Suffix to be added to the directory name. + :param prefix: Prefix to be added to the directory name. + :param dir: Parent directory where the temporary directory is created. + :return: The path of the created temporary directory. + + """ + return await to_thread.run_sync(tempfile.mkdtemp, suffix, prefix, dir) + + +async def gettempdir() -> str: + """ + Asynchronously return the name of the directory used for temporary files. + + This function wraps `tempfile.gettempdir` and executes it in a background thread. + + :return: The path of the temporary directory as a string. + + """ + return await to_thread.run_sync(tempfile.gettempdir) + + +async def gettempdirb() -> bytes: + """ + Asynchronously return the name of the directory used for temporary files in bytes. + + This function wraps `tempfile.gettempdirb` and executes it in a background thread. + + :return: The path of the temporary directory as bytes. + + """ + return await to_thread.run_sync(tempfile.gettempdirb) diff --git a/.deps_siteforge_v812/anyio/_core/_testing.py b/.deps_siteforge_v812/anyio/_core/_testing.py new file mode 100644 index 0000000..369e65c --- /dev/null +++ b/.deps_siteforge_v812/anyio/_core/_testing.py @@ -0,0 +1,82 @@ +from __future__ import annotations + +from collections.abc import Awaitable, Generator +from typing import Any, cast + +from ._eventloop import get_async_backend + + +class TaskInfo: + """ + Represents an asynchronous task. + + :ivar int id: the unique identifier of the task + :ivar parent_id: the identifier of the parent task, if any + :vartype parent_id: Optional[int] + :ivar str name: the description of the task (if any) + :ivar ~collections.abc.Coroutine coro: the coroutine object of the task + """ + + __slots__ = "_name", "id", "parent_id", "name", "coro" + + def __init__( + self, + id: int, + parent_id: int | None, + name: str | None, + coro: Generator[Any, Any, Any] | Awaitable[Any], + ): + func = get_current_task + self._name = f"{func.__module__}.{func.__qualname__}" + self.id: int = id + self.parent_id: int | None = parent_id + self.name: str | None = name + self.coro: Generator[Any, Any, Any] | Awaitable[Any] = coro + + def __eq__(self, other: object) -> bool: + if isinstance(other, TaskInfo): + return self.id == other.id + + return NotImplemented + + def __hash__(self) -> int: + return hash(self.id) + + def __repr__(self) -> str: + return f"{self.__class__.__name__}(id={self.id!r}, name={self.name!r})" + + def has_pending_cancellation(self) -> bool: + """ + Return ``True`` if the task has a cancellation pending, ``False`` otherwise. + + """ + return False + + +def get_current_task() -> TaskInfo: + """ + Return the current task. + + :return: a representation of the current task + :raises NoEventLoopError: if no supported asynchronous event loop is running in the + current thread + + """ + return get_async_backend().get_current_task() + + +def get_running_tasks() -> list[TaskInfo]: + """ + Return a list of running tasks in the current event loop. + + :return: a list of task info objects + :raises NoEventLoopError: if no supported asynchronous event loop is running in the + current thread + + """ + return cast("list[TaskInfo]", get_async_backend().get_running_tasks()) + + +async def wait_all_tasks_blocked() -> None: + """Wait until all other tasks are waiting for something.""" + await get_async_backend().wait_all_tasks_blocked() diff --git a/.deps_siteforge_v812/anyio/_core/_typedattr.py b/.deps_siteforge_v812/anyio/_core/_typedattr.py new file mode 100644 index 0000000..f358a44 --- /dev/null +++ b/.deps_siteforge_v812/anyio/_core/_typedattr.py @@ -0,0 +1,81 @@ +from __future__ import annotations + +from collections.abc import Callable, Mapping +from typing import Any, TypeVar, final, overload + +from ._exceptions import TypedAttributeLookupError + +T_Attr = TypeVar("T_Attr") +T_Default = TypeVar("T_Default") +undefined = object() + + +def typed_attribute() -> Any: + """Return a unique object, used to mark typed attributes.""" + return object() + + +class TypedAttributeSet: + """ + Superclass for typed attribute collections. + + Checks that every public attribute of every subclass has a type annotation. + """ + + def __init_subclass__(cls) -> None: + annotations: dict[str, Any] = getattr(cls, "__annotations__", {}) + for attrname in dir(cls): + if not attrname.startswith("_") and attrname not in annotations: + raise TypeError( + f"Attribute {attrname!r} is missing its type annotation" + ) + + super().__init_subclass__() + + +class TypedAttributeProvider: + """Base class for classes that wish to provide typed extra attributes.""" + + @property + def extra_attributes(self) -> Mapping[T_Attr, Callable[[], T_Attr]]: + """ + A mapping of the extra attributes to callables that return the corresponding + values. + + If the provider wraps another provider, the attributes from that wrapper should + also be included in the returned mapping (but the wrapper may override the + callables from the wrapped instance). + + """ + return {} + + @overload + def extra(self, attribute: T_Attr) -> T_Attr: ... + + @overload + def extra(self, attribute: T_Attr, default: T_Default) -> T_Attr | T_Default: ... + + @final + def extra(self, attribute: Any, default: object = undefined) -> object: + """ + extra(attribute, default=undefined) + + Return the value of the given typed extra attribute. + + :param attribute: the attribute (member of a :class:`~TypedAttributeSet`) to + look for + :param default: the value that should be returned if no value is found for the + attribute + :raises ~anyio.TypedAttributeLookupError: if the search failed and no default + value was given + + """ + try: + getter = self.extra_attributes[attribute] + except KeyError: + if default is undefined: + raise TypedAttributeLookupError("Attribute not found") from None + else: + return default + + return getter() diff --git a/.deps_siteforge_v812/anyio/abc/__init__.py b/.deps_siteforge_v812/anyio/abc/__init__.py new file mode 100644 index 0000000..d560ce3 --- /dev/null +++ b/.deps_siteforge_v812/anyio/abc/__init__.py @@ -0,0 +1,58 @@ +from __future__ import annotations + +from ._eventloop import AsyncBackend as AsyncBackend +from ._resources import AsyncResource as AsyncResource +from ._sockets import ConnectedUDPSocket as ConnectedUDPSocket +from ._sockets import ConnectedUNIXDatagramSocket as ConnectedUNIXDatagramSocket +from ._sockets import IPAddressType as IPAddressType +from ._sockets import IPSockAddrType as IPSockAddrType +from ._sockets import SocketAttribute as SocketAttribute +from ._sockets import SocketListener as SocketListener +from ._sockets import SocketStream as SocketStream +from ._sockets import UDPPacketType as UDPPacketType +from ._sockets import UDPSocket as UDPSocket +from ._sockets import UNIXDatagramPacketType as UNIXDatagramPacketType +from ._sockets import UNIXDatagramSocket as UNIXDatagramSocket +from ._sockets import UNIXSocketStream as UNIXSocketStream +from ._streams import AnyByteReceiveStream as AnyByteReceiveStream +from ._streams import AnyByteSendStream as AnyByteSendStream +from ._streams import AnyByteStream as AnyByteStream +from ._streams import AnyByteStreamConnectable as AnyByteStreamConnectable +from ._streams import AnyUnreliableByteReceiveStream as AnyUnreliableByteReceiveStream +from ._streams import AnyUnreliableByteSendStream as AnyUnreliableByteSendStream +from ._streams import AnyUnreliableByteStream as AnyUnreliableByteStream +from ._streams import ByteReceiveStream as ByteReceiveStream +from ._streams import ByteSendStream as ByteSendStream +from ._streams import ByteStream as ByteStream +from ._streams import ByteStreamConnectable as ByteStreamConnectable +from ._streams import Listener as Listener +from ._streams import ObjectReceiveStream as ObjectReceiveStream +from ._streams import ObjectSendStream as ObjectSendStream +from ._streams import ObjectStream as ObjectStream +from ._streams import ObjectStreamConnectable as ObjectStreamConnectable +from ._streams import UnreliableObjectReceiveStream as UnreliableObjectReceiveStream +from ._streams import UnreliableObjectSendStream as UnreliableObjectSendStream +from ._streams import UnreliableObjectStream as UnreliableObjectStream +from ._subprocesses import Process as Process +from ._tasks import TaskGroup as TaskGroup +from ._tasks import TaskStatus as TaskStatus +from ._testing import TestRunner as TestRunner + +# Re-exported here, for backwards compatibility +# isort: off +from .._core._synchronization import ( + CapacityLimiter as CapacityLimiter, + Condition as Condition, + Event as Event, + Lock as Lock, + Semaphore as Semaphore, +) +from .._core._tasks import CancelScope as CancelScope +from ..from_thread import BlockingPortal as BlockingPortal + +# Re-export imports so they look like they live directly in this package +for __value in list(locals().values()): + if getattr(__value, "__module__", "").startswith("anyio.abc."): + __value.__module__ = __name__ + +del __value diff --git a/.deps_siteforge_v812/anyio/abc/__pycache__/__init__.cpython-312.pyc b/.deps_siteforge_v812/anyio/abc/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3a5b2b8f9c86289f4c38300bfbd0343ae3b36fc6 GIT binary patch literal 2360 zcmZvdxo;as6vk&I@jfL{)JffxL`kG%9lm5Z))CtZ3|p`yBbYE)EV*N8X;^RwI6@q3vz-gKmoC5Bpy@Gpy`)HrwG;lxd7u*XxKnDc( z0T0qa!TrEPbV%?3aE4|C4+0Ou(ni=t_+EP6sEwm)vq(;Vo3ZCo-PYdjehij#(BRbWsTbk`KlW`ap ztvkC8XNm#s7_4Cj*XlH~gw8H1y?}2N7 zhQABjbYjMuW^;XgEkeIu{Gdn6Y`q_boAY*lEL|MUmJDIx zGzayZwpv#jJaYowcS=Wa`tNT?vTnCzk2bt!{@%0g))_LV8}ae{1MhcenJN82v^i&B zhg)SRJJ|2go?<_^!L4cq8ewegLhV;E6y9UDbGHh0&0oVUO>#arv8hy)vgXuoX@=%7 z?uKB+s)o;s>-+Q}Gaa|%7F53*+GU1PIkY&QX=GC|%S_)bTlFg6(4mWF9_(1$QFJ#f z%Z8;^btcO$ISwmkO|uG0sa&|7yN1F0Rj0}!$ng;egW){7WmUMZZom^Hoh7w0fpd?W`QMy1di`XukccYg==|M@O^rG~k^rH-* z45AF7WKf1tMo>mk#!$vl;N~KH5+w`b#$>x%s?-mJ*?bB;zwE$C*nAp$&7c#M9XJix zG@ip|S}w!h7G%7Z4!LD&$Kt}`b2wlgC67`-SpadPjREz%i)cmebIXvOL%20meh!@u z9WE8u567eEw(EbW=v8LB0a+I01`Zh1hQr;k?I_%_A8F1ZEKD{P1CM73Qv5*Pfs+63 zZC)vE)>@*6}bj*nOEqOO&Sm6~&C znMF|s3s43NvaFe!Bg=(K%?)0um&#SX4b$N5Z2t`Mrd^UGkBt9CRy=avBUe4L<2CrA zaF#@K-XqsV^s+}bJaR>tpLyh-=zmisE_com30e)L`R`_hX*?)SjTuYr{x di!TEke`KRfWMbK@q$=-q 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 literal 0 HcmV?d00001 diff --git a/.deps_siteforge_v812/anyio/abc/__pycache__/_resources.cpython-312.pyc b/.deps_siteforge_v812/anyio/abc/__pycache__/_resources.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..23e2a40d891881264f202b2d39116629893ff9c2 GIT binary patch literal 1632 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 literal 0 HcmV?d00001 diff --git a/.deps_siteforge_v812/anyio/abc/__pycache__/_subprocesses.cpython-312.pyc b/.deps_siteforge_v812/anyio/abc/__pycache__/_subprocesses.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d0b65f3e4108211def16fb48b3a983e712d8bc27 GIT binary patch literal 3240 zcmcIn-H#MS5bxfd{r-UCASyAWLnN{h27w^5G0Gw2yl@^D?g(bxI6J%3cQ-m;cF%I| z#ut~VZtjPp=iT~*yx_3P^D-tTI) zGJ)^6Z|{9PTO{OX98A7k#L#gInA^l42DOMyTa+T7u`+hHl@0lqr>Z^{(`V z-t%qlZ@30X3M=Niqg(PJjV9FQSGn!AJ<~hx>tdbz z$KO~#eQG#7BiokQZF>VKFsH3M1JgaBcY7zat;D%hy+NRCLK4GjjGc<38hDj?Ait8o zZj!$x7Sk6Qg-#&^G6JF7R}=^T%-b*Os2*0-zyGuN2sN@;d2 zp2F&i!>*f-;cm)fFw>L_QnV!NbdnY0NRKu;9X0U>)0VHSEPTM^K>FOCOqOsuaPrE+ z{OYBv?{_-Pchdo-9IGmmoWl`xn@!flgZX%osQx6aPU3kq7ucYbm=0$q z*e0R_U@^uLyGh1C$uoidK5=*1L^x7)lS<)#!|w9J;^KeC?h}aonXoBnwN;HcVgQiX zj|5@sA#CYydkub*K!vT5=}d;5b+YkW<(~rXx%M7wNsAWiq|Uad3*C;LL?=9U==AL_ z7wjlk*HzXu;J`vbg|Vnmj_+Mt?lh86qatc##2!d3$VX-pQ)Miwr%KBGaCTBrDdB5i zoPsIc%n+{aFdbS=HLQh;+nSO3gsRSZrZKEcyu;wk?!0a$8Ke8CE$-9|RkkqI9NC60 z8Qh?3z@Jv7PYcHF`_CUu9^Au(VLcTNjOTY3 z&znz+hYJCC?K&!$HWd|&7pJ0Hf`?N++Rq?sIzAk*BVZ6HSRV~VyoA>H7!1YYRp4Ti zVHwQX=PWdgW(^Nazqht&UwrI=HZj-}6gAD!ZLVoSS<`IS=v&BFHSN>BZpE5{rWtNe z(*)k9Vg|`9k{6I11QHZI0WTHd55&tTJA{NGIgI2LB;i$rZ8B^cqJkU}d9g&(0y|70Xgy`Ir~Tc5~cI>EBj9pGGUni0vSjr@Bjb+ literal 0 HcmV?d00001 diff --git a/.deps_siteforge_v812/anyio/abc/__pycache__/_tasks.cpython-312.pyc b/.deps_siteforge_v812/anyio/abc/__pycache__/_tasks.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..42d99156560cd0a1629877d8102474b9a487d8dc GIT binary patch literal 5139 zcmb_g&2Jn@6|bJ@`5xQxM?N+m-r9|JlX09$7H0z)Maw$I3X#D?n+XzQTkV?evZs@t z?xCtD_E;RU5QMm(+%|{(1BkNR`3E?0VF?o1mO>J(5JVi9!~p>(-mC8E8Ji>miMCw* z`c=KESMTHZs^-^2LwN$vUw`$*wVw_X@+E%so^(iP+lv|@pAd^!nnRjeP1BH0IEh*U z-bp9f)N6V(RZA(p?xdTUTBez;Wz~1e$u;w}yrR=ip;@dIn?tps=5TGeIZ_+Z2v1z2 zy(Rl^D|3CB5RriuvLH2D8`X%LR$pV`*DK_5N-hT@^B^}~8*fh3CO|f;IAeVdFN(BP zu!_#!=DylKjU)&kwTAA&Uh%2zSKq_dh?V$k&%I>CJpkNsYcwA9MuJ=<*4Qm#jq_oC z;0+iSk3FdRCZKOG*FW1A^AB|rrTtHF!jcvgnCp5z^KH+Si={-6nY}RgZSJ#RnAN2( znCUmU-|#Hx%FS*t+h=u$2iZC1I7q<^Lsb~X>&(1f?X-AF2gdg<&l_{!oS$2`w0JQ{ z&bpl-ds%qCXL?SM^=@$CcnoHIio1F`6e^NI>F+X8N(kgUEg(A)6eD)E-GX(~SKSsY z^fVF56y}&~a_5TawLqt;@e%iLFj%5$n4SyDS7Cyoki5Z!&0IgoSbUAO9UnCETZuuAm9esxek)beJIPWm$XA)X zeg!PsmS*JB!Bw>muFk8Z!n)N zf6FtOvs~rP)^f{ko%ESl=l;pF>oZ^3+In9uJ9d4!)d6?8%gpWA-YHf$PZ>TaBFn8# zkdYwZb8Bm?uUk3d9foyf3Yweb!Fy-6fgmPYdGG<_^wkzepi4|bb2lLp>*SP7^7L$@&6l0e9o|+<*6x3UbXN>1qRNEBqpwj=%?r_)JIPhbl zwpP-WfwuPa&AzngxqKx^3+}gt8|e56!`=lUu@9RPHdEN3oujU;kzQoeA5awO4=bgD ztYNsU$qggO8%EQ!+78l1!}xxiIbn||;*6+YxFs}v=&3_UVT1n?`M1b})KH+G*-n!5 z@B{tKWBi(lzGnWWr&2|=J0)d;K>T22!=r6~3T;1$t^$D9l2(H2R`NXb@RX%nsZX^W z8T_;IR{B;tPPFtZMis2gz^Dw2Dq7irbjC1QQ3Of-qyU7+S1R;7Zm68?O;g!!wLIa|HBV5^ z*0~6?Vhs@76E>IBmK2~++7=g}1B88@#}Pg?#W!1^U6Z>$bEc-s^pXo%B7ECyJ4{UD zZaUQR++(Pt*OUrTM=fF)>D7HQGqi)kGgiyHE7O((>4*1oEdg@8EIV`7K zWAS=>eI0z;2V00tNEwL=Sr)e-LTAdS<5fc#y&HmIprdNyJv6tDn~{hbi0=xIg1iD` zLwcUOH4*1%SgjNn3$!?n4T9AU-yrn<1bu^Ow?H(&ppN#7k!~DfCQkd2Y zPbwQ;+p%Jm(BqJOwh4->h*hJbDy*t@l+d`#I>cmHx(7CeGrV$R16zm^=hZ0|E)@C= zvIsQaF)i38a^()?dmD?H|MvuCF7;YbmaAk%h*ySC>qN?eW-jH-!Z7#@_8_6qzNX?{ssD3{^r>$v;y<0?!R<&v3xYK`~{jgq05C z^=YjQ_cFLX2{D2@e+ErAscG6n^7fY`|A-uWL{2>5PdzD&-LBj(Oy10Xfz;2d yzdZB1!iV^^|4#mX;polmi*!*d-ac^W_~)ZXUl5>QOfG2Jf_CTF--#lWNB#|Fba6}o literal 0 HcmV?d00001 diff --git a/.deps_siteforge_v812/anyio/abc/__pycache__/_testing.cpython-312.pyc b/.deps_siteforge_v812/anyio/abc/__pycache__/_testing.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..23fa2ee062bc6c6972e55921e932cc75f3f1c9dc GIT binary patch literal 2835 zcmd5;&2Jl35P!S&+Pmv@`jxgcqGVM;BSY#UDq0#o&G}C?bdc0Z8S{tt$TpCvI>lE1?wi#0fV>9C~8r?b`9Cp~{i9HE(ui-t4^J%zN{G zu2yvd&tE_NcI$MRkZ1TZdX$urhMT}VBrb6!kNA=$Nyy8dY{@VdynLYEn|6K@i3?Zihj%T$7XX;?xRL|Y1Z*p1r(OVk@Ns6hz63X4H6DK~l@c9Zf%wi`|wK}xf5q~Uk){*W+Y zNiKoKOIE>EU3m+Z&lHX1f3D^hwu+B4KUOJk;p%R2LR;RWt+>jBR^6jD+|qy>NO zL~pPt{=6H&lKWgY;kgz#v=enb8neixCI?+RSU1@n7R06(hMl^3?GEET(*bZypSsM9 zHvpXV4H(;z8MbaQCpPc4LEDW`$%NmC`fSpR5IR{Z#d47c%%ovS;B}9DK^{rVk1&bF zh9t$UP7g92G-=Rlhv#X_Id8)vA*<{4PA}1H8DVz8Oj1Vf4)v0f5VazlgbCos zgdu!)vmHY)$MV4l;WU_Ju?!4ya)uuVfs+*+u<0`yi&T>-0RRQ;_!&sLN#TzrZ?2DO z-dt;M#$C?p`q*%xLpk+F3VW>^I18B7k;Fs^Zm+#FZr_CzL<{C^*1H>W*MzsO4-iLY z9H!N?pBJc{cw^TUXAn-XfT6Z={S00Q7eg7yH{?LM>2HrC%q8ak znhr!hO-+`7aP8GKk>bt~mpr}|gl`1vi9N7p^0#3YH<28q&ig;?k7W#deO#@k_xsB~^WN;P@Dy=Z| zGD)q(PowPYsCBN?>-Bjv-&j}d28e>*>Z(}oiz&D5#4FH@Qz;i-jZvQk>(&2oJMPLW z;7v@PH^Dk}%jX~v;fou?C9N525sBG2W5q7HRQ znI4o#X?h?l%FNTF^8*D&kQ_ZbC?ltmBkv3}CpMER5_2Q-Z4~#?up51`3QY z^T_O=jGRjJ6FVAknLWy8UTtseZ?7uGlT#lJ3OIi9l^CCD)g1-ih41GwaOrYIX-bI( zH!tdVG>U465zVzOwjO{D58y<>nvIeI^l6U0QQ(*yazf983kF{2>ZroY0>`$@RU{ga zqy$r;KXAOHazs;9Knidk^hCXq=I}#)0qpRG5zPY`6eLOdot$|_jBPTvO>X>3Zfuj; aKNRCZP5%O`Ozv?2 literal 0 HcmV?d00001 diff --git a/.deps_siteforge_v812/anyio/abc/_eventloop.py b/.deps_siteforge_v812/anyio/abc/_eventloop.py new file mode 100644 index 0000000..ae06288 --- /dev/null +++ b/.deps_siteforge_v812/anyio/abc/_eventloop.py @@ -0,0 +1,409 @@ +from __future__ import annotations + +import math +import sys +from abc import ABCMeta, abstractmethod +from collections.abc import AsyncIterator, Awaitable, Callable, Sequence +from contextlib import AbstractContextManager +from os import PathLike +from signal import Signals +from socket import AddressFamily, SocketKind, socket +from typing import ( + IO, + TYPE_CHECKING, + Any, + TypeAlias, + TypeVar, + overload, +) + +if sys.version_info >= (3, 11): + from typing import TypeVarTuple, Unpack +else: + from typing_extensions import TypeVarTuple, Unpack + +if TYPE_CHECKING: + from _typeshed import FileDescriptorLike + + from .._core._synchronization import CapacityLimiter, Event, Lock, Semaphore + from .._core._tasks import CancelScope + from .._core._testing import TaskInfo + from ._sockets import ( + ConnectedUDPSocket, + ConnectedUNIXDatagramSocket, + IPSockAddrType, + SocketListener, + SocketStream, + UDPSocket, + UNIXDatagramSocket, + UNIXSocketStream, + ) + from ._subprocesses import Process + from ._tasks import TaskGroup + from ._testing import TestRunner + +T_Retval = TypeVar("T_Retval") +PosArgsT = TypeVarTuple("PosArgsT") +StrOrBytesPath: TypeAlias = str | bytes | PathLike[str] | PathLike[bytes] + + +class AsyncBackend(metaclass=ABCMeta): + @classmethod + @abstractmethod + def run( + cls, + func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval]], + args: tuple[Unpack[PosArgsT]], + kwargs: dict[str, Any], + options: dict[str, Any], + ) -> T_Retval: + """ + Run the given coroutine function in an asynchronous event loop. + + The current thread must not be already running an event loop. + + :param func: a coroutine function + :param args: positional arguments to ``func`` + :param kwargs: positional arguments to ``func`` + :param options: keyword arguments to call the backend ``run()`` implementation + with + :return: the return value of the coroutine function + """ + + @classmethod + @abstractmethod + def current_token(cls) -> object: + """ + Return an object that allows other threads to run code inside the event loop. + + :return: a token object, specific to the event loop running in the current + thread + """ + + @classmethod + @abstractmethod + def current_time(cls) -> float: + """ + Return the current value of the event loop's internal clock. + + :return: the clock value (seconds) + """ + + @classmethod + @abstractmethod + def cancelled_exception_class(cls) -> type[BaseException]: + """Return the exception class that is raised in a task if it's cancelled.""" + + @classmethod + @abstractmethod + async def checkpoint(cls) -> None: + """ + Check if the task has been cancelled, and allow rescheduling of other tasks. + + This is effectively the same as running :meth:`checkpoint_if_cancelled` and then + :meth:`cancel_shielded_checkpoint`. + """ + + @classmethod + async def checkpoint_if_cancelled(cls) -> None: + """ + Check if the current task group has been cancelled. + + This will check if the task has been cancelled, but will not allow other tasks + to be scheduled if not. + + """ + if cls.current_effective_deadline() == -math.inf: + await cls.checkpoint() + + @classmethod + async def cancel_shielded_checkpoint(cls) -> None: + """ + Allow the rescheduling of other tasks. + + This will give other tasks the opportunity to run, but without checking if the + current task group has been cancelled, unlike with :meth:`checkpoint`. + + """ + with cls.create_cancel_scope(shield=True): + await cls.sleep(0) + + @classmethod + @abstractmethod + async def sleep(cls, delay: float) -> None: + """ + Pause the current task for the specified duration. + + :param delay: the duration, in seconds + """ + + @classmethod + @abstractmethod + def create_cancel_scope( + cls, *, deadline: float = math.inf, shield: bool = False + ) -> CancelScope: + pass + + @classmethod + @abstractmethod + def current_effective_deadline(cls) -> float: + """ + Return the nearest deadline among all the cancel scopes effective for the + current task. + + :return: + - a clock value from the event loop's internal clock + - ``inf`` if there is no deadline in effect + - ``-inf`` if the current scope has been cancelled + :rtype: float + """ + + @classmethod + @abstractmethod + def create_task_group(cls) -> TaskGroup: + pass + + @classmethod + @abstractmethod + def create_event(cls) -> Event: + pass + + @classmethod + @abstractmethod + def create_lock(cls, *, fast_acquire: bool) -> Lock: + pass + + @classmethod + @abstractmethod + def create_semaphore( + cls, + initial_value: int, + *, + max_value: int | None = None, + fast_acquire: bool = False, + ) -> Semaphore: + pass + + @classmethod + @abstractmethod + def create_capacity_limiter(cls, total_tokens: float) -> CapacityLimiter: + pass + + @classmethod + @abstractmethod + async def run_sync_in_worker_thread( + cls, + func: Callable[[Unpack[PosArgsT]], T_Retval], + args: tuple[Unpack[PosArgsT]], + abandon_on_cancel: bool = False, + limiter: CapacityLimiter | None = None, + ) -> T_Retval: + pass + + @classmethod + @abstractmethod + def check_cancelled(cls) -> None: + pass + + @classmethod + @abstractmethod + def run_async_from_thread( + cls, + func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval]], + args: tuple[Unpack[PosArgsT]], + token: object, + ) -> T_Retval: + pass + + @classmethod + @abstractmethod + def run_sync_from_thread( + cls, + func: Callable[[Unpack[PosArgsT]], T_Retval], + args: tuple[Unpack[PosArgsT]], + token: object, + ) -> T_Retval: + pass + + @classmethod + @abstractmethod + async def open_process( + cls, + command: StrOrBytesPath | Sequence[StrOrBytesPath], + *, + stdin: int | IO[Any] | None, + stdout: int | IO[Any] | None, + stderr: int | IO[Any] | None, + **kwargs: Any, + ) -> Process: + pass + + @classmethod + @abstractmethod + def setup_process_pool_exit_at_shutdown(cls, workers: set[Process]) -> None: + pass + + @classmethod + @abstractmethod + async def connect_tcp( + cls, host: str, port: int, local_address: IPSockAddrType | None = None + ) -> SocketStream: + pass + + @classmethod + @abstractmethod + async def connect_unix(cls, path: str | bytes) -> UNIXSocketStream: + pass + + @classmethod + @abstractmethod + def create_tcp_listener(cls, sock: socket) -> SocketListener: + pass + + @classmethod + @abstractmethod + def create_unix_listener(cls, sock: socket) -> SocketListener: + pass + + @classmethod + @abstractmethod + async def create_udp_socket( + cls, + family: AddressFamily, + local_address: IPSockAddrType | None, + remote_address: IPSockAddrType | None, + reuse_port: bool, + ) -> UDPSocket | ConnectedUDPSocket: + pass + + @classmethod + @overload + async def create_unix_datagram_socket( + cls, raw_socket: socket, remote_path: None + ) -> UNIXDatagramSocket: ... + + @classmethod + @overload + async def create_unix_datagram_socket( + cls, raw_socket: socket, remote_path: str | bytes + ) -> ConnectedUNIXDatagramSocket: ... + + @classmethod + @abstractmethod + async def create_unix_datagram_socket( + cls, raw_socket: socket, remote_path: str | bytes | None + ) -> UNIXDatagramSocket | ConnectedUNIXDatagramSocket: + pass + + @classmethod + @abstractmethod + async def getaddrinfo( + cls, + host: bytes | str | None, + port: str | int | None, + *, + family: int | AddressFamily = 0, + type: int | SocketKind = 0, + proto: int = 0, + flags: int = 0, + ) -> Sequence[ + tuple[ + AddressFamily, + SocketKind, + int, + str, + tuple[str, int] | tuple[str, int, int, int] | tuple[int, bytes], + ] + ]: + pass + + @classmethod + @abstractmethod + async def getnameinfo( + cls, sockaddr: IPSockAddrType, flags: int = 0 + ) -> tuple[str, str]: + pass + + @classmethod + @abstractmethod + async def wait_readable(cls, obj: FileDescriptorLike) -> None: + pass + + @classmethod + @abstractmethod + async def wait_writable(cls, obj: FileDescriptorLike) -> None: + pass + + @classmethod + @abstractmethod + def notify_closing(cls, obj: FileDescriptorLike) -> None: + pass + + @classmethod + @abstractmethod + async def wrap_listener_socket(cls, sock: socket) -> SocketListener: + pass + + @classmethod + @abstractmethod + async def wrap_stream_socket(cls, sock: socket) -> SocketStream: + pass + + @classmethod + @abstractmethod + async def wrap_unix_stream_socket(cls, sock: socket) -> UNIXSocketStream: + pass + + @classmethod + @abstractmethod + async def wrap_udp_socket(cls, sock: socket) -> UDPSocket: + pass + + @classmethod + @abstractmethod + async def wrap_connected_udp_socket(cls, sock: socket) -> ConnectedUDPSocket: + pass + + @classmethod + @abstractmethod + async def wrap_unix_datagram_socket(cls, sock: socket) -> UNIXDatagramSocket: + pass + + @classmethod + @abstractmethod + async def wrap_connected_unix_datagram_socket( + cls, sock: socket + ) -> ConnectedUNIXDatagramSocket: + pass + + @classmethod + @abstractmethod + def current_default_thread_limiter(cls) -> CapacityLimiter: + pass + + @classmethod + @abstractmethod + def open_signal_receiver( + cls, *signals: Signals + ) -> AbstractContextManager[AsyncIterator[Signals]]: + pass + + @classmethod + @abstractmethod + def get_current_task(cls) -> TaskInfo: + pass + + @classmethod + @abstractmethod + def get_running_tasks(cls) -> Sequence[TaskInfo]: + pass + + @classmethod + @abstractmethod + async def wait_all_tasks_blocked(cls) -> None: + pass + + @classmethod + @abstractmethod + def create_test_runner(cls, options: dict[str, Any]) -> TestRunner: + pass diff --git a/.deps_siteforge_v812/anyio/abc/_resources.py b/.deps_siteforge_v812/anyio/abc/_resources.py new file mode 100644 index 0000000..10df115 --- /dev/null +++ b/.deps_siteforge_v812/anyio/abc/_resources.py @@ -0,0 +1,33 @@ +from __future__ import annotations + +from abc import ABCMeta, abstractmethod +from types import TracebackType +from typing import TypeVar + +T = TypeVar("T") + + +class AsyncResource(metaclass=ABCMeta): + """ + Abstract base class for all closeable asynchronous resources. + + Works as an asynchronous context manager which returns the instance itself on enter, + and calls :meth:`aclose` on exit. + """ + + __slots__ = () + + async def __aenter__(self: T) -> T: + return self + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + await self.aclose() + + @abstractmethod + async def aclose(self) -> None: + """Close the resource.""" diff --git a/.deps_siteforge_v812/anyio/abc/_sockets.py b/.deps_siteforge_v812/anyio/abc/_sockets.py new file mode 100644 index 0000000..feb26bd --- /dev/null +++ b/.deps_siteforge_v812/anyio/abc/_sockets.py @@ -0,0 +1,399 @@ +from __future__ import annotations + +import errno +import socket +from abc import abstractmethod +from collections.abc import Callable, Collection, Mapping +from contextlib import AsyncExitStack +from io import IOBase +from ipaddress import IPv4Address, IPv6Address +from socket import AddressFamily +from typing import Any, TypeAlias, TypeVar + +from .._core._eventloop import get_async_backend +from .._core._typedattr import ( + TypedAttributeProvider, + TypedAttributeSet, + typed_attribute, +) +from ._streams import ByteStream, Listener, UnreliableObjectStream +from ._tasks import TaskGroup + +IPAddressType: TypeAlias = str | IPv4Address | IPv6Address +IPSockAddrType: TypeAlias = tuple[str, int] +SockAddrType: TypeAlias = IPSockAddrType | str +UDPPacketType: TypeAlias = tuple[bytes, IPSockAddrType] +UNIXDatagramPacketType: TypeAlias = tuple[bytes, str] +T_Retval = TypeVar("T_Retval") + + +def _validate_socket( + sock_or_fd: socket.socket | int, + sock_type: socket.SocketKind, + addr_family: socket.AddressFamily = socket.AF_UNSPEC, + *, + require_connected: bool = False, + require_bound: bool = False, +) -> socket.socket: + if isinstance(sock_or_fd, int): + try: + sock = socket.socket(fileno=sock_or_fd) + except OSError as exc: + if exc.errno == errno.ENOTSOCK: + raise ValueError( + "the file descriptor does not refer to a socket" + ) from exc + elif require_connected: + raise ValueError("the socket must be connected") from exc + elif require_bound: + raise ValueError("the socket must be bound to a local address") from exc + else: + raise + elif isinstance(sock_or_fd, socket.socket): + sock = sock_or_fd + else: + raise TypeError( + f"expected an int or socket, got {type(sock_or_fd).__qualname__} instead" + ) + + try: + if require_connected: + try: + sock.getpeername() + except OSError as exc: + raise ValueError("the socket must be connected") from exc + + if require_bound: + try: + if sock.family in (socket.AF_INET, socket.AF_INET6): + bound_addr = sock.getsockname()[1] + else: + bound_addr = sock.getsockname() + except OSError: + bound_addr = None + + if not bound_addr: + raise ValueError("the socket must be bound to a local address") + + if addr_family != socket.AF_UNSPEC and sock.family != addr_family: + raise ValueError( + f"address family mismatch: expected {addr_family.name}, got " + f"{sock.family.name}" + ) + + if sock.type != sock_type: + raise ValueError( + f"socket type mismatch: expected {sock_type.name}, got {sock.type.name}" + ) + except BaseException: + # Avoid ResourceWarning from the locally constructed socket object + if isinstance(sock_or_fd, int): + sock.detach() + + raise + + sock.setblocking(False) + return sock + + +class SocketAttribute(TypedAttributeSet): + """ + .. attribute:: family + :type: socket.AddressFamily + + the address family of the underlying socket + + .. attribute:: local_address + :type: tuple[str, int] | str + + the local address the underlying socket is connected to + + .. attribute:: local_port + :type: int + + for IP based sockets, the local port the underlying socket is bound to + + .. attribute:: raw_socket + :type: socket.socket + + the underlying stdlib socket object + + .. attribute:: remote_address + :type: tuple[str, int] | str + + the remote address the underlying socket is connected to + + .. attribute:: remote_port + :type: int + + for IP based sockets, the remote port the underlying socket is connected to + """ + + family: AddressFamily = typed_attribute() + local_address: SockAddrType = typed_attribute() + local_port: int = typed_attribute() + raw_socket: socket.socket = typed_attribute() + remote_address: SockAddrType = typed_attribute() + remote_port: int = typed_attribute() + + +class _SocketProvider(TypedAttributeProvider): + @property + def extra_attributes(self) -> Mapping[Any, Callable[[], Any]]: + from .._core._sockets import convert_ipv6_sockaddr as convert + + attributes: dict[Any, Callable[[], Any]] = { + SocketAttribute.family: lambda: self._raw_socket.family, + SocketAttribute.local_address: lambda: convert( + self._raw_socket.getsockname() + ), + SocketAttribute.raw_socket: lambda: self._raw_socket, + } + try: + peername: tuple[str, int] | None = convert(self._raw_socket.getpeername()) + except OSError: + peername = None + + # Provide the remote address for connected sockets + if peername is not None: + attributes[SocketAttribute.remote_address] = lambda: peername + + # Provide local and remote ports for IP based sockets + if self._raw_socket.family in (AddressFamily.AF_INET, AddressFamily.AF_INET6): + attributes[SocketAttribute.local_port] = lambda: ( + self._raw_socket.getsockname()[1] + ) + if peername is not None: + remote_port = peername[1] + attributes[SocketAttribute.remote_port] = lambda: remote_port + + return attributes + + @property + @abstractmethod + def _raw_socket(self) -> socket.socket: + pass + + +class SocketStream(ByteStream, _SocketProvider): + """ + Transports bytes over a socket. + + Supports all relevant extra attributes from :class:`~SocketAttribute`. + """ + + @classmethod + async def from_socket(cls, sock_or_fd: socket.socket | int) -> SocketStream: + """ + Wrap an existing socket object or file descriptor as a socket stream. + + The newly created socket wrapper takes ownership of the socket being passed in. + The existing socket must already be connected. + + :param sock_or_fd: a socket object or file descriptor + :return: a socket stream + + """ + sock = _validate_socket(sock_or_fd, socket.SOCK_STREAM, require_connected=True) + return await get_async_backend().wrap_stream_socket(sock) + + +class UNIXSocketStream(SocketStream): + @classmethod + async def from_socket(cls, sock_or_fd: socket.socket | int) -> UNIXSocketStream: + """ + Wrap an existing socket object or file descriptor as a UNIX socket stream. + + The newly created socket wrapper takes ownership of the socket being passed in. + The existing socket must already be connected. + + :param sock_or_fd: a socket object or file descriptor + :return: a UNIX socket stream + + """ + sock = _validate_socket( + sock_or_fd, socket.SOCK_STREAM, socket.AF_UNIX, require_connected=True + ) + return await get_async_backend().wrap_unix_stream_socket(sock) + + @abstractmethod + async def send_fds(self, message: bytes, fds: Collection[int | IOBase]) -> None: + """ + Send file descriptors along with a message to the peer. + + :param message: a non-empty bytestring + :param fds: a collection of files (either numeric file descriptors or open file + or socket objects) + """ + + @abstractmethod + async def receive_fds(self, msglen: int, maxfds: int) -> tuple[bytes, list[int]]: + """ + Receive file descriptors along with a message from the peer. + + :param msglen: length of the message to expect from the peer + :param maxfds: maximum number of file descriptors to expect from the peer + :return: a tuple of (message, file descriptors) + """ + + +class SocketListener(Listener[SocketStream], _SocketProvider): + """ + Listens to incoming socket connections. + + Supports all relevant extra attributes from :class:`~SocketAttribute`. + """ + + @classmethod + async def from_socket( + cls, + sock_or_fd: socket.socket | int, + ) -> SocketListener: + """ + Wrap an existing socket object or file descriptor as a socket listener. + + The newly created listener takes ownership of the socket being passed in. + + :param sock_or_fd: a socket object or file descriptor + :return: a socket listener + + """ + sock = _validate_socket(sock_or_fd, socket.SOCK_STREAM, require_bound=True) + return await get_async_backend().wrap_listener_socket(sock) + + @abstractmethod + async def accept(self) -> SocketStream: + """Accept an incoming connection.""" + + async def serve( + self, + handler: Callable[[SocketStream], Any], + task_group: TaskGroup | None = None, + ) -> None: + from .. import create_task_group + + async with AsyncExitStack() as stack: + if task_group is None: + task_group = await stack.enter_async_context(create_task_group()) + + while True: + stream = await self.accept() + task_group.start_soon(handler, stream) + + +class UDPSocket(UnreliableObjectStream[UDPPacketType], _SocketProvider): + """ + Represents an unconnected UDP socket. + + Supports all relevant extra attributes from :class:`~SocketAttribute`. + """ + + @classmethod + async def from_socket(cls, sock_or_fd: socket.socket | int) -> UDPSocket: + """ + Wrap an existing socket object or file descriptor as a UDP socket. + + The newly created socket wrapper takes ownership of the socket being passed in. + The existing socket must be bound to a local address. + + :param sock_or_fd: a socket object or file descriptor + :return: a UDP socket + + """ + sock = _validate_socket(sock_or_fd, socket.SOCK_DGRAM, require_bound=True) + return await get_async_backend().wrap_udp_socket(sock) + + async def sendto(self, data: bytes, host: str, port: int) -> None: + """ + Alias for :meth:`~.UnreliableObjectSendStream.send` ((data, (host, port))). + + """ + return await self.send((data, (host, port))) + + +class ConnectedUDPSocket(UnreliableObjectStream[bytes], _SocketProvider): + """ + Represents an connected UDP socket. + + Supports all relevant extra attributes from :class:`~SocketAttribute`. + """ + + @classmethod + async def from_socket(cls, sock_or_fd: socket.socket | int) -> ConnectedUDPSocket: + """ + Wrap an existing socket object or file descriptor as a connected UDP socket. + + The newly created socket wrapper takes ownership of the socket being passed in. + The existing socket must already be connected. + + :param sock_or_fd: a socket object or file descriptor + :return: a connected UDP socket + + """ + sock = _validate_socket( + sock_or_fd, + socket.SOCK_DGRAM, + require_connected=True, + ) + return await get_async_backend().wrap_connected_udp_socket(sock) + + +class UNIXDatagramSocket( + UnreliableObjectStream[UNIXDatagramPacketType], _SocketProvider +): + """ + Represents an unconnected Unix datagram socket. + + Supports all relevant extra attributes from :class:`~SocketAttribute`. + """ + + @classmethod + async def from_socket( + cls, + sock_or_fd: socket.socket | int, + ) -> UNIXDatagramSocket: + """ + Wrap an existing socket object or file descriptor as a UNIX datagram + socket. + + The newly created socket wrapper takes ownership of the socket being passed in. + + :param sock_or_fd: a socket object or file descriptor + :return: a UNIX datagram socket + + """ + sock = _validate_socket(sock_or_fd, socket.SOCK_DGRAM, socket.AF_UNIX) + return await get_async_backend().wrap_unix_datagram_socket(sock) + + async def sendto(self, data: bytes, path: str) -> None: + """Alias for :meth:`~.UnreliableObjectSendStream.send` ((data, path)).""" + return await self.send((data, path)) + + +class ConnectedUNIXDatagramSocket(UnreliableObjectStream[bytes], _SocketProvider): + """ + Represents a connected Unix datagram socket. + + Supports all relevant extra attributes from :class:`~SocketAttribute`. + """ + + @classmethod + async def from_socket( + cls, + sock_or_fd: socket.socket | int, + ) -> ConnectedUNIXDatagramSocket: + """ + Wrap an existing socket object or file descriptor as a connected UNIX datagram + socket. + + The newly created socket wrapper takes ownership of the socket being passed in. + The existing socket must already be connected. + + :param sock_or_fd: a socket object or file descriptor + :return: a connected UNIX datagram socket + + """ + sock = _validate_socket( + sock_or_fd, socket.SOCK_DGRAM, socket.AF_UNIX, require_connected=True + ) + return await get_async_backend().wrap_connected_unix_datagram_socket(sock) diff --git a/.deps_siteforge_v812/anyio/abc/_streams.py b/.deps_siteforge_v812/anyio/abc/_streams.py new file mode 100644 index 0000000..186e3f5 --- /dev/null +++ b/.deps_siteforge_v812/anyio/abc/_streams.py @@ -0,0 +1,233 @@ +from __future__ import annotations + +from abc import ABCMeta, abstractmethod +from collections.abc import Callable +from typing import Any, Generic, TypeAlias, TypeVar + +from .._core._exceptions import EndOfStream +from .._core._typedattr import TypedAttributeProvider +from ._resources import AsyncResource +from ._tasks import TaskGroup + +T_Item = TypeVar("T_Item") +T_co = TypeVar("T_co", covariant=True) +T_contra = TypeVar("T_contra", contravariant=True) + + +class UnreliableObjectReceiveStream( + Generic[T_co], AsyncResource, TypedAttributeProvider +): + """ + An interface for receiving objects. + + This interface makes no guarantees that the received messages arrive in the order in + which they were sent, or that no messages are missed. + + Asynchronously iterating over objects of this type will yield objects matching the + given type parameter. + """ + + def __aiter__(self) -> UnreliableObjectReceiveStream[T_co]: + return self + + async def __anext__(self) -> T_co: + try: + return await self.receive() + except EndOfStream: + raise StopAsyncIteration from None + + @abstractmethod + async def receive(self) -> T_co: + """ + Receive the next item. + + :raises ~anyio.ClosedResourceError: if the receive stream has been explicitly + closed + :raises ~anyio.EndOfStream: if this stream has been closed from the other end + :raises ~anyio.BrokenResourceError: if this stream has been rendered unusable + due to external causes + """ + + +class UnreliableObjectSendStream( + Generic[T_contra], AsyncResource, TypedAttributeProvider +): + """ + An interface for sending objects. + + This interface makes no guarantees that the messages sent will reach the + recipient(s) in the same order in which they were sent, or at all. + """ + + @abstractmethod + async def send(self, item: T_contra) -> None: + """ + Send an item to the peer(s). + + :param item: the item to send + :raises ~anyio.ClosedResourceError: if the send stream has been explicitly + closed + :raises ~anyio.BrokenResourceError: if this stream has been rendered unusable + due to external causes + """ + + +class UnreliableObjectStream( + UnreliableObjectReceiveStream[T_Item], UnreliableObjectSendStream[T_Item] +): + """ + A bidirectional message stream which does not guarantee the order or reliability of + message delivery. + """ + + +class ObjectReceiveStream(UnreliableObjectReceiveStream[T_co]): + """ + A receive message stream which guarantees that messages are received in the same + order in which they were sent, and that no messages are missed. + """ + + +class ObjectSendStream(UnreliableObjectSendStream[T_contra]): + """ + A send message stream which guarantees that messages are delivered in the same order + in which they were sent, without missing any messages in the middle. + """ + + +class ObjectStream( + ObjectReceiveStream[T_Item], + ObjectSendStream[T_Item], + UnreliableObjectStream[T_Item], +): + """ + A bidirectional message stream which guarantees the order and reliability of message + delivery. + """ + + @abstractmethod + async def send_eof(self) -> None: + """ + Send an end-of-file indication to the peer. + + You should not try to send any further data to this stream after calling this + method. This method is idempotent (does nothing on successive calls). + """ + + +class ByteReceiveStream(AsyncResource, TypedAttributeProvider): + """ + An interface for receiving bytes from a single peer. + + Iterating this byte stream will yield a byte string of arbitrary length, but no more + than 65536 bytes. + """ + + def __aiter__(self) -> ByteReceiveStream: + return self + + async def __anext__(self) -> bytes: + try: + return await self.receive() + except EndOfStream: + raise StopAsyncIteration from None + + @abstractmethod + async def receive(self, max_bytes: int = 65536) -> bytes: + """ + Receive at most ``max_bytes`` bytes from the peer. + + .. note:: Implementers of this interface should not return an empty + :class:`bytes` object, and users should ignore them. + + :param max_bytes: maximum number of bytes to receive + :return: the received bytes + :raises ~anyio.EndOfStream: if this stream has been closed from the other end + """ + + +class ByteSendStream(AsyncResource, TypedAttributeProvider): + """An interface for sending bytes to a single peer.""" + + @abstractmethod + async def send(self, item: bytes) -> None: + """ + Send the given bytes to the peer. + + :param item: the bytes to send + """ + + +class ByteStream(ByteReceiveStream, ByteSendStream): + """A bidirectional byte stream.""" + + @abstractmethod + async def send_eof(self) -> None: + """ + Send an end-of-file indication to the peer. + + You should not try to send any further data to this stream after calling this + method. This method is idempotent (does nothing on successive calls). + """ + + +#: Type alias for all unreliable bytes-oriented receive streams. +AnyUnreliableByteReceiveStream: TypeAlias = ( + UnreliableObjectReceiveStream[bytes] | ByteReceiveStream +) +#: Type alias for all unreliable bytes-oriented send streams. +AnyUnreliableByteSendStream: TypeAlias = ( + UnreliableObjectSendStream[bytes] | ByteSendStream +) +#: Type alias for all unreliable bytes-oriented streams. +AnyUnreliableByteStream: TypeAlias = UnreliableObjectStream[bytes] | ByteStream +#: Type alias for all bytes-oriented receive streams. +AnyByteReceiveStream: TypeAlias = ObjectReceiveStream[bytes] | ByteReceiveStream +#: Type alias for all bytes-oriented send streams. +AnyByteSendStream: TypeAlias = ObjectSendStream[bytes] | ByteSendStream +#: Type alias for all bytes-oriented streams. +AnyByteStream: TypeAlias = ObjectStream[bytes] | ByteStream + + +class Listener(Generic[T_co], AsyncResource, TypedAttributeProvider): + """An interface for objects that let you accept incoming connections.""" + + @abstractmethod + async def serve( + self, handler: Callable[[T_co], Any], task_group: TaskGroup | None = None + ) -> None: + """ + Accept incoming connections as they come in and start tasks to handle them. + + :param handler: a callable that will be used to handle each accepted connection + :param task_group: the task group that will be used to start tasks for handling + each accepted connection (if omitted, an ad-hoc task group will be created) + """ + + +class ObjectStreamConnectable(Generic[T_co], metaclass=ABCMeta): + @abstractmethod + async def connect(self) -> ObjectStream[T_co]: + """ + Connect to the remote endpoint. + + :return: an object stream connected to the remote end + :raises ConnectionFailed: if the connection fails + """ + + +class ByteStreamConnectable(metaclass=ABCMeta): + @abstractmethod + async def connect(self) -> ByteStream: + """ + Connect to the remote endpoint. + + :return: a bytestream connected to the remote end + :raises ConnectionFailed: if the connection fails + """ + + +#: Type alias for all connectables returning bytestreams or bytes-oriented object streams +AnyByteStreamConnectable: TypeAlias = ( + ObjectStreamConnectable[bytes] | ByteStreamConnectable +) diff --git a/.deps_siteforge_v812/anyio/abc/_subprocesses.py b/.deps_siteforge_v812/anyio/abc/_subprocesses.py new file mode 100644 index 0000000..ce0564c --- /dev/null +++ b/.deps_siteforge_v812/anyio/abc/_subprocesses.py @@ -0,0 +1,79 @@ +from __future__ import annotations + +from abc import abstractmethod +from signal import Signals + +from ._resources import AsyncResource +from ._streams import ByteReceiveStream, ByteSendStream + + +class Process(AsyncResource): + """An asynchronous version of :class:`subprocess.Popen`.""" + + @abstractmethod + async def wait(self) -> int: + """ + Wait until the process exits. + + :return: the exit code of the process + """ + + @abstractmethod + def terminate(self) -> None: + """ + Terminates the process, gracefully if possible. + + On Windows, this calls ``TerminateProcess()``. + On POSIX systems, this sends ``SIGTERM`` to the process. + + .. seealso:: :meth:`subprocess.Popen.terminate` + """ + + @abstractmethod + def kill(self) -> None: + """ + Kills the process. + + On Windows, this calls ``TerminateProcess()``. + On POSIX systems, this sends ``SIGKILL`` to the process. + + .. seealso:: :meth:`subprocess.Popen.kill` + """ + + @abstractmethod + def send_signal(self, signal: Signals) -> None: + """ + Send a signal to the subprocess. + + .. seealso:: :meth:`subprocess.Popen.send_signal` + + :param signal: the signal number (e.g. :data:`signal.SIGHUP`) + """ + + @property + @abstractmethod + def pid(self) -> int: + """The process ID of the process.""" + + @property + @abstractmethod + def returncode(self) -> int | None: + """ + The return code of the process. If the process has not yet terminated, this will + be ``None``. + """ + + @property + @abstractmethod + def stdin(self) -> ByteSendStream | None: + """The stream for the standard input of the process.""" + + @property + @abstractmethod + def stdout(self) -> ByteReceiveStream | None: + """The stream for the standard output of the process.""" + + @property + @abstractmethod + def stderr(self) -> ByteReceiveStream | None: + """The stream for the standard error output of the process.""" diff --git a/.deps_siteforge_v812/anyio/abc/_tasks.py b/.deps_siteforge_v812/anyio/abc/_tasks.py new file mode 100644 index 0000000..516b3ec --- /dev/null +++ b/.deps_siteforge_v812/anyio/abc/_tasks.py @@ -0,0 +1,117 @@ +from __future__ import annotations + +import sys +from abc import ABCMeta, abstractmethod +from collections.abc import Awaitable, Callable +from types import TracebackType +from typing import TYPE_CHECKING, Any, Protocol, overload + +if sys.version_info >= (3, 13): + from typing import TypeVar +else: + from typing_extensions import TypeVar + +if sys.version_info >= (3, 11): + from typing import TypeVarTuple, Unpack +else: + from typing_extensions import TypeVarTuple, Unpack + +if TYPE_CHECKING: + from .._core._tasks import CancelScope + +T_Retval = TypeVar("T_Retval") +T_contra = TypeVar("T_contra", contravariant=True, default=None) +PosArgsT = TypeVarTuple("PosArgsT") + + +class TaskStatus(Protocol[T_contra]): + @overload + def started(self: TaskStatus[None]) -> None: ... + + @overload + def started(self, value: T_contra) -> None: ... + + def started(self, value: T_contra | None = None) -> None: + """ + Signal that the task has started. + + :param value: object passed back to the starter of the task + """ + + +class TaskGroup(metaclass=ABCMeta): + """ + Groups several asynchronous tasks together. + + :ivar cancel_scope: the cancel scope inherited by all child tasks + :vartype cancel_scope: CancelScope + + .. note:: On asyncio, support for eager task factories is considered to be + **experimental**. In particular, they don't follow the usual semantics of new + tasks being scheduled on the next iteration of the event loop, and may thus + cause unexpected behavior in code that wasn't written with such semantics in + mind. + """ + + cancel_scope: CancelScope + + @abstractmethod + def start_soon( + self, + func: Callable[[Unpack[PosArgsT]], Awaitable[Any]], + *args: Unpack[PosArgsT], + name: object = None, + ) -> None: + """ + Start a new task in this task group. + + :param func: a coroutine function + :param args: positional arguments to call the function with + :param name: name of the task, for the purposes of introspection and debugging + + .. versionadded:: 3.0 + """ + + @abstractmethod + async def start( + self, + func: Callable[..., Awaitable[Any]], + *args: object, + name: object = None, + ) -> Any: + """ + Start a new task and wait until it signals for readiness. + + The target callable must accept a keyword argument ``task_status`` (of type + :class:`TaskStatus`). Awaiting on this method will return whatever was passed to + ``task_status.started()`` (``None`` by default). + + .. note:: The :class:`TaskStatus` class is generic, and the type argument should + indicate the type of the value that will be passed to + ``task_status.started()``. + + :param func: a coroutine function that accepts the ``task_status`` keyword + argument + :param args: positional arguments to call the function with + :param name: an optional name for the task, for introspection and debugging + :return: the value passed to ``task_status.started()`` + :raises RuntimeError: if the task finishes without calling + ``task_status.started()`` + + .. seealso:: :ref:`start_initialize` + + .. versionadded:: 3.0 + """ + + @abstractmethod + async def __aenter__(self) -> TaskGroup: + """Enter the task group context and allow starting new tasks.""" + + @abstractmethod + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> bool: + """Exit the task group context waiting for all tasks to finish.""" diff --git a/.deps_siteforge_v812/anyio/abc/_testing.py b/.deps_siteforge_v812/anyio/abc/_testing.py new file mode 100644 index 0000000..7c50ed7 --- /dev/null +++ b/.deps_siteforge_v812/anyio/abc/_testing.py @@ -0,0 +1,65 @@ +from __future__ import annotations + +import types +from abc import ABCMeta, abstractmethod +from collections.abc import AsyncGenerator, Callable, Coroutine, Iterable +from typing import Any, TypeVar + +_T = TypeVar("_T") + + +class TestRunner(metaclass=ABCMeta): + """ + Encapsulates a running event loop. Every call made through this object will use the + same event loop. + """ + + def __enter__(self) -> TestRunner: + return self + + @abstractmethod + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: types.TracebackType | None, + ) -> bool | None: ... + + @abstractmethod + def run_asyncgen_fixture( + self, + fixture_func: Callable[..., AsyncGenerator[_T, Any]], + kwargs: dict[str, Any], + ) -> Iterable[_T]: + """ + Run an async generator fixture. + + :param fixture_func: the fixture function + :param kwargs: keyword arguments to call the fixture function with + :return: an iterator yielding the value yielded from the async generator + """ + + @abstractmethod + def run_fixture( + self, + fixture_func: Callable[..., Coroutine[Any, Any, _T]], + kwargs: dict[str, Any], + ) -> _T: + """ + Run an async fixture. + + :param fixture_func: the fixture function + :param kwargs: keyword arguments to call the fixture function with + :return: the return value of the fixture function + """ + + @abstractmethod + def run_test( + self, test_func: Callable[..., Coroutine[Any, Any, Any]], kwargs: dict[str, Any] + ) -> None: + """ + Run an async test function. + + :param test_func: the test function + :param kwargs: keyword arguments to call the test function with + """ diff --git a/.deps_siteforge_v812/anyio/from_thread.py b/.deps_siteforge_v812/anyio/from_thread.py new file mode 100644 index 0000000..837de5e --- /dev/null +++ b/.deps_siteforge_v812/anyio/from_thread.py @@ -0,0 +1,578 @@ +from __future__ import annotations + +__all__ = ( + "BlockingPortal", + "BlockingPortalProvider", + "check_cancelled", + "run", + "run_sync", + "start_blocking_portal", +) + +import sys +from collections.abc import Awaitable, Callable, Generator +from concurrent.futures import Future +from contextlib import ( + AbstractAsyncContextManager, + AbstractContextManager, + contextmanager, +) +from dataclasses import dataclass, field +from functools import partial +from inspect import isawaitable +from threading import Lock, Thread, current_thread, get_ident +from types import TracebackType +from typing import ( + Any, + Generic, + TypeVar, + cast, + overload, +) + +from ._core._eventloop import ( + get_cancelled_exc_class, + threadlocals, +) +from ._core._eventloop import run as run_eventloop +from ._core._exceptions import NoEventLoopError +from ._core._synchronization import Event +from ._core._tasks import CancelScope, create_task_group +from .abc._tasks import TaskStatus +from .lowlevel import EventLoopToken, current_token + +if sys.version_info >= (3, 11): + from typing import TypeVarTuple, Unpack +else: + from typing_extensions import TypeVarTuple, Unpack + +T_Retval = TypeVar("T_Retval") +T_co = TypeVar("T_co", covariant=True) +PosArgsT = TypeVarTuple("PosArgsT") + + +def _token_or_error(token: EventLoopToken | None) -> EventLoopToken: + if token is not None: + return token + + try: + return threadlocals.current_token + except AttributeError: + raise NoEventLoopError( + "Not running inside an AnyIO worker thread, and no event loop token was " + "provided" + ) from None + + +def run( + func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval]], + *args: Unpack[PosArgsT], + token: EventLoopToken | None = None, +) -> T_Retval: + """ + Call a coroutine function from a worker thread. + + :param func: a coroutine function + :param args: positional arguments for the callable + :param token: an event loop token to use to get back to the event loop thread + (required if calling this function from outside an AnyIO worker thread) + :return: the return value of the coroutine function + :raises MissingTokenError: if no token was provided and called from outside an + AnyIO worker thread + :raises RunFinishedError: if the event loop tied to ``token`` is no longer running + + .. versionchanged:: 4.11.0 + Added the ``token`` parameter. + + """ + explicit_token = token is not None + token = _token_or_error(token) + return token.backend_class.run_async_from_thread( + func, args, token=token.native_token if explicit_token else None + ) + + +def run_sync( + func: Callable[[Unpack[PosArgsT]], T_Retval], + *args: Unpack[PosArgsT], + token: EventLoopToken | None = None, +) -> T_Retval: + """ + Call a function in the event loop thread from a worker thread. + + :param func: a callable + :param args: positional arguments for the callable + :param token: an event loop token to use to get back to the event loop thread + (required if calling this function from outside an AnyIO worker thread) + :return: the return value of the callable + :raises MissingTokenError: if no token was provided and called from outside an + AnyIO worker thread + :raises RunFinishedError: if the event loop tied to ``token`` is no longer running + + .. versionchanged:: 4.11.0 + Added the ``token`` parameter. + + """ + explicit_token = token is not None + token = _token_or_error(token) + return token.backend_class.run_sync_from_thread( + func, args, token=token.native_token if explicit_token else None + ) + + +class _BlockingAsyncContextManager(Generic[T_co], AbstractContextManager): + _enter_future: Future[T_co] + _exit_future: Future[bool | None] + _exit_event: Event + _exit_exc_info: tuple[ + type[BaseException] | None, BaseException | None, TracebackType | None + ] = (None, None, None) + + def __init__( + self, async_cm: AbstractAsyncContextManager[T_co], portal: BlockingPortal + ): + self._async_cm = async_cm + self._portal = portal + + async def run_async_cm(self) -> bool | None: + try: + self._exit_event = Event() + value = await self._async_cm.__aenter__() + except BaseException as exc: + self._enter_future.set_exception(exc) + raise + else: + self._enter_future.set_result(value) + + try: + # Wait for the sync context manager to exit. + # This next statement can raise `get_cancelled_exc_class()` if + # something went wrong in a task group in this async context + # manager. + await self._exit_event.wait() + finally: + # In case of cancellation, it could be that we end up here before + # `_BlockingAsyncContextManager.__exit__` is called, and an + # `_exit_exc_info` has been set. + result = await self._async_cm.__aexit__(*self._exit_exc_info) + + return result + + def __enter__(self) -> T_co: + self._enter_future = Future() + self._exit_future = self._portal.start_task_soon(self.run_async_cm) + return self._enter_future.result() + + def __exit__( + self, + __exc_type: type[BaseException] | None, + __exc_value: BaseException | None, + __traceback: TracebackType | None, + ) -> bool | None: + self._exit_exc_info = __exc_type, __exc_value, __traceback + self._portal.call(self._exit_event.set) + return self._exit_future.result() + + +class _BlockingPortalTaskStatus(TaskStatus): + def __init__(self, future: Future): + self._future = future + + def started(self, value: object = None) -> None: + self._future.set_result(value) + + +class BlockingPortal: + """ + An object that lets external threads run code in an asynchronous event loop. + + :raises NoEventLoopError: if no supported asynchronous event loop is running in the + current thread + """ + + def __init__(self) -> None: + self._token = current_token() + self._event_loop_thread_id: int | None = get_ident() + self._stop_event = Event() + self._task_group = create_task_group() + + async def __aenter__(self) -> BlockingPortal: + await self._task_group.__aenter__() + return self + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> bool: + await self.stop() + return await self._task_group.__aexit__(exc_type, exc_val, exc_tb) + + def _check_running(self) -> None: + if self._event_loop_thread_id is None: + raise RuntimeError("This portal is not running") + if self._event_loop_thread_id == get_ident(): + raise RuntimeError( + "This method cannot be called from the event loop thread" + ) + + async def sleep_until_stopped(self) -> None: + """Sleep until :meth:`stop` is called.""" + await self._stop_event.wait() + + async def stop(self, cancel_remaining: bool = False) -> None: + """ + Signal the portal to shut down. + + This marks the portal as no longer accepting new calls and exits from + :meth:`sleep_until_stopped`. + + :param cancel_remaining: ``True`` to cancel all the remaining tasks, ``False`` + to let them finish before returning + + """ + self._event_loop_thread_id = None + self._stop_event.set() + if cancel_remaining: + self._task_group.cancel_scope.cancel("the blocking portal is shutting down") + + async def _call_func( + self, + func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval] | T_Retval], + args: tuple[Unpack[PosArgsT]], + kwargs: dict[str, Any], + future: Future[T_Retval], + ) -> None: + def callback(f: Future[T_Retval]) -> None: + if f.cancelled(): + if self._event_loop_thread_id == get_ident(): + scope.cancel("the future was cancelled") + elif self._event_loop_thread_id is not None: + self.call(scope.cancel, "the future was cancelled") + + try: + retval_or_awaitable = func(*args, **kwargs) + if isawaitable(retval_or_awaitable): + with CancelScope() as scope: + future.add_done_callback(callback) + retval = await retval_or_awaitable + else: + retval = retval_or_awaitable + except get_cancelled_exc_class(): + future.cancel() + future.set_running_or_notify_cancel() + except BaseException as exc: + if not future.cancelled(): + future.set_exception(exc) + + # Let base exceptions fall through + if not isinstance(exc, Exception): + raise + else: + if not future.cancelled(): + future.set_result(retval) + finally: + scope = None # type: ignore[assignment] + + def _spawn_task_from_thread( + self, + func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval] | T_Retval], + args: tuple[Unpack[PosArgsT]], + kwargs: dict[str, Any], + name: object, + future: Future[T_Retval], + ) -> None: + """ + Spawn a new task using the given callable. + + :param func: a callable + :param args: positional arguments to be passed to the callable + :param kwargs: keyword arguments to be passed to the callable + :param name: name of the task (will be coerced to a string if not ``None``) + :param future: a future that will resolve to the return value of the callable, + or the exception raised during its execution + + """ + run_sync( + partial(self._task_group.start_soon, name=name), + self._call_func, + func, + args, + kwargs, + future, + token=self._token, + ) + + @overload + def call( + self, + func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval]], + *args: Unpack[PosArgsT], + ) -> T_Retval: ... + + @overload + def call( + self, func: Callable[[Unpack[PosArgsT]], T_Retval], *args: Unpack[PosArgsT] + ) -> T_Retval: ... + + def call( + self, + func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval] | T_Retval], + *args: Unpack[PosArgsT], + ) -> T_Retval: + """ + Call the given function in the event loop thread. + + If the callable returns a coroutine object, it is awaited on. + + :param func: any callable + :raises RuntimeError: if the portal is not running or if this method is called + from within the event loop thread + + """ + return cast(T_Retval, self.start_task_soon(func, *args).result()) + + @overload + def start_task_soon( + self, + func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval]], + *args: Unpack[PosArgsT], + name: object = None, + ) -> Future[T_Retval]: ... + + @overload + def start_task_soon( + self, + func: Callable[[Unpack[PosArgsT]], T_Retval], + *args: Unpack[PosArgsT], + name: object = None, + ) -> Future[T_Retval]: ... + + def start_task_soon( + self, + func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval] | T_Retval], + *args: Unpack[PosArgsT], + name: object = None, + ) -> Future[T_Retval]: + """ + Start a task in the portal's task group. + + The task will be run inside a cancel scope which can be cancelled by cancelling + the returned future. + + :param func: the target function + :param args: positional arguments passed to ``func`` + :param name: name of the task (will be coerced to a string if not ``None``) + :return: a future that resolves with the return value of the callable if the + task completes successfully, or with the exception raised in the task + :raises RuntimeError: if the portal is not running or if this method is called + from within the event loop thread + :rtype: concurrent.futures.Future[T_Retval] + + .. versionadded:: 3.0 + + """ + self._check_running() + f: Future[T_Retval] = Future() + self._spawn_task_from_thread(func, args, {}, name, f) + return f + + def start_task( + self, + func: Callable[..., Awaitable[T_Retval]], + *args: object, + name: object = None, + ) -> tuple[Future[T_Retval], Any]: + """ + Start a task in the portal's task group and wait until it signals for readiness. + + This method works the same way as :meth:`.abc.TaskGroup.start`. + + :param func: the target function + :param args: positional arguments passed to ``func`` + :param name: name of the task (will be coerced to a string if not ``None``) + :return: a tuple of (future, task_status_value) where the ``task_status_value`` + is the value passed to ``task_status.started()`` from within the target + function + :rtype: tuple[concurrent.futures.Future[T_Retval], Any] + + .. versionadded:: 3.0 + + """ + + def task_done(future: Future[T_Retval]) -> None: + if not task_status_future.done(): + if future.cancelled(): + task_status_future.cancel() + elif future.exception(): + task_status_future.set_exception(future.exception()) + else: + exc = RuntimeError( + "Task exited without calling task_status.started()" + ) + task_status_future.set_exception(exc) + + self._check_running() + task_status_future: Future = Future() + task_status = _BlockingPortalTaskStatus(task_status_future) + f: Future = Future() + f.add_done_callback(task_done) + self._spawn_task_from_thread(func, args, {"task_status": task_status}, name, f) + return f, task_status_future.result() + + def wrap_async_context_manager( + self, cm: AbstractAsyncContextManager[T_co] + ) -> AbstractContextManager[T_co]: + """ + Wrap an async context manager as a synchronous context manager via this portal. + + Spawns a task that will call both ``__aenter__()`` and ``__aexit__()``, stopping + in the middle until the synchronous context manager exits. + + :param cm: an asynchronous context manager + :return: a synchronous context manager + + .. versionadded:: 2.1 + + """ + return _BlockingAsyncContextManager(cm, self) + + +@dataclass +class BlockingPortalProvider: + """ + A manager for a blocking portal. Used as a context manager. The first thread to + enter this context manager causes a blocking portal to be started with the specific + parameters, and the last thread to exit causes the portal to be shut down. Thus, + there will be exactly one blocking portal running in this context as long as at + least one thread has entered this context manager. + + The parameters are the same as for :func:`~anyio.run`. + + :param backend: name of the backend + :param backend_options: backend options + + .. versionadded:: 4.4 + """ + + backend: str = "asyncio" + backend_options: dict[str, Any] | None = None + _lock: Lock = field(init=False, default_factory=Lock) + _leases: int = field(init=False, default=0) + _portal: BlockingPortal = field(init=False) + _portal_cm: AbstractContextManager[BlockingPortal] | None = field( + init=False, default=None + ) + + def __enter__(self) -> BlockingPortal: + with self._lock: + if self._portal_cm is None: + self._portal_cm = start_blocking_portal( + self.backend, self.backend_options + ) + self._portal = self._portal_cm.__enter__() + + self._leases += 1 + return self._portal + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + portal_cm: AbstractContextManager[BlockingPortal] | None = None + with self._lock: + assert self._portal_cm + assert self._leases > 0 + self._leases -= 1 + if not self._leases: + portal_cm = self._portal_cm + self._portal_cm = None + del self._portal + + if portal_cm: + portal_cm.__exit__(None, None, None) + + +@contextmanager +def start_blocking_portal( + backend: str = "asyncio", + backend_options: dict[str, Any] | None = None, + *, + name: str | None = None, +) -> Generator[BlockingPortal, Any, None]: + """ + Start a new event loop in a new thread and run a blocking portal in its main task. + + The parameters are the same as for :func:`~anyio.run`. + + :param backend: name of the backend + :param backend_options: backend options + :param name: name of the thread + :return: a context manager that yields a blocking portal + + .. versionchanged:: 3.0 + Usage as a context manager is now required. + + """ + + async def run_portal() -> None: + async with BlockingPortal() as portal_: + if name is None: + current_thread().name = f"{backend}-portal-{id(portal_):x}" + + future.set_result(portal_) + await portal_.sleep_until_stopped() + + def run_blocking_portal() -> None: + if future.set_running_or_notify_cancel(): + try: + run_eventloop( + run_portal, backend=backend, backend_options=backend_options + ) + except BaseException as exc: + if not future.done(): + future.set_exception(exc) + + future: Future[BlockingPortal] = Future() + thread = Thread(target=run_blocking_portal, daemon=True, name=name) + thread.start() + try: + cancel_remaining_tasks = False + portal = future.result() + try: + yield portal + except BaseException: + cancel_remaining_tasks = True + raise + finally: + try: + portal.call(portal.stop, cancel_remaining_tasks) + except RuntimeError: + pass + finally: + thread.join() + + +def check_cancelled() -> None: + """ + Check if the cancel scope of the host task's running the current worker thread has + been cancelled. + + If the host task's current cancel scope has indeed been cancelled, the + backend-specific cancellation exception will be raised. + + :raises RuntimeError: if the current thread was not spawned by + :func:`.to_thread.run_sync` + + """ + try: + token: EventLoopToken = threadlocals.current_token + except AttributeError: + raise NoEventLoopError( + "This function can only be called inside an AnyIO worker thread" + ) from None + + token.backend_class.check_cancelled() diff --git a/.deps_siteforge_v812/anyio/functools.py b/.deps_siteforge_v812/anyio/functools.py new file mode 100644 index 0000000..f1d6c7c --- /dev/null +++ b/.deps_siteforge_v812/anyio/functools.py @@ -0,0 +1,409 @@ +from __future__ import annotations + +__all__ = ( + "AsyncCacheInfo", + "AsyncCacheParameters", + "AsyncLRUCacheWrapper", + "cache", + "lru_cache", + "reduce", +) + +import functools +import sys +from collections import OrderedDict +from collections.abc import ( + AsyncIterable, + Awaitable, + Callable, + Coroutine, + Hashable, + Iterable, +) +from functools import update_wrapper +from inspect import iscoroutinefunction +from typing import ( + Any, + Generic, + NamedTuple, + TypedDict, + TypeVar, + cast, + final, + overload, +) +from weakref import WeakKeyDictionary + +from ._core._eventloop import current_time +from ._core._synchronization import Lock +from .lowlevel import RunVar, checkpoint + +if sys.version_info >= (3, 11): + from typing import ParamSpec +else: + from typing_extensions import ParamSpec + +T = TypeVar("T") +S = TypeVar("S") +P = ParamSpec("P") +lru_cache_items: RunVar[ + WeakKeyDictionary[ + AsyncLRUCacheWrapper[Any, Any], + OrderedDict[ + Hashable, + tuple[_InitialMissingType, Lock, float | None] + | tuple[Any, None, float | None], + ], + ] +] = RunVar("lru_cache_items") + + +class _InitialMissingType: + pass + + +initial_missing: _InitialMissingType = _InitialMissingType() + + +class AsyncCacheInfo(NamedTuple): + hits: int + misses: int + maxsize: int | None + currsize: int + ttl: int | None + + +class AsyncCacheParameters(TypedDict): + maxsize: int | None + typed: bool + always_checkpoint: bool + ttl: int | None + + +class _LRUMethodWrapper(Generic[T]): + def __init__(self, wrapper: AsyncLRUCacheWrapper[..., T], instance: object): + self.__wrapper = wrapper + self.__instance = instance + + def cache_info(self) -> AsyncCacheInfo: + return self.__wrapper.cache_info() + + def cache_parameters(self) -> AsyncCacheParameters: + return self.__wrapper.cache_parameters() + + def cache_clear(self) -> None: + self.__wrapper.cache_clear() + + async def __call__(self, *args: Any, **kwargs: Any) -> T: + if self.__instance is None: + return await self.__wrapper(*args, **kwargs) + + return await self.__wrapper(self.__instance, *args, **kwargs) + + +@final +class AsyncLRUCacheWrapper(Generic[P, T]): + def __init__( + self, + func: Callable[P, Awaitable[T]], + maxsize: int | None, + typed: bool, + always_checkpoint: bool, + ttl: int | None, + ): + self.__wrapped__ = func + self._hits: int = 0 + self._misses: int = 0 + self._maxsize = max(maxsize, 0) if maxsize is not None else None + self._currsize: int = 0 + self._typed = typed + self._always_checkpoint = always_checkpoint + self._ttl = ttl + update_wrapper(self, func) + + def cache_info(self) -> AsyncCacheInfo: + return AsyncCacheInfo( + self._hits, self._misses, self._maxsize, self._currsize, self._ttl + ) + + def cache_parameters(self) -> AsyncCacheParameters: + return { + "maxsize": self._maxsize, + "typed": self._typed, + "always_checkpoint": self._always_checkpoint, + "ttl": self._ttl, + } + + def cache_clear(self) -> None: + if cache := lru_cache_items.get(None): + cache.pop(self, None) + self._hits = self._misses = self._currsize = 0 + + async def __call__(self, *args: P.args, **kwargs: P.kwargs) -> T: + # Easy case first: if maxsize == 0, no caching is done + if self._maxsize == 0: + value = await self.__wrapped__(*args, **kwargs) + self._misses += 1 + return value + + # The key is constructed as a flat tuple to avoid memory overhead + key: tuple[Any, ...] = args + if kwargs: + # initial_missing is used as a separator + key += (initial_missing,) + sum(kwargs.items(), ()) + + if self._typed: + key += tuple(type(arg) for arg in args) + if kwargs: + key += (initial_missing,) + tuple(type(val) for val in kwargs.values()) + + try: + cache = lru_cache_items.get() + except LookupError: + cache = WeakKeyDictionary() + lru_cache_items.set(cache) + + try: + cache_entry = cache[self] + except KeyError: + cache_entry = cache[self] = OrderedDict() + + cached_value: T | _InitialMissingType + try: + cached_value, lock, expires_at = cache_entry[key] + except KeyError: + # We're the first task to call this function + cached_value, lock, expires_at = ( + initial_missing, + Lock(fast_acquire=not self._always_checkpoint), + None, + ) + cache_entry[key] = cached_value, lock, expires_at + + if lock is None: + if expires_at is not None and current_time() >= expires_at: + self._currsize -= 1 + cached_value, lock, expires_at = ( + initial_missing, + Lock(fast_acquire=not self._always_checkpoint), + None, + ) + cache_entry[key] = cached_value, lock, expires_at + else: + # The value was already cached + self._hits += 1 + cache_entry.move_to_end(key) + if self._always_checkpoint: + await checkpoint() + + return cast(T, cached_value) + + async with lock: + # Check if another task filled the cache while we acquired the lock + if (cached_value := cache_entry[key][0]) is initial_missing: + self._misses += 1 + if self._maxsize is not None and self._currsize >= self._maxsize: + cache_entry.popitem(last=False) + else: + self._currsize += 1 + + value = await self.__wrapped__(*args, **kwargs) + expires_at = ( + current_time() + self._ttl if self._ttl is not None else None + ) + cache_entry[key] = value, None, expires_at + else: + # Another task filled the cache while we were waiting for the lock + self._hits += 1 + cache_entry.move_to_end(key) + value = cast(T, cached_value) + + return value + + def __get__( + self, instance: object, owner: type | None = None + ) -> _LRUMethodWrapper[T]: + wrapper = _LRUMethodWrapper(self, instance) + update_wrapper(wrapper, self.__wrapped__) + return wrapper + + +class _LRUCacheWrapper(Generic[T]): + def __init__( + self, maxsize: int | None, typed: bool, always_checkpoint: bool, ttl: int | None + ): + self._maxsize = maxsize + self._typed = typed + self._always_checkpoint = always_checkpoint + self._ttl = ttl + + @overload + def __call__( # type: ignore[overload-overlap] + self, func: Callable[P, Coroutine[Any, Any, T]], / + ) -> AsyncLRUCacheWrapper[P, T]: ... + + @overload + def __call__( + self, func: Callable[..., T], / + ) -> functools._lru_cache_wrapper[T]: ... + + def __call__( + self, f: Callable[P, Coroutine[Any, Any, T]] | Callable[..., T], / + ) -> AsyncLRUCacheWrapper[P, T] | functools._lru_cache_wrapper[T]: + if iscoroutinefunction(f): + return AsyncLRUCacheWrapper( + f, self._maxsize, self._typed, self._always_checkpoint, self._ttl + ) + + return functools.lru_cache(maxsize=self._maxsize, typed=self._typed)(f) # type: ignore[arg-type] + + +@overload +def cache( # type: ignore[overload-overlap] + func: Callable[P, Coroutine[Any, Any, T]], / +) -> AsyncLRUCacheWrapper[P, T]: ... + + +@overload +def cache(func: Callable[..., T], /) -> functools._lru_cache_wrapper[T]: ... + + +def cache( + func: Callable[..., T] | Callable[P, Coroutine[Any, Any, T]], / +) -> AsyncLRUCacheWrapper[P, T] | functools._lru_cache_wrapper[T]: + """ + A convenient shortcut for :func:`lru_cache` with ``maxsize=None``. + + This is the asynchronous equivalent to :func:`functools.cache`. + + """ + return lru_cache(maxsize=None)(func) + + +@overload +def lru_cache( + *, + maxsize: int | None = ..., + typed: bool = ..., + always_checkpoint: bool = ..., + ttl: int | None = ..., +) -> _LRUCacheWrapper[Any]: ... + + +@overload +def lru_cache( # type: ignore[overload-overlap] + func: Callable[P, Coroutine[Any, Any, T]], / +) -> AsyncLRUCacheWrapper[P, T]: ... + + +@overload +def lru_cache(func: Callable[..., T], /) -> functools._lru_cache_wrapper[T]: ... + + +def lru_cache( + func: Callable[P, Coroutine[Any, Any, T]] | Callable[..., T] | None = None, + /, + *, + maxsize: int | None = 128, + typed: bool = False, + always_checkpoint: bool = False, + ttl: int | None = None, +) -> ( + AsyncLRUCacheWrapper[P, T] | functools._lru_cache_wrapper[T] | _LRUCacheWrapper[Any] +): + """ + An asynchronous version of :func:`functools.lru_cache`. + + If a synchronous function is passed, the standard library + :func:`functools.lru_cache` is applied instead. + + :param always_checkpoint: if ``True``, every call to the cached function will be + guaranteed to yield control to the event loop at least once + :param ttl: time in seconds after which to invalidate cache entries + + .. note:: Caches and locks are managed on a per-event loop basis. + + """ + if func is None: + return _LRUCacheWrapper[Any](maxsize, typed, always_checkpoint, ttl) + + if not callable(func): + raise TypeError("the first argument must be callable") + + return _LRUCacheWrapper[T](maxsize, typed, always_checkpoint, ttl)(func) + + +@overload +async def reduce( + function: Callable[[T, S], Awaitable[T]], + iterable: Iterable[S] | AsyncIterable[S], + /, + initial: T, +) -> T: ... + + +@overload +async def reduce( + function: Callable[[T, T], Awaitable[T]], + iterable: Iterable[T] | AsyncIterable[T], + /, +) -> T: ... + + +async def reduce( # type: ignore[misc] + function: Callable[[T, T], Awaitable[T]] | Callable[[T, S], Awaitable[T]], + iterable: Iterable[T] | Iterable[S] | AsyncIterable[T] | AsyncIterable[S], + /, + initial: T | _InitialMissingType = initial_missing, +) -> T: + """ + Asynchronous version of :func:`functools.reduce`. + + :param function: a coroutine function that takes two arguments: the accumulated + value and the next element from the iterable + :param iterable: an iterable or async iterable + :param initial: the initial value (if missing, the first element of the iterable is + used as the initial value) + + """ + element: Any + function_called = False + if isinstance(iterable, AsyncIterable): + async_it = iterable.__aiter__() + if initial is initial_missing: + try: + value = cast(T, await async_it.__anext__()) + except StopAsyncIteration: + raise TypeError( + "reduce() of empty sequence with no initial value" + ) from None + else: + value = cast(T, initial) + + async for element in async_it: + value = await function(value, element) + function_called = True + elif isinstance(iterable, Iterable): + it = iter(iterable) + if initial is initial_missing: + try: + value = cast(T, next(it)) + except StopIteration: + raise TypeError( + "reduce() of empty sequence with no initial value" + ) from None + else: + value = cast(T, initial) + + for element in it: + value = await function(value, element) + function_called = True + else: + raise TypeError("reduce() argument 2 must be an iterable or async iterable") + + # Make sure there is at least one checkpoint, even if an empty iterable and an + # initial value were given + if not function_called: + await checkpoint() + + return value diff --git a/.deps_siteforge_v812/anyio/lowlevel.py b/.deps_siteforge_v812/anyio/lowlevel.py new file mode 100644 index 0000000..ffbb75a --- /dev/null +++ b/.deps_siteforge_v812/anyio/lowlevel.py @@ -0,0 +1,196 @@ +from __future__ import annotations + +__all__ = ( + "EventLoopToken", + "RunvarToken", + "RunVar", + "checkpoint", + "checkpoint_if_cancelled", + "cancel_shielded_checkpoint", + "current_token", +) + +import enum +from dataclasses import dataclass +from types import TracebackType +from typing import Any, Generic, Literal, TypeVar, final, overload +from weakref import WeakKeyDictionary + +from ._core._eventloop import get_async_backend +from .abc import AsyncBackend + +T = TypeVar("T") +D = TypeVar("D") + + +async def checkpoint() -> None: + """ + Check for cancellation and allow the scheduler to switch to another task. + + Equivalent to (but more efficient than):: + + await checkpoint_if_cancelled() + await cancel_shielded_checkpoint() + + .. versionadded:: 3.0 + + """ + await get_async_backend().checkpoint() + + +async def checkpoint_if_cancelled() -> None: + """ + Enter a checkpoint if the enclosing cancel scope has been cancelled. + + This does not allow the scheduler to switch to a different task. + + .. versionadded:: 3.0 + + """ + await get_async_backend().checkpoint_if_cancelled() + + +async def cancel_shielded_checkpoint() -> None: + """ + Allow the scheduler to switch to another task but without checking for cancellation. + + Equivalent to (but potentially more efficient than):: + + with CancelScope(shield=True): + await checkpoint() + + .. versionadded:: 3.0 + + """ + await get_async_backend().cancel_shielded_checkpoint() + + +@final +@dataclass(frozen=True, repr=False) +class EventLoopToken: + """ + An opaque object that holds a reference to an event loop. + + .. versionadded:: 4.11.0 + """ + + backend_class: type[AsyncBackend] + native_token: object + + +def current_token() -> EventLoopToken: + """ + Return a token object that can be used to call code in the current event loop from + another thread. + + :raises NoEventLoopError: if no supported asynchronous event loop is running in the + current thread + + .. versionadded:: 4.11.0 + + """ + backend_class = get_async_backend() + raw_token = backend_class.current_token() + return EventLoopToken(backend_class, raw_token) + + +_run_vars: WeakKeyDictionary[object, dict[RunVar[Any], Any]] = WeakKeyDictionary() + + +class _NoValueSet(enum.Enum): + NO_VALUE_SET = enum.auto() + + +class RunvarToken(Generic[T]): + __slots__ = "_var", "_value", "_redeemed" + + def __init__(self, var: RunVar[T], value: T | Literal[_NoValueSet.NO_VALUE_SET]): + self._var = var + self._value: T | Literal[_NoValueSet.NO_VALUE_SET] = value + self._redeemed = False + + def __enter__(self) -> RunvarToken[T]: + return self + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + self._var.reset(self) + + +class RunVar(Generic[T]): + """ + Like a :class:`~contextvars.ContextVar`, except scoped to the running event loop. + + Can be used as a context manager, Just like :class:`~contextvars.ContextVar`, that + will reset the variable to its previous value when the context block is exited. + """ + + __slots__ = "_name", "_default" + + NO_VALUE_SET: Literal[_NoValueSet.NO_VALUE_SET] = _NoValueSet.NO_VALUE_SET + + def __init__( + self, name: str, default: T | Literal[_NoValueSet.NO_VALUE_SET] = NO_VALUE_SET + ): + self._name = name + self._default = default + + @property + def _current_vars(self) -> dict[RunVar[T], T]: + native_token = current_token().native_token + try: + return _run_vars[native_token] + except KeyError: + run_vars = _run_vars[native_token] = {} + return run_vars + + @overload + def get(self, default: D) -> T | D: ... + + @overload + def get(self) -> T: ... + + def get( + self, default: D | Literal[_NoValueSet.NO_VALUE_SET] = NO_VALUE_SET + ) -> T | D: + try: + return self._current_vars[self] + except KeyError: + if default is not RunVar.NO_VALUE_SET: + return default + elif self._default is not RunVar.NO_VALUE_SET: + return self._default + + raise LookupError( + f'Run variable "{self._name}" has no value and no default set' + ) + + def set(self, value: T) -> RunvarToken[T]: + current_vars = self._current_vars + token = RunvarToken(self, current_vars.get(self, RunVar.NO_VALUE_SET)) + current_vars[self] = value + return token + + def reset(self, token: RunvarToken[T]) -> None: + if token._var is not self: + raise ValueError("This token does not belong to this RunVar") + + if token._redeemed: + raise ValueError("This token has already been used") + + if token._value is _NoValueSet.NO_VALUE_SET: + try: + del self._current_vars[self] + except KeyError: + pass + else: + self._current_vars[self] = token._value + + token._redeemed = True + + def __repr__(self) -> str: + return f"" diff --git a/.deps_siteforge_v812/anyio/py.typed b/.deps_siteforge_v812/anyio/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/.deps_siteforge_v812/anyio/pytest_plugin.py b/.deps_siteforge_v812/anyio/pytest_plugin.py new file mode 100644 index 0000000..a5183f0 --- /dev/null +++ b/.deps_siteforge_v812/anyio/pytest_plugin.py @@ -0,0 +1,363 @@ +from __future__ import annotations + +import dataclasses +import socket +import sys +from collections.abc import Callable, Generator, Iterator +from contextlib import ExitStack, contextmanager +from inspect import isasyncgenfunction, iscoroutinefunction, ismethod +from typing import Any, cast + +import pytest +from _pytest.fixtures import FuncFixtureInfo, SubRequest +from _pytest.outcomes import Exit +from _pytest.python import CallSpec2 +from _pytest.scope import Scope + +from . import get_available_backends +from ._core._eventloop import ( + current_async_library, + get_async_backend, + reset_current_async_library, + set_current_async_library, +) +from ._core._exceptions import iterate_exceptions +from .abc import TestRunner + +if sys.version_info < (3, 11): + from exceptiongroup import ExceptionGroup + +_current_runner: TestRunner | None = None +_runner_stack: ExitStack | None = None +_runner_leases = 0 + + +def extract_backend_and_options(backend: object) -> tuple[str, dict[str, Any]]: + if isinstance(backend, str): + return backend, {} + elif isinstance(backend, tuple) and len(backend) == 2: + if isinstance(backend[0], str) and isinstance(backend[1], dict): + return cast(tuple[str, dict[str, Any]], backend) + + raise TypeError("anyio_backend must be either a string or tuple of (string, dict)") + + +@contextmanager +def get_runner( + backend_name: str, backend_options: dict[str, Any] +) -> Iterator[TestRunner]: + global _current_runner, _runner_leases, _runner_stack + if _current_runner is None: + asynclib = get_async_backend(backend_name) + _runner_stack = ExitStack() + if current_async_library() is None: + # Since we're in control of the event loop, we can cache the name of the + # async library + token = set_current_async_library(backend_name) + _runner_stack.callback(reset_current_async_library, token) + + backend_options = backend_options or {} + _current_runner = _runner_stack.enter_context( + asynclib.create_test_runner(backend_options) + ) + + _runner_leases += 1 + try: + yield _current_runner + finally: + _runner_leases -= 1 + if not _runner_leases: + assert _runner_stack is not None + _runner_stack.close() + _runner_stack = _current_runner = None + + +def pytest_addoption(parser: pytest.Parser) -> None: + parser.addini( + "anyio_mode", + default="strict", + help='AnyIO plugin mode (either "strict" or "auto")', + ) + + +def pytest_configure(config: pytest.Config) -> None: + config.addinivalue_line( + "markers", + "anyio: mark the (coroutine function) test to be run asynchronously via anyio.", + ) + if ( + config.getini("anyio_mode") == "auto" + and config.pluginmanager.has_plugin("asyncio") + and config.getini("asyncio_mode") == "auto" + ): + config.issue_config_time_warning( + pytest.PytestConfigWarning( + "AnyIO auto mode has been enabled together with pytest-asyncio auto " + "mode. This may cause unexpected behavior." + ), + 1, + ) + + +@pytest.hookimpl(hookwrapper=True) +def pytest_fixture_setup(fixturedef: Any, request: Any) -> Generator[Any]: + def wrapper(anyio_backend: Any, request: SubRequest, **kwargs: Any) -> Any: + # Rebind any fixture methods to the request instance + if ( + request.instance + and ismethod(func) + and type(func.__self__) is type(request.instance) + ): + local_func = func.__func__.__get__(request.instance) + else: + local_func = func + + backend_name, backend_options = extract_backend_and_options(anyio_backend) + if has_backend_arg: + kwargs["anyio_backend"] = anyio_backend + + if has_request_arg: + kwargs["request"] = request + + with get_runner(backend_name, backend_options) as runner: + if isasyncgenfunction(local_func): + yield from runner.run_asyncgen_fixture(local_func, kwargs) + else: + yield runner.run_fixture(local_func, kwargs) + + # Only apply this to coroutine functions and async generator functions in requests + # that involve the anyio_backend fixture + func = fixturedef.func + if isasyncgenfunction(func) or iscoroutinefunction(func): + if "anyio_backend" in request.fixturenames: + fixturedef.func = wrapper + original_argname = fixturedef.argnames + + if not (has_backend_arg := "anyio_backend" in fixturedef.argnames): + fixturedef.argnames += ("anyio_backend",) + + if not (has_request_arg := "request" in fixturedef.argnames): + fixturedef.argnames += ("request",) + + try: + return (yield) + finally: + fixturedef.func = func + fixturedef.argnames = original_argname + + return (yield) + + +@pytest.hookimpl(tryfirst=True) +def pytest_pycollect_makeitem( + collector: pytest.Module | pytest.Class, name: str, obj: object +) -> None: + if collector.istestfunction(obj, name): + inner_func = obj.hypothesis.inner_test if hasattr(obj, "hypothesis") else obj + if iscoroutinefunction(inner_func): + anyio_auto_mode = collector.config.getini("anyio_mode") == "auto" + marker = collector.get_closest_marker("anyio") + own_markers = getattr(obj, "pytestmark", ()) + if ( + anyio_auto_mode + or marker + or any(marker.name == "anyio" for marker in own_markers) + ): + pytest.mark.usefixtures("anyio_backend")(obj) + + +def pytest_collection_finish(session: pytest.Session) -> None: + for i, item in reversed(list(enumerate(session.items))): + if ( + isinstance(item, pytest.Function) + and iscoroutinefunction(item.function) + and item.get_closest_marker("anyio") is not None + and "anyio_backend" not in item.fixturenames + ): + new_items = [] + try: + cs_fields = {f.name for f in dataclasses.fields(CallSpec2)} + except TypeError: + cs_fields = set() + + for param_index, backend in enumerate(get_available_backends()): + if "_arg2scope" in cs_fields: # pytest >= 8 + callspec = CallSpec2( + params={"anyio_backend": backend}, + indices={"anyio_backend": param_index}, + _arg2scope={"anyio_backend": Scope.Module}, + _idlist=[backend], + marks=[], + ) + else: # pytest 7.x + callspec = CallSpec2( # type: ignore[call-arg] + funcargs={}, + params={"anyio_backend": backend}, + indices={"anyio_backend": param_index}, + arg2scope={"anyio_backend": Scope.Module}, + idlist=[backend], + marks=[], + ) + + fi = item._fixtureinfo + new_names_closure = list(fi.names_closure) + if "anyio_backend" not in new_names_closure: + new_names_closure.append("anyio_backend") + + new_fixtureinfo = FuncFixtureInfo( + argnames=fi.argnames, + initialnames=fi.initialnames, + names_closure=new_names_closure, + name2fixturedefs=fi.name2fixturedefs, + ) + new_item = pytest.Function.from_parent( + item.parent, + name=f"{item.originalname}[{backend}]", + callspec=callspec, + callobj=item.obj, + fixtureinfo=new_fixtureinfo, + keywords=item.keywords, + originalname=item.originalname, + ) + new_items.append(new_item) + + session.items[i : i + 1] = new_items + + +@pytest.hookimpl(tryfirst=True) +def pytest_pyfunc_call(pyfuncitem: Any) -> bool | None: + def run_with_hypothesis(**kwargs: Any) -> None: + with get_runner(backend_name, backend_options) as runner: + runner.run_test(original_func, kwargs) + + backend = pyfuncitem.funcargs.get("anyio_backend") + if backend: + backend_name, backend_options = extract_backend_and_options(backend) + + if hasattr(pyfuncitem.obj, "hypothesis"): + # Wrap the inner test function unless it's already wrapped + original_func = pyfuncitem.obj.hypothesis.inner_test + if original_func.__qualname__ != run_with_hypothesis.__qualname__: + if iscoroutinefunction(original_func): + pyfuncitem.obj.hypothesis.inner_test = run_with_hypothesis + + return None + + if iscoroutinefunction(pyfuncitem.obj): + funcargs = pyfuncitem.funcargs + testargs = {arg: funcargs[arg] for arg in pyfuncitem._fixtureinfo.argnames} + with get_runner(backend_name, backend_options) as runner: + try: + runner.run_test(pyfuncitem.obj, testargs) + except ExceptionGroup as excgrp: + for exc in iterate_exceptions(excgrp): + if isinstance(exc, (Exit, KeyboardInterrupt, SystemExit)): + raise exc from excgrp + + raise + + return True + + return None + + +@pytest.fixture(scope="module", params=get_available_backends()) +def anyio_backend(request: Any) -> Any: + return request.param + + +@pytest.fixture +def anyio_backend_name(anyio_backend: Any) -> str: + if isinstance(anyio_backend, str): + return anyio_backend + else: + return anyio_backend[0] + + +@pytest.fixture +def anyio_backend_options(anyio_backend: Any) -> dict[str, Any]: + if isinstance(anyio_backend, str): + return {} + else: + return anyio_backend[1] + + +class FreePortFactory: + """ + Manages port generation based on specified socket kind, ensuring no duplicate + ports are generated. + + This class provides functionality for generating available free ports on the + system. It is initialized with a specific socket kind and can generate ports + for given address families while avoiding reuse of previously generated ports. + + Users should not instantiate this class directly, but use the + ``free_tcp_port_factory`` and ``free_udp_port_factory`` fixtures instead. For simple + uses cases, ``free_tcp_port`` and ``free_udp_port`` can be used instead. + """ + + def __init__(self, kind: socket.SocketKind) -> None: + self._kind = kind + self._generated = set[int]() + + @property + def kind(self) -> socket.SocketKind: + """ + The type of socket connection (e.g., :data:`~socket.SOCK_STREAM` or + :data:`~socket.SOCK_DGRAM`) used to bind for checking port availability + + """ + return self._kind + + def __call__(self, family: socket.AddressFamily | None = None) -> int: + """ + Return an unbound port for the given address family. + + :param family: if omitted, both IPv4 and IPv6 addresses will be tried + :return: a port number + + """ + if family is not None: + families = [family] + else: + families = [socket.AF_INET] + if socket.has_ipv6: + families.append(socket.AF_INET6) + + while True: + port = 0 + with ExitStack() as stack: + for family in families: + sock = stack.enter_context(socket.socket(family, self._kind)) + addr = "::1" if family == socket.AF_INET6 else "127.0.0.1" + try: + sock.bind((addr, port)) + except OSError: + break + + if not port: + port = sock.getsockname()[1] + else: + if port not in self._generated: + self._generated.add(port) + return port + + +@pytest.fixture(scope="session") +def free_tcp_port_factory() -> FreePortFactory: + return FreePortFactory(socket.SOCK_STREAM) + + +@pytest.fixture(scope="session") +def free_udp_port_factory() -> FreePortFactory: + return FreePortFactory(socket.SOCK_DGRAM) + + +@pytest.fixture +def free_tcp_port(free_tcp_port_factory: Callable[[], int]) -> int: + return free_tcp_port_factory() + + +@pytest.fixture +def free_udp_port(free_udp_port_factory: Callable[[], int]) -> int: + return free_udp_port_factory() diff --git a/.deps_siteforge_v812/anyio/streams/__init__.py b/.deps_siteforge_v812/anyio/streams/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/.deps_siteforge_v812/anyio/streams/__pycache__/__init__.cpython-312.pyc b/.deps_siteforge_v812/anyio/streams/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1da5926c6aef493c54f0127af8b484e5f7ab7e10 GIT binary patch literal 187 zcmX@j%ge<81n-aD%mUGmK?FMZ%mNgd&QQsq$>_I|p@<2{`wUX^E6Uj_CbT%Us5r*h zz|zt@#<8HlC9xzi#wR~HF()P@HMbz9AhSTXB(W$xwM5q}-PowA%CtBpCo?Igpt2+* zKQAURuQD@Vzqq6*H8HnXKR!M)FS8^*Uaz3?7Kcr4eoARhs$CH)&?-hCE(S3^GBYwV I7BK@^068NvLjV8( literal 0 HcmV?d00001 diff --git a/.deps_siteforge_v812/anyio/streams/__pycache__/buffered.cpython-312.pyc b/.deps_siteforge_v812/anyio/streams/__pycache__/buffered.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e06573f399fad1b0fa0357024825ab408610c978 GIT binary patch literal 9066 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 literal 0 HcmV?d00001 diff --git a/.deps_siteforge_v812/anyio/streams/__pycache__/file.cpython-312.pyc b/.deps_siteforge_v812/anyio/streams/__pycache__/file.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..30a04c2e3610c48ec69bbca1772cf172d41d536a GIT binary patch literal 7458 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 MdFCGk2rkC|03j@v-~a#s literal 0 HcmV?d00001 diff --git a/.deps_siteforge_v812/anyio/streams/__pycache__/memory.cpython-312.pyc b/.deps_siteforge_v812/anyio/streams/__pycache__/memory.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..79186d4eeb55181354f7f4c86a4f56ca9bf9eb0f GIT binary patch literal 15008 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 BuM_|P literal 0 HcmV?d00001 diff --git a/.deps_siteforge_v812/anyio/streams/__pycache__/text.cpython-312.pyc b/.deps_siteforge_v812/anyio/streams/__pycache__/text.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9202f543d180c1985e4a1068a8ef7140141a0968 GIT binary patch literal 9333 zcmeHNYit}>6}~e&vpZgUXZ_msBZ)n6+}P{KI*yZ+xKMCjEl^U4qycRl*5jRVyy?EU zGj6a+k<}DrBoIL$QY1i0r9wiA)SvwA4}KsdD@7Edu!$=iEE{blj#8 zAt5o+-Z}Su?>Xn5^W8K1t3bd{;Q8w>?qB+9n2QCEXNjO8aB}baSkkm3dPw=|C(1w9imd zYC0GTrdwmJ>9$x~x;@sO?ud1yJ7b;cP%M-V$HD?JrAyKBlB#c`>BU(>s1IiG!-(Cn zZh>UHEZ4(xA~F3&^P4SqVD_}Qo3b1@5Z=vZixDzfUzI947g%nKgsBX}=|2K4`!#sD z=(NieBxF*}Ues;PFcVqBw6u9(zopq(&9W&-&(fAGctIOey9ip4(aa1qgrcaQjdcS1 zVT+`t8QF~Kwx4EFo}T*(j+(HdpUfnvnKm=Fo?<;rI;UM;OePjJYT8RQV`+M3nGKl8 zrgJINHdD(`#Wc^*mNH2kM@uZ~R8QC@h31xKWwRN4!`hZL=w@okjJvAo95hU8Hq|p$ zE=%n=>;|-V8fW2K9G}-M(>pVQS@D|<)&ELoB@|Yww<%4CnjJjE1Fxj7qgjYmR_8gnn)$*sZN(CTyaw zG~u39n3)iy;U|OK71Qo-4u~xBg|1NUnIUh;LR(u8?@8$1;hCry~O0 z3;n?wdikQ9&_&ZuR*!|d96E@tBuBE$)B^2?4D06v-1;~>Se678s8L(zj!)0NV8Mx) zojP#z=##TEx!egbva`=+6MAa)teMWu=90N_TcqfK2Uk`OTeBdKSuUViI1VssJ{I0vN*nKG9edw*v7rLKb z3*7I1;?}u)-G|nsZw5-xf+Q+jg$#21Dsue0Kzy1A!1X3_gQ za}5%ck>lkOiFoKRR?3ORrOb)VwCL6eNd7|pxk@(F z_Kn`YV(&!0ccR$)WWM*wLhsRH=h3y38{yvT>2Ift;gNiJt zvxx*dHad@PS$K$E>7P-Ao5GA&di@EY9?tSLXvkUqpEF!Qp*#G66RCBbzCL zuri~)01~pG)Pqpw$ken<`?kQ?Y@zPBOg#kTO80iA^3KPh)|!H(%+sQ}FR$(^sv~)I zq@a!!m9ceYY(r`JR^aP_qOvQm?D~zeXVXKP#$6Wn&4|JcVTMRIHK6KPh^qhr3ng6j z^_}#!VgaC$`uc%1tlpj+bcFD~gRx5gKb zgO7mXU_4%fLF4hN#iNL_m2LpZnH=E0@ z1wda5)CI841$#{{*zT1QAB4Ede!q4zY}pkY(g2YjhPc>-?}NH3 zlRN_Is?3Wl{U1bKt)M8(9??BWp&RGtP(unekC}^8bO6Ye>Zy-Lqhp%A1aSR42^%3yYgihF>E2?#Qj-LiZG+7Ixa}_JWS?YCUQJ_lI7g;kkl1q?QYA z{T;W=5HM2kviRuE^NLtD4UL$`b*D1AQ$U;)rvQ(NoZ;qvH&6&HGe#M-NJ-mFJ3h|D z2F!9ATCa38Fbz8=pu$4a%ROs*`1<&_$8ShCjGw%C^Tk5@!M7&!?T6QV0AtE7cQ~&O z-|8x;lSO6nt}?l0wL$M!o(5p@HbIQ?w&2|nq@0DOj}N3Uz`;1gHHrIS=2y14UUW7q zp#}zUaB&f;7~I_%T-;U0wo2&3Ug;YK7_ncv=^cK&AcEpS+O}#27#Liy(e*~Ofbg15 zW5SRDXbHP%u%piWKlAw#eIA?Cm(ovQ*$gBRl^#R-I3_1BIf=1R035uETsFci zisLuBFoqLrHoDQR$6AaYqaEG>cz3{CHG@X4(OKj0TbDzTT@>#&`Y++wGlw*z%_of{ z+%WJeucyjB{{5pp5K(dOBoHbwxJ!n3kP92wxBwkENPxCz4&0E-Nkp<*70GIslc|(8 zZ)$+nUVi|$gaF=zuh^qS>KVWM&zhWEo@2sU_0Pc6Yhk{K-lfg6lb zVC*8Zkb>YxebA?jO4vw`f=H@Uy13@w0V5Oa-p4{W6^J!~JFb8O&b0qSlMfXTLPxPP zG(nWlQ3(V6aGP-T$c^_gD*{&!R8zO8F5H6v9*(yuq+d(9Z$odhKf4`XB>CIqW`shN*Nt!okbFg&qSU zH2nuQT3U-Od-E-OZ$10-bMJXX&A%qyQ+B`SC3{92>-Iq1Z83m+)WN*U25|`=c7xTh()$6mG7LfaQgn`G5R0z24#YA% z#y-)w;zh0g4z|)DsREJyyxLz>2lMLSjk5)Hq^OMCRYu&6_D3KnG$g(5jnqSt%0~Yi zXvKT%hHa-@|?4#iY7V9?coct78QyuSNdjJ zF!H5HH}?YdwkXW_rMEp^r2PV5qtVQ=gdrNuEDo;L(Hb_Q0E z9To(-+%`1wh(X|8HD;j^{{9fUTFA*&vMG65{fsZ1UkCE)flUvTy@S^m&a0`sI<+Z7 z*(UKc`8i~!PvP{)af)ZA1{JqGGAd>QW_w}Kn3*_+kMS6M07yoM!vJ3v2n!R8bOiw< z50I`wh-%e<>)A41Si^{22S7Ljkq&lifq;HoF<0>JALD z=qetVMIauRNqSkZ5zVlt>Uo$m4c3|$+?V5XH_Q~s-7))$2H3lH)6{_0D_wY!YWM5* z{uxwSYk?)!)bWIl9CfB^b3--LWzYyHJuIvRX(|)k*aSwul zIUwC^JqVvyM2f4TC~BbD{S);H^x_znAIIbbCi9r|V1f&4yf;c%hRGZ7vvxu9Dk+Jw zJhZ8@%bzF@xatcOS6^%$>Bm6z!V-LjhM+Y?={OLdhW{}@4z(Bp=F657Ok`6jewnhO z`h0>aP(_=tzPa+Z1_=(p37II_Y7I#fMFzLi3gh?0@ zrv?69V8PW5lncHyix5WQPGX6(0nJ1IKC*glcO@jCj0!ayz%`lOCMNdcG z({b(cT~FU1r2aokBM-bpY+f_(37zkWL&A~u?$KMv^W6tZ1Tw&MF|>)r56%kJX8q7C z^u&!sJ~UDyka3#<_25KJvx9>F+V0m9`L=-)0orW^)O(kNW?^7GJbo*Y4UJEsDw%l&FWCS%Q`< z+60Yl^-jka#vVhD?KR%D*Ywg3;~4MS-k9AnZFkr)`>{$2JF9c>n2z4!3OpWrCN@DgZ{B%V(EP70p?^VZu_KiN)E{}Vs-$EHT; zrA`w?U8kO-IEtf_REnOUX^JyVnBd!-G*6hxo0(w9+cIGxZ|j5=-b~V#vQOAkjtK{p zp*Ty@nQ~3INZguqr#urL61OESDer`r#O+C6%0J;xRZLW*Dkmx-&A~a7fmCoJn5vqn zN`)pusp^U9RLw*U$#W%ZQ*{${sWlU8$hSLLpK6$BNHtD0rkW<2QtSkqYMy9Lt({m) zQ@nL*uinApoAaDIK~aJOTIhw^TPIpm>n7IGRN78b+KMSpDDNZXZ2;|u^6eAtP!{{| zPqwLFCW^1*DsIziof94K-O5!O-(kKJ{;lWNPnv20gbS$ei7qb4cXL%cAPw@njPwXe z4`I5A+DCEKS17KAue)v5@b>_I7gzg6on8ZhS_4q^e3K4!TLq>jHk5^K0O&@(ncsjd zT2-c(U~B>ymS^?ciH)=7aPzx}H%u$8SUR1_#k?}Yr8;?eW^pXk4{W`@{ z6EsInn1F%M6J{Va#vnWkXX34#Sta9=bQ{M&x@A?mowGu^ZB@F1vqQRLRl1XNLb_{J zx{Gr|x@T3ooAW}tZ&kX7^Fw;Ys&p^GQ@JYL#{~dBi0QLc;gG;UWNtGOW-O5w*>NG3 z7N;{pmOUJs?|wr$yk=>Sc68y$YZCN71()pG?V5PPb@i|j!p}iY$l#b zDwfkgJEBq-KMO03PexBC(j2UgXf`QEXVQsJ%9f2r z*NIpGIhTlM4H)>=r?nV8sraXPUWmqdA)A;?#G#pUZc@G8w(6Dn*qoR!t1zcC&{mho z3qV;>5lCyUISDKMg>p?hsiX6XXJY~^7qzA0V~;2%Be1%UoSp)fp*Z0)no;8cF&j&B zF#&)xscGOh$WP4BTg8PV0kw+2f=0uBpx0eV9F9sVi*o47m47NbiU@a4|HLV~)SmwLRChbCFqd z<3x_x`A$uHt|olx(A8j$2?O-D{d8{YflH%{!Oc15z&mUY)^qTx2kOzQU1X|n0Cev= z)or=z4k#M9&vXd8pmn6Jdx6oI_)je38dk`JhhF+Age$oQ@Cioc6Tlf5l`{Y@U{qcJ ztDjL&} zNn|I5Oo|;Km>aqw;o+-Voo&fJD#Ixc_*Zz(&8u6DFyJ*d7(jwr*cCC2kt+%!Oi%b?bc=8nM z;Y_!+)(Nfi%!~9CZtyFfBJ>n)>?*mi`EYwAZiJGV8>3Pe%1EJqwZd=sS7oGFYsRkWW@jdUD zLMdy-qxX3wewvzhj!|2vSNq}5{w?zbI&6vEhX# zPn0jT#fs$#=K%f#f^7=e4hu^hSV)?KUH2TUQpAGG75DU#QUlX50eIKSl{x?jh%C@; z?GmPn{YUX82RUnkJ!MU~2Fm>sNtT6dKqJX7MbnT~r!iNAEIF zVvkOp+8axXWpKok3FN2*M+e}~q!L-+TwTyhV11{tk}M02G!QI93k6P{(zypT5c%wB zo=>wNg`J0H0Uo0un}G%j7ZM^LjqtO){Ak^UyWCi)C`)elMo@Ip?Oqx3Oaef+!OQl401+ZL( zC-k@ss|W*ZBn%J?BKwjirV?F}v>7X1zPcu3iKM7*eb~BDrRK=d=z)EsM~;m|pBo+d z*wK;U@sT~zkt2ISH$$;f@o9@ipW?7Z7=mwvWyny7!T~_6IEvdXgQ~26 z8;W8_kpeecp^AJ8GwG-}n~p0XP?D2L+_P0uiJ}CO;S@KQi5hbib}8m~QdE0GB zMc9V{3bcxCTI+@ts)#1iiVfeHnJkgA6`LkTiMRpcU}}%B8w)d-JBOBrF(PzD@H-AU zVh?Pc|C{>o5>*(W+|`S&#(ZPjjjn}5@A&H8S>OK!^L5uXm+Wu9=WqY3;lDoggG2I; zF=@wG!AyCAkF3<1M!9a2RJZBQ^SQd63x^=ZU;PDfvA+MlZ%f|4M)tSd^S8WVmOJ)H z9eWnr_elOdKl8Wzsv>y3;abDzn-;7ODyyz{U+eyS&w}ldopSrHx^k}iubaQ={hC*D zMe?<+a&5O%+nx7^ZrEcxGu6pnd-c+9E1XFSZ?Z-ntDlHt&+bL0Q3H8_{{rj z^8VnG$Hsa;plo&Cg@f-^Qh`vup*zp^jp@*xEv6iLn&Rl@q2~sjIYLrJ;9EsJ)D^ zDD3zb>l2LJ7a0^^w!ox7ugZ_5@5v)XMrIk7iws~XBM43zVqFniQNRP!=roL_A>6f6 zLIY_}IZR~)J$=S#>f==UP!^R3@&KM^E?S<@qNn14GCGoycR3-mSa;SCC+97f!4!Vc z@{;8OeSvz>{Bi06{c)g$^sW+msV_mSMWMaBpEwVHe456YGZ*I{#_k_V+E+$6{r_P+ zOIpa59l?cv!5MGSTQF}aX;-VBT72t@msmG%nQAMh>oK8&1J$8i&Us3x18VHhA&YOp zww&UM>xZK*n=S*_T26siO|O2JC=RAkrGB|o5q*CDfx{!wVz}&zDY-gwuR4-%Tq`&BNsWDS<5sD0>-U3y-SmSddE2wnwr6wOj^rAT$~8x&nxhY! z*U8QOQggrDyi01{m1}-RUh~Z2nrE)=Dfp?X+Uuj&M&)3K6zsSa{8=#a;Ugba)ABw= zSM=ne3na5k3U>WG7*s?7BelUXHN3I=_gWXcF`+l(JL0$KqzQthwZwfYm z{=bjhq$3?tpaal)4$%2fog7*(h1ScV4N_>so#6MHzSAW4j!3;Dx!!%b(0Q+A{*AwZV%K+E z+wnf-@_+#JI|>CFZp6Rd_s!j3+kHRKmk(B7Km2Eh7uOB{Q2fdK-Fa!<7>u`E-E*(H z=Rsrd9dWT{;8%^|TiL~$zK8y*-xR8$ipTHu!t{S6ej8}wvgzPf`qP%dChFe|x*>Wu zL=QGFcdKm}ZwemlGvD1b=-y*7{fMH6&DI~$YcbwJ54Tx=)M~+axE_FhY-t(UX!&u6 z3FDo1jBngN(rtOG+XC^oHku**t<7dY`qtJ#JAC|vqW77tKcOucw_+oHVz0z_-S+)e z=ATxDApXPJ$~@<;7%JW# zO$j90oUT$DnoOycMBuYCLK>|iKu?e2c^!zoOejJ4=vh<@^<&lf{k(vgC2om-sSL+ zo4!pCIhi*Gn;^Q3JV8=X$@4Kp4Dy8d%15XrvNn?Qibt2z3~8Q7KAPNH#+W+gT7dvQ zSK-i%e>63Vrnz>IVX>v;0a=NqsjMNj0oj$QrDTy%pIjl2U8Z!|5pee*@x4xuhOMfK zkJG1khGHT=@}g#+5KzWe_iC$pNG+U)l+sC`gYVKwSHXUTP$>w=q~~2e+0}B-)lx7q zz(YfI*Uw!$_xTj4u#5hdf|F20i{x#Qy`7S`Gw1D+?Ol?+tKgz4T0eRaVBe=qp3s9( z`z=Q9*eP}F%!PKz{#}xPSKeP$ut479f}L`7KNgX4FM9^J(^mt79_ruIgD&Rl48(3V zK-ygkJ-CgzYqen9MMLskPxD~E`EG) zK{~q4*EDYutyzPcTm}V}a0)L@mhd7p0ZtoGs35CUz?DeEp|F`ON5zqfy%;5CQbB+` z5y=+h5p{ub@G)0WT%ek4^$!RLjyc+7Rn^K>8>A}OM&;f?sdrHB-7EF({i!2Ybxigh zTVUSy1s+s3EV>&W`T`5Cf`jsp!Gc4!c)4>Y{RX|A`Ic|z6UGqvz(4mGYD>pb95Hjr zi1E+@uy`TwQWy{rQzC9Y`Uu4!UTVuh0rl6ACZe#c(z3U5CHs0t>U`#htvT;L*}m_d zeV;xSn_w(9GT*XpTF!WIZePZIu>)x6X=9N!cEzXdg%{}}Lny}%KnXCY4NjL#!n4J5 zoO#of2U@e@fioSAy7l+(EMe9mU!+Cw$KZ@}mWvD+U(EB&q=~bfgZ>EKtg$O{)_M9% z)K`q!F4HA>HXY+-n(UGG(YY!h-18kLxb@+V-2*2gBo^NNF1A%bb}!&~!PH*DII5a+ z+j07$pdoaG!Rz4@Ghly)V~8y5g)y*QBf8#5-*y%($O19m^CF(%qpewF=eCp-)@|dn zV8kbO`9umF9#T*U%U){Vg0?ha7%)(1It@nIwAS@S5 zbaiXKwddx}f|;(`xMVh0R~A6JuY3dyer?`8vSc>-oCV6{aDGJ8q{~D8p~~fJqYByz z9E)Ds0dx6TI2TP&uaq~N7;6JcXwNt3;#OHs9s^8ew#%t9GGGawGSc1?@Obrh6yGIy z%9x#=fTs*(2_95^S3C>UB+b!R>?Kf!*{cXjpiIl4Ld9G?Mxe~gpbXQO4nt7{idhC_ z7^+r4S(ZVSF;Ed))@4v-%tTKZE4Emh9!ogCL61>k+h|-*!8Z^U1+omiGysu5r>{SW ztPpO+0|&GkI|Q_*(Y>#mQ~}uq9yEC5h0aPvspyh=C}7`_jHOOi|Qd|2&w9H{1ZA6#7>|&2|96$lOwyN$Syf@@LuHL>c(TENaN4`-i;UPfU=>~ z%4Z7M{EA0c6~@2}r+3<{ih<-%i@1gT;XSXyp7%lB(DR1@Fc`H+ffhN?b1%?SFjEy( zvY(awtnBZR{5?5;FEI3qx`$P@Act4ftsWa=V(S6plP6Z|4=9zJ0q&1LgmqBy66#yf zi#wd$KYFi!^bc)am{v#o4rzjD{e?l#HYhPvEVsbj0$aYX7k@&J?jBsW^+ zbOPHX`8N^pCdu7&GblIrO3l4;^H!;OD*+En?r?7XaT4Dkxi`SkWuF(0Nj@7E zfeJdD>g2dp^9SyTgwM>m(CbulCKdK#au|bs5InBZ4(ZV$7acED?^>0i)XS zX-=~UorH}HVH5`abHj83wntPh=Ml#h3<$RM5Q9`yeg%xHsb7JZR(`HY^Ot`CYUyH& z9e@Ty%E^;qpRg4RZo>fgUtv22J0MV8#QRS@v=>fd@^K7iFhI+KfGVpH!{8MN6gxP? zfKxzrRzPYLc4Dv#13Zc+BT0gP1z+ONA^0@)9%HxI3qh*7HOI8%Ya4P*L%yOh$28`B z^*N?K@5hvf7C)FFT08PhE&1-=d~3&hzCE<1`e9vr!GaMmFNB&44ia@zfrf&MMBP+q zeZfPbUaE0zffNE8xHIs61*R^UDQiW+L;wz(x-5V4l-WpN@l?|T?iyNq_ARk8r2(Mvdc|2XGX zxJP3Hw^ln&2Iqj=G*0*;uI#*xtKi%vj;3}8#Z?xaSGfSVwOYVcHAq}oop2MR3Y}Q> zU24KT>j9^9)OKTk0Xw7Xl*?3tywk}N7EH(WknAz&1iZ+_3Q%vRpP~y zO{4%HczUp)#A)6qbcfT`YJnqMoLVZoQ%u*OEo@esI^fqN`0Tc{Kql{j!jgYIB! zSa6NWc7yjQSQ*f1DUJ;$&QPSy91j{K$AX_3q=OH;*3L22>lIo>+`7QAX8`?VR(Qxp z%DAJSTIph|VbmP0^s5`j*sAVU#aSqEp?ivNmf=aJdfbq%=-}l%s$%S9GIoZYhD$U! zLT5qqJp+D!8QojBxS=2goGQgv9BG^Y>XxnAl2Z`kVXAH)s{dRcP#6>v2a4w!EfIFFakGlJ>A{jm!pR7UGjMwZ zoZ*S%meC|Aa5!^Dg@H3&%~h{sCY^+Z$^zwG0Qbxota)-hi(EZ}m8Zdl!F;5(+CT{8 zcp-uW=!4pn(+q1dV!6gN42%3mDgv+EN??ZGi$p zb!77_g&rb;MSs&(N~j!RuXs9^B|AGjO5=iV-E8!vKvEiVeFA zKsKabuxDqZs&m8Ck_=Ao5x2v-C=K@ruDNIMV_R1{4#Rf807txM&8I zQd`1+zQ+r?=H-o;+htXe)T7cRe$dqs1drE8724;O7( zD_~FJn)?et_#s(zfA3ybV4R-F+f_|B8JX>u*#4hYZF#VM)3$+6tbh)r~jOxlp(4@0R@Cg$l^~kYp`Z zZFyX%q$(mGJ+e{3W^@(VO*}<s>E{@k%=+->q{2#NBn|FtN>o@%1$3c3TGfZFe`?5M~>V>DwJc zVe^|z)ljSX&02b>-uh;p1>;Rv>djUQ=7!A>2L+2v4ACr!+Jy=hoawpXCHy-G6bl(% z08C@jUt*Fo3s-QX}eAWi>( z-p%|MD@2y@2V94Qg^fc*46R`lq26a7fnxf)6iIn*Bwyu@uZEG@!tdk6MqFa2b^$l_7Z5kZUE> zJy%z>BtYe3L`2g}3c`Pe%E0MBzk#mXR*aP+d>Om*Zy^YKo@jhfyNg^?af3FMO~f@j z1hPs2SC2|rH!)Vu+<@$nD7Y*Rz(x2%bAu1e4QnYhF09E{H07A4Jlm?>RBg>SH7_!7 zYL;V~A6hEjH#Jx)QLC>kV63o*raHO`wkpf+hZXA!7I+t|RMk3?0u~6TcgX>1OA*?# z4l>XxU^c7*5L2xJ*r+)C84u1ZJoM6D2#Zz$ye7|?;GdaezHBb)^?Ho6ah5CA2?nmX z!iD$}z1})&3OfXp_G6Er-$W>UewpsBsowZw3Lw{m9FIOtFo&S(swzAlvJwr9jlm^P z(AY&0e)eFs%C5>7OL&z|cU1aD; zgM>k%pbeK>>3J#&N$N9i1}A~;QsuIokXYjUfY??%+(dlowjiQU(kPCajvReGz6XIMg9D!bxL)e zZw!BX^!3sEzU>bx;Zo9p2Z8Dvj9k+p)pX;vJhVl%iS1iavm;PC<{ zt!-XlUiIW#`WKj=*;&Fjmx(V(aW5mw#8ad{miTg!c9i(?mAy{{>!ciIm#1AwHK+yJ zt37Yfw>5@uh)Rsl5^(JJ_g;o%bhW9n3n@hgIDL3%p_3z`2J5V@^EwaAC}iryd9exBNgn@nNikNG2{- zvVrpoh^dkZRvGTYA4Qp*frBsjQ4|3;3;3ZQIKxcB*DY+*Weg5sx>=kR6(9Tr4IXrY z$RI{yr{e;;@CXjfcj2uIa(={%0y1~Sfrj2J9Lb5Q;zSsm6|Rwj!AZcafc&zMfF5YV zcQ8P*C+?G~nF=|!dc)KY_f!Qw62&X1P)$}4LYRlH8HyR3K{PcoV|AE;(yxFrywHFk zZtV<}7{LS<)0;89LTwBDo)a8=;NgX+&QL2ttV0M3Ij|G@F`*5E9T>D@fa_XaTF9Ts z;exv}r&*p0(dD16Dj5YAEIN7h<8yclSGM8W&Lt(3{Nz~85Xk4%Tm zwDYQSiNZHX(59*aNqX;uwFvVZ4KN)ywn>%imMHwt05SS#+SEl?E>?B^nu0f>&;JMV CT;4PQ literal 0 HcmV?d00001 diff --git a/.deps_siteforge_v812/anyio/streams/buffered.py b/.deps_siteforge_v812/anyio/streams/buffered.py new file mode 100644 index 0000000..57c7cd7 --- /dev/null +++ b/.deps_siteforge_v812/anyio/streams/buffered.py @@ -0,0 +1,188 @@ +from __future__ import annotations + +__all__ = ( + "BufferedByteReceiveStream", + "BufferedByteStream", + "BufferedConnectable", +) + +import sys +from collections.abc import Callable, Iterable, Mapping +from dataclasses import dataclass, field +from typing import Any, SupportsIndex + +from .. import ClosedResourceError, DelimiterNotFound, EndOfStream, IncompleteRead +from ..abc import ( + AnyByteReceiveStream, + AnyByteStream, + AnyByteStreamConnectable, + ByteReceiveStream, + ByteStream, + ByteStreamConnectable, +) + +if sys.version_info >= (3, 12): + from typing import override +else: + from typing_extensions import override + + +@dataclass(eq=False) +class BufferedByteReceiveStream(ByteReceiveStream): + """ + Wraps any bytes-based receive stream and uses a buffer to provide sophisticated + receiving capabilities in the form of a byte stream. + """ + + receive_stream: AnyByteReceiveStream + _buffer: bytearray = field(init=False, default_factory=bytearray) + _closed: bool = field(init=False, default=False) + + async def aclose(self) -> None: + await self.receive_stream.aclose() + self._closed = True + + @property + def buffer(self) -> bytes: + """The bytes currently in the buffer.""" + return bytes(self._buffer) + + @property + def extra_attributes(self) -> Mapping[Any, Callable[[], Any]]: + return self.receive_stream.extra_attributes + + def feed_data(self, data: Iterable[SupportsIndex], /) -> None: + """ + Append data directly into the buffer. + + Any data in the buffer will be consumed by receive operations before receiving + anything from the wrapped stream. + + :param data: the data to append to the buffer (can be bytes or anything else + that supports ``__index__()``) + + """ + self._buffer.extend(data) + + async def receive(self, max_bytes: int = 65536) -> bytes: + if self._closed: + raise ClosedResourceError + + if self._buffer: + chunk = bytes(self._buffer[:max_bytes]) + del self._buffer[:max_bytes] + return chunk + elif isinstance(self.receive_stream, ByteReceiveStream): + return await self.receive_stream.receive(max_bytes) + else: + # With a bytes-oriented object stream, we need to handle any surplus bytes + # we get from the receive() call + chunk = await self.receive_stream.receive() + if len(chunk) > max_bytes: + # Save the surplus bytes in the buffer + self._buffer.extend(chunk[max_bytes:]) + return chunk[:max_bytes] + else: + return chunk + + async def receive_exactly(self, nbytes: int) -> bytes: + """ + Read exactly the given amount of bytes from the stream. + + :param nbytes: the number of bytes to read + :return: the bytes read + :raises ~anyio.IncompleteRead: if the stream was closed before the requested + amount of bytes could be read from the stream + + """ + while True: + remaining = nbytes - len(self._buffer) + if remaining <= 0: + retval = self._buffer[:nbytes] + del self._buffer[:nbytes] + return bytes(retval) + + try: + if isinstance(self.receive_stream, ByteReceiveStream): + chunk = await self.receive_stream.receive(remaining) + else: + chunk = await self.receive_stream.receive() + except EndOfStream as exc: + raise IncompleteRead from exc + + self._buffer.extend(chunk) + + async def receive_until(self, delimiter: bytes, max_bytes: int) -> bytes: + """ + Read from the stream until the delimiter is found or max_bytes have been read. + + :param delimiter: the marker to look for in the stream + :param max_bytes: maximum number of bytes that will be read before raising + :exc:`~anyio.DelimiterNotFound` + :return: the bytes read (not including the delimiter) + :raises ~anyio.IncompleteRead: if the stream was closed before the delimiter + was found + :raises ~anyio.DelimiterNotFound: if the delimiter is not found within the + bytes read up to the maximum allowed + + """ + delimiter_size = len(delimiter) + offset = 0 + while True: + # Check if the delimiter can be found in the current buffer + index = self._buffer.find(delimiter, offset) + if index >= 0: + found = self._buffer[:index] + del self._buffer[: index + len(delimiter) :] + return bytes(found) + + # Check if the buffer is already at or over the limit + if len(self._buffer) >= max_bytes: + raise DelimiterNotFound(max_bytes) + + # Read more data into the buffer from the socket + try: + data = await self.receive_stream.receive() + except EndOfStream as exc: + raise IncompleteRead from exc + + # Move the offset forward and add the new data to the buffer + offset = max(len(self._buffer) - delimiter_size + 1, 0) + self._buffer.extend(data) + + +class BufferedByteStream(BufferedByteReceiveStream, ByteStream): + """ + A full-duplex variant of :class:`BufferedByteReceiveStream`. All writes are passed + through to the wrapped stream as-is. + """ + + def __init__(self, stream: AnyByteStream): + """ + :param stream: the stream to be wrapped + + """ + super().__init__(stream) + self._stream = stream + + @override + async def send_eof(self) -> None: + await self._stream.send_eof() + + @override + async def send(self, item: bytes) -> None: + await self._stream.send(item) + + +class BufferedConnectable(ByteStreamConnectable): + def __init__(self, connectable: AnyByteStreamConnectable): + """ + :param connectable: the connectable to wrap + + """ + self.connectable = connectable + + @override + async def connect(self) -> BufferedByteStream: + stream = await self.connectable.connect() + return BufferedByteStream(stream) diff --git a/.deps_siteforge_v812/anyio/streams/file.py b/.deps_siteforge_v812/anyio/streams/file.py new file mode 100644 index 0000000..79c3d50 --- /dev/null +++ b/.deps_siteforge_v812/anyio/streams/file.py @@ -0,0 +1,154 @@ +from __future__ import annotations + +__all__ = ( + "FileReadStream", + "FileStreamAttribute", + "FileWriteStream", +) + +from collections.abc import Callable, Mapping +from io import SEEK_SET, UnsupportedOperation +from os import PathLike +from pathlib import Path +from typing import IO, Any + +from .. import ( + BrokenResourceError, + ClosedResourceError, + EndOfStream, + TypedAttributeSet, + to_thread, + typed_attribute, +) +from ..abc import ByteReceiveStream, ByteSendStream + + +class FileStreamAttribute(TypedAttributeSet): + #: the open file descriptor + file: IO[bytes] = typed_attribute() + #: the path of the file on the file system, if available (file must be a real file) + path: Path = typed_attribute() + #: the file number, if available (file must be a real file or a TTY) + fileno: int = typed_attribute() + + +class _BaseFileStream: + def __init__(self, file: IO[bytes]): + self._file = file + + async def aclose(self) -> None: + await to_thread.run_sync(self._file.close) + + @property + def extra_attributes(self) -> Mapping[Any, Callable[[], Any]]: + attributes: dict[Any, Callable[[], Any]] = { + FileStreamAttribute.file: lambda: self._file, + } + + if hasattr(self._file, "name"): + attributes[FileStreamAttribute.path] = lambda: Path(self._file.name) + + try: + self._file.fileno() + except UnsupportedOperation: + pass + else: + attributes[FileStreamAttribute.fileno] = lambda: self._file.fileno() + + return attributes + + +class FileReadStream(_BaseFileStream, ByteReceiveStream): + """ + A byte stream that reads from a file in the file system. + + :param file: a file that has been opened for reading in binary mode + + .. versionadded:: 3.0 + """ + + @classmethod + async def from_path(cls, path: str | PathLike[str]) -> FileReadStream: + """ + Create a file read stream by opening the given file. + + :param path: path of the file to read from + + """ + file = await to_thread.run_sync(Path(path).open, "rb") + return cls(file) + + async def receive(self, max_bytes: int = 65536) -> bytes: + try: + data = await to_thread.run_sync(self._file.read, max_bytes) + except ValueError: + raise ClosedResourceError from None + except OSError as exc: + raise BrokenResourceError from exc + + if data: + return data + else: + raise EndOfStream + + async def seek(self, position: int, whence: int = SEEK_SET) -> int: + """ + Seek the file to the given position. + + .. seealso:: :meth:`io.IOBase.seek` + + .. note:: Not all file descriptors are seekable. + + :param position: position to seek the file to + :param whence: controls how ``position`` is interpreted + :return: the new absolute position + :raises OSError: if the file is not seekable + + """ + return await to_thread.run_sync(self._file.seek, position, whence) + + async def tell(self) -> int: + """ + Return the current stream position. + + .. note:: Not all file descriptors are seekable. + + :return: the current absolute position + :raises OSError: if the file is not seekable + + """ + return await to_thread.run_sync(self._file.tell) + + +class FileWriteStream(_BaseFileStream, ByteSendStream): + """ + A byte stream that writes to a file in the file system. + + :param file: a file that has been opened for writing in binary mode + + .. versionadded:: 3.0 + """ + + @classmethod + async def from_path( + cls, path: str | PathLike[str], append: bool = False + ) -> FileWriteStream: + """ + Create a file write stream by opening the given file for writing. + + :param path: path of the file to write to + :param append: if ``True``, open the file for appending; if ``False``, any + existing file at the given path will be truncated + + """ + mode = "ab" if append else "wb" + file = await to_thread.run_sync(Path(path).open, mode) + return cls(file) + + async def send(self, item: bytes) -> None: + try: + await to_thread.run_sync(self._file.write, item) + except ValueError: + raise ClosedResourceError from None + except OSError as exc: + raise BrokenResourceError from exc diff --git a/.deps_siteforge_v812/anyio/streams/memory.py b/.deps_siteforge_v812/anyio/streams/memory.py new file mode 100644 index 0000000..a3fa0c3 --- /dev/null +++ b/.deps_siteforge_v812/anyio/streams/memory.py @@ -0,0 +1,325 @@ +from __future__ import annotations + +__all__ = ( + "MemoryObjectReceiveStream", + "MemoryObjectSendStream", + "MemoryObjectStreamStatistics", +) + +import warnings +from collections import OrderedDict, deque +from dataclasses import dataclass, field +from types import TracebackType +from typing import Generic, NamedTuple, TypeVar + +from .. import ( + BrokenResourceError, + ClosedResourceError, + EndOfStream, + WouldBlock, +) +from .._core._testing import TaskInfo, get_current_task +from ..abc import Event, ObjectReceiveStream, ObjectSendStream +from ..lowlevel import checkpoint + +T_Item = TypeVar("T_Item") +T_co = TypeVar("T_co", covariant=True) +T_contra = TypeVar("T_contra", contravariant=True) + + +class MemoryObjectStreamStatistics(NamedTuple): + current_buffer_used: int #: number of items stored in the buffer + #: maximum number of items that can be stored on this stream (or :data:`math.inf`) + max_buffer_size: float + open_send_streams: int #: number of unclosed clones of the send stream + open_receive_streams: int #: number of unclosed clones of the receive stream + #: number of tasks blocked on :meth:`MemoryObjectSendStream.send` + tasks_waiting_send: int + #: number of tasks blocked on :meth:`MemoryObjectReceiveStream.receive` + tasks_waiting_receive: int + + +@dataclass(eq=False) +class _MemoryObjectItemReceiver(Generic[T_Item]): + task_info: TaskInfo = field(init=False, default_factory=get_current_task) + item: T_Item = field(init=False) + + def __repr__(self) -> str: + # When item is not defined, we get following error with default __repr__: + # AttributeError: 'MemoryObjectItemReceiver' object has no attribute 'item' + item = getattr(self, "item", None) + return f"{self.__class__.__name__}(task_info={self.task_info}, item={item!r})" + + +@dataclass(eq=False) +class _MemoryObjectStreamState(Generic[T_Item]): + max_buffer_size: float = field() + buffer: deque[T_Item] = field(init=False, default_factory=deque) + open_send_channels: int = field(init=False, default=0) + open_receive_channels: int = field(init=False, default=0) + waiting_receivers: OrderedDict[Event, _MemoryObjectItemReceiver[T_Item]] = field( + init=False, default_factory=OrderedDict + ) + waiting_senders: OrderedDict[Event, T_Item] = field( + init=False, default_factory=OrderedDict + ) + + def statistics(self) -> MemoryObjectStreamStatistics: + return MemoryObjectStreamStatistics( + len(self.buffer), + self.max_buffer_size, + self.open_send_channels, + self.open_receive_channels, + len(self.waiting_senders), + len(self.waiting_receivers), + ) + + +@dataclass(eq=False) +class MemoryObjectReceiveStream(Generic[T_co], ObjectReceiveStream[T_co]): + _state: _MemoryObjectStreamState[T_co] + _closed: bool = field(init=False, default=False) + + def __post_init__(self) -> None: + self._state.open_receive_channels += 1 + + def receive_nowait(self) -> T_co: + """ + Receive the next item if it can be done without waiting. + + :return: the received item + :raises ~anyio.ClosedResourceError: if this send stream has been closed + :raises ~anyio.EndOfStream: if the buffer is empty and this stream has been + closed from the sending end + :raises ~anyio.WouldBlock: if there are no items in the buffer and no tasks + waiting to send + + """ + if self._closed: + raise ClosedResourceError + + if self._state.waiting_senders: + # Get the item from the next sender + send_event, item = self._state.waiting_senders.popitem(last=False) + self._state.buffer.append(item) + send_event.set() + + if self._state.buffer: + return self._state.buffer.popleft() + elif not self._state.open_send_channels: + raise EndOfStream + + raise WouldBlock + + async def receive(self) -> T_co: + await checkpoint() + try: + return self.receive_nowait() + except WouldBlock: + # Add ourselves in the queue + receive_event = Event() + receiver = _MemoryObjectItemReceiver[T_co]() + self._state.waiting_receivers[receive_event] = receiver + + try: + await receive_event.wait() + finally: + self._state.waiting_receivers.pop(receive_event, None) + + try: + return receiver.item + except AttributeError: + raise EndOfStream from None + + def clone(self) -> MemoryObjectReceiveStream[T_co]: + """ + Create a clone of this receive stream. + + Each clone can be closed separately. Only when all clones have been closed will + the receiving end of the memory stream be considered closed by the sending ends. + + :return: the cloned stream + + """ + if self._closed: + raise ClosedResourceError + + return MemoryObjectReceiveStream(_state=self._state) + + def close(self) -> None: + """ + Close the stream. + + This works the exact same way as :meth:`aclose`, but is provided as a special + case for the benefit of synchronous callbacks. + + """ + if not self._closed: + self._closed = True + self._state.open_receive_channels -= 1 + if self._state.open_receive_channels == 0: + send_events = list(self._state.waiting_senders.keys()) + for event in send_events: + event.set() + + async def aclose(self) -> None: + self.close() + + def statistics(self) -> MemoryObjectStreamStatistics: + """ + Return statistics about the current state of this stream. + + .. versionadded:: 3.0 + """ + return self._state.statistics() + + def __enter__(self) -> MemoryObjectReceiveStream[T_co]: + return self + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + self.close() + + def __del__(self) -> None: + if not self._closed: + warnings.warn( + f"Unclosed <{self.__class__.__name__} at {id(self):x}>", + ResourceWarning, + stacklevel=1, + source=self, + ) + + +@dataclass(eq=False) +class MemoryObjectSendStream(Generic[T_contra], ObjectSendStream[T_contra]): + _state: _MemoryObjectStreamState[T_contra] + _closed: bool = field(init=False, default=False) + + def __post_init__(self) -> None: + self._state.open_send_channels += 1 + + def send_nowait(self, item: T_contra) -> None: + """ + Send an item immediately if it can be done without waiting. + + :param item: the item to send + :raises ~anyio.ClosedResourceError: if this send stream has been closed + :raises ~anyio.BrokenResourceError: if the stream has been closed from the + receiving end + :raises ~anyio.WouldBlock: if the buffer is full and there are no tasks waiting + to receive + + """ + if self._closed: + raise ClosedResourceError + if not self._state.open_receive_channels: + raise BrokenResourceError + + while self._state.waiting_receivers: + receive_event, receiver = self._state.waiting_receivers.popitem(last=False) + if not receiver.task_info.has_pending_cancellation(): + receiver.item = item + receive_event.set() + return + + if len(self._state.buffer) < self._state.max_buffer_size: + self._state.buffer.append(item) + else: + raise WouldBlock + + async def send(self, item: T_contra) -> None: + """ + Send an item to the stream. + + If the buffer is full, this method blocks until there is again room in the + buffer or the item can be sent directly to a receiver. + + :param item: the item to send + :raises ~anyio.ClosedResourceError: if this send stream has been closed + :raises ~anyio.BrokenResourceError: if the stream has been closed from the + receiving end + + """ + await checkpoint() + try: + self.send_nowait(item) + except WouldBlock: + # Wait until there's someone on the receiving end + send_event = Event() + self._state.waiting_senders[send_event] = item + try: + await send_event.wait() + except BaseException: + self._state.waiting_senders.pop(send_event, None) + raise + + if send_event in self._state.waiting_senders: + del self._state.waiting_senders[send_event] + raise BrokenResourceError from None + + def clone(self) -> MemoryObjectSendStream[T_contra]: + """ + Create a clone of this send stream. + + Each clone can be closed separately. Only when all clones have been closed will + the sending end of the memory stream be considered closed by the receiving ends. + + :return: the cloned stream + + """ + if self._closed: + raise ClosedResourceError + + return MemoryObjectSendStream(_state=self._state) + + def close(self) -> None: + """ + Close the stream. + + This works the exact same way as :meth:`aclose`, but is provided as a special + case for the benefit of synchronous callbacks. + + """ + if not self._closed: + self._closed = True + self._state.open_send_channels -= 1 + if self._state.open_send_channels == 0: + receive_events = list(self._state.waiting_receivers.keys()) + self._state.waiting_receivers.clear() + for event in receive_events: + event.set() + + async def aclose(self) -> None: + self.close() + + def statistics(self) -> MemoryObjectStreamStatistics: + """ + Return statistics about the current state of this stream. + + .. versionadded:: 3.0 + """ + return self._state.statistics() + + def __enter__(self) -> MemoryObjectSendStream[T_contra]: + return self + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + self.close() + + def __del__(self) -> None: + if not self._closed: + warnings.warn( + f"Unclosed <{self.__class__.__name__} at {id(self):x}>", + ResourceWarning, + stacklevel=1, + source=self, + ) diff --git a/.deps_siteforge_v812/anyio/streams/stapled.py b/.deps_siteforge_v812/anyio/streams/stapled.py new file mode 100644 index 0000000..9248b68 --- /dev/null +++ b/.deps_siteforge_v812/anyio/streams/stapled.py @@ -0,0 +1,147 @@ +from __future__ import annotations + +__all__ = ( + "MultiListener", + "StapledByteStream", + "StapledObjectStream", +) + +from collections.abc import Callable, Mapping, Sequence +from dataclasses import dataclass +from typing import Any, Generic, TypeVar + +from ..abc import ( + ByteReceiveStream, + ByteSendStream, + ByteStream, + Listener, + ObjectReceiveStream, + ObjectSendStream, + ObjectStream, + TaskGroup, +) + +T_Item = TypeVar("T_Item") +T_Stream = TypeVar("T_Stream") + + +@dataclass(eq=False) +class StapledByteStream(ByteStream): + """ + Combines two byte streams into a single, bidirectional byte stream. + + Extra attributes will be provided from both streams, with the receive stream + providing the values in case of a conflict. + + :param ByteSendStream send_stream: the sending byte stream + :param ByteReceiveStream receive_stream: the receiving byte stream + """ + + send_stream: ByteSendStream + receive_stream: ByteReceiveStream + + async def receive(self, max_bytes: int = 65536) -> bytes: + return await self.receive_stream.receive(max_bytes) + + async def send(self, item: bytes) -> None: + await self.send_stream.send(item) + + async def send_eof(self) -> None: + await self.send_stream.aclose() + + async def aclose(self) -> None: + await self.send_stream.aclose() + await self.receive_stream.aclose() + + @property + def extra_attributes(self) -> Mapping[Any, Callable[[], Any]]: + return { + **self.send_stream.extra_attributes, + **self.receive_stream.extra_attributes, + } + + +@dataclass(eq=False) +class StapledObjectStream(Generic[T_Item], ObjectStream[T_Item]): + """ + Combines two object streams into a single, bidirectional object stream. + + Extra attributes will be provided from both streams, with the receive stream + providing the values in case of a conflict. + + :param ObjectSendStream send_stream: the sending object stream + :param ObjectReceiveStream receive_stream: the receiving object stream + """ + + send_stream: ObjectSendStream[T_Item] + receive_stream: ObjectReceiveStream[T_Item] + + async def receive(self) -> T_Item: + return await self.receive_stream.receive() + + async def send(self, item: T_Item) -> None: + await self.send_stream.send(item) + + async def send_eof(self) -> None: + await self.send_stream.aclose() + + async def aclose(self) -> None: + await self.send_stream.aclose() + await self.receive_stream.aclose() + + @property + def extra_attributes(self) -> Mapping[Any, Callable[[], Any]]: + return { + **self.send_stream.extra_attributes, + **self.receive_stream.extra_attributes, + } + + +@dataclass(eq=False) +class MultiListener(Generic[T_Stream], Listener[T_Stream]): + """ + Combines multiple listeners into one, serving connections from all of them at once. + + Any MultiListeners in the given collection of listeners will have their listeners + moved into this one. + + Extra attributes are provided from each listener, with each successive listener + overriding any conflicting attributes from the previous one. + + :param listeners: listeners to serve + :type listeners: Sequence[Listener[T_Stream]] + """ + + listeners: Sequence[Listener[T_Stream]] + + def __post_init__(self) -> None: + listeners: list[Listener[T_Stream]] = [] + for listener in self.listeners: + if isinstance(listener, MultiListener): + listeners.extend(listener.listeners) + del listener.listeners[:] # type: ignore[attr-defined] + else: + listeners.append(listener) + + self.listeners = listeners + + async def serve( + self, handler: Callable[[T_Stream], Any], task_group: TaskGroup | None = None + ) -> None: + from .. import create_task_group + + async with create_task_group() as tg: + for listener in self.listeners: + tg.start_soon(listener.serve, handler, task_group) + + async def aclose(self) -> None: + for listener in self.listeners: + await listener.aclose() + + @property + def extra_attributes(self) -> Mapping[Any, Callable[[], Any]]: + attributes: dict = {} + for listener in self.listeners: + attributes.update(listener.extra_attributes) + + return attributes diff --git a/.deps_siteforge_v812/anyio/streams/text.py b/.deps_siteforge_v812/anyio/streams/text.py new file mode 100644 index 0000000..296cd25 --- /dev/null +++ b/.deps_siteforge_v812/anyio/streams/text.py @@ -0,0 +1,176 @@ +from __future__ import annotations + +__all__ = ( + "TextConnectable", + "TextReceiveStream", + "TextSendStream", + "TextStream", +) + +import codecs +import sys +from collections.abc import Callable, Mapping +from dataclasses import InitVar, dataclass, field +from typing import Any + +from ..abc import ( + AnyByteReceiveStream, + AnyByteSendStream, + AnyByteStream, + AnyByteStreamConnectable, + ObjectReceiveStream, + ObjectSendStream, + ObjectStream, + ObjectStreamConnectable, +) + +if sys.version_info >= (3, 12): + from typing import override +else: + from typing_extensions import override + + +@dataclass(eq=False) +class TextReceiveStream(ObjectReceiveStream[str]): + """ + Stream wrapper that decodes bytes to strings using the given encoding. + + Decoding is done using :class:`~codecs.IncrementalDecoder` which returns any + completely received unicode characters as soon as they come in. + + :param transport_stream: any bytes-based receive stream + :param encoding: character encoding to use for decoding bytes to strings (defaults + to ``utf-8``) + :param errors: handling scheme for decoding errors (defaults to ``strict``; see the + `codecs module documentation`_ for a comprehensive list of options) + + .. _codecs module documentation: + https://docs.python.org/3/library/codecs.html#codec-objects + """ + + transport_stream: AnyByteReceiveStream + encoding: InitVar[str] = "utf-8" + errors: InitVar[str] = "strict" + _decoder: codecs.IncrementalDecoder = field(init=False) + + def __post_init__(self, encoding: str, errors: str) -> None: + decoder_class = codecs.getincrementaldecoder(encoding) + self._decoder = decoder_class(errors=errors) + + async def receive(self) -> str: + while True: + chunk = await self.transport_stream.receive() + decoded = self._decoder.decode(chunk) + if decoded: + return decoded + + async def aclose(self) -> None: + await self.transport_stream.aclose() + self._decoder.reset() + + @property + def extra_attributes(self) -> Mapping[Any, Callable[[], Any]]: + return self.transport_stream.extra_attributes + + +@dataclass(eq=False) +class TextSendStream(ObjectSendStream[str]): + """ + Sends strings to the wrapped stream as bytes using the given encoding. + + :param AnyByteSendStream transport_stream: any bytes-based send stream + :param str encoding: character encoding to use for encoding strings to bytes + (defaults to ``utf-8``) + :param str errors: handling scheme for encoding errors (defaults to ``strict``; see + the `codecs module documentation`_ for a comprehensive list of options) + + .. _codecs module documentation: + https://docs.python.org/3/library/codecs.html#codec-objects + """ + + transport_stream: AnyByteSendStream + encoding: InitVar[str] = "utf-8" + errors: str = "strict" + _encoder: Callable[..., tuple[bytes, int]] = field(init=False) + + def __post_init__(self, encoding: str) -> None: + self._encoder = codecs.getencoder(encoding) + + async def send(self, item: str) -> None: + encoded = self._encoder(item, self.errors)[0] + await self.transport_stream.send(encoded) + + async def aclose(self) -> None: + await self.transport_stream.aclose() + + @property + def extra_attributes(self) -> Mapping[Any, Callable[[], Any]]: + return self.transport_stream.extra_attributes + + +@dataclass(eq=False) +class TextStream(ObjectStream[str]): + """ + A bidirectional stream that decodes bytes to strings on receive and encodes strings + to bytes on send. + + Extra attributes will be provided from both streams, with the receive stream + providing the values in case of a conflict. + + :param AnyByteStream transport_stream: any bytes-based stream + :param str encoding: character encoding to use for encoding/decoding strings to/from + bytes (defaults to ``utf-8``) + :param str errors: handling scheme for encoding errors (defaults to ``strict``; see + the `codecs module documentation`_ for a comprehensive list of options) + + .. _codecs module documentation: + https://docs.python.org/3/library/codecs.html#codec-objects + """ + + transport_stream: AnyByteStream + encoding: InitVar[str] = "utf-8" + errors: InitVar[str] = "strict" + _receive_stream: TextReceiveStream = field(init=False) + _send_stream: TextSendStream = field(init=False) + + def __post_init__(self, encoding: str, errors: str) -> None: + self._receive_stream = TextReceiveStream( + self.transport_stream, encoding=encoding, errors=errors + ) + self._send_stream = TextSendStream( + self.transport_stream, encoding=encoding, errors=errors + ) + + async def receive(self) -> str: + return await self._receive_stream.receive() + + async def send(self, item: str) -> None: + await self._send_stream.send(item) + + async def send_eof(self) -> None: + await self.transport_stream.send_eof() + + async def aclose(self) -> None: + await self._send_stream.aclose() + await self._receive_stream.aclose() + + @property + def extra_attributes(self) -> Mapping[Any, Callable[[], Any]]: + return { + **self._send_stream.extra_attributes, + **self._receive_stream.extra_attributes, + } + + +class TextConnectable(ObjectStreamConnectable[str]): + def __init__(self, connectable: AnyByteStreamConnectable): + """ + :param connectable: the bytestream endpoint to wrap + + """ + self.connectable = connectable + + @override + async def connect(self) -> TextStream: + stream = await self.connectable.connect() + return TextStream(stream) diff --git a/.deps_siteforge_v812/anyio/streams/tls.py b/.deps_siteforge_v812/anyio/streams/tls.py new file mode 100644 index 0000000..e2a7ca5 --- /dev/null +++ b/.deps_siteforge_v812/anyio/streams/tls.py @@ -0,0 +1,421 @@ +from __future__ import annotations + +__all__ = ( + "TLSAttribute", + "TLSConnectable", + "TLSListener", + "TLSStream", +) + +import logging +import re +import ssl +import sys +from collections.abc import Callable, Mapping +from dataclasses import dataclass +from functools import wraps +from ssl import SSLContext +from typing import Any, TypeAlias, TypeVar + +from .. import ( + BrokenResourceError, + EndOfStream, + aclose_forcefully, + get_cancelled_exc_class, + to_thread, +) +from .._core._typedattr import TypedAttributeSet, typed_attribute +from ..abc import ( + AnyByteStream, + AnyByteStreamConnectable, + ByteStream, + ByteStreamConnectable, + Listener, + TaskGroup, +) + +if sys.version_info >= (3, 11): + from typing import TypeVarTuple, Unpack +else: + from typing_extensions import TypeVarTuple, Unpack + +if sys.version_info >= (3, 12): + from typing import override +else: + from typing_extensions import override + +T_Retval = TypeVar("T_Retval") +PosArgsT = TypeVarTuple("PosArgsT") +_PCTRTT: TypeAlias = tuple[tuple[str, str], ...] +_PCTRTTT: TypeAlias = tuple[_PCTRTT, ...] + + +class TLSAttribute(TypedAttributeSet): + """Contains Transport Layer Security related attributes.""" + + #: the selected ALPN protocol + alpn_protocol: str | None = typed_attribute() + #: the channel binding for type ``tls-unique`` + channel_binding_tls_unique: bytes = typed_attribute() + #: the selected cipher + cipher: tuple[str, str, int] = typed_attribute() + #: the peer certificate in dictionary form (see :meth:`ssl.SSLSocket.getpeercert` + # for more information) + peer_certificate: None | (dict[str, str | _PCTRTTT | _PCTRTT]) = typed_attribute() + #: the peer certificate in binary form + peer_certificate_binary: bytes | None = typed_attribute() + #: ``True`` if this is the server side of the connection + server_side: bool = typed_attribute() + #: ciphers shared by the client during the TLS handshake (``None`` if this is the + #: client side) + shared_ciphers: list[tuple[str, str, int]] | None = typed_attribute() + #: the :class:`~ssl.SSLObject` used for encryption + ssl_object: ssl.SSLObject = typed_attribute() + #: ``True`` if this stream does (and expects) a closing TLS handshake when the + #: stream is being closed + standard_compatible: bool = typed_attribute() + #: the TLS protocol version (e.g. ``TLSv1.2``) + tls_version: str = typed_attribute() + + +@dataclass(eq=False) +class TLSStream(ByteStream): + """ + A stream wrapper that encrypts all sent data and decrypts received data. + + This class has no public initializer; use :meth:`wrap` instead. + All extra attributes from :class:`~TLSAttribute` are supported. + + :var AnyByteStream transport_stream: the wrapped stream + + """ + + transport_stream: AnyByteStream + standard_compatible: bool + _ssl_object: ssl.SSLObject + _read_bio: ssl.MemoryBIO + _write_bio: ssl.MemoryBIO + + @classmethod + async def wrap( + cls, + transport_stream: AnyByteStream, + *, + server_side: bool | None = None, + hostname: str | None = None, + ssl_context: ssl.SSLContext | None = None, + standard_compatible: bool = True, + ) -> TLSStream: + """ + Wrap an existing stream with Transport Layer Security. + + This performs a TLS handshake with the peer. + + :param transport_stream: a bytes-transporting stream to wrap + :param server_side: ``True`` if this is the server side of the connection, + ``False`` if this is the client side (if omitted, will be set to ``False`` + if ``hostname`` has been provided, ``False`` otherwise). Used only to create + a default context when an explicit context has not been provided. + :param hostname: host name of the peer (if host name checking is desired) + :param ssl_context: the SSLContext object to use (if not provided, a secure + default will be created) + :param standard_compatible: if ``False``, skip the closing handshake when + closing the connection, and don't raise an exception if the peer does the + same + :raises ~ssl.SSLError: if the TLS handshake fails + + """ + if server_side is None: + server_side = not hostname + + if not ssl_context: + purpose = ( + ssl.Purpose.CLIENT_AUTH if server_side else ssl.Purpose.SERVER_AUTH + ) + ssl_context = ssl.create_default_context(purpose) + + # Re-enable detection of unexpected EOFs if it was disabled by Python + if hasattr(ssl, "OP_IGNORE_UNEXPECTED_EOF"): + ssl_context.options &= ~ssl.OP_IGNORE_UNEXPECTED_EOF + + bio_in = ssl.MemoryBIO() + bio_out = ssl.MemoryBIO() + + # External SSLContext implementations may do blocking I/O in wrap_bio(), + # but the standard library implementation won't + if type(ssl_context) is ssl.SSLContext: + ssl_object = ssl_context.wrap_bio( + bio_in, bio_out, server_side=server_side, server_hostname=hostname + ) + else: + ssl_object = await to_thread.run_sync( + ssl_context.wrap_bio, + bio_in, + bio_out, + server_side, + hostname, + None, + ) + + wrapper = cls( + transport_stream=transport_stream, + standard_compatible=standard_compatible, + _ssl_object=ssl_object, + _read_bio=bio_in, + _write_bio=bio_out, + ) + await wrapper._call_sslobject_method(ssl_object.do_handshake) + return wrapper + + async def _call_sslobject_method( + self, func: Callable[[Unpack[PosArgsT]], T_Retval], *args: Unpack[PosArgsT] + ) -> T_Retval: + while True: + try: + result = func(*args) + except ssl.SSLWantReadError: + try: + # Flush any pending writes first + if self._write_bio.pending: + await self.transport_stream.send(self._write_bio.read()) + + data = await self.transport_stream.receive() + except EndOfStream: + self._read_bio.write_eof() + except OSError as exc: + self._read_bio.write_eof() + self._write_bio.write_eof() + raise BrokenResourceError from exc + else: + self._read_bio.write(data) + except ssl.SSLWantWriteError: + await self.transport_stream.send(self._write_bio.read()) + except ssl.SSLSyscallError as exc: + self._read_bio.write_eof() + self._write_bio.write_eof() + raise BrokenResourceError from exc + except ssl.SSLError as exc: + self._read_bio.write_eof() + self._write_bio.write_eof() + if isinstance(exc, ssl.SSLEOFError) or ( + exc.strerror and "UNEXPECTED_EOF_WHILE_READING" in exc.strerror + ): + if self.standard_compatible: + raise BrokenResourceError from exc + else: + raise EndOfStream from None + + raise + else: + # Flush any pending writes first + if self._write_bio.pending: + await self.transport_stream.send(self._write_bio.read()) + + return result + + async def unwrap(self) -> tuple[AnyByteStream, bytes]: + """ + Does the TLS closing handshake. + + :return: a tuple of (wrapped byte stream, bytes left in the read buffer) + + """ + await self._call_sslobject_method(self._ssl_object.unwrap) + self._read_bio.write_eof() + self._write_bio.write_eof() + return self.transport_stream, self._read_bio.read() + + async def aclose(self) -> None: + if self.standard_compatible: + try: + await self.unwrap() + except BaseException: + await aclose_forcefully(self.transport_stream) + raise + + await self.transport_stream.aclose() + + async def receive(self, max_bytes: int = 65536) -> bytes: + data = await self._call_sslobject_method(self._ssl_object.read, max_bytes) + if not data: + raise EndOfStream + + return data + + async def send(self, item: bytes) -> None: + await self._call_sslobject_method(self._ssl_object.write, item) + + async def send_eof(self) -> None: + tls_version = self.extra(TLSAttribute.tls_version) + match = re.match(r"TLSv(\d+)(?:\.(\d+))?", tls_version) + if match: + major, minor = int(match.group(1)), int(match.group(2) or 0) + if (major, minor) < (1, 3): + raise NotImplementedError( + f"send_eof() requires at least TLSv1.3; current " + f"session uses {tls_version}" + ) + + raise NotImplementedError( + "send_eof() has not yet been implemented for TLS streams" + ) + + @property + def extra_attributes(self) -> Mapping[Any, Callable[[], Any]]: + return { + **self.transport_stream.extra_attributes, + TLSAttribute.alpn_protocol: self._ssl_object.selected_alpn_protocol, + TLSAttribute.channel_binding_tls_unique: ( + self._ssl_object.get_channel_binding + ), + TLSAttribute.cipher: self._ssl_object.cipher, + TLSAttribute.peer_certificate: lambda: self._ssl_object.getpeercert(False), + TLSAttribute.peer_certificate_binary: lambda: self._ssl_object.getpeercert( + True + ), + TLSAttribute.server_side: lambda: self._ssl_object.server_side, + TLSAttribute.shared_ciphers: lambda: ( + self._ssl_object.shared_ciphers() + if self._ssl_object.server_side + else None + ), + TLSAttribute.standard_compatible: lambda: self.standard_compatible, + TLSAttribute.ssl_object: lambda: self._ssl_object, + TLSAttribute.tls_version: self._ssl_object.version, + } + + +@dataclass(eq=False) +class TLSListener(Listener[TLSStream]): + """ + A convenience listener that wraps another listener and auto-negotiates a TLS session + on every accepted connection. + + If the TLS handshake times out or raises an exception, + :meth:`handle_handshake_error` is called to do whatever post-mortem processing is + deemed necessary. + + Supports only the :attr:`~TLSAttribute.standard_compatible` extra attribute. + + :param Listener listener: the listener to wrap + :param ssl_context: the SSL context object + :param standard_compatible: a flag passed through to :meth:`TLSStream.wrap` + :param handshake_timeout: time limit for the TLS handshake + (passed to :func:`~anyio.fail_after`) + """ + + listener: Listener[Any] + ssl_context: ssl.SSLContext + standard_compatible: bool = True + handshake_timeout: float = 30 + + @staticmethod + async def handle_handshake_error(exc: BaseException, stream: AnyByteStream) -> None: + """ + Handle an exception raised during the TLS handshake. + + This method does 3 things: + + #. Forcefully closes the original stream + #. Logs the exception (unless it was a cancellation exception) using the + ``anyio.streams.tls`` logger + #. Reraises the exception if it was a base exception or a cancellation exception + + :param exc: the exception + :param stream: the original stream + + """ + await aclose_forcefully(stream) + + # Log all except cancellation exceptions + if not isinstance(exc, get_cancelled_exc_class()): + # CPython (as of 3.11.5) returns incorrect `sys.exc_info()` here when using + # any asyncio implementation, so we explicitly pass the exception to log + # (https://github.com/python/cpython/issues/108668). Trio does not have this + # issue because it works around the CPython bug. + logging.getLogger(__name__).exception( + "Error during TLS handshake", exc_info=exc + ) + + # Only reraise base exceptions and cancellation exceptions + if not isinstance(exc, Exception) or isinstance(exc, get_cancelled_exc_class()): + raise + + async def serve( + self, + handler: Callable[[TLSStream], Any], + task_group: TaskGroup | None = None, + ) -> None: + @wraps(handler) + async def handler_wrapper(stream: AnyByteStream) -> None: + from .. import fail_after + + try: + with fail_after(self.handshake_timeout): + wrapped_stream = await TLSStream.wrap( + stream, + ssl_context=self.ssl_context, + standard_compatible=self.standard_compatible, + ) + except BaseException as exc: + await self.handle_handshake_error(exc, stream) + else: + await handler(wrapped_stream) + + await self.listener.serve(handler_wrapper, task_group) + + async def aclose(self) -> None: + await self.listener.aclose() + + @property + def extra_attributes(self) -> Mapping[Any, Callable[[], Any]]: + return { + TLSAttribute.standard_compatible: lambda: self.standard_compatible, + } + + +class TLSConnectable(ByteStreamConnectable): + """ + Wraps another connectable and does TLS negotiation after a successful connection. + + :param connectable: the connectable to wrap + :param hostname: host name of the server (if host name checking is desired) + :param ssl_context: the SSLContext object to use (if not provided, a secure default + will be created) + :param standard_compatible: if ``False``, skip the closing handshake when closing + the connection, and don't raise an exception if the server does the same + """ + + def __init__( + self, + connectable: AnyByteStreamConnectable, + *, + hostname: str | None = None, + ssl_context: ssl.SSLContext | None = None, + standard_compatible: bool = True, + ) -> None: + self.connectable = connectable + self.ssl_context: SSLContext = ssl_context or ssl.create_default_context( + ssl.Purpose.SERVER_AUTH + ) + if not isinstance(self.ssl_context, ssl.SSLContext): + raise TypeError( + "ssl_context must be an instance of ssl.SSLContext, not " + f"{type(self.ssl_context).__name__}" + ) + self.hostname = hostname + self.standard_compatible = standard_compatible + + @override + async def connect(self) -> TLSStream: + stream = await self.connectable.connect() + try: + return await TLSStream.wrap( + stream, + hostname=self.hostname, + ssl_context=self.ssl_context, + standard_compatible=self.standard_compatible, + ) + except BaseException: + await aclose_forcefully(stream) + raise diff --git a/.deps_siteforge_v812/anyio/to_interpreter.py b/.deps_siteforge_v812/anyio/to_interpreter.py new file mode 100644 index 0000000..694dbe7 --- /dev/null +++ b/.deps_siteforge_v812/anyio/to_interpreter.py @@ -0,0 +1,246 @@ +from __future__ import annotations + +__all__ = ( + "run_sync", + "current_default_interpreter_limiter", +) + +import atexit +import os +import sys +from collections import deque +from collections.abc import Callable +from typing import Any, Final, TypeVar + +from . import current_time, to_thread +from ._core._exceptions import BrokenWorkerInterpreter +from ._core._synchronization import CapacityLimiter +from .lowlevel import RunVar + +if sys.version_info >= (3, 11): + from typing import TypeVarTuple, Unpack +else: + from typing_extensions import TypeVarTuple, Unpack + +if sys.version_info >= (3, 14): + from concurrent.interpreters import ExecutionFailed, create + + def _interp_call( + func: Callable[..., Any], args: tuple[Any, ...] + ) -> tuple[Any, bool]: + try: + retval = func(*args) + except BaseException as exc: + return exc, True + else: + return retval, False + + class _Worker: + last_used: float = 0 + + def __init__(self) -> None: + self._interpreter = create() + + def destroy(self) -> None: + self._interpreter.close() + + def call( + self, + func: Callable[..., T_Retval], + args: tuple[Any, ...], + ) -> T_Retval: + try: + res, is_exception = self._interpreter.call(_interp_call, func, args) + except ExecutionFailed as exc: + raise BrokenWorkerInterpreter(exc.excinfo) from exc + + if is_exception: + raise res + + return res +elif sys.version_info >= (3, 13): + import _interpqueues + import _interpreters + + UNBOUND: Final = 2 # I have no clue how this works, but it was used in the stdlib + FMT_UNPICKLED: Final = 0 + FMT_PICKLED: Final = 1 + QUEUE_PICKLE_ARGS: Final = (FMT_PICKLED, UNBOUND) + QUEUE_UNPICKLE_ARGS: Final = (FMT_UNPICKLED, UNBOUND) + + _run_func = compile( + """ +import _interpqueues +from _interpreters import NotShareableError +from pickle import loads, dumps, HIGHEST_PROTOCOL + +QUEUE_PICKLE_ARGS = (1, 2) +QUEUE_UNPICKLE_ARGS = (0, 2) + +item = _interpqueues.get(queue_id)[0] +try: + func, args = loads(item) + retval = func(*args) +except BaseException as exc: + is_exception = True + retval = exc +else: + is_exception = False + +try: + _interpqueues.put(queue_id, (retval, is_exception), *QUEUE_UNPICKLE_ARGS) +except NotShareableError: + retval = dumps(retval, HIGHEST_PROTOCOL) + _interpqueues.put(queue_id, (retval, is_exception), *QUEUE_PICKLE_ARGS) + """, + "", + "exec", + ) + + class _Worker: + last_used: float = 0 + + def __init__(self) -> None: + self._interpreter_id = _interpreters.create() + self._queue_id = _interpqueues.create(1, *QUEUE_UNPICKLE_ARGS) + _interpreters.set___main___attrs( + self._interpreter_id, {"queue_id": self._queue_id} + ) + + def destroy(self) -> None: + _interpqueues.destroy(self._queue_id) + _interpreters.destroy(self._interpreter_id) + + def call( + self, + func: Callable[..., T_Retval], + args: tuple[Any, ...], + ) -> T_Retval: + import pickle + + item = pickle.dumps((func, args), pickle.HIGHEST_PROTOCOL) + _interpqueues.put(self._queue_id, item, *QUEUE_PICKLE_ARGS) + exc_info = _interpreters.exec(self._interpreter_id, _run_func) + if exc_info: + raise BrokenWorkerInterpreter(exc_info) + + res = _interpqueues.get(self._queue_id) + (res, is_exception), fmt = res[:2] + if fmt == FMT_PICKLED: + res = pickle.loads(res) + + if is_exception: + raise res + + return res +else: + + class _Worker: + last_used: float = 0 + + def __init__(self) -> None: + raise RuntimeError("subinterpreters require at least Python 3.13") + + def call( + self, + func: Callable[..., T_Retval], + args: tuple[Any, ...], + ) -> T_Retval: + raise NotImplementedError + + def destroy(self) -> None: + pass + + +DEFAULT_CPU_COUNT: Final = 8 # this is just an arbitrarily selected value +MAX_WORKER_IDLE_TIME = ( + 30 # seconds a subinterpreter can be idle before becoming eligible for pruning +) + +T_Retval = TypeVar("T_Retval") +PosArgsT = TypeVarTuple("PosArgsT") + +_idle_workers = RunVar[deque[_Worker]]("_available_workers") +_default_interpreter_limiter = RunVar[CapacityLimiter]("_default_interpreter_limiter") + + +def _stop_workers(workers: deque[_Worker]) -> None: + for worker in workers: + worker.destroy() + + workers.clear() + + +async def run_sync( + func: Callable[[Unpack[PosArgsT]], T_Retval], + *args: Unpack[PosArgsT], + limiter: CapacityLimiter | None = None, +) -> T_Retval: + """ + Call the given function with the given arguments in a subinterpreter. + + .. warning:: On Python 3.13, the :mod:`concurrent.interpreters` module was not yet + available, so the code path for that Python version relies on an undocumented, + private API. As such, it is recommended to not rely on this function for anything + mission-critical on Python 3.13. + + :param func: a callable + :param args: the positional arguments for the callable + :param limiter: capacity limiter to use to limit the total number of subinterpreters + running (if omitted, the default limiter is used) + :return: the result of the call + :raises BrokenWorkerInterpreter: if there's an internal error in a subinterpreter + + """ + if limiter is None: + limiter = current_default_interpreter_limiter() + + try: + idle_workers = _idle_workers.get() + except LookupError: + idle_workers = deque() + _idle_workers.set(idle_workers) + atexit.register(_stop_workers, idle_workers) + + async with limiter: + try: + worker = idle_workers.pop() + except IndexError: + worker = _Worker() + + try: + return await to_thread.run_sync( + worker.call, + func, + args, + limiter=limiter, + ) + finally: + # Prune workers that have been idle for too long + now = current_time() + while idle_workers: + if now - idle_workers[0].last_used <= MAX_WORKER_IDLE_TIME: + break + + await to_thread.run_sync(idle_workers.popleft().destroy, limiter=limiter) + + worker.last_used = current_time() + idle_workers.append(worker) + + +def current_default_interpreter_limiter() -> CapacityLimiter: + """ + Return the capacity limiter used by default to limit the number of concurrently + running subinterpreters. + + Defaults to the number of CPU cores. + + :return: a capacity limiter object + + """ + try: + return _default_interpreter_limiter.get() + except LookupError: + limiter = CapacityLimiter(os.cpu_count() or DEFAULT_CPU_COUNT) + _default_interpreter_limiter.set(limiter) + return limiter diff --git a/.deps_siteforge_v812/anyio/to_process.py b/.deps_siteforge_v812/anyio/to_process.py new file mode 100644 index 0000000..b289234 --- /dev/null +++ b/.deps_siteforge_v812/anyio/to_process.py @@ -0,0 +1,266 @@ +from __future__ import annotations + +__all__ = ( + "current_default_process_limiter", + "process_worker", + "run_sync", +) + +import os +import pickle +import subprocess +import sys +from collections import deque +from collections.abc import Callable +from importlib.util import module_from_spec, spec_from_file_location +from typing import TypeVar, cast + +from ._core._eventloop import current_time, get_async_backend, get_cancelled_exc_class +from ._core._exceptions import BrokenWorkerProcess +from ._core._subprocesses import open_process +from ._core._synchronization import CapacityLimiter +from ._core._tasks import CancelScope, fail_after +from .abc import ByteReceiveStream, ByteSendStream, Process +from .lowlevel import RunVar, checkpoint_if_cancelled +from .streams.buffered import BufferedByteReceiveStream + +if sys.version_info >= (3, 11): + from typing import TypeVarTuple, Unpack +else: + from typing_extensions import TypeVarTuple, Unpack + +WORKER_MAX_IDLE_TIME = 300 # 5 minutes + +T_Retval = TypeVar("T_Retval") +PosArgsT = TypeVarTuple("PosArgsT") + +_process_pool_workers: RunVar[set[Process]] = RunVar("_process_pool_workers") +_process_pool_idle_workers: RunVar[deque[tuple[Process, float]]] = RunVar( + "_process_pool_idle_workers" +) +_default_process_limiter: RunVar[CapacityLimiter] = RunVar("_default_process_limiter") + + +async def run_sync( # type: ignore[return] + func: Callable[[Unpack[PosArgsT]], T_Retval], + *args: Unpack[PosArgsT], + cancellable: bool = False, + limiter: CapacityLimiter | None = None, +) -> T_Retval: + """ + Call the given function with the given arguments in a worker process. + + If the ``cancellable`` option is enabled and the task waiting for its completion is + cancelled, the worker process running it will be abruptly terminated using SIGKILL + (or ``terminateProcess()`` on Windows). + + :param func: a callable + :param args: positional arguments for the callable + :param cancellable: ``True`` to allow cancellation of the operation while it's + running + :param limiter: capacity limiter to use to limit the total amount of processes + running (if omitted, the default limiter is used) + :raises NoEventLoopError: if no supported asynchronous event loop is running in the + current thread + :return: an awaitable that yields the return value of the function. + + """ + + async def send_raw_command(pickled_cmd: bytes) -> object: + try: + await stdin.send(pickled_cmd) + response = await buffered.receive_until(b"\n", 50) + status, length = response.split(b" ") + if status not in (b"RETURN", b"EXCEPTION"): + raise RuntimeError( + f"Worker process returned unexpected response: {response!r}" + ) + + pickled_response = await buffered.receive_exactly(int(length)) + except BaseException as exc: + workers.discard(process) + try: + process.kill() + with CancelScope(shield=True): + await process.aclose() + except ProcessLookupError: + pass + + if isinstance(exc, get_cancelled_exc_class()): + raise + else: + raise BrokenWorkerProcess from exc + + retval = pickle.loads(pickled_response) + if status == b"EXCEPTION": + assert isinstance(retval, BaseException) + raise retval + else: + return retval + + # First pickle the request before trying to reserve a worker process + await checkpoint_if_cancelled() + request = pickle.dumps(("run", func, args), protocol=pickle.HIGHEST_PROTOCOL) + + # If this is the first run in this event loop thread, set up the necessary variables + try: + workers = _process_pool_workers.get() + idle_workers = _process_pool_idle_workers.get() + except LookupError: + workers = set() + idle_workers = deque() + _process_pool_workers.set(workers) + _process_pool_idle_workers.set(idle_workers) + get_async_backend().setup_process_pool_exit_at_shutdown(workers) + + async with limiter or current_default_process_limiter(): + # Pop processes from the pool (starting from the most recently used) until we + # find one that hasn't exited yet + process: Process + while idle_workers: + process, idle_since = idle_workers.pop() + if process.returncode is None: + stdin = cast(ByteSendStream, process.stdin) + buffered = BufferedByteReceiveStream( + cast(ByteReceiveStream, process.stdout) + ) + + # Prune any other workers that have been idle for WORKER_MAX_IDLE_TIME + # seconds or longer + now = current_time() + killed_processes: list[Process] = [] + while idle_workers: + if now - idle_workers[0][1] < WORKER_MAX_IDLE_TIME: + break + + process_to_kill, idle_since = idle_workers.popleft() + process_to_kill.kill() + workers.remove(process_to_kill) + killed_processes.append(process_to_kill) + + with CancelScope(shield=True): + for killed_process in killed_processes: + await killed_process.aclose() + + break + + workers.remove(process) + else: + command = [sys.executable, "-u", "-m", __name__] + process = await open_process( + command, stdin=subprocess.PIPE, stdout=subprocess.PIPE + ) + try: + stdin = cast(ByteSendStream, process.stdin) + buffered = BufferedByteReceiveStream( + cast(ByteReceiveStream, process.stdout) + ) + with fail_after(20): + message = await buffered.receive(6) + + if message != b"READY\n": + raise BrokenWorkerProcess( + f"Worker process returned unexpected response: {message!r}" + ) + + main_module_path = getattr(sys.modules["__main__"], "__file__", None) + pickled = pickle.dumps( + ("init", sys.path, main_module_path), + protocol=pickle.HIGHEST_PROTOCOL, + ) + await send_raw_command(pickled) + except (BrokenWorkerProcess, get_cancelled_exc_class()): + raise + except BaseException as exc: + process.kill() + raise BrokenWorkerProcess( + "Error during worker process initialization" + ) from exc + + workers.add(process) + + with CancelScope(shield=not cancellable): + try: + return cast(T_Retval, await send_raw_command(request)) + finally: + if process in workers: + idle_workers.append((process, current_time())) + + +def current_default_process_limiter() -> CapacityLimiter: + """ + Return the capacity limiter that is used by default to limit the number of worker + processes. + + :return: a capacity limiter object + + """ + try: + return _default_process_limiter.get() + except LookupError: + limiter = CapacityLimiter(os.cpu_count() or 2) + _default_process_limiter.set(limiter) + return limiter + + +def process_worker() -> None: + # Redirect standard streams to os.devnull so that user code won't interfere with the + # parent-worker communication + stdin = sys.stdin + stdout = sys.stdout + sys.stdin = open(os.devnull) + sys.stdout = open(os.devnull, "w") + + stdout.buffer.write(b"READY\n") + while True: + retval = exception = None + try: + command, *args = pickle.load(stdin.buffer) + except EOFError: + return + except BaseException as exc: + exception = exc + else: + if command == "run": + func, args = args + try: + retval = func(*args) + except BaseException as exc: + exception = exc + elif command == "init": + main_module_path: str | None + sys.path, main_module_path = args + del sys.modules["__main__"] + if main_module_path and os.path.isfile(main_module_path): + # Load the parent's main module but as __mp_main__ instead of + # __main__ (like multiprocessing does) to avoid infinite recursion + try: + spec = spec_from_file_location("__mp_main__", main_module_path) + if spec and spec.loader: + main = module_from_spec(spec) + spec.loader.exec_module(main) + sys.modules["__main__"] = main + except BaseException as exc: + exception = exc + try: + if exception is not None: + status = b"EXCEPTION" + pickled = pickle.dumps(exception, pickle.HIGHEST_PROTOCOL) + else: + status = b"RETURN" + pickled = pickle.dumps(retval, pickle.HIGHEST_PROTOCOL) + except BaseException as exc: + exception = exc + status = b"EXCEPTION" + pickled = pickle.dumps(exc, pickle.HIGHEST_PROTOCOL) + + stdout.buffer.write(b"%s %d\n" % (status, len(pickled))) + stdout.buffer.write(pickled) + + # Respect SIGTERM + if isinstance(exception, SystemExit): + raise exception + + +if __name__ == "__main__": + process_worker() diff --git a/.deps_siteforge_v812/anyio/to_thread.py b/.deps_siteforge_v812/anyio/to_thread.py new file mode 100644 index 0000000..83c79d1 --- /dev/null +++ b/.deps_siteforge_v812/anyio/to_thread.py @@ -0,0 +1,78 @@ +from __future__ import annotations + +__all__ = ( + "run_sync", + "current_default_thread_limiter", +) + +import sys +from collections.abc import Callable +from typing import TypeVar +from warnings import warn + +from ._core._eventloop import get_async_backend +from .abc import CapacityLimiter + +if sys.version_info >= (3, 11): + from typing import TypeVarTuple, Unpack +else: + from typing_extensions import TypeVarTuple, Unpack + +T_Retval = TypeVar("T_Retval") +PosArgsT = TypeVarTuple("PosArgsT") + + +async def run_sync( + func: Callable[[Unpack[PosArgsT]], T_Retval], + *args: Unpack[PosArgsT], + abandon_on_cancel: bool = False, + cancellable: bool | None = None, + limiter: CapacityLimiter | None = None, +) -> T_Retval: + """ + Call the given function with the given arguments in a worker thread. + + If the ``abandon_on_cancel`` option is enabled and the task waiting for its + completion is cancelled, the thread will still run its course but its + return value (or any raised exception) will be ignored. + + :param func: a callable + :param args: positional arguments for the callable + :param abandon_on_cancel: ``True`` to abandon the thread (leaving it to run + unchecked on own) if the host task is cancelled, ``False`` to ignore + cancellations in the host task until the operation has completed in the worker + thread + :param cancellable: deprecated alias of ``abandon_on_cancel``; will override + ``abandon_on_cancel`` if both parameters are passed + :param limiter: capacity limiter to use to limit the total amount of threads running + (if omitted, the default limiter is used) + :raises NoEventLoopError: if no supported asynchronous event loop is running in the + current thread + :return: an awaitable that yields the return value of the function. + + """ + if cancellable is not None: + abandon_on_cancel = cancellable + warn( + "The `cancellable=` keyword argument to `anyio.to_thread.run_sync` is " + "deprecated since AnyIO 4.1.0; use `abandon_on_cancel=` instead", + DeprecationWarning, + stacklevel=2, + ) + + return await get_async_backend().run_sync_in_worker_thread( + func, args, abandon_on_cancel=abandon_on_cancel, limiter=limiter + ) + + +def current_default_thread_limiter() -> CapacityLimiter: + """ + Return the capacity limiter that is used by default to limit the number of + concurrent threads. + + :return: a capacity limiter object + :raises NoEventLoopError: if no supported asynchronous event loop is running in the + current thread + + """ + return get_async_backend().current_default_thread_limiter() diff --git a/.deps_siteforge_v812/beautifulsoup4-4.14.3.dist-info/INSTALLER b/.deps_siteforge_v812/beautifulsoup4-4.14.3.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/.deps_siteforge_v812/beautifulsoup4-4.14.3.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.deps_siteforge_v812/beautifulsoup4-4.14.3.dist-info/METADATA b/.deps_siteforge_v812/beautifulsoup4-4.14.3.dist-info/METADATA new file mode 100644 index 0000000..7fd97b7 --- /dev/null +++ b/.deps_siteforge_v812/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/.deps_siteforge_v812/beautifulsoup4-4.14.3.dist-info/RECORD b/.deps_siteforge_v812/beautifulsoup4-4.14.3.dist-info/RECORD new file mode 100644 index 0000000..45cdf49 --- /dev/null +++ b/.deps_siteforge_v812/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-312.pyc,, +bs4/__pycache__/_deprecation.cpython-312.pyc,, +bs4/__pycache__/_typing.cpython-312.pyc,, +bs4/__pycache__/_warnings.cpython-312.pyc,, +bs4/__pycache__/css.cpython-312.pyc,, +bs4/__pycache__/dammit.cpython-312.pyc,, +bs4/__pycache__/diagnose.cpython-312.pyc,, +bs4/__pycache__/element.cpython-312.pyc,, +bs4/__pycache__/exceptions.cpython-312.pyc,, +bs4/__pycache__/filter.cpython-312.pyc,, +bs4/__pycache__/formatter.cpython-312.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-312.pyc,, +bs4/builder/__pycache__/_html5lib.cpython-312.pyc,, +bs4/builder/__pycache__/_htmlparser.cpython-312.pyc,, +bs4/builder/__pycache__/_lxml.cpython-312.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/.deps_siteforge_v812/beautifulsoup4-4.14.3.dist-info/REQUESTED b/.deps_siteforge_v812/beautifulsoup4-4.14.3.dist-info/REQUESTED new file mode 100644 index 0000000..e69de29 diff --git a/.deps_siteforge_v812/beautifulsoup4-4.14.3.dist-info/WHEEL b/.deps_siteforge_v812/beautifulsoup4-4.14.3.dist-info/WHEEL new file mode 100644 index 0000000..12228d4 --- /dev/null +++ b/.deps_siteforge_v812/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/.deps_siteforge_v812/beautifulsoup4-4.14.3.dist-info/licenses/AUTHORS b/.deps_siteforge_v812/beautifulsoup4-4.14.3.dist-info/licenses/AUTHORS new file mode 100644 index 0000000..18926c2 --- /dev/null +++ b/.deps_siteforge_v812/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/.deps_siteforge_v812/beautifulsoup4-4.14.3.dist-info/licenses/LICENSE b/.deps_siteforge_v812/beautifulsoup4-4.14.3.dist-info/licenses/LICENSE new file mode 100644 index 0000000..08e3a9c --- /dev/null +++ b/.deps_siteforge_v812/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/.deps_siteforge_v812/bin/fastapi.exe b/.deps_siteforge_v812/bin/fastapi.exe new file mode 100644 index 0000000000000000000000000000000000000000..e70d022c367b7e6f3ca5b1af37d3a2af8f5866d3 GIT binary patch literal 108411 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 a0sf?E$IGCEzM>pI+CnF-D=M7PHU9^2^E$Es literal 0 HcmV?d00001 diff --git a/.deps_siteforge_v812/bin/httpx.exe b/.deps_siteforge_v812/bin/httpx.exe new file mode 100644 index 0000000000000000000000000000000000000000..d5b4bb272c24a3b1c5f07cc4966c7436060aad89 GIT binary patch literal 108405 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 XXdld=hCXAQzu!WCtvf25(>4DGi^M*5 literal 0 HcmV?d00001 diff --git a/.deps_siteforge_v812/bin/pygmentize.exe b/.deps_siteforge_v812/bin/pygmentize.exe new file mode 100644 index 0000000000000000000000000000000000000000..fd06e884f271ddec8974aef3fe1d7246d3a643a9 GIT binary patch literal 108416 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 literal 0 HcmV?d00001 diff --git a/.deps_siteforge_v812/bin/pytest.exe b/.deps_siteforge_v812/bin/pytest.exe new file mode 100644 index 0000000000000000000000000000000000000000..ca6ff0e7065ccab9336f8a582f154d53151fc1cf GIT binary patch literal 108422 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 XXdld=hCXAQzu!WCtvf25(>4DGi^M*5 literal 0 HcmV?d00001 diff --git a/.deps_siteforge_v812/bin/uvicorn.exe b/.deps_siteforge_v812/bin/uvicorn.exe new file mode 100644 index 0000000000000000000000000000000000000000..8170b2b0843612cf82723908afc9b11e776f1906 GIT binary patch literal 108412 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.6 +Requires-Dist: packaging +Requires-Dist: six (>=1.9.0) +Requires-Dist: webencodings + +====== +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 4.1.0 (August 25th, 2021) +--------------------------------- + +**Features** + +* Python 3.9 support + +**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** + +None + +**Features** + +* Add relative link to code of conduct. (#442) + +* Drop deprecated 'setup.py test' support. (#507) + +* Fix typo: curren -> current in tests/test_clean.py (#504) + +* Test on PyPy 7 + +* Drop test support for end of life Python 3.4 + +**Bug fixes** + +None + +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/.deps_siteforge_v812/bleach-4.1.0.dist-info/RECORD b/.deps_siteforge_v812/bleach-4.1.0.dist-info/RECORD new file mode 100644 index 0000000..5d19920 --- /dev/null +++ b/.deps_siteforge_v812/bleach-4.1.0.dist-info/RECORD @@ -0,0 +1,98 @@ +bleach-4.1.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +bleach-4.1.0.dist-info/LICENSE,sha256=vsIjjBSaYyuPsmgT9oes6rq4AyfzJwdpwsFhV4g9MTA,569 +bleach-4.1.0.dist-info/METADATA,sha256=hjsHFM8kAmkW_FldYHtQ1l27pQCSwmHUXsF5K0bsQ2U,24434 +bleach-4.1.0.dist-info/RECORD,, +bleach-4.1.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +bleach-4.1.0.dist-info/WHEEL,sha256=Z-nyYpwrcSqxfdux5Mbn_DQ525iP7J2DG3JgGvOYyTQ,110 +bleach-4.1.0.dist-info/top_level.txt,sha256=dcv0wKIySB0zMjAEXLwY4V0-3IN9UZQGAT1wDmfQICY,7 +bleach/__init__.py,sha256=qxudCJ5lqQRO-oHRS1jUxy5bKjpGg4KkC2FsQrqfBJo,3712 +bleach/__pycache__/__init__.cpython-312.pyc,, +bleach/__pycache__/callbacks.cpython-312.pyc,, +bleach/__pycache__/html5lib_shim.cpython-312.pyc,, +bleach/__pycache__/linkifier.cpython-312.pyc,, +bleach/__pycache__/sanitizer.cpython-312.pyc,, +bleach/__pycache__/utils.cpython-312.pyc,, +bleach/_vendor/README.rst,sha256=eXeKT2JdZB4WX1kuhTa8W9Jp9VXtwIKFxo5RUL5exmM,2160 +bleach/_vendor/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +bleach/_vendor/__pycache__/__init__.cpython-312.pyc,, +bleach/_vendor/__pycache__/parse.cpython-312.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/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/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-312.pyc,, +bleach/_vendor/html5lib/__pycache__/_ihatexml.cpython-312.pyc,, +bleach/_vendor/html5lib/__pycache__/_inputstream.cpython-312.pyc,, +bleach/_vendor/html5lib/__pycache__/_tokenizer.cpython-312.pyc,, +bleach/_vendor/html5lib/__pycache__/_utils.cpython-312.pyc,, +bleach/_vendor/html5lib/__pycache__/constants.cpython-312.pyc,, +bleach/_vendor/html5lib/__pycache__/html5parser.cpython-312.pyc,, +bleach/_vendor/html5lib/__pycache__/serializer.cpython-312.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-312.pyc,, +bleach/_vendor/html5lib/_trie/__pycache__/_base.cpython-312.pyc,, +bleach/_vendor/html5lib/_trie/__pycache__/py.cpython-312.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-312.pyc,, +bleach/_vendor/html5lib/filters/__pycache__/alphabeticalattributes.cpython-312.pyc,, +bleach/_vendor/html5lib/filters/__pycache__/base.cpython-312.pyc,, +bleach/_vendor/html5lib/filters/__pycache__/inject_meta_charset.cpython-312.pyc,, +bleach/_vendor/html5lib/filters/__pycache__/lint.cpython-312.pyc,, +bleach/_vendor/html5lib/filters/__pycache__/optionaltags.cpython-312.pyc,, +bleach/_vendor/html5lib/filters/__pycache__/sanitizer.cpython-312.pyc,, +bleach/_vendor/html5lib/filters/__pycache__/whitespace.cpython-312.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-312.pyc,, +bleach/_vendor/html5lib/treeadapters/__pycache__/genshi.cpython-312.pyc,, +bleach/_vendor/html5lib/treeadapters/__pycache__/sax.cpython-312.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-312.pyc,, +bleach/_vendor/html5lib/treebuilders/__pycache__/base.cpython-312.pyc,, +bleach/_vendor/html5lib/treebuilders/__pycache__/dom.cpython-312.pyc,, +bleach/_vendor/html5lib/treebuilders/__pycache__/etree.cpython-312.pyc,, +bleach/_vendor/html5lib/treebuilders/__pycache__/etree_lxml.cpython-312.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-312.pyc,, +bleach/_vendor/html5lib/treewalkers/__pycache__/base.cpython-312.pyc,, +bleach/_vendor/html5lib/treewalkers/__pycache__/dom.cpython-312.pyc,, +bleach/_vendor/html5lib/treewalkers/__pycache__/etree.cpython-312.pyc,, +bleach/_vendor/html5lib/treewalkers/__pycache__/etree_lxml.cpython-312.pyc,, +bleach/_vendor/html5lib/treewalkers/__pycache__/genshi.cpython-312.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/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/html5lib_shim.py,sha256=JrmPCoUmCKepAtXoBuFZqJVOKyUGnFDYxGxRAQNqsvE,20124 +bleach/linkifier.py,sha256=knUMY4wv4SnCh0iWU6G8D2Dp9zb2qfPmgy9S05x8yPw,19997 +bleach/sanitizer.py,sha256=uRB_QPSWqBrHHPGXImlfZpBlhxNVY_zM1TBr3wJfcNk,21875 +bleach/utils.py,sha256=JmXJaAjbAlkdTR-L5gcid3XCT-R_nhU6DepHmbijbd0,613 diff --git a/.deps_siteforge_v812/bleach-4.1.0.dist-info/REQUESTED b/.deps_siteforge_v812/bleach-4.1.0.dist-info/REQUESTED new file mode 100644 index 0000000..e69de29 diff --git a/.deps_siteforge_v812/bleach-4.1.0.dist-info/WHEEL b/.deps_siteforge_v812/bleach-4.1.0.dist-info/WHEEL new file mode 100644 index 0000000..01b8fc7 --- /dev/null +++ b/.deps_siteforge_v812/bleach-4.1.0.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.36.2) +Root-Is-Purelib: true +Tag: py2-none-any +Tag: py3-none-any + diff --git a/.deps_siteforge_v812/bleach-4.1.0.dist-info/top_level.txt b/.deps_siteforge_v812/bleach-4.1.0.dist-info/top_level.txt new file mode 100644 index 0000000..a02d600 --- /dev/null +++ b/.deps_siteforge_v812/bleach-4.1.0.dist-info/top_level.txt @@ -0,0 +1 @@ +bleach diff --git a/.deps_siteforge_v812/bleach/__init__.py b/.deps_siteforge_v812/bleach/__init__.py new file mode 100644 index 0000000..d619fb2 --- /dev/null +++ b/.deps_siteforge_v812/bleach/__init__.py @@ -0,0 +1,131 @@ +# -*- coding: utf-8 -*- + +import packaging.version + +from bleach.linkifier import ( + DEFAULT_CALLBACKS, + Linker, +) +from bleach.sanitizer import ( + ALLOWED_ATTRIBUTES, + ALLOWED_PROTOCOLS, + ALLOWED_STYLES, + ALLOWED_TAGS, + Cleaner, +) + + +# yyyymmdd +__releasedate__ = "20210825" +# x.y.z or x.y.z.dev0 -- semver +__version__ = "4.1.0" +VERSION = packaging.version.Version(__version__) + + +__all__ = ["clean", "linkify"] + + +def clean( + text, + tags=ALLOWED_TAGS, + attributes=ALLOWED_ATTRIBUTES, + styles=ALLOWED_STYLES, + protocols=ALLOWED_PROTOCOLS, + strip=False, + strip_comments=True, +): + """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 list tags: allowed list of 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 styles: allowed list of css styles; defaults to + ``bleach.sanitizer.ALLOWED_STYLES`` + + :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 + + :returns: cleaned text as unicode + + """ + cleaner = Cleaner( + tags=tags, + attributes=attributes, + styles=styles, + protocols=protocols, + strip=strip, + strip_comments=strip_comments, + ) + 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/.deps_siteforge_v812/bleach/__pycache__/__init__.cpython-312.pyc b/.deps_siteforge_v812/bleach/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3f462a4a406ccb29c60f073eaeb35f2c7717f73c GIT binary patch literal 3980 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 Q*^!5E?!(b6&UN?v1EfnY2><{9 literal 0 HcmV?d00001 diff --git a/.deps_siteforge_v812/bleach/__pycache__/html5lib_shim.cpython-312.pyc b/.deps_siteforge_v812/bleach/__pycache__/html5lib_shim.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cc7df43337ffc2a288dd5dff7c1c8c4c1d8a404d GIT binary patch literal 17403 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 literal 0 HcmV?d00001 diff --git a/.deps_siteforge_v812/bleach/__pycache__/linkifier.cpython-312.pyc b/.deps_siteforge_v812/bleach/__pycache__/linkifier.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fd8b4966f7e0c83e84d41b3ce6711e6785f2413b GIT binary patch literal 16516 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 literal 0 HcmV?d00001 diff --git a/.deps_siteforge_v812/bleach/__pycache__/utils.cpython-312.pyc b/.deps_siteforge_v812/bleach/__pycache__/utils.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c2f3ef27519e967cba6b689faa9ab9328a22a653 GIT binary patch literal 1111 zcmZ8gy-yTD6rb6>L%2o7Pc&LgVvKA~P{faD3<*kQj2s^pnl;|s?r^uTx4W5{6Yp{) z6c&=0h?QuCrGY|Y;Xk024T2`PiG|U|#u%y8#y7)(;!Ebu?0awC@6CI^eY16I27!Eg z{b7t52z^!`>jK|eRu97V5P8VM0V?C`7$eWPgRUE%3EHft?DSU^Xye2h;2z`5>-Syh zVtso}nXsv~+6C$%sza{*a9Y#1u`pE54XdeqqsAnz<6&f{YQKk=tng3}8pk|}xlbh{ zsmqS)dv-og zCQ9&zwJgd(TpE%B)A{oGD^bWG27oCMfEX6L$ViC^9d<-QAaSE|3_u?7Lz1h?QWTP& zqfjTAP`>aM<3yJJ%O@iOD6AaI>U!F>7jQL(1btQAe2Q!%NvVe>COls#7a_lL7tc9Y z1>?f$Iezx+8K*Cf`>CYP<;bOhGXhDS*pE9U;Y2CJf=NlC5 zNB8l4!!?V@HEy&Z>4K_@{vVAyKxHjKz4yun2nVVTe%F3|#$evNrH=70@+bS?E66 zW|CHUMS{l@d`qY}ASqY>V6yk0T)O?5%!2O%XB#Zh3u+2|vf5U*@4nmm#oD`Y`q|-^ z+2`38ov*IF&knq6&%MnKJUKbnJx6DE%=SHQTFeeCwdat4)@Nj{ z*r~*Fqaa|e^rKMdl68$5k8goM-l~+Yt@(U8@+txA<$J)UdMH#`-%X71M|9{%Qw!d{ Lx-X4~u)g#MwbLT2 literal 0 HcmV?d00001 diff --git a/.deps_siteforge_v812/bleach/_vendor/README.rst b/.deps_siteforge_v812/bleach/_vendor/README.rst new file mode 100644 index 0000000..e53aede --- /dev/null +++ b/.deps_siteforge_v812/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/.deps_siteforge_v812/bleach/_vendor/__init__.py b/.deps_siteforge_v812/bleach/_vendor/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/.deps_siteforge_v812/bleach/_vendor/__pycache__/__init__.cpython-312.pyc b/.deps_siteforge_v812/bleach/_vendor/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c56b14e4e40e980f1412effa075c112c432a8c87 GIT binary patch literal 188 zcmX@j%ge<81n-XC%mUGmK?FMZ%mNgd&QQsq$>_I|p@<2{`wUX^E85vACbT%Us5r*h zz|zt@#<8HlC9xzi#wR~HF()P@HMbz9AhSTXB(W$xwM5q}-PowA%CtBpCo?Igpt2+* zKQAUJCp9rSLqEPOH7_NPO4oIE6^@RAT9HSNA7{~oJ zU8sjeIaw*k*-cL5&T=9z8YcL&Jo`1AHLzdfS>pvxH2&B8SrdLu_%-9#j9&|WE%+_K zZvlR-__gA<5Wj`^wTZT~cAVRB?hqYkoj&etk?4GtJL?jQ&K8TVvn66Nu1dYd-jZ>H zSn?`=woExM#d*0{hPxF8?r}~mf0Yv}yp``7ZZZ=dBYPMB!b7@>D&`X%w5ZoQ*7} zS!~gA8pKv_{fGi!juYFw^_LE4m)eQ0L_10x)NW`ex)L4Ujk+thcjKi)S~2ZJS7H-N z9MNuQC%O`wQR1j}Lp#xx=tPO*+70bQSE36gKEL1H-iB;>l+M%ZOIpp@6K_Mlk~uZy zdAoIL%2P6@raVwsr=~n5vo(oZ(CZV~cTmr=CGx{_owkmv&u4#gYRUumb!y5}Qdd*$ z*>*M)dN30jy!E;n(!uh3k>BXu=&jXeh@0haMSiolL6^UY<@X_f8|ulYJMwp+ zo()<(oh*MR@^_)0Y<`!x8#BKf^Q!w@<>h(qg>Lby;$xr-Tf{xM+ar3!y}0TX_u*=* zxF2aBN*qAmHgNz~+r@)OcZj3nAo6yKhj6t^JbZSyXz=ulM{xBRTHr5T%feSZ6CZz< zm(S1c701M*cz$0YSICK?cpT^ZmGdWXegOAR;CBFZ9TZP$&kf@GkatkKJ`C!2guU+& z-uFqb@zOWd*0R4EeLE_8#ivl~F>zdc8o$TI3*ssKJ|P|x`$dCj@E#vGi^Iq{A)dyY zoD|O>9m1PEDL#X6i(BT;k1}3IMUcuuR1<5<%3Hh#g-NKnuCxYEJ+q5(> z#mY?v#OVpI;0p?)o}gD0rl$gaA#~9zj0Ywr0#|+h3&N=*gF0@Mk2UPQ=&!^v*j$+1DtMc>3kr=TB|Z91$2zK#JA zeWF(a(h|OoO-m9+RtScGN*+mM0QUH2gz@Rnw1oQ?J^l;8j8I?=@&_*lrYA(Ikm2uD zU+ALX3jrAk@@r>`O=${H^#?)%;W*~k44$8yn(%qh2UbJqB05RaM4a}bRYCNQd&bau zaLnuXNWMUj;V;cX;J=IygwUg&vCCJf|5P2O+o*5C7n%{KrlqL>FuPL-1}42WVsqgBO;(ArA&h>}IbK@&-d=nBg=vY;-ZAzF7AZYL^06082b5 zTnGe2lo4sJqdS3dL2YZ)d(m^n7m&JbpAZ2Ya#v)mY9$#0>a?GVdNTz$zZCHKGle+8 zYfGLBtYi@22`MGzb1LBVj|D_;#>$=@c`=wNRL>?d<`<^}A#cXUenzGyrh}Pc`JCQ( zWON2^g68~kF^6)Z6jZN50i~eh5bF!*)AU3rV}FV|t6mIK-;|3IeYu0IjbdG4B}exJ z%W;=wxQuZ;G?g)33?a2#2uxk{N*R;iPkFvc57Mh1Uoc}D_f2>+W-1lTn1igeadI*U z7)Sw7S}Bk*Nul6W#!TsS#uTJljlnDa&kB%i4hAn~3|E60aA1%U(*hB5^z9Ua)4fg#^0K+GcOhV5GgQ?rCTC9VBX6 z2h#0yNpO|h7U+!C$U49vOE8=r@QjWD=`URLUAjCm=?_f3C0nH@Mdct7p21YLhsfZvQ zK$2aKU@gFQeGS8*$Mf1m5OJn*mq7h+L6Vg-Fm-G!IkB%@OHmG9iFee>G z);oO0A`?xwQ96Q*jFnwt8Nm%n!pa?_dIdr0RndrCd<4u9D4%o^H!tEpScYVt`^a8& zqko}4dNFQE+1nGQ_Rr}m&57UUf7<}W(>fdBBL-*#_*boA?hvP>4oJD7boXTVYtj>FNg_cnNI(~a`FVM~3in(9*LH16+Be%UB0n?�z%s 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.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,

  • oB$mf zs}@c}s;8!Tv3;d_XSR7~((;+53gS@jOCQYKn^|q_O*i%~@6I%y$W#nMSjts(`;D7# zthgGfo!*`tPrjULdp_lUf!gUDF_bwUSxII4qLT6MojaFh?eCs@>)b-u3d=v_DpFiS zifw>+M&gyJ4tjr0t3_mF-@5x7P+a+MEK z7Cg8K>1jN;0gjVc;K5Ddu~R=^{>pji%FEQziucn9JAIQc*vaGP!jGchPF_EhPS5mS z-cZ9d*7Yy(c2chuw8VPjWrcbnd7iJ#=u(^nCrAv=;d8b4W>KgMc_@ z;IWC?hWAT&o3Cht1nC}rSi)PeBT%{co?@H7dt+HT+Qos9{t!g@+VC*&*fg9Jg~PxS zMwu`a7!K!i{@sBqL5W2baG9xG;UvE(OJ;nH{vn-c7}t+og-pLz!!yWkh6B$q(Ire? z(et@l79Wg4P^3v?O&RSfm>)@ zVXFU!p`2;T5xA`ziJ>M(e|C`C?a7hFU8(I|>6-32`}|Obbg!FjOlPhpz%cHtvm$3i z;-RT(-GoH$jIonxfarb>pAYRdITJj=BAAac>lP$`$W$}iC<8nna str: + return "" + + +NOT_SET = NotSet() + + +@final +class Parser: + """Parser for command line arguments and config-file values. + + :ivar extra_info: Dict of generic param -> value to display in case + there's an error processing the command line arguments. + """ + + def __init__( + self, + usage: str | None = None, + processopt: Callable[[Argument], None] | None = None, + *, + _ispytest: bool = False, + ) -> None: + check_ispytest(_ispytest) + + from _pytest._argcomplete import filescompleter + + self._processopt = processopt + self.extra_info: dict[str, Any] = {} + self.optparser = PytestArgumentParser(self, usage, self.extra_info) + anonymous_arggroup = self.optparser.add_argument_group("Custom options") + self._anonymous = OptionGroup( + anonymous_arggroup, "_anonymous", self, _ispytest=True + ) + self._groups = [self._anonymous] + file_or_dir_arg = self.optparser.add_argument(FILE_OR_DIR, nargs="*") + file_or_dir_arg.completer = filescompleter # type: ignore + + self._inidict: dict[str, tuple[str, str, Any]] = {} + # Maps alias -> canonical name. + self._ini_aliases: dict[str, str] = {} + + @property + def prog(self) -> str: + return self.optparser.prog + + @prog.setter + def prog(self, value: str) -> None: + self.optparser.prog = value + + 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 + + arggroup = self.optparser.add_argument_group(description or name) + group = OptionGroup(arggroup, name, self, _ispytest=True) + i = 0 + for i, grp in enumerate(self._groups): + if grp.name == after: + break + self._groups.insert(i + 1, group) + # argparse doesn't provide a way to control `--help` order, so must + # access its internals ☹. + self.optparser._action_groups.insert(i + 1, self.optparser._action_groups.pop()) + 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: + """Parse the arguments. + + Unlike ``parse_known_args`` and ``parse_known_and_unknown_args``, + raises PrintHelp on `--help` and UsageError on unknown flags + + :meta private: + """ + from _pytest._argcomplete import try_argcomplete + + try_argcomplete(self.optparser) + strargs = [os.fspath(x) for x in args] + if namespace is None: + namespace = argparse.Namespace() + try: + namespace._raise_print_help = True + return self.optparser.parse_intermixed_args(strargs, namespace=namespace) + finally: + del namespace._raise_print_help + + 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 flag arguments. + + :returns: + A tuple containing an argparse namespace object for the known + arguments, and a list of unknown flag arguments. + """ + strargs = [os.fspath(x) for x in args] + if sys.version_info < (3, 12, 8) or (3, 13) <= sys.version_info < (3, 13, 1): + # Older argparse have a bugged parse_known_intermixed_args. + namespace, unknown = self.optparser.parse_known_args(strargs, namespace) + assert namespace is not None + file_or_dir = getattr(namespace, FILE_OR_DIR) + unknown_flags: list[str] = [] + for arg in unknown: + (unknown_flags if arg.startswith("-") else file_or_dir).append(arg) + return namespace, unknown_flags + else: + return self.optparser.parse_known_intermixed_args(strargs, namespace) + + def addini( + self, + name: str, + help: str, + type: Literal[ + "string", "paths", "pathlist", "args", "linelist", "bool", "int", "float" + ] + | None = None, + default: Any = NOT_SET, + *, + aliases: Sequence[str] = (), + ) -> None: + """Register a configuration file option. + + :param name: + Name of the configuration. + :param type: + Type of the configuration. 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 config-file. + In case the execution is happening without a config-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 a config-file. + + Defaults to ``string`` if ``None`` or not passed. + :param default: + Default value if no config-file option exists but is queried. + :param aliases: + Additional names by which this option can be referenced. + Aliases resolve to the canonical name. + + .. versionadded:: 9.0 + The ``aliases`` parameter. + + The value of configuration keys 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 type is None: + type = "string" + if default is NOT_SET: + default = get_ini_default_for_type(type) + + self._inidict[name] = (help, type, default) + + for alias in aliases: + if alias in self._inidict: + raise ValueError( + f"alias {alias!r} conflicts with existing configuration option" + ) + if (already := self._ini_aliases.get(alias)) is not None: + raise ValueError(f"{alias!r} is already an alias of {already!r}") + self._ini_aliases[alias] = name + + +def get_ini_default_for_type( + type: Literal[ + "string", "paths", "pathlist", "args", "linelist", "bool", "int", "float" + ], +) -> Any: + """ + Used by addini to get the default value for a given config option type, when + default is not supplied. + """ + if 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. + for attr in ("default", "dest", "help", self.dest): + 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, + arggroup: argparse._ArgumentGroup, + name: str, + parser: Parser | None, + _ispytest: bool = False, + ) -> None: + check_ispytest(_ispytest) + self._arggroup = arggroup + self.name = name + 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._arggroup.add_argument(*option.names(), **option.attrs()) + self.options.append(option) + + +class PytestArgumentParser(argparse.ArgumentParser): + def __init__( + self, + parser: Parser, + usage: str | None, + extra_info: dict[str, str], + ) -> None: + self._parser = parser + super().__init__( + usage=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 + + def error(self, message: str) -> NoReturn: + """Transform argparse error message into UsageError.""" + msg = f"{self.prog}: error: {message}" + if self.extra_info: + msg += "\n" + "\n".join( + f" {k}: {v}" for k, v in sorted(self.extra_info.items()) + ) + raise UsageError(self.format_usage() + msg) + + +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 + + +class OverrideIniAction(argparse.Action): + """Custom argparse action that makes a CLI flag equivalent to overriding an + option, in addition to behaving like `store_true`. + + This can simplify things since code only needs to inspect the config option + and not consider the CLI flag. + """ + + def __init__( + self, + option_strings: Sequence[str], + dest: str, + nargs: int | str | None = None, + *args, + ini_option: str, + ini_value: str, + **kwargs, + ) -> None: + super().__init__(option_strings, dest, 0, *args, **kwargs) + self.ini_option = ini_option + self.ini_value = ini_value + + def __call__( + self, + parser: argparse.ArgumentParser, + namespace: argparse.Namespace, + *args, + **kwargs, + ) -> None: + setattr(namespace, self.dest, True) + current_overrides = getattr(namespace, "override_ini", None) + if current_overrides is None: + current_overrides = [] + current_overrides.append(f"{self.ini_option}={self.ini_value}") + setattr(namespace, "override_ini", current_overrides) diff --git a/.deps_siteforge_v812/_pytest/config/compat.py b/.deps_siteforge_v812/_pytest/config/compat.py new file mode 100644 index 0000000..21eab4c --- /dev/null +++ b/.deps_siteforge_v812/_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/.deps_siteforge_v812/_pytest/config/exceptions.py b/.deps_siteforge_v812/_pytest/config/exceptions.py new file mode 100644 index 0000000..d84a9ea --- /dev/null +++ b/.deps_siteforge_v812/_pytest/config/exceptions.py @@ -0,0 +1,15 @@ +from __future__ import annotations + +from typing import final + + +@final +class UsageError(Exception): + """Error in pytest usage or invocation.""" + + __module__ = "pytest" + + +class PrintHelp(Exception): + """Raised when pytest should print its help to skip the rest of the + argument parsing and validation.""" diff --git a/.deps_siteforge_v812/_pytest/config/findpaths.py b/.deps_siteforge_v812/_pytest/config/findpaths.py new file mode 100644 index 0000000..3c628a0 --- /dev/null +++ b/.deps_siteforge_v812/_pytest/config/findpaths.py @@ -0,0 +1,350 @@ +from __future__ import annotations + +from collections.abc import Iterable +from collections.abc import Sequence +from dataclasses import dataclass +from dataclasses import KW_ONLY +import os +from pathlib import Path +import sys +from typing import Literal +from typing import TypeAlias + +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 + + +@dataclass(frozen=True) +class ConfigValue: + """Represents a configuration value with its origin and parsing mode. + + This allows tracking whether a value came from a configuration file + or from a CLI override (--override-ini), which is important for + determining precedence when dealing with ini option aliases. + + The mode tracks the parsing mode/data model used for the value: + - "ini": from INI files or [tool.pytest.ini_options], where the only + supported value types are `str` or `list[str]`. + - "toml": from TOML files (not in INI mode), where native TOML types + are preserved. + """ + + value: object + _: KW_ONLY + origin: Literal["file", "override"] + mode: Literal["ini", "toml"] + + +ConfigDict: TypeAlias = dict[str, ConfigValue] + + +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 { + k: ConfigValue(v, origin="file", mode="ini") + for k, v in iniconfig["pytest"].items() + } + else: + # "pytest.ini" files are always the source of configuration, even if empty. + if filepath.name in {"pytest.ini", ".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 { + k: ConfigValue(v, origin="file", mode="ini") + for k, v in 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] table (toml mode) + # or [tool.pytest.ini_options] table (ini mode) for pyproject.toml, + # or [pytest] table (toml mode) for pytest.toml/.pytest.toml. + 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 + + # pytest.toml and .pytest.toml use [pytest] table directly. + if filepath.name in ("pytest.toml", ".pytest.toml"): + pytest_config = config.get("pytest", {}) + if pytest_config: + # TOML mode - preserve native TOML types. + return { + k: ConfigValue(v, origin="file", mode="toml") + for k, v in pytest_config.items() + } + # "pytest.toml" files are always the source of configuration, even if empty. + return {} + + # pyproject.toml uses [tool.pytest] or [tool.pytest.ini_options]. + else: + tool_pytest = config.get("tool", {}).get("pytest", {}) + + # Check for toml mode config: [tool.pytest] with content outside of ini_options. + toml_config = {k: v for k, v in tool_pytest.items() if k != "ini_options"} + # Check for ini mode config: [tool.pytest.ini_options]. + ini_config = tool_pytest.get("ini_options", None) + + if toml_config and ini_config: + raise UsageError( + f"{filepath}: Cannot use both [tool.pytest] (native TOML types) and " + "[tool.pytest.ini_options] (string-based INI format) simultaneously. " + "Please use [tool.pytest] with native TOML types (recommended) " + "or [tool.pytest.ini_options] for backwards compatibility." + ) + + if toml_config: + # TOML mode - preserve native TOML types. + return { + k: ConfigValue(v, origin="file", mode="toml") + for k, v in toml_config.items() + } + + elif ini_config is not None: + # INI mode - TOML supports richer data types than INI files, but we need to + # convert all scalar values to str for compatibility with the INI system. + def make_scalar(v: object) -> str | list[str]: + return v if isinstance(v, list) else str(v) + + return { + k: ConfigValue(make_scalar(v), origin="file", mode="ini") + for k, v in ini_config.items() + } + + return None + + +def locate_config( + invocation_dir: Path, + args: Iterable[Path], +) -> tuple[Path | None, Path | None, ConfigDict, Sequence[str]]: + """Search in the list of arguments for a valid ini-file for pytest, + and return a tuple of (rootdir, inifile, cfg-dict, ignored-config-files), where + ignored-config-files is a list of config basenames found that contain + pytest configuration but were ignored.""" + config_names = [ + "pytest.toml", + ".pytest.toml", + "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 + ignored_config_files: list[str] = [] + + 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: + index = config_names.index(config_name) + for remainder in config_names[index + 1 :]: + p2 = base / remainder + if ( + p2.is_file() + and load_config_dict_from_file(p2) is not None + ): + ignored_config_files.append(remainder) + return base, p, ini_config, ignored_config_files + 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)] + + +def parse_override_ini(override_ini: Sequence[str] | None) -> ConfigDict: + """Parse the -o/--override-ini command line arguments and return the overrides. + + :raises UsageError: + If one of the values is malformed. + """ + overrides = {} + # 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 override_ini or (): + 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: + overrides[key] = ConfigValue(user_ini_value, origin="override", mode="ini") + return overrides + + +CFG_PYTEST_SECTION = "[pytest] section in {filename} files is no longer supported, change to [tool:pytest] instead." + + +def determine_setup( + *, + inifile: str | None, + override_ini: Sequence[str] | None, + args: Sequence[str], + rootdir_cmd_arg: str | None, + invocation_dir: Path, +) -> tuple[Path, Path | None, ConfigDict, Sequence[str]]: + """Determine the rootdir, inifile and ini configuration values from the + command line arguments. + + :param inifile: + The `--inifile` command line argument, if given. + :param override_ini: + The -o/--override-ini command line arguments, 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. + + :raises UsageError: + """ + rootdir = None + dirs = get_dirs_from_args(args) + ignored_config_files: Sequence[str] = [] + + 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, ignored_config_files = 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." + ) + + ini_overrides = parse_override_ini(override_ini) + inicfg.update(ini_overrides) + + assert rootdir is not None + return rootdir, inipath, inicfg, ignored_config_files + + +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/.deps_siteforge_v812/_pytest/debugging.py b/.deps_siteforge_v812/_pytest/debugging.py new file mode 100644 index 0000000..de1b268 --- /dev/null +++ b/.deps_siteforge_v812/_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/.deps_siteforge_v812/_pytest/deprecated.py b/.deps_siteforge_v812/_pytest/deprecated.py new file mode 100644 index 0000000..cb5d2e9 --- /dev/null +++ b/.deps_siteforge_v812/_pytest/deprecated.py @@ -0,0 +1,99 @@ +"""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 PytestRemovedIn10Warning +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", + "pytest_subtests", +} + + +# This could have been removed pytest 8, but it's harmless and common, so no rush to remove. +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" +) + +MONKEYPATCH_LEGACY_NAMESPACE_PACKAGES = PytestRemovedIn10Warning( + "monkeypatch.syspath_prepend() called with pkg_resources legacy namespace packages detected.\n" + "Legacy namespace packages (using pkg_resources.declare_namespace) are deprecated.\n" + "Please use native namespace packages (PEP 420) instead.\n" + "See https://docs.pytest.org/en/stable/deprecations.html#monkeypatch-fixup-namespace-packages" +) + +# 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/.deps_siteforge_v812/_pytest/doctest.py b/.deps_siteforge_v812/_pytest/doctest.py new file mode 100644 index 0000000..cd255f5 --- /dev/null +++ b/.deps_siteforge_v812/_pytest/doctest.py @@ -0,0 +1,736 @@ +# 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, 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, strict=True): + 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/.deps_siteforge_v812/_pytest/faulthandler.py b/.deps_siteforge_v812/_pytest/faulthandler.py new file mode 100644 index 0000000..080cf58 --- /dev/null +++ b/.deps_siteforge_v812/_pytest/faulthandler.py @@ -0,0 +1,119 @@ +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_timeout = ( + "Dump the traceback of all threads if a test takes " + "more than TIMEOUT seconds to finish" + ) + help_exit_on_timeout = ( + "Exit the test process if a test takes more than " + "faulthandler_timeout seconds to finish" + ) + parser.addini("faulthandler_timeout", help_timeout, default=0.0) + parser.addini( + "faulthandler_exit_on_timeout", help_exit_on_timeout, type="bool", default=False + ) + + +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) + + +def get_exit_on_timeout_config_value(config: Config) -> bool: + exit_on_timeout = config.getini("faulthandler_exit_on_timeout") + assert isinstance(exit_on_timeout, bool) + return exit_on_timeout + + +@pytest.hookimpl(wrapper=True, trylast=True) +def pytest_runtest_protocol(item: Item) -> Generator[None, object, object]: + timeout = get_timeout_config_value(item.config) + exit_on_timeout = get_exit_on_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, exit=exit_on_timeout) + 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/.deps_siteforge_v812/_pytest/fixtures.py b/.deps_siteforge_v812/_pytest/fixtures.py new file mode 100644 index 0000000..27846db --- /dev/null +++ b/.deps_siteforge_v812/_pytest/fixtures.py @@ -0,0 +1,2047 @@ +# 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 overload +from typing import TYPE_CHECKING +from typing import TypeVar +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", covariant=True) +# 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 = Callable[..., FixtureValue] | Callable[..., Generator[FixtureValue]] +# The type of FixtureDef.cached_result (type alias generic in fixture value). +_FixtureCachedResult = ( + tuple[ + # The result. + FixtureValue, + # Cache key. + object, + None, + ] + | tuple[ + None, + # Cache key. + object, + # The exception and the original traceback. + tuple[BaseException, types.TracebackType | None], + ] +) + + +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], + 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]: + if argname == "request": + return RequestFixtureDef(self) + + # 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], + 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], + requested_scope: Scope, + ) -> None: + 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 + + +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. + 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: + return self.cached_result[0] + # 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: FixtureValue = 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"" + + +class RequestFixtureDef(FixtureDef[FixtureRequest]): + """A custom FixtureDef for the special "request" fixture. + + A new one is generated on-demand whenever "request" is requested. + """ + + def __init__(self, request: FixtureRequest) -> None: + super().__init__( + config=request.config, + baseid=None, + argname="request", + func=lambda: request, + scope=Scope.Function, + params=None, + _ispytest=True, + ) + self.cached_result = (request, [0], None) + + def addfinalizer(self, finalizer: Callable[[], object]) -> None: + pass + + +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: + + - config-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]]] = {} + + # Track the index for each fixture name in the simulated stack. + # Needed for handling override chains correctly, similar to _get_active_fixturedef. + # Using negative indices: -1 is the most specific (last), -2 is second to last, etc. + current_indices: dict[str, int] = {} + + def process_argname(argname: str) -> None: + # Optimization: already processed this argname. + if current_indices.get(argname) == -1: + return + + if argname not in fixturenames_closure: + fixturenames_closure.append(argname) + + if argname in ignore_args: + return + + fixturedefs = arg2fixturedefs.get(argname) + if not fixturedefs: + fixturedefs = self.getfixturedefs(argname, parentnode) + if not fixturedefs: + # Fixture not defined or not visible (will error during runtest). + return + arg2fixturedefs[argname] = fixturedefs + + index = current_indices.get(argname, -1) + if -index > len(fixturedefs): + # Exhausted the override chain (will error during runtest). + return + fixturedef = fixturedefs[index] + + current_indices[argname] = index - 1 + for dep in fixturedef.argnames: + process_argname(dep) + current_indices[argname] = index + + for name in initialnames: + process_argname(name) + + 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/.deps_siteforge_v812/_pytest/freeze_support.py b/.deps_siteforge_v812/_pytest/freeze_support.py new file mode 100644 index 0000000..959ff07 --- /dev/null +++ b/.deps_siteforge_v812/_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/.deps_siteforge_v812/_pytest/helpconfig.py b/.deps_siteforge_v812/_pytest/helpconfig.py new file mode 100644 index 0000000..6a22c9f --- /dev/null +++ b/.deps_siteforge_v812/_pytest/helpconfig.py @@ -0,0 +1,293 @@ +# mypy: allow-untyped-defs +"""Version info, help messages, tracing configuration.""" + +from __future__ import annotations + +import argparse +from collections.abc import Generator +from collections.abc import Sequence +import os +import sys +from typing import Any + +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(argparse.Action): + """An argparse Action that will raise a PrintHelp exception in order to skip + the rest of the argument parsing when --help is passed. + + This prevents argparse from raising UsageError when `--help` is used along + with 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. + + To opt in to this behavior, the parse caller must set + `namespace._raise_print_help = True`. Otherwise it just sets the option. + """ + + def __init__( + self, option_strings: Sequence[str], dest: str, *, help: str | None = None + ) -> None: + super().__init__( + option_strings=option_strings, + dest=dest, + nargs=0, + const=True, + default=False, + help=help, + ) + + def __call__( + self, + parser: argparse.ArgumentParser, + namespace: argparse.Namespace, + values: str | Sequence[Any] | None, + option_string: str | None = None, + ) -> None: + setattr(namespace, self.dest, self.const) + + if getattr(namespace, "_raise_print_help", 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 configuration option with "option=value" style, ' + "e.g. `-o strict_xfail=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 show_version_verbose(config: Config) -> None: + """Show verbose pytest version installation, including plugins.""" + 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") + + +def pytest_cmdline_main(config: Config) -> int | ExitCode | None: + # Note: a single `--version` argument is handled directly by `Config.main()` to avoid starting up the entire + # pytest infrastructure just to display the version (#13574). + if config.option.version > 1: + show_version_verbose(config) + return ExitCode.OK + elif config.option.help: + config._do_configure() + showhelp(config) + config._ensure_unconfigure() + return ExitCode.OK + 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] configuration options in the first " + "pytest.toml|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._inidict: + help, type, _default = config._parser._inidict[name] + 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 to a non-empty 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"), + ("PYTEST_DEBUG_TEMPROOT", "Override the system temporary directory"), + ("PYTEST_THEME", "The Pygments style to use for code output"), + ("PYTEST_THEME_MODE", "Set the PYTEST_THEME to be either 'dark' or 'light'"), + ] + 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) + + +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/.deps_siteforge_v812/_pytest/hookspec.py b/.deps_siteforge_v812/_pytest/hookspec.py new file mode 100644 index 0000000..c5bcc36 --- /dev/null +++ b/.deps_siteforge_v812/_pytest/hookspec.py @@ -0,0 +1,1342 @@ +# 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 config-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 config-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 a configuration 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 :confval:`enable_assertion_pass_hook` + configuration option: + + .. tab:: toml + + .. code-block:: toml + + [pytest] + enable_assertion_pass_hook = true + + .. tab:: ini + + .. 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/.deps_siteforge_v812/_pytest/junitxml.py b/.deps_siteforge_v812/_pytest/junitxml.py new file mode 100644 index 0000000..ae8d2b9 --- /dev/null +++ b/.deps_siteforge_v812/_pytest/junitxml.py @@ -0,0 +1,695 @@ +# 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, config: pytest.Config + ) -> None: + if config.get_verbosity() >= 0: + 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/.deps_siteforge_v812/_pytest/legacypath.py b/.deps_siteforge_v812/_pytest/legacypath.py new file mode 100644 index 0000000..59e8ef6 --- /dev/null +++ b/.deps_siteforge_v812/_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/.deps_siteforge_v812/_pytest/logging.py b/.deps_siteforge_v812/_pytest/logging.py new file mode 100644 index 0000000..e4fed57 --- /dev/null +++ b/.deps_siteforge_v812/_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/.deps_siteforge_v812/_pytest/main.py b/.deps_siteforge_v812/_pytest/main.py new file mode 100644 index 0000000..9bc930d --- /dev/null +++ b/.deps_siteforge_v812/_pytest/main.py @@ -0,0 +1,1203 @@ +"""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 OverrideIniAction +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 samefile_nofollow +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") + 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=OverrideIniAction, + ini_option="strict_config", + ini_value="true", + help="Enables the strict_config option", + ) + group.addoption( + "--strict-markers", + action=OverrideIniAction, + ini_option="strict_markers", + ini_value="true", + help="Enables the strict_markers option", + ) + group.addoption( + "--strict", + action=OverrideIniAction, + ini_option="strict", + ini_value="true", + help="Enables the strict option", + ) + parser.addini( + "strict_config", + "Any warnings encountered while parsing the `pytest` section of the " + "configuration file raise errors", + type="bool", + # None => fallback to `strict`. + default=None, + ) + parser.addini( + "strict_markers", + "Markers not registered in the `markers` section of the configuration " + "file raise errors", + type="bool", + # None => fallback to `strict`. + default=None, + ) + parser.addini( + "strict", + "Enables all strictness options, currently: " + "strict_config, strict_markers, strict_xfail, strict_parametrization_ids", + type="bool", + default=False, + ) + + 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 + consider_namespace_packages: bool = self.config.getini( + "consider_namespace_packages" + ) + try: + initialpaths: list[Path] = [] + initialpaths_with_parents: list[Path] = [] + + collection_args = [ + resolve_collection_argument( + self.config.invocation_params.dir, + arg, + i, + as_pypath=self.config.option.pyargs, + consider_namespace_packages=consider_namespace_packages, + ) + for i, arg in enumerate(args) + ] + + if not self.config.getoption("keepduplicates"): + # Normalize the collection arguments -- remove duplicates and overlaps. + self._initial_parts = normalize_collection_arguments(collection_args) + else: + self._initial_parts = collection_args + + for collection_argument in self._initial_parts: + 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 + parametrization = collection_argument.parametrization + 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). But use a version + # which doesn't resolve symlinks, otherwise we might match the + # same file more than once (#12039). + is_match = samefile_nofollow(node.path, matchparts[0]) + + # Name part e.g. `TestIt` in `/a/b/test_file.py::TestIt::test_it`. + else: + if len(matchparts) == 1: + # This the last part, one parametrization goes. + if parametrization is not None: + # A parametrized arg must match exactly. + is_match = node.name == matchparts[0] + parametrization + else: + # A non-parameterized arg matches all parametrizations (if any). + # TODO: Remove the hacky split once the collection structure + # contains parametrization. + is_match = node.name.split("[")[0] == matchparts[0] + else: + is_match = node.name == 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) + # For backward compat, dedup only applies to files. + handle_dupes = not isinstance(node, nodes.File) + rep, duplicate = self._collect_one_node(node, handle_dupes) + 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, *, consider_namespace_packages: bool = False +) -> 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: + return None + + if ( + spec.submodule_search_locations is None + or len(spec.submodule_search_locations) == 0 + ): + # Must be a simple module. + return spec.origin + + if consider_namespace_packages: + # If submodule_search_locations is set, it's a package (regular or namespace). + # Typically there is a single entry, but documentation claims it can be empty too + # (e.g. if the package has no physical location). + return spec.submodule_search_locations[0] + + if spec.origin is None: + # This is only the case for namespace packages + return None + + return os.path.dirname(spec.origin) + + +@dataclasses.dataclass(frozen=True) +class CollectionArgument: + """A resolved collection argument.""" + + path: Path + parts: Sequence[str] + parametrization: str | None + module_name: str | None + original_index: int + + +def resolve_collection_argument( + invocation_path: Path, + arg: str, + arg_index: int, + *, + as_pypath: bool = False, + consider_namespace_packages: 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[a,b]" + + 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"], + parametrization="[a,b]", + 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 = arg.partition("[") + strpath, *parts = base.split("::") + if squacket and not parts: + raise UsageError(f"path cannot contain [] parametrization: {arg}") + parametrization = f"{squacket}{rest}" if squacket else None + module_name = None + if as_pypath: + pyarg_strpath = search_pypath( + strpath, consider_namespace_packages=consider_namespace_packages + ) + 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, + parametrization=parametrization, + module_name=module_name, + original_index=arg_index, + ) + + +def is_collection_argument_subsumed_by( + arg: CollectionArgument, by: CollectionArgument +) -> bool: + """Check if `arg` is subsumed (contained) by `by`.""" + # First check path subsumption. + if by.path != arg.path: + # `by` subsumes `arg` if `by` is a parent directory of `arg` and has no + # parts (collects everything in that directory). + if not by.parts: + return arg.path.is_relative_to(by.path) + return False + # Paths are equal, check parts. + # For example: ("TestClass",) is a prefix of ("TestClass", "test_method"). + if len(by.parts) > len(arg.parts) or arg.parts[: len(by.parts)] != by.parts: + return False + # Paths and parts are equal, check parametrization. + # A `by` without parametrization (None) matches everything, e.g. + # `pytest x.py::test_it` matches `x.py::test_it[0]`. Otherwise must be + # exactly equal. + if by.parametrization is not None and by.parametrization != arg.parametrization: + return False + return True + + +def normalize_collection_arguments( + collection_args: Sequence[CollectionArgument], +) -> list[CollectionArgument]: + """Normalize collection arguments to eliminate overlapping paths and parts. + + Detects when collection arguments overlap in either paths or parts and only + keeps the shorter prefix, or the earliest argument if duplicate, preserving + order. The result is prefix-free. + """ + # A quadratic algorithm is not acceptable since large inputs are possible. + # So this uses an O(n*log(n)) algorithm which takes advantage of the + # property that after sorting, a collection argument will immediately + # precede collection arguments it subsumes. An O(n) algorithm is not worth + # it. + collection_args_sorted = sorted( + collection_args, + key=lambda arg: (arg.path, arg.parts, arg.parametrization or ""), + ) + normalized: list[CollectionArgument] = [] + last_kept = None + for arg in collection_args_sorted: + if last_kept is None or not is_collection_argument_subsumed_by(arg, last_kept): + normalized.append(arg) + last_kept = arg + normalized.sort(key=lambda arg: arg.original_index) + return normalized diff --git a/.deps_siteforge_v812/_pytest/mark/__init__.py b/.deps_siteforge_v812/_pytest/mark/__init__.py new file mode 100644 index 0000000..841d781 --- /dev/null +++ b/.deps_siteforge_v812/_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 TYPE_CHECKING + +from .expression import Expression +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[Config | None]() + + +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 SyntaxError as e: + raise UsageError( + f"{exc_message}: {e.text}: at column {e.offset}: {e.msg}" + ) 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/.deps_siteforge_v812/_pytest/mark/__pycache__/__init__.cpython-312.pyc b/.deps_siteforge_v812/_pytest/mark/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f8b6cf92679a8389c48fdd8939e4b74af27e1081 GIT binary patch literal 13049 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 literal 0 HcmV?d00001 diff --git a/.deps_siteforge_v812/_pytest/mark/__pycache__/structures.cpython-312.pyc b/.deps_siteforge_v812/_pytest/mark/__pycache__/structures.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..77ffd0e6626776fdde6b4867ef9c84da611657cb GIT binary patch literal 27483 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 None: + self.input = input + 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 SyntaxError( + f'closing quote "{quote_char}" is missing', + (FILE_NAME, 1, pos + 1, input), + ) + value = input[pos : end_quote_pos + 1] + if (backslash_pos := input.find("\\")) != -1: + raise SyntaxError( + r'escaping with "\" not supported in marker expression', + (FILE_NAME, 1, backslash_pos + 1, input), + ) + 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 SyntaxError( + f'unexpected character "{input[pos]}"', + (FILE_NAME, 1, pos + 1, input), + ) + 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 SyntaxError( + "expected {}; got {}".format( + " OR ".join(type.value for type in expected), + self.current.type.value, + ), + (FILE_NAME, 1, self.current.pos + 1, self.input), + ) + + +# 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 SyntaxError( + f"not a valid python identifier {keyword_name.value}", + (FILE_NAME, 1, keyword_name.pos + 1, s.input), + ) + if keyword.iskeyword(keyword_name.value): + raise SyntaxError( + f"unexpected reserved python keyword `{keyword_name.value}`", + (FILE_NAME, 1, keyword_name.pos + 1, s.input), + ) + 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 SyntaxError( + f'unexpected character/s "{value_token.value}"', + (FILE_NAME, 1, value_token.pos + 1, s.input), + ) + + 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 ExpressionMatcher(Protocol): + """A callable which, given an identifier and optional kwargs, should return + whether it matches in an :class:`Expression` evaluation. + + Should be prepared to handle arbitrary strings as input. + + If no kwargs are provided, the expression of the form `foo`. + If kwargs are provided, the expression is of the form `foo(1, b=True, "s")`. + + If the expression is not supported (e.g. don't want to accept the kwargs + syntax variant), should raise :class:`~pytest.UsageError`. + + Example:: + + def matcher(name: str, /, **kwargs: str | int | bool | None) -> bool: + # Match `cat`. + if name == "cat" and not kwargs: + return True + # Match `dog(barks=True)`. + if name == "dog" and kwargs == {"barks": False}: + return True + return False + """ + + def __call__(self, name: str, /, **kwargs: str | int | bool | None) -> bool: ... + + +@dataclasses.dataclass +class MatcherNameAdapter: + matcher: ExpressionMatcher + 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: ExpressionMatcher) -> 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() + + +@final +class Expression: + """A compiled match expression as used by -k and -m. + + The expression can be evaluated against different matchers. + """ + + __slots__ = ("_code", "input") + + def __init__(self, input: str, code: types.CodeType) -> None: + #: The original input line, as a string. + self.input: Final = input + self._code: Final = code + + @classmethod + def compile(cls, input: str) -> Expression: + """Compile a match expression. + + :param input: The input expression - one line. + + :raises SyntaxError: If the expression is malformed. + """ + astexpr = expression(Scanner(input)) + code = compile( + astexpr, + filename="", + mode="eval", + ) + return Expression(input, code) + + def evaluate(self, matcher: ExpressionMatcher) -> bool: + """Evaluate the match expression. + + :param matcher: + A callback which determines whether an identifier matches or not. + See the :class:`ExpressionMatcher` protocol for details and example. + + :returns: Whether the expression matches or not. + + :raises UsageError: + If the matcher doesn't support the expression. Cannot happen if the + matcher supports all expressions. + """ + return bool(eval(self._code, {"__builtins__": {}}, MatcherAdapter(matcher))) diff --git a/.deps_siteforge_v812/_pytest/mark/structures.py b/.deps_siteforge_v812/_pytest/mark/structures.py new file mode 100644 index 0000000..16bb6d8 --- /dev/null +++ b/.deps_siteforge_v812/_pytest/mark/structures.py @@ -0,0 +1,664 @@ +# 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 +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=(40, 2, 42), 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=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: + # 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'?") + + strict_markers = self._config.getini("strict_markers") + if strict_markers is None: + strict_markers = self._config.getini("strict") + if strict_markers: + fail( + f"{name!r} not found in `markers` configuration option", + pytrace=False, + ) + + 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/.deps_siteforge_v812/_pytest/monkeypatch.py b/.deps_siteforge_v812/_pytest/monkeypatch.py new file mode 100644 index 0000000..07cc3fc --- /dev/null +++ b/.deps_siteforge_v812/_pytest/monkeypatch.py @@ -0,0 +1,435 @@ +# 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 +from pathlib import Path +import re +import sys +from typing import Any +from typing import final +from typing import overload +from typing import TypeVar +import warnings + +from _pytest.deprecated import MONKEYPATCH_LEGACY_NAMESPACE_PACKAGES +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: + import pkg_resources + from pkg_resources import fixup_namespace_packages + + # Only issue deprecation warning if this call would actually have an + # effect for this specific path. + if ( + hasattr(pkg_resources, "_namespace_packages") + and pkg_resources._namespace_packages + ): + path_obj = Path(str(path)) + for ns_pkg in pkg_resources._namespace_packages: + if ns_pkg is None: + continue + ns_pkg_path = path_obj / ns_pkg.replace(".", os.sep) + if ns_pkg_path.is_dir(): + warnings.warn( + MONKEYPATCH_LEGACY_NAMESPACE_PACKAGES, stacklevel=2 + ) + break + + 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/.deps_siteforge_v812/_pytest/nodes.py b/.deps_siteforge_v812/_pytest/nodes.py new file mode 100644 index 0000000..6690f6a --- /dev/null +++ b/.deps_siteforge_v812/_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/.deps_siteforge_v812/_pytest/outcomes.py b/.deps_siteforge_v812/_pytest/outcomes.py new file mode 100644 index 0000000..766be95 --- /dev/null +++ b/.deps_siteforge_v812/_pytest/outcomes.py @@ -0,0 +1,308 @@ +"""Exception classes and constants handling test outcomes as well as +functions creating them.""" + +from __future__ import annotations + +import sys +from typing import Any +from typing import ClassVar +from typing import NoReturn + +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) + + +class XFailed(Failed): + """Raised from an explicit call to pytest.xfail().""" + + +class _Exit: + """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. + """ + + Exception: ClassVar[type[Exit]] = Exit + + def __call__(self, reason: str = "", returncode: int | None = None) -> NoReturn: + __tracebackhide__ = True + raise Exit(msg=reason, returncode=returncode) + + +exit: _Exit = _Exit() + + +class _Skip: + """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. + """ + + Exception: ClassVar[type[Skipped]] = Skipped + + def __call__(self, reason: str = "", allow_module_level: bool = False) -> NoReturn: + __tracebackhide__ = True + raise Skipped(msg=reason, allow_module_level=allow_module_level) + + +skip: _Skip = _Skip() + + +class _Fail: + """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. + """ + + Exception: ClassVar[type[Failed]] = Failed + + def __call__(self, reason: str = "", pytrace: bool = True) -> NoReturn: + __tracebackhide__ = True + raise Failed(msg=reason, pytrace=pytrace) + + +fail: _Fail = _Fail() + + +class _XFail: + """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. + """ + + Exception: ClassVar[type[XFailed]] = XFailed + + def __call__(self, reason: str = "") -> NoReturn: + __tracebackhide__ = True + raise XFailed(msg=reason) + + +xfail: _XFail = _XFail() + + +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/.deps_siteforge_v812/_pytest/pastebin.py b/.deps_siteforge_v812/_pytest/pastebin.py new file mode 100644 index 0000000..c7b39d9 --- /dev/null +++ b/.deps_siteforge_v812/_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/.deps_siteforge_v812/_pytest/pathlib.py b/.deps_siteforge_v812/_pytest/pathlib.py new file mode 100644 index 0000000..cd15434 --- /dev/null +++ b/.deps_siteforge_v812/_pytest/pathlib.py @@ -0,0 +1,1063 @@ +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, strict=True): + 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 + + +def samefile_nofollow(p1: Path, p2: Path) -> bool: + """Test whether two paths reference the same actual file or directory. + + Unlike Path.samefile(), does not resolve symlinks. + """ + return os.path.samestat(p1.lstat(), p2.lstat()) diff --git a/.deps_siteforge_v812/_pytest/py.typed b/.deps_siteforge_v812/_pytest/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/.deps_siteforge_v812/_pytest/pytester.py b/.deps_siteforge_v812/_pytest/pytester.py new file mode 100644 index 0000000..1cd5f05 --- /dev/null +++ b/.deps_siteforge_v812/_pytest/pytester.py @@ -0,0 +1,1791 @@ +# 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. + #: + #: When running in subprocess mode, specify plugins by name (str) - adding + #: plugin objects directly is not supported. + 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 maketoml(self, source: str) -> Path: + """Write a pytest.toml file. + + :param source: The contents. + :returns: The pytest.toml file. + + .. versionadded:: 9.0 + """ + return self.makefile(".toml", pytest=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 = [str(x) for x in self._ensure_basetemp(args)] + + config = _pytest.config._prepareconfig(new_args, self.plugins) + # 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, + ) + 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() + f1.flush() + f2.flush() + + 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) + for plugin in self.plugins: + if not isinstance(plugin, str): + raise ValueError( + f"Specifying plugins as objects is not supported in pytester subprocess mode; " + f"specify by name instead: {plugin}" + ) + args = ("-p", plugin, *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/.deps_siteforge_v812/_pytest/pytester_assertions.py b/.deps_siteforge_v812/_pytest/pytester_assertions.py new file mode 100644 index 0000000..915cc8a --- /dev/null +++ b/.deps_siteforge_v812/_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/.deps_siteforge_v812/_pytest/python.py b/.deps_siteforge_v812/_pytest/python.py new file mode 100644 index 0000000..e637518 --- /dev/null +++ b/.deps_siteforge_v812/_pytest/python.py @@ -0,0 +1,1772 @@ +# 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 textwrap +import types +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 +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)", + ) + parser.addini( + "strict_parametrization_ids", + type="bool", + # None => fallback to `strict`. + default=None, + help="Emit an error if non-unique parameter set IDs are detected", + ) + + +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 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, determining parametrization ID + # strictness, 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. + + If strict_parametrization_ids is enabled, and duplicates are detected, + raises CollectError. Otherwise makes the IDs unique as follows: + + 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) + + if self._strict_parametrization_ids_enabled(): + parameters = ", ".join(self.argnames) + parametersets = ", ".join( + [saferepr(list(param.values)) for param in self.parametersets] + ) + ids = ", ".join( + id if id is not HIDDEN_PARAM else "" for id in resolved_ids + ) + duplicates = ", ".join( + id if id is not HIDDEN_PARAM else "" + for id, count in id_counts.items() + if count > 1 + ) + msg = textwrap.dedent(f""" + Duplicate parametrization IDs detected, but strict_parametrization_ids is set. + + Test name: {self.nodeid} + Parameters: {parameters} + Parameter sets: {parametersets} + IDs: {ids} + Duplicates: {duplicates} + + You can fix this problem using `@pytest.mark.parametrize(..., ids=...)` or `pytest.param(..., id=...)`. + """).strip() # noqa: E501 + raise nodes.Collector.CollectError(msg) + + # 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 _strict_parametrization_ids_enabled(self) -> bool: + if self.config is None: + return False + strict_parametrization_ids = self.config.getini("strict_parametrization_ids") + if strict_parametrization_ids is None: + strict_parametrization_ids = self.config.getini("strict") + return cast(bool, strict_parametrization_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, strict=True + ) + ) + + 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, strict=True): + 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, strict=True) + ): + 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/.deps_siteforge_v812/_pytest/python_api.py b/.deps_siteforge_v812/_pytest/python_api.py new file mode 100644 index 0000000..1e389eb --- /dev/null +++ b/.deps_siteforge_v812/_pytest/python_api.py @@ -0,0 +1,820 @@ +# 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 + + if len(self.expected) != len(other_side): + return [ + "Impossible to compare mappings with different sizes.", + f"Lengths: {len(self.expected)} and {len(other_side)}", + ] + + if set(self.expected.keys()) != set(other_side.keys()): + return [ + "comparison failed.", + f"Mappings has different keys: expected {self.expected.keys()} but got {other_side.keys()}", + ] + + 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(), strict=True + ): + 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, strict=True) + ): + 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, strict=True) + + 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 + + __hash__ = None + + @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/.deps_siteforge_v812/_pytest/raises.py b/.deps_siteforge_v812/_pytest/raises.py new file mode 100644 index 0000000..7c246fd --- /dev/null +++ b/.deps_siteforge_v812/_pytest/raises.py @@ -0,0 +1,1517 @@ +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 import TypeGuard + + from typing_extensions import ParamSpec + 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 issubclass(origin_exc, ExceptionGroup): + 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 {expected}, but got " + if isinstance(exc, type): # type: ignore[unreachable] + raise ValueError(msg + f"{exc.__name__!r}") + if isinstance(exc, BaseException): # type: ignore[unreachable] + 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 + + self._fail_reason = ( + f"Regex pattern did not match{maybe_specify_type}.\n" + f" Expected regex: {_match_pattern(self.match)!r}\n" + f" Actual message: {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 {expected}, but got {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/.deps_siteforge_v812/_pytest/recwarn.py b/.deps_siteforge_v812/_pytest/recwarn.py new file mode 100644 index 0000000..e3db717 --- /dev/null +++ b/.deps_siteforge_v812/_pytest/recwarn.py @@ -0,0 +1,367 @@ +# 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): + """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[:] = [] + + # Type ignored because we basically want the `catch_warnings` generic type + # parameter to be ourselves but that is not possible(?). + def __enter__(self) -> Self: # type: ignore[override] + 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/.deps_siteforge_v812/_pytest/reports.py b/.deps_siteforge_v812/_pytest/reports.py new file mode 100644 index 0000000..011a69d --- /dev/null +++ b/.deps_siteforge_v812/_pytest/reports.py @@ -0,0 +1,694 @@ +# 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 +import sys +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 sys.version_info < (3, 11): + from exceptiongroup import BaseExceptionGroup + + +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()) + + +def _format_failed_longrepr( + item: Item, call: CallInfo[None], excinfo: ExceptionInfo[BaseException] +): + 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") + ) + return longrepr + + +def _format_exception_group_all_skipped_longrepr( + item: Item, + excinfo: ExceptionInfo[BaseExceptionGroup[BaseException | BaseExceptionGroup]], +) -> tuple[str, int, str]: + r = excinfo._getreprcrash() + assert r is not None, ( + "There should always be a traceback entry for skipping a test." + ) + if all( + getattr(skip, "_use_item_location", False) for skip in excinfo.value.exceptions + ): + path, line = item.reportinfo()[:2] + assert line is not None + loc = (os.fspath(path), line + 1) + default_msg = "skipped" + else: + loc = (str(r.path), r.lineno) + default_msg = r.message + + # Get all unique skip messages. + msgs: list[str] = [] + for exception in excinfo.value.exceptions: + m = getattr(exception, "msg", None) or ( + exception.args[0] if exception.args else None + ) + if m and m not in msgs: + msgs.append(m) + + reason = "; ".join(msgs) if msgs else default_msg + longrepr = (*loc, reason) + return longrepr + + +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) + elif isinstance(excinfo.value, BaseExceptionGroup) and ( + excinfo.value.split(skip.Exception)[1] is None + ): + # All exceptions in the group are skip exceptions. + outcome = "skipped" + excinfo = cast( + ExceptionInfo[ + BaseExceptionGroup[BaseException | BaseExceptionGroup] + ], + excinfo, + ) + longrepr = _format_exception_group_all_skipped_longrepr(item, excinfo) + else: + outcome = "failed" + longrepr = _format_failed_longrepr(item, call, excinfo) + 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/.deps_siteforge_v812/_pytest/runner.py b/.deps_siteforge_v812/_pytest/runner.py new file mode 100644 index 0000000..9c20ff9 --- /dev/null +++ b/.deps_siteforge_v812/_pytest/runner.py @@ -0,0 +1,580 @@ +# 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 .config import Config +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}" + + call = CallInfo.from_call( + lambda: runtest_hook(item=item, **kwds), + when=when, + reraise=get_reraise_exceptions(item.config), + ) + 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 get_reraise_exceptions(config: Config) -> tuple[type[BaseException], ...]: + """Return exception types that should not be suppressed in general.""" + reraise: tuple[type[BaseException], ...] = (Exit,) + if not config.getoption("usepdb", False): + reraise += (KeyboardInterrupt,) + return reraise + + +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/.deps_siteforge_v812/_pytest/scope.py b/.deps_siteforge_v812/_pytest/scope.py new file mode 100644 index 0000000..2b007e8 --- /dev/null +++ b/.deps_siteforge_v812/_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/.deps_siteforge_v812/_pytest/setuponly.py b/.deps_siteforge_v812/_pytest/setuponly.py new file mode 100644 index 0000000..7e6b46b --- /dev/null +++ b/.deps_siteforge_v812/_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/.deps_siteforge_v812/_pytest/setupplan.py b/.deps_siteforge_v812/_pytest/setupplan.py new file mode 100644 index 0000000..4e124cc --- /dev/null +++ b/.deps_siteforge_v812/_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/.deps_siteforge_v812/_pytest/skipping.py b/.deps_siteforge_v812/_pytest/skipping.py new file mode 100644 index 0000000..3b06762 --- /dev/null +++ b/.deps_siteforge_v812/_pytest/skipping.py @@ -0,0 +1,321 @@ +# 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 _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( + "strict_xfail", + "Default for the strict parameter of xfail " + "markers when not given explicitly (default: False) (alias: xfail_strict)", + type="bool", + # None => fallback to `strict`. + default=None, + aliases=["xfail_strict"], + ) + + +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=strict_xfail): " + "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") + if strict is None: + strict = item.config.getini("strict_xfail") + if strict is None: + strict = item.config.getini("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[Xfail | None]() + + +@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/.deps_siteforge_v812/_pytest/stash.py b/.deps_siteforge_v812/_pytest/stash.py new file mode 100644 index 0000000..6a9ff88 --- /dev/null +++ b/.deps_siteforge_v812/_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/.deps_siteforge_v812/_pytest/stepwise.py b/.deps_siteforge_v812/_pytest/stepwise.py new file mode 100644 index 0000000..8901540 --- /dev/null +++ b/.deps_siteforge_v812/_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/.deps_siteforge_v812/_pytest/subtests.py b/.deps_siteforge_v812/_pytest/subtests.py new file mode 100644 index 0000000..e0ceb27 --- /dev/null +++ b/.deps_siteforge_v812/_pytest/subtests.py @@ -0,0 +1,411 @@ +"""Builtin plugin that adds subtests support.""" + +from __future__ import annotations + +from collections import defaultdict +from collections.abc import Callable +from collections.abc import Iterator +from collections.abc import Mapping +from contextlib import AbstractContextManager +from contextlib import contextmanager +from contextlib import ExitStack +from contextlib import nullcontext +import dataclasses +import time +from types import TracebackType +from typing import Any +from typing import TYPE_CHECKING + +import pluggy + +from _pytest._code import ExceptionInfo +from _pytest._io.saferepr import saferepr +from _pytest.capture import CaptureFixture +from _pytest.capture import FDCapture +from _pytest.capture import SysCapture +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.logging import catching_logs +from _pytest.logging import LogCaptureHandler +from _pytest.logging import LoggingPlugin +from _pytest.reports import TestReport +from _pytest.runner import CallInfo +from _pytest.runner import check_interactive_exception +from _pytest.runner import get_reraise_exceptions +from _pytest.stash import StashKey + + +if TYPE_CHECKING: + from typing_extensions import Self + + +def pytest_addoption(parser: Parser) -> None: + Config._add_verbosity_ini( + parser, + Config.VERBOSITY_SUBTESTS, + help=( + "Specify verbosity level for subtests. " + "Higher levels will generate output for passed subtests. Failed subtests are always reported." + ), + ) + + +@dataclasses.dataclass(frozen=True, slots=True, kw_only=True) +class SubtestContext: + """The values passed to Subtests.test() that are included in the test report.""" + + msg: str | None + kwargs: Mapping[str, Any] + + def _to_json(self) -> dict[str, Any]: + return dataclasses.asdict(self) + + @classmethod + def _from_json(cls, d: dict[str, Any]) -> Self: + return cls(msg=d["msg"], kwargs=d["kwargs"]) + + +@dataclasses.dataclass(init=False) +class SubtestReport(TestReport): + context: SubtestContext + + @property + def head_line(self) -> str: + _, _, domain = self.location + return f"{domain} {self._sub_test_description()}" + + def _sub_test_description(self) -> str: + parts = [] + if self.context.msg is not None: + parts.append(f"[{self.context.msg}]") + if self.context.kwargs: + params_desc = ", ".join( + f"{k}={saferepr(v)}" for (k, v) in self.context.kwargs.items() + ) + parts.append(f"({params_desc})") + return " ".join(parts) or "()" + + def _to_json(self) -> dict[str, Any]: + data = super()._to_json() + del data["context"] + data["_report_type"] = "SubTestReport" + data["_subtest.context"] = self.context._to_json() + return data + + @classmethod + def _from_json(cls, reportdict: dict[str, Any]) -> SubtestReport: + report = super()._from_json(reportdict) + report.context = SubtestContext._from_json(reportdict["_subtest.context"]) + return report + + @classmethod + def _new( + cls, + test_report: TestReport, + context: SubtestContext, + captured_output: Captured | None, + captured_logs: CapturedLogs | None, + ) -> Self: + result = super()._from_json(test_report._to_json()) + result.context = context + + if captured_output: + if captured_output.out: + result.sections.append(("Captured stdout call", captured_output.out)) + if captured_output.err: + result.sections.append(("Captured stderr call", captured_output.err)) + + if captured_logs and (log := captured_logs.handler.stream.getvalue()): + result.sections.append(("Captured log call", log)) + + return result + + +@fixture +def subtests(request: SubRequest) -> Subtests: + """Provides subtests functionality.""" + capmam = request.node.config.pluginmanager.get_plugin("capturemanager") + suspend_capture_ctx = ( + capmam.global_and_fixture_disabled if capmam is not None else nullcontext + ) + return Subtests(request.node.ihook, suspend_capture_ctx, request, _ispytest=True) + + +class Subtests: + """Subtests fixture, enables declaring subtests inside test functions via the :meth:`test` method.""" + + def __init__( + self, + ihook: pluggy.HookRelay, + suspend_capture_ctx: Callable[[], AbstractContextManager[None]], + request: SubRequest, + *, + _ispytest: bool = False, + ) -> None: + check_ispytest(_ispytest) + self._ihook = ihook + self._suspend_capture_ctx = suspend_capture_ctx + self._request = request + + def test( + self, + msg: str | None = None, + **kwargs: Any, + ) -> _SubTestContextManager: + """ + Context manager for subtests, capturing exceptions raised inside the subtest scope and + reporting assertion failures and errors individually. + + Usage + ----- + + .. code-block:: python + + def test(subtests): + for i in range(5): + with subtests.test("custom message", i=i): + assert i % 2 == 0 + + :param msg: + If given, the message will be shown in the test report in case of subtest failure. + + :param kwargs: + Arbitrary values that are also added to the subtest report. + """ + return _SubTestContextManager( + self._ihook, + msg, + kwargs, + request=self._request, + suspend_capture_ctx=self._suspend_capture_ctx, + config=self._request.config, + ) + + +@dataclasses.dataclass +class _SubTestContextManager: + """ + Context manager for subtests, capturing exceptions raised inside the subtest scope and handling + them through the pytest machinery. + """ + + # Note: initially the logic for this context manager was implemented directly + # in Subtests.test() as a @contextmanager, however, it is not possible to control the output fully when + # exiting from it due to an exception when in `--exitfirst` mode, so this was refactored into an + # explicit context manager class (pytest-dev/pytest-subtests#134). + + ihook: pluggy.HookRelay + msg: str | None + kwargs: dict[str, Any] + suspend_capture_ctx: Callable[[], AbstractContextManager[None]] + request: SubRequest + config: Config + + def __enter__(self) -> None: + __tracebackhide__ = True + + self._start = time.time() + self._precise_start = time.perf_counter() + self._exc_info = None + + self._exit_stack = ExitStack() + self._captured_output = self._exit_stack.enter_context( + capturing_output(self.request) + ) + self._captured_logs = self._exit_stack.enter_context( + capturing_logs(self.request) + ) + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> bool: + __tracebackhide__ = True + if exc_val is not None: + exc_info = ExceptionInfo.from_exception(exc_val) + else: + exc_info = None + + self._exit_stack.close() + + precise_stop = time.perf_counter() + duration = precise_stop - self._precise_start + stop = time.time() + + call_info = CallInfo[None]( + None, + exc_info, + start=self._start, + stop=stop, + duration=duration, + when="call", + _ispytest=True, + ) + report = self.ihook.pytest_runtest_makereport( + item=self.request.node, call=call_info + ) + sub_report = SubtestReport._new( + report, + SubtestContext(msg=self.msg, kwargs=self.kwargs), + captured_output=self._captured_output, + captured_logs=self._captured_logs, + ) + + if sub_report.failed: + failed_subtests = self.config.stash[failed_subtests_key] + failed_subtests[self.request.node.nodeid] += 1 + + with self.suspend_capture_ctx(): + self.ihook.pytest_runtest_logreport(report=sub_report) + + if check_interactive_exception(call_info, sub_report): + self.ihook.pytest_exception_interact( + node=self.request.node, call=call_info, report=sub_report + ) + + if exc_val is not None: + if isinstance(exc_val, get_reraise_exceptions(self.config)): + return False + if self.request.session.shouldfail: + return False + return True + + +@contextmanager +def capturing_output(request: SubRequest) -> Iterator[Captured]: + option = request.config.getoption("capture", None) + + capman = request.config.pluginmanager.getplugin("capturemanager") + if getattr(capman, "_capture_fixture", None): + # capsys or capfd are active, subtest should not capture. + fixture = None + elif option == "sys": + fixture = CaptureFixture(SysCapture, request, _ispytest=True) + elif option == "fd": + fixture = CaptureFixture(FDCapture, request, _ispytest=True) + else: + fixture = None + + if fixture is not None: + fixture._start() + + captured = Captured() + try: + yield captured + finally: + if fixture is not None: + out, err = fixture.readouterr() + fixture.close() + captured.out = out + captured.err = err + + +@contextmanager +def capturing_logs( + request: SubRequest, +) -> Iterator[CapturedLogs | None]: + logging_plugin: LoggingPlugin | None = request.config.pluginmanager.getplugin( + "logging-plugin" + ) + if logging_plugin is None: + yield None + else: + handler = LogCaptureHandler() + handler.setFormatter(logging_plugin.formatter) + + captured_logs = CapturedLogs(handler) + with catching_logs(handler, level=logging_plugin.log_level): + yield captured_logs + + +@dataclasses.dataclass +class Captured: + out: str = "" + err: str = "" + + +@dataclasses.dataclass +class CapturedLogs: + handler: LogCaptureHandler + + +def pytest_report_to_serializable(report: TestReport) -> dict[str, Any] | None: + if isinstance(report, SubtestReport): + return report._to_json() + return None + + +def pytest_report_from_serializable(data: dict[str, Any]) -> SubtestReport | None: + if data.get("_report_type") == "SubTestReport": + return SubtestReport._from_json(data) + return None + + +# Dict of nodeid -> number of failed subtests. +# Used to fail top-level tests that passed but contain failed subtests. +failed_subtests_key = StashKey[defaultdict[str, int]]() + + +def pytest_configure(config: Config) -> None: + config.stash[failed_subtests_key] = defaultdict(lambda: 0) + + +@hookimpl(tryfirst=True) +def pytest_report_teststatus( + report: TestReport, + config: Config, +) -> tuple[str, str, str | Mapping[str, bool]] | None: + if report.when != "call": + return None + + quiet = config.get_verbosity(Config.VERBOSITY_SUBTESTS) == 0 + if isinstance(report, SubtestReport): + outcome = report.outcome + description = report._sub_test_description() + + if hasattr(report, "wasxfail"): + if quiet: + return "", "", "" + elif outcome == "skipped": + category = "xfailed" + short = "y" # x letter is used for regular xfail, y for subtest xfail + status = "SUBXFAIL" + # outcome == "passed" in an xfail is only possible via a @pytest.mark.xfail mark, which + # is not applicable to a subtest, which only handles pytest.xfail(). + else: # pragma: no cover + # This should not normally happen, unless some plugin is setting wasxfail without + # the correct outcome. Pytest expects the call outcome to be either skipped or + # passed in case of xfail. + # Let's pass this report to the next hook. + return None + return category, short, f"{status}{description}" + + if report.failed: + return outcome, "u", f"SUBFAILED{description}" + else: + if report.passed: + if quiet: + return "", "", "" + else: + return f"subtests {outcome}", "u", f"SUBPASSED{description}" + elif report.skipped: + if quiet: + return "", "", "" + else: + return outcome, "-", f"SUBSKIPPED{description}" + + else: + failed_subtests_count = config.stash[failed_subtests_key][report.nodeid] + # Top-level test, fail if it contains failed subtests and it has passed. + if report.passed and failed_subtests_count > 0: + report.outcome = "failed" + suffix = "s" if failed_subtests_count > 1 else "" + report.longrepr = f"contains {failed_subtests_count} failed subtest{suffix}" + + return None diff --git a/.deps_siteforge_v812/_pytest/terminal.py b/.deps_siteforge_v812/_pytest/terminal.py new file mode 100644 index 0000000..e66e4f4 --- /dev/null +++ b/.deps_siteforge_v812/_pytest/terminal.py @@ -0,0 +1,1763 @@ +# 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.compat 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", + "subtests passed", + "subtests failed", + "subtests skipped", +) + +_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) + + # See terminalprogress.py. + # On Windows it's safe to load by default. + if sys.platform == "win32": + config.pluginmanager.import_plugin("terminalprogress") + + +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 + + @property + def reported_progress(self) -> int: + """The amount of items reported in the progress so far. + + :meta private: + """ + return len(self._progress_nodeids_reported) + + 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 write_raw(self, content: str, *, flush: bool = False) -> None: + self._tw.write_raw(content, flush=flush) + + 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 self.reported_progress == 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.reported_progress + ): + 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 = self.reported_progress + 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" [{self.reported_progress * 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: + warning = "" + if config._ignored_config_files: + warning = f" (WARNING: ignoring pytest config in {', '.join(config._ignored_config_files)}!)" + result.append( + "configfile: " + bestrelpath(config.rootpath, config.inipath) + warning + ) + + 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._outrep_summary(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) + + msg: str | None + try: + if isinstance(rep.longrepr, str): + msg = rep.longrepr + else: + # 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", + "subtests passed": "green", + "subtests failed": "red", +} +_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 + + +class TerminalProgressPlugin: + """Terminal progress reporting plugin using OSC 9;4 ANSI sequences. + + Emits OSC 9;4 sequences to indicate test progress to terminal + tabs/windows/etc. + + Not all terminal emulators support this feature. + + Ref: https://conemu.github.io/en/AnsiEscapeCodes.html#ConEmu_specific_OSC + """ + + def __init__(self, tr: TerminalReporter) -> None: + self._tr = tr + self._session: Session | None = None + self._has_failures = False + + def _emit_progress( + self, + state: Literal["remove", "normal", "error", "indeterminate", "paused"], + progress: int | None = None, + ) -> None: + """Emit OSC 9;4 sequence for indicating progress to the terminal. + + :param state: + Progress state to set. + :param progress: + Progress value 0-100. Required for "normal", optional for "error" + and "paused", otherwise ignored. + """ + assert progress is None or 0 <= progress <= 100 + + # OSC 9;4 sequence: ESC ] 9 ; 4 ; state ; progress ST + # ST can be ESC \ or BEL. ESC \ seems better supported. + match state: + case "remove": + sequence = "\x1b]9;4;0;\x1b\\" + case "normal": + assert progress is not None + sequence = f"\x1b]9;4;1;{progress}\x1b\\" + case "error": + if progress is not None: + sequence = f"\x1b]9;4;2;{progress}\x1b\\" + else: + sequence = "\x1b]9;4;2;\x1b\\" + case "indeterminate": + sequence = "\x1b]9;4;3;\x1b\\" + case "paused": + if progress is not None: + sequence = f"\x1b]9;4;4;{progress}\x1b\\" + else: + sequence = "\x1b]9;4;4;\x1b\\" + + self._tr.write_raw(sequence, flush=True) + + @hookimpl + def pytest_sessionstart(self, session: Session) -> None: + self._session = session + # Show indeterminate progress during collection. + self._emit_progress("indeterminate") + + @hookimpl + def pytest_collection_finish(self) -> None: + assert self._session is not None + if self._session.testscollected > 0: + # Switch from indeterminate to 0% progress. + self._emit_progress("normal", 0) + + @hookimpl + def pytest_runtest_logreport(self, report: TestReport) -> None: + if report.failed: + self._has_failures = True + + # Let's consider the "call" phase for progress. + if report.when != "call": + return + + # Calculate and emit progress. + assert self._session is not None + collected = self._session.testscollected + if collected > 0: + reported = self._tr.reported_progress + progress = min(reported * 100 // collected, 100) + self._emit_progress("error" if self._has_failures else "normal", progress) + + @hookimpl + def pytest_sessionfinish(self) -> None: + self._emit_progress("remove") diff --git a/.deps_siteforge_v812/_pytest/terminalprogress.py b/.deps_siteforge_v812/_pytest/terminalprogress.py new file mode 100644 index 0000000..287f0d5 --- /dev/null +++ b/.deps_siteforge_v812/_pytest/terminalprogress.py @@ -0,0 +1,30 @@ +# A plugin to register the TerminalProgressPlugin plugin. +# +# This plugin is not loaded by default due to compatibility issues (#13896), +# but can be enabled in one of these ways: +# - The terminal plugin enables it in a few cases where it's safe, and not +# blocked by the user (using e.g. `-p no:terminalprogress`). +# - The user explicitly requests it, e.g. using `-p terminalprogress`. +# +# In a few years, if it's safe, we can consider enabling it by default. Then, +# this file will become unnecessary and can be inlined into terminal.py. + +from __future__ import annotations + +import os + +from _pytest.config import Config +from _pytest.config import hookimpl +from _pytest.terminal import TerminalProgressPlugin +from _pytest.terminal import TerminalReporter + + +@hookimpl(trylast=True) +def pytest_configure(config: Config) -> None: + reporter: TerminalReporter | None = config.pluginmanager.get_plugin( + "terminalreporter" + ) + + if reporter is not None and reporter.isatty() and os.environ.get("TERM") != "dumb": + plugin = TerminalProgressPlugin(reporter) + config.pluginmanager.register(plugin, name="terminalprogress-plugin") diff --git a/.deps_siteforge_v812/_pytest/threadexception.py b/.deps_siteforge_v812/_pytest/threadexception.py new file mode 100644 index 0000000..eb57783 --- /dev/null +++ b/.deps_siteforge_v812/_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/.deps_siteforge_v812/_pytest/timing.py b/.deps_siteforge_v812/_pytest/timing.py new file mode 100644 index 0000000..51c3db2 --- /dev/null +++ b/.deps_siteforge_v812/_pytest/timing.py @@ -0,0 +1,95 @@ +"""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: + # pylint: disable-next=import-self + 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/.deps_siteforge_v812/_pytest/tmpdir.py b/.deps_siteforge_v812/_pytest/tmpdir.py new file mode 100644 index 0000000..855ad27 --- /dev/null +++ b/.deps_siteforge_v812/_pytest/tmpdir.py @@ -0,0 +1,315 @@ +# 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", + # NOTE: Would have been better as an `int` but can't change it now. + type="string", + ) + + 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)", + type="string", + 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/.deps_siteforge_v812/_pytest/tracemalloc.py b/.deps_siteforge_v812/_pytest/tracemalloc.py new file mode 100644 index 0000000..5d0b198 --- /dev/null +++ b/.deps_siteforge_v812/_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/.deps_siteforge_v812/_pytest/unittest.py b/.deps_siteforge_v812/_pytest/unittest.py new file mode 100644 index 0000000..23b9272 --- /dev/null +++ b/.deps_siteforge_v812/_pytest/unittest.py @@ -0,0 +1,628 @@ +# 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 Any +from typing import TYPE_CHECKING +from unittest import TestCase + +import _pytest._code +from _pytest._code import ExceptionInfo +from _pytest.compat import assert_never +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 +from _pytest.runner import check_interactive_exception +from _pytest.subtests import SubtestContext +from _pytest.subtests import SubtestReport + + +if sys.version_info[:2] < (3, 11): + from exceptiongroup import ExceptionGroup + +if TYPE_CHECKING: + from types import TracebackType + import unittest + + import twisted.trial.unittest + + +_SysExcInfoType = ( + 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 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 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 + failfast = False + _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() + if sys.version_info < (3, 11): + # A cache of the subTest errors and non-subtest skips in self._outcome. + # Compute and cache these lists once, instead of computing them again and again for each subtest (#13965). + self._cached_errors_and_skips: tuple[list[Any], list[Any]] | None = None + + 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, *, handle_subtests: bool = True + ) -> None: + from unittest.case import _SubTest # type: ignore[attr-defined] + + def add_skip() -> None: + try: + raise skip.Exception(reason, _use_item_location=True) + except skip.Exception: + self._addexcinfo(sys.exc_info()) + + if not handle_subtests: + add_skip() + return + + if isinstance(testcase, _SubTest): + add_skip() + if self._excinfo is not None: + exc_info = self._excinfo[-1] + self.addSubTest(testcase.test_case, testcase, exc_info) + else: + # For python < 3.11: the non-subtest skips have to be added by `add_skip` only after all subtest + # failures are processed by `_addSubTest`: `self.instance._outcome` has no attribute + # `skipped/errors` anymore. + # We also need to check if `self.instance._outcome` is `None` (this happens if the test + # class/method is decorated with `unittest.skip`, see pytest-dev/pytest-subtests#173). + if sys.version_info < (3, 11) and self.instance._outcome is not None: + subtest_errors, _ = self._obtain_errors_and_skips() + if len(subtest_errors) == 0: + add_skip() + else: + add_skip() + + 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 + + def addSubTest( + self, + test_case: Any, + test: TestCase, + exc_info: ExceptionInfo[BaseException] + | tuple[type[BaseException], BaseException, TracebackType] + | None, + ) -> None: + exception_info: ExceptionInfo[BaseException] | None + match exc_info: + case tuple(): + exception_info = ExceptionInfo(exc_info, _ispytest=True) + case ExceptionInfo() | None: + exception_info = exc_info + case unreachable: + assert_never(unreachable) + + call_info = CallInfo[None]( + None, + exception_info, + start=0, + stop=0, + duration=0, + when="call", + _ispytest=True, + ) + msg = test._message if isinstance(test._message, str) else None # type: ignore[attr-defined] + report = self.ihook.pytest_runtest_makereport(item=self, call=call_info) + sub_report = SubtestReport._new( + report, + SubtestContext(msg=msg, kwargs=dict(test.params)), # type: ignore[attr-defined] + captured_output=None, + captured_logs=None, + ) + self.ihook.pytest_runtest_logreport(report=sub_report) + if check_interactive_exception(call_info, sub_report): + self.ihook.pytest_exception_interact( + node=self, call=call_info, report=sub_report + ) + + # For python < 3.11: add non-subtest skips once all subtest failures are processed by # `_addSubTest`. + if sys.version_info < (3, 11): + subtest_errors, non_subtest_skip = self._obtain_errors_and_skips() + + # Check if we have non-subtest skips: if there are also sub failures, non-subtest skips are not treated in + # `_addSubTest` and have to be added using `add_skip` after all subtest failures are processed. + if len(non_subtest_skip) > 0 and len(subtest_errors) > 0: + # Make sure we have processed the last subtest failure + last_subset_error = subtest_errors[-1] + if exc_info is last_subset_error[-1]: + # Add non-subtest skips (as they could not be treated in `_addSkip`) + for testcase, reason in non_subtest_skip: + self.addSkip(testcase, reason, handle_subtests=False) + + def _obtain_errors_and_skips(self) -> tuple[list[Any], list[Any]]: + """Compute or obtain the cached values for subtest errors and non-subtest skips.""" + from unittest.case import _SubTest # type: ignore[attr-defined] + + assert sys.version_info < (3, 11), ( + "This workaround only should be used in Python 3.10" + ) + if self._cached_errors_and_skips is not None: + return self._cached_errors_and_skips + + subtest_errors = [ + (x, y) + for x, y in self.instance._outcome.errors + if isinstance(x, _SubTest) and y is not None + ] + + non_subtest_skips = [ + (x, y) + for x, y in self.instance._outcome.skipped + if not isinstance(x, _SubTest) + ] + self._cached_errors_and_skips = (subtest_errors, non_subtest_skips) + return subtest_errors, non_subtest_skips + + +@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 covers explicit `raise unittest.SkipTest`. + 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: 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/.deps_siteforge_v812/_pytest/unraisableexception.py b/.deps_siteforge_v812/_pytest/unraisableexception.py new file mode 100644 index 0000000..0faca36 --- /dev/null +++ b/.deps_siteforge_v812/_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/.deps_siteforge_v812/_pytest/warning_types.py b/.deps_siteforge_v812/_pytest/warning_types.py new file mode 100644 index 0000000..93071b4 --- /dev/null +++ b/.deps_siteforge_v812/_pytest/warning_types.py @@ -0,0 +1,172 @@ +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" + + +class PytestRemovedIn10Warning(PytestDeprecationWarning): + """Warning class for features that will be removed in pytest 10.""" + + __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/.deps_siteforge_v812/_pytest/warnings.py b/.deps_siteforge_v812/_pytest/warnings.py new file mode 100644 index 0000000..1dbf002 --- /dev/null +++ b/.deps_siteforge_v812/_pytest/warnings.py @@ -0,0 +1,151 @@ +# 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) + + 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/.deps_siteforge_v812/_yaml/__init__.py b/.deps_siteforge_v812/_yaml/__init__.py new file mode 100644 index 0000000..7baa8c4 --- /dev/null +++ b/.deps_siteforge_v812/_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/.deps_siteforge_v812/_yaml/__pycache__/__init__.cpython-312.pyc b/.deps_siteforge_v812/_yaml/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6fb5cf33b3f27c5b9de8a8ae8a28eb8c674ba203 GIT binary patch literal 861 zcmYjP&1(}u6rb7MB%80+w$vgjOiyW~NyVb2MTC-A6p|jKBBl$INp_lSyI;&sY?D*# zMUUS67nCA+^dIo#rG6ajh#)9PZ;^WPLbC`L*_kO?k-n^N2`FsY*_4DoV_89G;^rM9tbu~JKf0InMSS;i`f(9UKi&u z-gS5vyH2Z5JQw4&A{4ntltoV9W5?eLirR^4^du>_!OVJqyuj|c6#2xXHY#8xD@YN% zGC1DoQZ#ZPy2q)na-7kf2>HP-a)UPEQb#ya!g9$OsgL^54SSVS38d8p@bPT4$iyf9!KZLz>ZO;xP9f|?_w zz*)Fb3y7_x5sIyORH^p7Fm}aOvn&#$Mg=J5g`wmk)uEhl&V(NIBWB3qh^hK$GV1|f z3wpkNo3Vh2?A=itG0^E=TV(!ST$sCr`HtU-grO`kS*0PPazt_n58{w8F;=t2SY$B{ zNqdKMBzOr(EFat97?;DoFmJ|he2ZO>CDg}@9Jvj32;pfCz}znrnB!0R z(RAU(^rzIqR|9>UExkVfVRq?#=JK1({mOABe>7J3n3+2==6)ELz8F)Vjfwp`PwEHt kp{A#+aA<<`#oyUvA~_rb=?MvQQ~Rbo#wQN4Y(`f53q7d?%>V!Z literal 0 HcmV?d00001 diff --git a/.deps_siteforge_v812/annotated_doc-0.0.4.dist-info/INSTALLER b/.deps_siteforge_v812/annotated_doc-0.0.4.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/.deps_siteforge_v812/annotated_doc-0.0.4.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.deps_siteforge_v812/annotated_doc-0.0.4.dist-info/METADATA b/.deps_siteforge_v812/annotated_doc-0.0.4.dist-info/METADATA new file mode 100644 index 0000000..9bf7a9e --- /dev/null +++ b/.deps_siteforge_v812/annotated_doc-0.0.4.dist-info/METADATA @@ -0,0 +1,145 @@ +Metadata-Version: 2.4 +Name: annotated-doc +Version: 0.0.4 +Summary: Document parameters, class attributes, return types, and variables inline, with Annotated. +Author-Email: =?utf-8?q?Sebasti=C3=A1n_Ram=C3=ADrez?= +License-Expression: MIT +License-File: LICENSE +Classifier: Intended Audience :: Information Technology +Classifier: Intended Audience :: System Administrators +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python +Classifier: Topic :: Internet +Classifier: Topic :: Software Development :: Libraries :: Application Frameworks +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Classifier: Topic :: Software Development :: Libraries +Classifier: Topic :: Software Development +Classifier: Typing :: Typed +Classifier: Development Status :: 4 - Beta +Classifier: Intended Audience :: Developers +Classifier: Programming Language :: Python :: 3 :: Only +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 :: 3.12 +Classifier: Programming Language :: Python :: 3.13 +Classifier: Programming Language :: Python :: 3.14 +Project-URL: Homepage, https://github.com/fastapi/annotated-doc +Project-URL: Documentation, https://github.com/fastapi/annotated-doc +Project-URL: Repository, https://github.com/fastapi/annotated-doc +Project-URL: Issues, https://github.com/fastapi/annotated-doc/issues +Project-URL: Changelog, https://github.com/fastapi/annotated-doc/release-notes.md +Requires-Python: >=3.8 +Description-Content-Type: text/markdown + +# Annotated Doc + +Document parameters, class attributes, return types, and variables inline, with `Annotated`. + + + Test + + + Coverage + + + Package version + + + Supported Python versions + + +## Installation + +```bash +pip install annotated-doc +``` + +Or with `uv`: + +```Python +uv add annotated-doc +``` + +## Usage + +Import `Doc` and pass a single literal string with the documentation for the specific parameter, class attribute, return type, or variable. + +For example, to document a parameter `name` in a function `hi` you could do: + +```Python +from typing import Annotated + +from annotated_doc import Doc + +def hi(name: Annotated[str, Doc("Who to say hi to")]) -> None: + print(f"Hi, {name}!") +``` + +You can also use it to document class attributes: + +```Python +from typing import Annotated + +from annotated_doc import Doc + +class User: + name: Annotated[str, Doc("The user's name")] + age: Annotated[int, Doc("The user's age")] +``` + +The same way, you could document return types and variables, or anything that could have a type annotation with `Annotated`. + +## Who Uses This + +`annotated-doc` was made for: + +* [FastAPI](https://fastapi.tiangolo.com/) +* [Typer](https://typer.tiangolo.com/) +* [SQLModel](https://sqlmodel.tiangolo.com/) +* [Asyncer](https://asyncer.tiangolo.com/) + +`annotated-doc` is supported by [griffe-typingdoc](https://github.com/mkdocstrings/griffe-typingdoc), which powers reference documentation like the one in the [FastAPI Reference](https://fastapi.tiangolo.com/reference/). + +## Reasons not to use `annotated-doc` + +You are already comfortable with one of the existing docstring formats, like: + +* Sphinx +* numpydoc +* Google +* Keras + +Your team is already comfortable using them. + +You prefer having the documentation about parameters all together in a docstring, separated from the code defining them. + +You care about a specific set of users, using one specific editor, and that editor already has support for the specific docstring format you use. + +## Reasons to use `annotated-doc` + +* No micro-syntax to learn for newcomers, it’s **just Python** syntax. +* **Editing** would be already fully supported by default by any editor (current or future) supporting Python syntax, including syntax errors, syntax highlighting, etc. +* **Rendering** would be relatively straightforward to implement by static tools (tools that don't need runtime execution), as the information can be extracted from the AST they normally already create. +* **Deduplication of information**: the name of a parameter would be defined in a single place, not duplicated inside of a docstring. +* **Elimination** of the possibility of having **inconsistencies** when removing a parameter or class variable and **forgetting to remove** its documentation. +* **Minimization** of the probability of adding a new parameter or class variable and **forgetting to add its documentation**. +* **Elimination** of the possibility of having **inconsistencies** between the **name** of a parameter in the **signature** and the name in the docstring when it is renamed. +* **Access** to the documentation string for each symbol at **runtime**, including existing (older) Python versions. +* A more formalized way to document other symbols, like type aliases, that could use Annotated. +* **Support** for apps using FastAPI, Typer and others. +* **AI Accessibility**: AI tools will have an easier way understanding each parameter as the distance from documentation to parameter is much closer. + +## History + +I ([@tiangolo](https://github.com/tiangolo)) originally wanted for this to be part of the Python standard library (in [PEP 727](https://peps.python.org/pep-0727/)), but the proposal was withdrawn as there was a fair amount of negative feedback and opposition. + +The conclusion was that this was better done as an external effort, in a third-party library. + +So, here it is, with a simpler approach, as a third-party library, in a way that can be used by others, starting with FastAPI and friends. + +## License + +This project is licensed under the terms of the MIT license. diff --git a/.deps_siteforge_v812/annotated_doc-0.0.4.dist-info/RECORD b/.deps_siteforge_v812/annotated_doc-0.0.4.dist-info/RECORD new file mode 100644 index 0000000..fd33817 --- /dev/null +++ b/.deps_siteforge_v812/annotated_doc-0.0.4.dist-info/RECORD @@ -0,0 +1,12 @@ +annotated_doc-0.0.4.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +annotated_doc-0.0.4.dist-info/METADATA,sha256=Irm5KJua33dY2qKKAjJ-OhKaVBVIfwFGej_dSe3Z1TU,6566 +annotated_doc-0.0.4.dist-info/RECORD,, +annotated_doc-0.0.4.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +annotated_doc-0.0.4.dist-info/WHEEL,sha256=9P2ygRxDrTJz3gsagc0Z96ukrxjr-LFBGOgv3AuKlCA,90 +annotated_doc-0.0.4.dist-info/entry_points.txt,sha256=6OYgBcLyFCUgeqLgnvMyOJxPCWzgy7se4rLPKtNonMs,34 +annotated_doc-0.0.4.dist-info/licenses/LICENSE,sha256=__Fwd5pqy_ZavbQFwIfxzuF4ZpHkqWpANFF-SlBKDN8,1086 +annotated_doc/__init__.py,sha256=VuyxxUe80kfEyWnOrCx_Bk8hybo3aKo6RYBlkBBYW8k,52 +annotated_doc/__pycache__/__init__.cpython-312.pyc,, +annotated_doc/__pycache__/main.cpython-312.pyc,, +annotated_doc/main.py,sha256=5Zfvxv80SwwLqpRW73AZyZyiM4bWma9QWRbp_cgD20s,1075 +annotated_doc/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 diff --git a/.deps_siteforge_v812/annotated_doc-0.0.4.dist-info/REQUESTED b/.deps_siteforge_v812/annotated_doc-0.0.4.dist-info/REQUESTED new file mode 100644 index 0000000..e69de29 diff --git a/.deps_siteforge_v812/annotated_doc-0.0.4.dist-info/WHEEL b/.deps_siteforge_v812/annotated_doc-0.0.4.dist-info/WHEEL new file mode 100644 index 0000000..045c8ac --- /dev/null +++ b/.deps_siteforge_v812/annotated_doc-0.0.4.dist-info/WHEEL @@ -0,0 +1,4 @@ +Wheel-Version: 1.0 +Generator: pdm-backend (2.4.5) +Root-Is-Purelib: true +Tag: py3-none-any diff --git a/.deps_siteforge_v812/annotated_doc-0.0.4.dist-info/entry_points.txt b/.deps_siteforge_v812/annotated_doc-0.0.4.dist-info/entry_points.txt new file mode 100644 index 0000000..c3ad472 --- /dev/null +++ b/.deps_siteforge_v812/annotated_doc-0.0.4.dist-info/entry_points.txt @@ -0,0 +1,4 @@ +[console_scripts] + +[gui_scripts] + diff --git a/.deps_siteforge_v812/annotated_doc-0.0.4.dist-info/licenses/LICENSE b/.deps_siteforge_v812/annotated_doc-0.0.4.dist-info/licenses/LICENSE new file mode 100644 index 0000000..7a25446 --- /dev/null +++ b/.deps_siteforge_v812/annotated_doc-0.0.4.dist-info/licenses/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2025 Sebastián Ramírez + +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/.deps_siteforge_v812/annotated_doc/__init__.py b/.deps_siteforge_v812/annotated_doc/__init__.py new file mode 100644 index 0000000..a0152a7 --- /dev/null +++ b/.deps_siteforge_v812/annotated_doc/__init__.py @@ -0,0 +1,3 @@ +from .main import Doc as Doc + +__version__ = "0.0.4" diff --git a/.deps_siteforge_v812/annotated_doc/__pycache__/__init__.cpython-312.pyc b/.deps_siteforge_v812/annotated_doc/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..02e1f7e3c30191814aa0834f27c203f176257725 GIT binary patch literal 251 zcmX@j%ge<81h0?Y%rXJek3k$5V1hC}ivStZ8B!Qh7;_kM8KW2(8B&;{m@1hySza;% zr8F6DF}vg^SFsxC8R(h#X)@np$xY16D`E!9-QtdqFH0>d&dkq?k6+2~8D!+IC}*pf z&|;uyjIn{GrFo2FL4iwRNn(soesW??Oh{^OK}~Lu35UVQB{>`aZFBT zQcOW*Nk)ENOk!SMeo10UYD#=cezJaid}dx|NqoFsLFFwDo80`A(wtPgB2J)HAm(Z_#zsVS literal 0 HcmV?d00001 diff --git a/.deps_siteforge_v812/annotated_doc/__pycache__/main.cpython-312.pyc b/.deps_siteforge_v812/annotated_doc/__pycache__/main.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5e4aa9973c6c64e633e9a8cbb3d06fab0f3f26ae GIT binary patch literal 1899 zcmZux&2Jk;6rc639mh>EkhJ1MpmD0SHnqb?1SAovRFOiFkcE^Mq+qlj?~LP3*Sp)< zalj!+J>vj4ZkbRON?-Ne;2sa*N62%7TXDX(X2+2Oab5q|DWW(TO zBg2gilV^m*O>VUi03t%@5^0~fMDa7Vl;4lS# z*F@X(1yvnEdEoTA!dFbWflq@rWmNS;L7DFdqr+b0`kSuk{}KtFPI|m zw9Q-(!@_)F{>Ry(%`*s^BX2jpVCZQzNkX&P{HPCE^mPnv8FaB)7_r+81F7h^NE3rm zB?Y&^L29=aILV7#v}wmJ`%seA$?Z=8ewBhzdH$15fN6|aAMAjeuY6XawQIB<_<$;H zfe_NJ%I*1^Zk2wCk9}2|uV^hm)-*nUCcZeA)V}WE9E@f`QxoLVXLaZA8oR^>E?IqY zWYmugiQ`ep#8xD{wiY71PU*iNzq{JF9YJF?mR?&~dAo5v4A+=qjSr#iy~Zuk4I7~w z))bSQLe<{dTzX??=gp|$xvfU1v)VY5t5>_s^%ui_oVRV)ca?4L%s@&OQwb+J!pXl! z9=`O-*VeE37fwMUj)D7VVKh3Td0_To?sv&ns(Xxnli`_f&2>^S>pK?aJvJ&b4+aqL zG>{12q!PU$94SIMfs;rtq8VZWIL}&BAt%JSxuKUptw*d^~sg z=i-$?{)z?|X9JiEsoMg~^umhkBsPILgVVs@S+dd}YLruGuh%+(NOGw(p}WSDPVbZn zNn^9(f(!pW_;2`*kmx*qsvf8h(yd(0bYrfi&VI5uH+1m+!!Z8ITryVvBCzQT{{ha;+^7Hm literal 0 HcmV?d00001 diff --git a/.deps_siteforge_v812/annotated_doc/main.py b/.deps_siteforge_v812/annotated_doc/main.py new file mode 100644 index 0000000..7063c59 --- /dev/null +++ b/.deps_siteforge_v812/annotated_doc/main.py @@ -0,0 +1,36 @@ +class Doc: + """Define the documentation of a type annotation using `Annotated`, to be + used in class attributes, function and method parameters, return values, + and variables. + + The value should be a positional-only string literal to allow static tools + like editors and documentation generators to use it. + + This complements docstrings. + + The string value passed is available in the attribute `documentation`. + + Example: + + ```Python + from typing import Annotated + from annotated_doc import Doc + + def hi(name: Annotated[str, Doc("Who to say hi to")]) -> None: + print(f"Hi, {name}!") + ``` + """ + + def __init__(self, documentation: str, /) -> None: + self.documentation = documentation + + def __repr__(self) -> str: + return f"Doc({self.documentation!r})" + + def __hash__(self) -> int: + return hash(self.documentation) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, Doc): + return NotImplemented + return self.documentation == other.documentation diff --git a/.deps_siteforge_v812/annotated_doc/py.typed b/.deps_siteforge_v812/annotated_doc/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/.deps_siteforge_v812/annotated_types-0.8.0.dist-info/INSTALLER b/.deps_siteforge_v812/annotated_types-0.8.0.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/.deps_siteforge_v812/annotated_types-0.8.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.deps_siteforge_v812/annotated_types-0.8.0.dist-info/METADATA b/.deps_siteforge_v812/annotated_types-0.8.0.dist-info/METADATA new file mode 100644 index 0000000..eb5a088 --- /dev/null +++ b/.deps_siteforge_v812/annotated_types-0.8.0.dist-info/METADATA @@ -0,0 +1,295 @@ +Metadata-Version: 2.4 +Name: annotated-types +Version: 0.8.0 +Summary: Reusable constraint types to use with typing.Annotated +Project-URL: Homepage, https://github.com/annotated-types/annotated-types +Project-URL: Source, https://github.com/annotated-types/annotated-types +Project-URL: Changelog, https://github.com/annotated-types/annotated-types/releases +Author-email: Adrian Garcia Badaracco <1755071+adriangb@users.noreply.github.com>, Samuel Colvin , Zac Hatfield-Dodds +License-Expression: MIT +License-File: LICENSE +Classifier: Development Status :: 4 - Beta +Classifier: Environment :: Console +Classifier: Environment :: MacOS X +Classifier: Intended Audience :: Developers +Classifier: Intended Audience :: Information Technology +Classifier: License :: OSI Approved :: MIT License +Classifier: Operating System :: POSIX :: Linux +Classifier: Operating System :: Unix +Classifier: Programming Language :: Python :: 3 :: Only +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: Topic :: Software Development :: Libraries :: Python Modules +Classifier: Typing :: Typed +Requires-Python: >=3.10 +Description-Content-Type: text/markdown + +# annotated-types + +[![CI](https://github.com/annotated-types/annotated-types/workflows/CI/badge.svg?event=push)](https://github.com/annotated-types/annotated-types/actions?query=event%3Apush+branch%3Amain+workflow%3ACI) +[![pypi](https://img.shields.io/pypi/v/annotated-types.svg)](https://pypi.python.org/pypi/annotated-types) +[![versions](https://img.shields.io/pypi/pyversions/annotated-types.svg)](https://github.com/annotated-types/annotated-types) +[![license](https://img.shields.io/github/license/annotated-types/annotated-types.svg)](https://github.com/annotated-types/annotated-types/blob/main/LICENSE) + +[PEP-593](https://peps.python.org/pep-0593/) added `typing.Annotated` as a way of +adding context-specific metadata to existing types, and specifies that +`Annotated[T, x]` _should_ be treated as `T` by any tool or library without special +logic for `x`. + +This package provides metadata objects which can be used to represent common +constraints such as upper and lower bounds on scalar values and collection sizes, +a `Predicate` marker for runtime checks, and +descriptions of how we intend these metadata to be interpreted. In some cases, +we also note alternative representations which do not require this package. + +## Install + +```bash +pip install annotated-types +``` + +## Examples + +```python +from typing import Annotated +from annotated_types import Gt, Len, Predicate + +class MyClass: + age: Annotated[int, Gt(18)] # Valid: 19, 20, ... + # Invalid: 17, 18, "19", 19.0, ... + factors: list[Annotated[int, Predicate(is_prime)]] # Valid: 2, 3, 5, 7, 11, ... + # Invalid: 4, 8, -2, 5.0, "prime", ... + + my_list: Annotated[list[int], Len(0, 10)] # Valid: [], [10, 20, 30, 40, 50] + # Invalid: (1, 2), ["abc"], [0] * 20 +``` + +## Documentation + +_While `annotated-types` avoids runtime checks for performance, users should not +construct invalid combinations such as `MultipleOf("non-numeric")` or `Annotated[int, Len(3)]`. +Downstream implementors may choose to raise an error, emit a warning, silently ignore +a metadata item, etc., if the metadata objects described below are used with an +incompatible type - or for any other reason!_ + +### Gt, Ge, Lt, Le + +Express inclusive and/or exclusive bounds on orderable values - which may be numbers, +dates, times, strings, sets, etc. Note that the boundary value need not be of the +same type that was annotated, so long as they can be compared: `Annotated[int, Gt(1.5)]` +is fine, for example, and implies that the value is an integer x such that `x > 1.5`. + +We suggest that implementors may also interpret `functools.partial(operator.le, 1.5)` +as being equivalent to `Gt(1.5)`, for users who wish to avoid a runtime dependency on +the `annotated-types` package. + +To be explicit, these types have the following meanings: + +* `Gt(x)` - value must be "Greater Than" `x` - equivalent to exclusive minimum +* `Ge(x)` - value must be "Greater than or Equal" to `x` - equivalent to inclusive minimum +* `Lt(x)` - value must be "Less Than" `x` - equivalent to exclusive maximum +* `Le(x)` - value must be "Less than or Equal" to `x` - equivalent to inclusive maximum + +### Interval + +`Interval(gt, ge, lt, le)` allows you to specify an upper and lower bound with a single +metadata object. `None` attributes should be ignored, and non-`None` attributes +treated as per the single bounds above. + +### MultipleOf + +`MultipleOf(multiple_of=x)` might be interpreted in two ways: + +1. Python semantics, implying `value % multiple_of == 0`, or +2. [JSONschema semantics](https://json-schema.org/draft/2020-12/json-schema-validation.html#rfc.section.6.2.1), + where `int(value / multiple_of) == value / multiple_of`. + +We encourage users to be aware of these two common interpretations and their +distinct behaviours, especially since very large or non-integer numbers make +it easy to cause silent data corruption due to floating-point imprecision. + +We encourage libraries to carefully document which interpretation they implement. + +### MinLen, MaxLen, Len + +`Len()` implies that `min_length <= len(value) <= max_length` - lower and upper bounds are inclusive. + +As well as `Len()` which can optionally include upper and lower bounds, we also +provide `MinLen(x)` and `MaxLen(y)` which are equivalent to `Len(min_length=x)` +and `Len(max_length=y)` respectively. + +`Len`, `MinLen`, and `MaxLen` may be used with any type which supports `len(value)`. + +Examples of usage: + +* `Annotated[list, MaxLen(10)]` (or `Annotated[list, Len(max_length=10)]`) - list must have a length of 10 or less +* `Annotated[str, MaxLen(10)]` - string must have a length of 10 or less +* `Annotated[list, MinLen(3)]` (or `Annotated[list, Len(min_length=3)]`) - list must have a length of 3 or more +* `Annotated[list, Len(4, 6)]` - list must have a length of 4, 5, or 6 +* `Annotated[list, Len(8, 8)]` - list must have a length of exactly 8 + +#### Changed in v0.4.0 + +* `min_inclusive` has been renamed to `min_length`, no change in meaning +* `max_exclusive` has been renamed to `max_length`, upper bound is now **inclusive** instead of **exclusive** +* The recommendation that slices are interpreted as `Len` has been removed due to ambiguity and different semantic + meaning of the upper bound in slices vs. `Len` + +See [issue #23](https://github.com/annotated-types/annotated-types/issues/23) for discussion. + +### Timezone + +`Timezone` can be used with a `datetime` or a `time` to express which timezones +are allowed. `Annotated[datetime, Timezone(None)]` must be a naive datetime. +`Timezone[...]` ([literal ellipsis](https://docs.python.org/3/library/constants.html#Ellipsis)) +expresses that any timezone-aware datetime is allowed. You may also pass a specific +timezone string or [`tzinfo`](https://docs.python.org/3/library/datetime.html#tzinfo-objects) +object such as `Timezone(timezone.utc)` or `Timezone("Africa/Abidjan")` to express that you only +allow a specific timezone, though we note that this is often a symptom of fragile design. + +#### Changed in v0.x.x + +* `Timezone` accepts [`tzinfo`](https://docs.python.org/3/library/datetime.html#tzinfo-objects) objects instead of + `timezone`, extending compatibility to [`zoneinfo`](https://docs.python.org/3/library/zoneinfo.html) and third party libraries. + +### Unit + +`Unit(unit: str)` expresses that the annotated numeric value is the magnitude of +a quantity with the specified unit. For example, `Annotated[float, Unit("m/s")]` +would be a float representing a velocity in meters per second. + +Please note that `annotated_types` itself makes no attempt to parse or validate +the unit string in any way. That is left entirely to downstream libraries, +such as [`pint`](https://pint.readthedocs.io) or +[`astropy.units`](https://docs.astropy.org/en/stable/units/). + +An example of how a library might use this metadata: + +```python +from annotated_types import Unit +from typing import Annotated, TypeVar, Callable, Any, get_origin, get_args + +# given a type annotated with a unit: +Meters = Annotated[float, Unit("m")] + + +# you can cast the annotation to a specific unit type with any +# callable that accepts a string and returns the desired type +T = TypeVar("T") +def cast_unit(tp: Any, unit_cls: Callable[[str], T]) -> T | None: + if get_origin(tp) is Annotated: + for arg in get_args(tp): + if isinstance(arg, Unit): + return unit_cls(arg.unit) + return None + + +# using `pint` +import pint +pint_unit = cast_unit(Meters, pint.Unit) + + +# using `astropy.units` +import astropy.units as u +astropy_unit = cast_unit(Meters, u.Unit) +``` + +### Predicate + +`Predicate(func: Callable)` expresses that `func(value)` is truthy for valid values. +Users should prefer the statically inspectable metadata above, but if you need +the full power and flexibility of arbitrary runtime predicates... here it is. + +For some common constraints, we provide generic types: + +* `LowerCase = Annotated[T, Predicate(str.islower)]` +* `UpperCase = Annotated[T, Predicate(str.isupper)]` +* `IsDigit = Annotated[T, Predicate(str.isdigit)]` +* `IsFinite = Annotated[T, Predicate(math.isfinite)]` +* `IsNotFinite = Annotated[T, Predicate(Not(math.isfinite))]` +* `IsNan = Annotated[T, Predicate(math.isnan)]` +* `IsNotNan = Annotated[T, Predicate(Not(math.isnan))]` +* `IsInfinite = Annotated[T, Predicate(math.isinf)]` +* `IsNotInfinite = Annotated[T, Predicate(Not(math.isinf))]` + +so that you can write e.g. `x: IsFinite[float] = 2.0` instead of the longer +(but exactly equivalent) `x: Annotated[float, Predicate(math.isfinite)] = 2.0`. + +Some libraries might have special logic to handle known or understandable predicates, +for example by checking for `str.isdigit` and using its presence to both call custom +logic to enforce digit-only strings, and customise some generated external schema. +Users are therefore encouraged to avoid indirection like `lambda s: s.lower()`, in +favor of introspectable methods such as `str.lower` or `re.compile("pattern").search`. + +To enable basic negation of commonly used predicates like `math.isnan` without introducing introspection that makes it impossible for implementers to introspect the predicate we provide a `Not` wrapper that simply negates the predicate in an introspectable manner. Several of the predicates listed above are created in this manner. + +We do not specify what behaviour should be expected for predicates that raise +an exception. For example `Annotated[int, Predicate(str.isdigit)]` might silently +skip invalid constraints, or statically raise an error; or it might try calling it +and then propagate or discard the resulting +`TypeError: descriptor 'isdigit' for 'str' objects doesn't apply to a 'int' object` +exception. We encourage libraries to document the behaviour they choose. + +### Doc + +`doc()` can be used to add documentation information in `Annotated`, for function and method parameters, variables, class attributes, return types, and any place where `Annotated` can be used. + +It expects a value that can be statically analyzed, as the main use case is for static analysis, editors, documentation generators, and similar tools. + +It returns a `DocInfo` class with a single attribute `documentation` containing the value passed to `doc()`. + +This is the early adopter's alternative form of the [`typing-doc` proposal](https://github.com/tiangolo/fastapi/blob/typing-doc/typing_doc.md). + +### Integrating downstream types with `GroupedMetadata` + +Implementers may choose to provide a convenience wrapper that groups multiple pieces of metadata. +This can help reduce verbosity and cognitive overhead for users. +For example, an implementer like Pydantic might provide a `Field` or `Meta` type that accepts keyword arguments and transforms these into low-level metadata: + +```python +from dataclasses import dataclass +from typing import Iterator +from annotated_types import GroupedMetadata, Ge + +@dataclass +class Field(GroupedMetadata): + ge: int | None = None + description: str | None = None + + def __iter__(self) -> Iterator[object]: + # Iterating over a GroupedMetadata object should yield annotated-types + # constraint metadata objects which describe it as fully as possible, + # and may include other unknown objects too. + if self.ge is not None: + yield Ge(self.ge) + if self.description is not None: + yield Description(self.description) +``` + +Libraries consuming annotated-types constraints should check for `GroupedMetadata` and unpack it by iterating over the object and treating the results as if they had been "unpacked" in the `Annotated` type. The same logic should be applied to the [PEP 646 `Unpack` type](https://peps.python.org/pep-0646/), so that `Annotated[T, Field(...)]`, `Annotated[T, Unpack[Field(...)]]` and `Annotated[T, *Field(...)]` are all treated consistently. + +Libraries consuming annotated-types should also ignore any metadata they do not recongize that came from unpacking a `GroupedMetadata`, just like they ignore unrecognized metadata in `Annotated` itself. + +Our own `annotated_types.Interval` class is a `GroupedMetadata` which unpacks itself into `Gt`, `Lt`, etc., so this is not an abstract concern. Similarly, `annotated_types.Len` is a `GroupedMetadata` which unpacks itself into `MinLen` (optionally) and `MaxLen`. + +### Consuming metadata + +We intend to not be prescriptive as to _how_ the metadata and constraints are used, but as an example of how one might parse constraints from types annotations see our [implementation in `test_main.py`](https://github.com/annotated-types/annotated-types/blob/f59cf6d1b5255a0fe359b93896759a180bec30ae/tests/test_main.py#L94-L103). + +It is up to the implementer to determine how this metadata is used. +You could use the metadata for runtime type checking, for generating schemas or to generate example data, amongst other use cases. + +## Design & History + +This package was designed at the PyCon 2022 sprints by the maintainers of Pydantic +and Hypothesis, with the goal of making it as easy as possible for end-users to +provide more informative annotations for use by runtime libraries. + +It is deliberately minimal, and following PEP-593 allows considerable downstream +discretion in what (if anything!) they choose to support. Nonetheless, we expect +that staying simple and covering _only_ the most common use-cases will give users +and maintainers the best experience we can. If you'd like more constraints for your +types - follow our lead, by defining them and documenting them downstream! diff --git a/.deps_siteforge_v812/annotated_types-0.8.0.dist-info/RECORD b/.deps_siteforge_v812/annotated_types-0.8.0.dist-info/RECORD new file mode 100644 index 0000000..3e15b36 --- /dev/null +++ b/.deps_siteforge_v812/annotated_types-0.8.0.dist-info/RECORD @@ -0,0 +1,11 @@ +annotated_types-0.8.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +annotated_types-0.8.0.dist-info/METADATA,sha256=YUmFsnj2Abjvhj-CeDuZHMOtpLifWS63HHxIxfLvIpg,15009 +annotated_types-0.8.0.dist-info/RECORD,, +annotated_types-0.8.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +annotated_types-0.8.0.dist-info/WHEEL,sha256=lCkmxWfQsSc9CfIClYeavTdQeEX2toPqufh9gI35EQA,87 +annotated_types-0.8.0.dist-info/licenses/LICENSE,sha256=_hBJiEsaDZNCkB6I4H8ykl0ksxIdmXK2poBfuYJLCV0,1083 +annotated_types/__init__.py,sha256=pxBKTUObJ6n3T8C-I2ubobeDHmBEAmgCogWrwSmKm8g,13273 +annotated_types/__pycache__/__init__.cpython-312.pyc,, +annotated_types/__pycache__/test_cases.cpython-312.pyc,, +annotated_types/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +annotated_types/test_cases.py,sha256=2GdHKstXuBzpf3iXKCjmvFZpyk2zP9Hm9kXGGDhGBo4,6310 diff --git a/.deps_siteforge_v812/annotated_types-0.8.0.dist-info/REQUESTED b/.deps_siteforge_v812/annotated_types-0.8.0.dist-info/REQUESTED new file mode 100644 index 0000000..e69de29 diff --git a/.deps_siteforge_v812/annotated_types-0.8.0.dist-info/WHEEL b/.deps_siteforge_v812/annotated_types-0.8.0.dist-info/WHEEL new file mode 100644 index 0000000..7401812 --- /dev/null +++ b/.deps_siteforge_v812/annotated_types-0.8.0.dist-info/WHEEL @@ -0,0 +1,4 @@ +Wheel-Version: 1.0 +Generator: hatchling 1.31.0 +Root-Is-Purelib: true +Tag: py3-none-any diff --git a/.deps_siteforge_v812/annotated_types-0.8.0.dist-info/licenses/LICENSE b/.deps_siteforge_v812/annotated_types-0.8.0.dist-info/licenses/LICENSE new file mode 100644 index 0000000..d99323a --- /dev/null +++ b/.deps_siteforge_v812/annotated_types-0.8.0.dist-info/licenses/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2022 the 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/.deps_siteforge_v812/annotated_types/__init__.py b/.deps_siteforge_v812/annotated_types/__init__.py new file mode 100644 index 0000000..dcb35c5 --- /dev/null +++ b/.deps_siteforge_v812/annotated_types/__init__.py @@ -0,0 +1,416 @@ +import math +import types +from collections.abc import Callable, Iterator +from dataclasses import dataclass +from datetime import tzinfo +from types import EllipsisType +from typing import ( + TYPE_CHECKING, + Annotated, + Any, + Literal, + Protocol, + SupportsFloat, + SupportsIndex, + TypeVar, + Union, + runtime_checkable, +) + +__all__ = ( + 'BaseMetadata', + 'GroupedMetadata', + 'Gt', + 'Ge', + 'Lt', + 'Le', + 'Interval', + 'MultipleOf', + 'MinLen', + 'MaxLen', + 'Len', + 'Timezone', + 'Predicate', + 'LowerCase', + 'UpperCase', + 'IsDigits', + 'IsFinite', + 'IsNotFinite', + 'IsNan', + 'IsNotNan', + 'IsInfinite', + 'IsNotInfinite', + 'doc', + 'DocInfo', + '__version__', +) + +__version__ = '0.8.0' + + +T = TypeVar('T') + + +# arguments that start with __ are considered +# positional only +# see https://peps.python.org/pep-0484/#positional-only-arguments + + +class SupportsGt(Protocol): + def __gt__(self: T, __other: T) -> bool: + ... + + +class SupportsGe(Protocol): + def __ge__(self: T, __other: T) -> bool: + ... + + +class SupportsLt(Protocol): + def __lt__(self: T, __other: T) -> bool: + ... + + +class SupportsLe(Protocol): + def __le__(self: T, __other: T) -> bool: + ... + + +class SupportsMod(Protocol): + def __mod__(self: T, __other: T) -> T: + ... + + +class SupportsDiv(Protocol): + def __div__(self: T, __other: T) -> T: + ... + + +class BaseMetadata: + """Base class for all metadata. + + This exists mainly so that implementers + can do `isinstance(..., BaseMetadata)` while traversing field annotations. + """ + + __slots__ = () + + +@dataclass(frozen=True, slots=True) +class Gt(BaseMetadata): + """Gt(gt=x) implies that the value must be greater than x. + + It can be used with any type that supports the ``>`` operator, + including numbers, dates and times, strings, sets, and so on. + """ + + gt: SupportsGt + + +@dataclass(frozen=True, slots=True) +class Ge(BaseMetadata): + """Ge(ge=x) implies that the value must be greater than or equal to x. + + It can be used with any type that supports the ``>=`` operator, + including numbers, dates and times, strings, sets, and so on. + """ + + ge: SupportsGe + + +@dataclass(frozen=True, slots=True) +class Lt(BaseMetadata): + """Lt(lt=x) implies that the value must be less than x. + + It can be used with any type that supports the ``<`` operator, + including numbers, dates and times, strings, sets, and so on. + """ + + lt: SupportsLt + + +@dataclass(frozen=True, slots=True) +class Le(BaseMetadata): + """Le(le=x) implies that the value must be less than or equal to x. + + It can be used with any type that supports the ``<=`` operator, + including numbers, dates and times, strings, sets, and so on. + """ + + le: SupportsLe + + +@runtime_checkable +class GroupedMetadata(Protocol): + """A grouping of multiple objects, like typing.Unpack. + + `GroupedMetadata` on its own is not metadata and has no meaning. + All of the constraints and metadata should be fully expressable + in terms of the `BaseMetadata`'s returned by `GroupedMetadata.__iter__()`. + + Concrete implementations should override `GroupedMetadata.__iter__()` + to add their own metadata. + For example: + + >>> @dataclass + >>> class Field(GroupedMetadata): + >>> gt: float | None = None + >>> description: str | None = None + ... + >>> def __iter__(self) -> Iterable[object]: + >>> if self.gt is not None: + >>> yield Gt(self.gt) + >>> if self.description is not None: + >>> yield Description(self.gt) + + Also see the implementation of `Interval` below for an example. + + Parsers should recognize this and unpack it so that it can be used + both with and without unpacking: + + - `Annotated[int, Field(...)]` (parser must unpack Field) + - `Annotated[int, *Field(...)]` (PEP-646) + """ # noqa: trailing-whitespace + + @property + def __is_annotated_types_grouped_metadata__(self) -> Literal[True]: + return True + + def __iter__(self) -> Iterator[object]: + ... + + if not TYPE_CHECKING: + __slots__ = () # allow subclasses to use slots + + def __init_subclass__(cls, *args: Any, **kwargs: Any) -> None: + # Basic ABC like functionality without the complexity of an ABC + super().__init_subclass__(*args, **kwargs) + if cls.__iter__ is GroupedMetadata.__iter__: + raise TypeError("Can't subclass GroupedMetadata without implementing __iter__") + + def __iter__(self) -> Iterator[object]: # noqa: F811 + raise NotImplementedError # more helpful than "None has no attribute..." type errors + + +@dataclass(frozen=True, kw_only=True, slots=True) +class Interval(GroupedMetadata): + """Interval can express inclusive or exclusive bounds with a single object. + + It accepts keyword arguments ``gt``, ``ge``, ``lt``, and/or ``le``, which + are interpreted the same way as the single-bound constraints. + """ + + gt: SupportsGt | None = None + ge: SupportsGe | None = None + lt: SupportsLt | None = None + le: SupportsLe | None = None + + def __iter__(self) -> Iterator[BaseMetadata]: + """Unpack an Interval into zero or more single-bounds.""" + if self.gt is not None: + yield Gt(self.gt) + if self.ge is not None: + yield Ge(self.ge) + if self.lt is not None: + yield Lt(self.lt) + if self.le is not None: + yield Le(self.le) + + +@dataclass(frozen=True, slots=True) +class MultipleOf(BaseMetadata): + """MultipleOf(multiple_of=x) might be interpreted in two ways: + + 1. Python semantics, implying ``value % multiple_of == 0``, or + 2. JSONschema semantics, where ``int(value / multiple_of) == value / multiple_of`` + + We encourage users to be aware of these two common interpretations, + and libraries to carefully document which they implement. + """ + + multiple_of: SupportsDiv | SupportsMod + + +@dataclass(frozen=True, slots=True) +class MinLen(BaseMetadata): + """ + MinLen() implies minimum inclusive length, + e.g. ``len(value) >= min_length``. + """ + + min_length: Annotated[int, Ge(0)] + + +@dataclass(frozen=True, slots=True) +class MaxLen(BaseMetadata): + """ + MaxLen() implies maximum inclusive length, + e.g. ``len(value) <= max_length``. + """ + + max_length: Annotated[int, Ge(0)] + + +@dataclass(frozen=True, slots=True) +class Len(GroupedMetadata): + """ + Len() implies that ``min_length <= len(value) <= max_length``. + + Upper bound may be omitted or ``None`` to indicate no upper length bound. + """ + + min_length: Annotated[int, Ge(0)] = 0 + max_length: Annotated[int, Ge(0)] | None = None + + def __iter__(self) -> Iterator[BaseMetadata]: + """Unpack a Len into zero or more single-bounds.""" + if self.min_length > 0: + yield MinLen(self.min_length) + if self.max_length is not None: + yield MaxLen(self.max_length) + + +@dataclass(frozen=True, slots=True) +class Timezone(BaseMetadata): + """Timezone(tz=...) requires a datetime to be aware (or ``tz=None``, naive). + + ``Annotated[datetime, Timezone(None)]`` must be a naive datetime. + ``Timezone(...)`` (the ellipsis literal) expresses that the datetime must be + tz-aware but any timezone is allowed. + + You may also pass a specific timezone string or tzinfo object such as + ``Timezone(timezone.utc)`` or ``Timezone("Africa/Abidjan")`` to express that + you only allow a specific timezone, though we note that this is often + a symptom of poor design. + """ + + tz: str | tzinfo | EllipsisType | None + + +@dataclass(frozen=True, slots=True) +class Unit(BaseMetadata): + """Indicates that the value is a physical quantity with the specified unit. + + It is intended for usage with numeric types, where the value represents the + magnitude of the quantity. For example, ``distance: Annotated[float, Unit('m')]`` + or ``speed: Annotated[float, Unit('m/s')]``. + + Interpretation of the unit string is left to the discretion of the consumer. + It is suggested to follow conventions established by python libraries that work + with physical quantities, such as + + - ``pint`` : + - ``astropy.units``: + + For indicating a quantity with a certain dimensionality but without a specific unit + it is recommended to use square brackets, e.g. `Annotated[float, Unit('[time]')]`. + Note, however, ``annotated_types`` itself makes no use of the unit string. + """ + + unit: str + + +@dataclass(frozen=True, slots=True) +class Predicate(BaseMetadata): + """``Predicate(func: Callable)`` implies `func(value)` is truthy for valid values. + + Users should prefer statically inspectable metadata, but if you need the full + power and flexibility of arbitrary runtime predicates... here it is. + + We provide a few predefined predicates for common string constraints: + ``LowerCase = Predicate(str.islower)``, ``UpperCase = Predicate(str.isupper)``, and + ``IsDigits = Predicate(str.isdigit)``. Users are encouraged to use methods which + can be given special handling, and avoid indirection like ``lambda s: s.lower()``. + + Some libraries might have special logic to handle certain predicates, e.g. by + checking for `str.isdigit` and using its presence to both call custom logic to + enforce digit-only strings, and customise some generated external schema. + + We do not specify what behaviour should be expected for predicates that raise + an exception. For example `Annotated[int, Predicate(str.isdigit)]` might silently + skip invalid constraints, or statically raise an error; or it might try calling it + and then propagate or discard the resulting exception. + """ + + func: Callable[[Any], bool] + + def __repr__(self) -> str: + if getattr(self.func, "__name__", "") == "": + return f"{self.__class__.__name__}({self.func!r})" + if isinstance(self.func, (types.MethodType, types.BuiltinMethodType)) and ( + namespace := getattr(self.func.__self__, "__name__", None) + ): + return f"{self.__class__.__name__}({namespace}.{self.func.__name__})" + if isinstance(self.func, type(str.isascii)): # method descriptor + return f"{self.__class__.__name__}({self.func.__qualname__})" + return f"{self.__class__.__name__}({self.func.__name__})" + + +@dataclass +class Not: + func: Callable[[Any], bool] + + def __call__(self, __v: Any) -> bool: + return not self.func(__v) + + +_StrType = TypeVar("_StrType", bound=str) + +LowerCase = Annotated[_StrType, Predicate(str.islower)] +""" +Return True if the string is a lowercase string, False otherwise. + +A string is lowercase if all cased characters in the string are lowercase and there is at least one cased character in the string. +""" # noqa: E501 +UpperCase = Annotated[_StrType, Predicate(str.isupper)] +""" +Return True if the string is an uppercase string, False otherwise. + +A string is uppercase if all cased characters in the string are uppercase and there is at least one cased character in the string. +""" # noqa: E501 +IsDigit = Annotated[_StrType, Predicate(str.isdigit)] +IsDigits = IsDigit # type: ignore # plural for backwards compatibility, see #63 +""" +Return True if the string is a digit string, False otherwise. + +A string is a digit string if all characters in the string are digits and there is at least one character in the string. +""" # noqa: E501 +IsAscii = Annotated[_StrType, Predicate(str.isascii)] +""" +Return True if all characters in the string are ASCII, False otherwise. + +ASCII characters have code points in the range U+0000-U+007F. Empty string is ASCII too. +""" + +_NumericType = TypeVar('_NumericType', bound=Union[SupportsFloat, SupportsIndex]) +IsFinite = Annotated[_NumericType, Predicate(math.isfinite)] +"""Return True if x is neither an infinity nor a NaN, and False otherwise.""" +IsNotFinite = Annotated[_NumericType, Predicate(Not(math.isfinite))] +"""Return True if x is one of infinity or NaN, and False otherwise""" +IsNan = Annotated[_NumericType, Predicate(math.isnan)] +"""Return True if x is a NaN (not a number), and False otherwise.""" +IsNotNan = Annotated[_NumericType, Predicate(Not(math.isnan))] +"""Return True if x is anything but NaN (not a number), and False otherwise.""" +IsInfinite = Annotated[_NumericType, Predicate(math.isinf)] +"""Return True if x is a positive or negative infinity, and False otherwise.""" +IsNotInfinite = Annotated[_NumericType, Predicate(Not(math.isinf))] +"""Return True if x is neither a positive or negative infinity, and False otherwise.""" + +try: + # PEP 727 – Documentation in Annotated Metadata + from typing_extensions import Doc # type: ignore[attr-defined] +except ImportError: + + @dataclass(frozen=True, slots=True) + class Doc: # type: ignore [no-redef] + """ " + The return value of doc(), mainly to be used by tools that want to extract the + Annotated documentation at runtime. + """ + + documentation: str + """The documentation string passed to doc().""" + + +DocInfo = Doc # backwards compatibility +doc = Doc diff --git a/.deps_siteforge_v812/annotated_types/__pycache__/__init__.cpython-312.pyc b/.deps_siteforge_v812/annotated_types/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..03438ddb3eedf6c96c7214423a3c49b2a25421cd GIT binary patch literal 17790 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 literal 0 HcmV?d00001 diff --git a/.deps_siteforge_v812/annotated_types/__pycache__/test_cases.cpython-312.pyc b/.deps_siteforge_v812/annotated_types/__pycache__/test_cases.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fda4e5d70f5b41cef4260eaab426b4cbe08e963c GIT binary patch literal 13064 zcmc&*e@q)!o}aPDU=JY3!iA@|pztoi z5De)g%sAILNtec~acRw362EJmluKvUxhON`N-?LnQq8GSJ>}H93}%B=);VccnmNt2 z#k|FpZcdl#Nat3wQA3<2n3P)tlgg&w*UCK2+Za8oVYlVLt4+fgZc*lp>!gWZ#m!wN zji~S7S_=+B7SCiiub?y|y&-ia|MJ1j$y(=aaLX&t=3OR$8QWC*jS4|HPIvJ|UhG`NLL zVJVCm-nDV>6n?+1Go^@Bi`C0c7SjdrG!^iymoFHGngW}LE4X4+@L2fkW32Z|snkuR z!}Si2TTFY^>U1y`JjN@gJKRyPU8Yn~Z?U*R2#ZCeEf$xDnQ-Fr7K>$k!s-mSq*^SD zXV_xl(g2ibv{+Q~UZ};=E)Y*4CI0wI%kjZJFUxrcE6a`@J381jHr8t8t%IGOVXJen zmvxN|jyc9kcq?aT`H~uYWyR!VwRh0z7#bYA&X0K9gK;7q!4k{pOUJHrTY-s`U`O;8 zLh`4??=%{n{)wK*$oi9Wzs|^++V{>Lfy(>!_T5u0@0?tr3OI08d61aGvHS$*jxm-I zTKB=<)3V(JafTqlU_1%$(!*;aB`Q)%RZ63JrKIKzc|SbbroqN#G>lep)W0C#CCS9p zrNF{e_>HMkanyB8N)pt!DEKy}&ZjH=m{dm3=oQMbJmY#9lwNW5v{G-^#I;1|M%AY= zTbM{d+cnDA4K$XlOxV8Cv!P6}y;PZE>!W3gZBb=X>%TvlG9ofy zMvacCUo&T^GeYbf88ss$AyXoI>{P6v=4}-vFuEkLC$dKZ>QNd^ z&K@znqDKFJ*#qwu;vR-v+@;Ke&$X&2vUfuIO!i{V{qSBR%;5!QcM?`fWb;HEC`P}S z8^>V}la~aCM0QWWA>QeR{ad`s#DDpjTQ zEve(}y!7x9E8rdU^GRYSn1dgXkQRMFGI~47WKY7;)TV_R{HA9Nc8x8W9r@;H3S_j= zX}sj$8Ja|P-5ouyvWTh@F#JCpSk^kU+C*WdF|aM0676>4^}~%#k?lmopXLp>=8S-dO8aGF3^? zKcHw!ja#)8K98uU+C-8W)^LV6WvXX1cG3ZH%jg|a6|T8k>q=aNP; zq8TBWn!BV;!K7Z7IVuX@8lP0G_iGWcxucU30>y~^uj9{>>ne$$*A?U zUZJe!%-LIUb08q@lJ=74HsGUbwNa&|l=z5eNLD%zvuCr?HrlF`)I6%SO|4Z*8@Hwr z8?1{e%65gCn#ZDy;_}=G?HUGTOv0mIQn@Ro=RpDcz4K8x6X{7^}a9FM=j5hZiUgOTPL)@H~bBJpa;Qq8?XD&o>Q^ZPN4a4UhkF8b7NIkN^Hl z{QPGFjNc30!=s=e_xR-J4g^PuRs7G3Rf#tK`snx-uEQIU)qF@Mb>*ey6_t}IQdTZ% z;YB3NOgb6T9_Yg1|A}z8vs7o&NIe8BxR>K`5@QTs=BR;BjuhAQdvylCmnA1 z3XeSr%%B_bOmN=G^iva@XN*14>2Wh2H`G)3Bf3bsaB;%zP<+BJJNmXp(21tnHAz*K zR=7m1b!b>jv3Wd0Rt~sgVMVe@q?$b*Xl!b35vkTwZRbpstOu+w?kw)4Z5nD(25789 zB4sTfs+dd#N(B68`Q$#)o|Y>qlnkb3Mo+5mx;+>}`@?;h?hr^z^?@ zX8rVOLt@mJs*L(-tAtn6B~!#uxl52lLFESEeO;7q;m&$U-XxZ~moq-=lMqhon+n_$ zt|68ihNj4aoeV!uDon(?7-N~UEN_J?D^?Ew*EmLY%o-o)p5pL`zsN9()fx9Nq`FMXhmY{2uIXAsSaJnC z<3!(%fYl=GGNX#zQ7n{|1e}G$w{Uber^m{Rw8Lw0xOvvja*6HGl$-)eEfxn{>#Z^@hH%2*L~>aC!9|X$UF-S7AawaL{CTlQ-6Ec7NJAqzH8RWHyD@hom{;|Tz!mak z7#6zN;SWEJ_Sds;EZu70@UhTzSiWR~T~*YAp(H$_<}}Y)po+sQJcnl!4zDjz4+~c( z4}bV_6_}NS3q~S+HoPXCvx!IUQaes;t+fH?RQ@y;xq<@%Sp0 zsP7HmzT$AyiP{N%n1eMImZLtHKt)5R=QWmVfeTHdp>J#~TuklowmR(44234|u)`s# z5HNY$959L%DVLQW5w&hBpn!>6OzZG=dH8Un4ql)EdP5y9(H-6nw@n_IF4aa}wQw0q zG{%J^EDl(y(H^9e{D%88sK&8sccK9xhat;NJhfzrrYUvT^-VoIbUB1$1{G@26`TbMVr{;@% zRsIZrlTcVAuM8P?_D_*h<$__)%rzh7v-&fH{7R&+TGKM*FBI~tk-p|> zDwUSf7#Q3x*eFPS2ituM6o9e`VNqXBTF5bhh%nI;1a( zqO}FkCQvCdod{eK=u;u;)VextL+$+nJrJS>zIUCjgVMJPRkUt`@+4|_PL!IdX2Fm@ zpY7Z4*Z7--y+_eTKC!tjeJ}V2H*rq@`R&~^;)HA5Gvc^WAh)UpzdC)@*S(T=1mzqF z8q55}OSC|r2~lTci{mJhdiX0=@@h~{P0)BWuoE@)3G~Gfby0PTw7E2&*FU~ea17-f z3mT6H&Mu!2=u08$Qj}kunLe{v>u(M04Ri~K8$NGe?nOPl=;9@zwSOgd0A&sY_2%{Q zuip|SS(KBwrd+o6ccA}iJpt1a;j!$naTbFxKPu~~)sPZBjcp1H7L6@(fVFuakXlMlW zI|O<(M2*H$X`5^FmEY|^IVC}3=|?%Ab}pAM??dM=d{KnTdeFsw^ztAwUq)A~n2?1< zeKvu%hbX%`X~u}-8$>6%1o~WvIu|F7D)O2>2_^D32et?7fvvwVqRM_WXhAPyS%NP! z$Ucf}SCM-xXd6T3ae?MS6qi6KRXNW`N|j79gNhrKKtv4*^l*q8USGI=|4o5z3sG${ z9guCla%yBcZ!z=1&V`+R(qDc*7v)q3jWvPprSea516!9fm#wJ#!ZH}mj4W3WYeQF^ z!9EvicMG&9M0pfuJ=59q758f9YJ7Wr-FNH45!U1%_t$)S3{{-^Qu|9CI^VtQ4fS3@ zr>z1#6rzUY!8>NWvn^BCrt=pI9~@XXfLEvc#d7$eW1XlgY*ookm30Q9%SQ>hB`C8b zs4sn(9oWA_E|m-Ar%+kjm)YpTMbtk4?ysm+I?TDJVxd zR z%e_5$dva#q?H|nUm`x3tVD797QFZdLNQ=*a3MxWX7v+gnb$sWvGkCZ`$ZJG7jX`76 zXUBd4a|fQHa2M|F*KfZ*0}i}*wsp2cL)1yRm&$u_|AV51BDAkkX6`o&bYqBWl)Jrg`;D2tpmESd6T+oa*vGpL1X^BE@(WknDrogA=_VtifVsW_4C?KY6FMlJ%HrJAT*{p zFh8+4^uW2`{G>rBJ%bLO3GV3(8qcnr?|pQ>KX|@>d2;2l_0eTE2(CQHBKy@9*Z3pX zM9?*XUVc@eUkg#M$>ikiCZ1o+LSC6Xh5MyI4pazqeTb@mmZH%$lJkX+3af&Ja9F@^ z$|ABOSa4K`ER5Yt+CcjfzudEQ71kqxy6^`tJ{0`$)vQ-{lBP`V4R{fa!yYI6DuRcf zAb3mRzZ%l71myFLdV;{4dU=nZ%E&*mkPj6p@M{K#+s@%Li1eca4j<=5W87~83;#er` Iterable[Case]: + # Gt, Ge, Lt, Le + yield Case(Annotated[int, at.Gt(4)], (5, 6, 1000), (4, 0, -1)) + yield Case(Annotated[float, at.Gt(0.5)], (0.6, 0.7, 0.8, 0.9), (0.5, 0.0, -0.1)) + yield Case( + Annotated[datetime, at.Gt(datetime(2000, 1, 1))], + [datetime(2000, 1, 2), datetime(2000, 1, 3)], + [datetime(2000, 1, 1), datetime(1999, 12, 31)], + ) + yield Case( + Annotated[datetime, at.Gt(date(2000, 1, 1))], + [date(2000, 1, 2), date(2000, 1, 3)], + [date(2000, 1, 1), date(1999, 12, 31)], + ) + yield Case( + Annotated[datetime, at.Gt(Decimal('1.123'))], + [Decimal('1.1231'), Decimal('123')], + [Decimal('1.123'), Decimal('0')], + ) + + yield Case(Annotated[int, at.Ge(4)], (4, 5, 6, 1000, 4), (0, -1)) + yield Case(Annotated[float, at.Ge(0.5)], (0.5, 0.6, 0.7, 0.8, 0.9), (0.4, 0.0, -0.1)) + yield Case( + Annotated[datetime, at.Ge(datetime(2000, 1, 1))], + [datetime(2000, 1, 2), datetime(2000, 1, 3)], + [datetime(1998, 1, 1), datetime(1999, 12, 31)], + ) + + yield Case(Annotated[int, at.Lt(4)], (0, -1), (4, 5, 6, 1000, 4)) + yield Case(Annotated[float, at.Lt(0.5)], (0.4, 0.0, -0.1), (0.5, 0.6, 0.7, 0.8, 0.9)) + yield Case( + Annotated[datetime, at.Lt(datetime(2000, 1, 1))], + [datetime(1999, 12, 31), datetime(1999, 12, 31)], + [datetime(2000, 1, 2), datetime(2000, 1, 3)], + ) + + yield Case(Annotated[int, at.Le(4)], (4, 0, -1), (5, 6, 1000)) + yield Case(Annotated[float, at.Le(0.5)], (0.5, 0.0, -0.1), (0.6, 0.7, 0.8, 0.9)) + yield Case( + Annotated[datetime, at.Le(datetime(2000, 1, 1))], + [datetime(2000, 1, 1), datetime(1999, 12, 31)], + [datetime(2000, 1, 2), datetime(2000, 1, 3)], + ) + + # Interval + yield Case(Annotated[int, at.Interval(gt=4)], (5, 6, 1000), (4, 0, -1)) + yield Case(Annotated[int, at.Interval(gt=4, lt=10)], (5, 6), (4, 10, 1000, 0, -1)) + yield Case(Annotated[float, at.Interval(ge=0.5, le=1)], (0.5, 0.9, 1), (0.49, 1.1)) + yield Case( + Annotated[datetime, at.Interval(gt=datetime(2000, 1, 1), le=datetime(2000, 1, 3))], + [datetime(2000, 1, 2), datetime(2000, 1, 3)], + [datetime(2000, 1, 1), datetime(2000, 1, 4)], + ) + + yield Case(Annotated[int, at.MultipleOf(multiple_of=3)], (0, 3, 9), (1, 2, 4)) + yield Case(Annotated[float, at.MultipleOf(multiple_of=0.5)], (0, 0.5, 1, 1.5), (0.4, 1.1)) + + # lengths + + yield Case(Annotated[str, at.MinLen(3)], ('123', '1234', 'x' * 10), ('', '1', '12')) + yield Case(Annotated[str, at.Len(3)], ('123', '1234', 'x' * 10), ('', '1', '12')) + yield Case(Annotated[list[int], at.MinLen(3)], ([1, 2, 3], [1, 2, 3, 4], [1] * 10), ([], [1], [1, 2])) + yield Case(Annotated[list[int], at.Len(3)], ([1, 2, 3], [1, 2, 3, 4], [1] * 10), ([], [1], [1, 2])) + + yield Case(Annotated[str, at.MaxLen(4)], ('', '1234'), ('12345', 'x' * 10)) + yield Case(Annotated[str, at.Len(0, 4)], ('', '1234'), ('12345', 'x' * 10)) + yield Case(Annotated[list[str], at.MaxLen(4)], ([], ['a', 'bcdef'], ['a', 'b', 'c']), (['a'] * 5, ['b'] * 10)) + yield Case(Annotated[list[str], at.Len(0, 4)], ([], ['a', 'bcdef'], ['a', 'b', 'c']), (['a'] * 5, ['b'] * 10)) + + yield Case(Annotated[str, at.Len(3, 5)], ('123', '12345'), ('', '1', '12', '123456', 'x' * 10)) + yield Case(Annotated[str, at.Len(3, 3)], ('123',), ('12', '1234')) + + yield Case(Annotated[dict[int, int], at.Len(2, 3)], [{1: 1, 2: 2}], [{}, {1: 1}, {1: 1, 2: 2, 3: 3, 4: 4}]) + yield Case(Annotated[set[int], at.Len(2, 3)], ({1, 2}, {1, 2, 3}), (set(), {1}, {1, 2, 3, 4})) + yield Case(Annotated[tuple[int, ...], at.Len(2, 3)], ((1, 2), (1, 2, 3)), ((), (1,), (1, 2, 3, 4))) + + # Timezone + + yield Case( + Annotated[datetime, at.Timezone(None)], [datetime(2000, 1, 1)], [datetime(2000, 1, 1, tzinfo=timezone.utc)] + ) + yield Case( + Annotated[datetime, at.Timezone(...)], [datetime(2000, 1, 1, tzinfo=timezone.utc)], [datetime(2000, 1, 1)] + ) + yield Case( + Annotated[datetime, at.Timezone(timezone.utc)], + [datetime(2000, 1, 1, tzinfo=timezone.utc)], + [datetime(2000, 1, 1), datetime(2000, 1, 1, tzinfo=timezone(timedelta(hours=6)))], + ) + yield Case( + Annotated[datetime, at.Timezone('Europe/London')], + [datetime(2000, 1, 1, tzinfo=timezone(timedelta(0), name='Europe/London'))], + [datetime(2000, 1, 1), datetime(2000, 1, 1, tzinfo=timezone(timedelta(hours=6)))], + ) + + # Quantity + + yield Case(Annotated[float, at.Unit(unit='m')], (5, 4.2), ('5m', '4.2m')) + + # predicate types + + yield Case(at.LowerCase[str], ['abc', 'foobar'], ['', 'A', 'Boom']) + yield Case(at.UpperCase[str], ['ABC', 'DEFO'], ['', 'a', 'abc', 'AbC']) + yield Case(at.IsDigit[str], ['123'], ['', 'ab', 'a1b2']) + yield Case(at.IsAscii[str], ['123', 'foo bar'], ['£100', '😊', 'whatever 👀']) + + yield Case(Annotated[int, at.Predicate(lambda x: x % 2 == 0)], [0, 2, 4], [1, 3, 5]) + + yield Case(at.IsFinite[float], [1.23], [math.nan, math.inf, -math.inf]) + yield Case(at.IsNotFinite[float], [math.nan, math.inf], [1.23]) + yield Case(at.IsNan[float], [math.nan], [1.23, math.inf]) + yield Case(at.IsNotNan[float], [1.23, math.inf], [math.nan]) + yield Case(at.IsInfinite[float], [math.inf], [math.nan, 1.23]) + yield Case(at.IsNotInfinite[float], [math.nan, 1.23], [math.inf]) + + # check stacked predicates + yield Case(at.IsInfinite[Annotated[float, at.Predicate(lambda x: x > 0)]], [math.inf], [-math.inf, 1.23, math.nan]) + + # doc + yield Case(Annotated[int, at.doc("A number")], [1, 2], []) + + # custom GroupedMetadata + class MyCustomGroupedMetadata(at.GroupedMetadata): + def __iter__(self) -> Iterator[at.Predicate]: + yield at.Predicate(lambda x: float(x).is_integer()) + + yield Case(Annotated[float, MyCustomGroupedMetadata()], [0, 2.0], [0.01, 1.5]) diff --git a/.deps_siteforge_v812/anyio-4.13.0.dist-info/INSTALLER b/.deps_siteforge_v812/anyio-4.13.0.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/.deps_siteforge_v812/anyio-4.13.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.deps_siteforge_v812/anyio-4.13.0.dist-info/METADATA b/.deps_siteforge_v812/anyio-4.13.0.dist-info/METADATA new file mode 100644 index 0000000..2de1cd5 --- /dev/null +++ b/.deps_siteforge_v812/anyio-4.13.0.dist-info/METADATA @@ -0,0 +1,105 @@ +Metadata-Version: 2.4 +Name: anyio +Version: 4.13.0 +Summary: High-level concurrency and networking framework on top of asyncio or Trio +Author-email: Alex Grönholm +License-Expression: MIT +Project-URL: Documentation, https://anyio.readthedocs.io/en/latest/ +Project-URL: Changelog, https://anyio.readthedocs.io/en/stable/versionhistory.html +Project-URL: Source code, https://github.com/agronholm/anyio +Project-URL: Issue tracker, https://github.com/agronholm/anyio/issues +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: Framework :: AnyIO +Classifier: Typing :: Typed +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +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 +Requires-Python: >=3.10 +Description-Content-Type: text/x-rst +License-File: LICENSE +Requires-Dist: exceptiongroup>=1.0.2; python_version < "3.11" +Requires-Dist: idna>=2.8 +Requires-Dist: typing_extensions>=4.5; python_version < "3.13" +Provides-Extra: trio +Requires-Dist: trio>=0.32.0; extra == "trio" +Dynamic: license-file + +.. image:: https://github.com/agronholm/anyio/actions/workflows/test.yml/badge.svg + :target: https://github.com/agronholm/anyio/actions/workflows/test.yml + :alt: Build Status +.. image:: https://coveralls.io/repos/github/agronholm/anyio/badge.svg?branch=master + :target: https://coveralls.io/github/agronholm/anyio?branch=master + :alt: Code Coverage +.. image:: https://readthedocs.org/projects/anyio/badge/?version=latest + :target: https://anyio.readthedocs.io/en/latest/?badge=latest + :alt: Documentation +.. image:: https://badges.gitter.im/gitterHQ/gitter.svg + :target: https://gitter.im/python-trio/AnyIO + :alt: Gitter chat +.. image:: https://tidelift.com/badges/package/pypi/anyio + :target: https://tidelift.com/subscription/pkg/pypi-anyio + :alt: Tidelift + +AnyIO is an asynchronous networking and concurrency library that works on top of either asyncio_ or +Trio_. It implements Trio-like `structured concurrency`_ (SC) on top of asyncio and works in harmony +with the native SC of Trio itself. + +Applications and libraries written against AnyIO's API will run unmodified on either asyncio_ or +Trio_. AnyIO can also be adopted into a library or application incrementally – bit by bit, no full +refactoring necessary. It will blend in with the native libraries of your chosen backend. + +To find out why you might want to use AnyIO's APIs instead of asyncio's, you can read about it +`here `_. + +Documentation +------------- + +View full documentation at: https://anyio.readthedocs.io/ + +Features +-------- + +AnyIO offers the following functionality: + +* Task groups (nurseries_ in trio terminology) +* High-level networking (TCP, UDP and UNIX sockets) + + * `Happy eyeballs`_ algorithm for TCP connections (more robust than that of asyncio on Python + 3.8) + * async/await style UDP sockets (unlike asyncio where you still have to use Transports and + Protocols) + +* A versatile API for byte streams and object streams +* Inter-task synchronization and communication (locks, conditions, events, semaphores, object + streams) +* Worker threads +* Subprocesses +* Subinterpreter support for code parallelization (on Python 3.13 and later) +* Asynchronous file I/O (using worker threads) +* Signal handling +* Asynchronous version of the functools_ module + +AnyIO also comes with its own pytest_ plugin which also supports asynchronous fixtures. +It even works with the popular Hypothesis_ library. + +.. _asyncio: https://docs.python.org/3/library/asyncio.html +.. _Trio: https://github.com/python-trio/trio +.. _structured concurrency: https://en.wikipedia.org/wiki/Structured_concurrency +.. _nurseries: https://trio.readthedocs.io/en/stable/reference-core.html#nurseries-and-spawning +.. _Happy eyeballs: https://en.wikipedia.org/wiki/Happy_Eyeballs +.. _pytest: https://docs.pytest.org/en/latest/ +.. _functools: https://docs.python.org/3/library/functools.html +.. _Hypothesis: https://hypothesis.works/ + +Security contact information +---------------------------- + +To report a security vulnerability, please use the `Tidelift security contact`_. +Tidelift will coordinate the fix and disclosure. + +.. _Tidelift security contact: https://tidelift.com/security diff --git a/.deps_siteforge_v812/anyio-4.13.0.dist-info/RECORD b/.deps_siteforge_v812/anyio-4.13.0.dist-info/RECORD new file mode 100644 index 0000000..a460a27 --- /dev/null +++ b/.deps_siteforge_v812/anyio-4.13.0.dist-info/RECORD @@ -0,0 +1,93 @@ +anyio-4.13.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +anyio-4.13.0.dist-info/METADATA,sha256=F0EYfiPlmTRwmJN2JktNxJg1GNnl0wHhzOWmz7pFvjM,4513 +anyio-4.13.0.dist-info/RECORD,, +anyio-4.13.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +anyio-4.13.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91 +anyio-4.13.0.dist-info/entry_points.txt,sha256=_d6Yu6uiaZmNe0CydowirE9Cmg7zUL2g08tQpoS3Qvc,39 +anyio-4.13.0.dist-info/licenses/LICENSE,sha256=U2GsncWPLvX9LpsJxoKXwX8ElQkJu8gCO9uC6s8iwrA,1081 +anyio-4.13.0.dist-info/top_level.txt,sha256=QglSMiWX8_5dpoVAEIHdEYzvqFMdSYWmCj6tYw2ITkQ,6 +anyio/__init__.py,sha256=7iDVqMUprUuKNY91FuoKqayAhR-OY136YDPI6P78HHk,6170 +anyio/__pycache__/__init__.cpython-312.pyc,, +anyio/__pycache__/from_thread.cpython-312.pyc,, +anyio/__pycache__/functools.cpython-312.pyc,, +anyio/__pycache__/lowlevel.cpython-312.pyc,, +anyio/__pycache__/pytest_plugin.cpython-312.pyc,, +anyio/__pycache__/to_interpreter.cpython-312.pyc,, +anyio/__pycache__/to_process.cpython-312.pyc,, +anyio/__pycache__/to_thread.cpython-312.pyc,, +anyio/_backends/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +anyio/_backends/__pycache__/__init__.cpython-312.pyc,, +anyio/_backends/__pycache__/_asyncio.cpython-312.pyc,, +anyio/_backends/__pycache__/_trio.cpython-312.pyc,, +anyio/_backends/_asyncio.py,sha256=kuqlg2sBUsFdgY80xSDAw60Gx_4WNCl9iSL5XlY6lCU,99476 +anyio/_backends/_trio.py,sha256=l9U-TsKRxzmTQxSMvOhn0bNeFn_iRx3Ho30jvR5Bdu0,41366 +anyio/_core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +anyio/_core/__pycache__/__init__.cpython-312.pyc,, +anyio/_core/__pycache__/_asyncio_selector_thread.cpython-312.pyc,, +anyio/_core/__pycache__/_contextmanagers.cpython-312.pyc,, +anyio/_core/__pycache__/_eventloop.cpython-312.pyc,, +anyio/_core/__pycache__/_exceptions.cpython-312.pyc,, +anyio/_core/__pycache__/_fileio.cpython-312.pyc,, +anyio/_core/__pycache__/_resources.cpython-312.pyc,, +anyio/_core/__pycache__/_signals.cpython-312.pyc,, +anyio/_core/__pycache__/_sockets.cpython-312.pyc,, +anyio/_core/__pycache__/_streams.cpython-312.pyc,, +anyio/_core/__pycache__/_subprocesses.cpython-312.pyc,, +anyio/_core/__pycache__/_synchronization.cpython-312.pyc,, +anyio/_core/__pycache__/_tasks.cpython-312.pyc,, +anyio/_core/__pycache__/_tempfile.cpython-312.pyc,, +anyio/_core/__pycache__/_testing.cpython-312.pyc,, +anyio/_core/__pycache__/_typedattr.cpython-312.pyc,, +anyio/_core/_asyncio_selector_thread.py,sha256=2PdxFM3cs02Kp6BSppbvmRT7q7asreTW5FgBxEsflBo,5626 +anyio/_core/_contextmanagers.py,sha256=YInBCabiEeS-UaP_Jdxa1CaFC71ETPW8HZTHIM8Rsc8,7215 +anyio/_core/_eventloop.py,sha256=c2EdcBX-xnKwxPcC4Pjn3_qG9I-x4IWFO2R9RqCGjM4,6448 +anyio/_core/_exceptions.py,sha256=Y3aq-Wxd7Q2HqwSg7nZPvRsHEuGazv_qeet6gqEBdPk,4407 +anyio/_core/_fileio.py,sha256=CKi1gFNiW2G4knWeBE7He7-rptQwgYjDUWfG8DSlvLs,25665 +anyio/_core/_resources.py,sha256=NbmU5O5UX3xEyACnkmYX28Fmwdl-f-ny0tHym26e0w0,435 +anyio/_core/_signals.py,sha256=mjTBB2hTKNPRlU0IhnijeQedpWOGERDiMjSlJQsFrug,1016 +anyio/_core/_sockets.py,sha256=RBXHcUqZt5gg_-OOfgHVv8uq2FSKk1uVUzTdpjBoI1o,34977 +anyio/_core/_streams.py,sha256=FczFwIgDpnkK0bODWJXMpsUJYdvAD04kaUaGzJU8DK0,1806 +anyio/_core/_subprocesses.py,sha256=tkmkPKEkEaiMD8C9WRZBlmgjOYRDRbZdte6e-unay2E,7916 +anyio/_core/_synchronization.py,sha256=9G3fvRsPNrrWJ_Z6gD_80wXq8I8qgAyhwM8PvHQnT2c,21061 +anyio/_core/_tasks.py,sha256=pVB7K6AAulzUM8YgXAeqNZG44nSyZ1bYJjH8GznC00I,5435 +anyio/_core/_tempfile.py,sha256=jE2w59FRF3yRo4vjkjfZF2YcqsBZvc66VWRwrJGDYGk,19624 +anyio/_core/_testing.py,sha256=u7MPqGXwpTxqI7hclSdNA30z2GH1Nw258uwKvy_RfBg,2340 +anyio/_core/_typedattr.py,sha256=P4ozZikn3-DbpoYcvyghS_FOYAgbmUxeoU8-L_07pZM,2508 +anyio/abc/__init__.py,sha256=6mWhcl_pGXhrgZVHP_TCfMvIXIOp9mroEFM90fYCU_U,2869 +anyio/abc/__pycache__/__init__.cpython-312.pyc,, +anyio/abc/__pycache__/_eventloop.cpython-312.pyc,, +anyio/abc/__pycache__/_resources.cpython-312.pyc,, +anyio/abc/__pycache__/_sockets.cpython-312.pyc,, +anyio/abc/__pycache__/_streams.cpython-312.pyc,, +anyio/abc/__pycache__/_subprocesses.cpython-312.pyc,, +anyio/abc/__pycache__/_tasks.cpython-312.pyc,, +anyio/abc/__pycache__/_testing.cpython-312.pyc,, +anyio/abc/_eventloop.py,sha256=39lYnmtvoHaZw22sWBKOTA_zv7bamOnr8O49PqgDXdw,10629 +anyio/abc/_resources.py,sha256=DrYvkNN1hH6Uvv5_5uKySvDsnknGVDe8FCKfko0VtN8,783 +anyio/abc/_sockets.py,sha256=OmVDrfemVvF9c5K1tpBgQyV6fn5v0XyCExLAqBOGz9o,13124 +anyio/abc/_streams.py,sha256=HYvna1iZbWcwLROTO6IhLX79RTRLPShZMWe0sG1q54I,7481 +anyio/abc/_subprocesses.py,sha256=cumAPJTktOQtw63IqG0lDpyZqu_l1EElvQHMiwJgL08,2067 +anyio/abc/_tasks.py,sha256=KC7wrciE48AINOI-AhPutnFhe1ewfP7QnamFlDzqesQ,3721 +anyio/abc/_testing.py,sha256=tBJUzkSfOXJw23fe8qSJ03kJlShOYjjaEyFB6k6MYT8,1821 +anyio/from_thread.py,sha256=L-0w1HxJ6BSb-KuVi57k5Tkc3yzQrx3QK5tAxMPcY-0,19141 +anyio/functools.py,sha256=5AWM1iYTKkTzptvUhQDdLSh5GvbBW-vcs-SAUfIfA9A,12076 +anyio/lowlevel.py,sha256=AyKLVK3LaWSoK39LkCKxE4_GDMLKZBNqTrLUgk63y80,5158 +anyio/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +anyio/pytest_plugin.py,sha256=t6h4KJstqIxfxwTZ1YO8vpUVuB99nfCLltn0NHfatHo,12775 +anyio/streams/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +anyio/streams/__pycache__/__init__.cpython-312.pyc,, +anyio/streams/__pycache__/buffered.cpython-312.pyc,, +anyio/streams/__pycache__/file.cpython-312.pyc,, +anyio/streams/__pycache__/memory.cpython-312.pyc,, +anyio/streams/__pycache__/stapled.cpython-312.pyc,, +anyio/streams/__pycache__/text.cpython-312.pyc,, +anyio/streams/__pycache__/tls.cpython-312.pyc,, +anyio/streams/buffered.py,sha256=2R3PeJhe4EXrdYqz44Y6-Eg9R6DrmlsYrP36Ir43-po,6263 +anyio/streams/file.py,sha256=msnrotVKGMQomUu_Rj2qz9MvIdUp6d3JGr7MOEO8kV4,4428 +anyio/streams/memory.py,sha256=F0zwzvFJKAhX_LRZGoKzzqDC2oMM-f-yyTBrEYEGOaU,10740 +anyio/streams/stapled.py,sha256=T8Xqwf8K6EgURPxbt1N4i7A8BAk-gScv-GRhjLXIf_o,4390 +anyio/streams/text.py,sha256=BcVAGJw1VRvtIqnv-o0Rb0pwH7p8vwlvl21xHq522ag,5765 +anyio/streams/tls.py,sha256=DQVkXUvsTEYKkBO8dlVU7j_5H8QOtLy4sGi1Wrjqevo,15303 +anyio/to_interpreter.py,sha256=_mLngrMy97TMR6VbW4Y6YzDUk9ZuPcQMPlkuyRh3C9k,7100 +anyio/to_process.py,sha256=J7gAA_YOuoHqnpDAf5fm1Qu6kOmTzdFbiDNvnV755vk,9798 +anyio/to_thread.py,sha256=f6h_k2d743GBv9FhAnhM_YpTvWgIrzBy9cOE0eJ1UJw,2693 diff --git a/.deps_siteforge_v812/anyio-4.13.0.dist-info/REQUESTED b/.deps_siteforge_v812/anyio-4.13.0.dist-info/REQUESTED new file mode 100644 index 0000000..e69de29 diff --git a/.deps_siteforge_v812/anyio-4.13.0.dist-info/WHEEL b/.deps_siteforge_v812/anyio-4.13.0.dist-info/WHEEL new file mode 100644 index 0000000..14a883f --- /dev/null +++ b/.deps_siteforge_v812/anyio-4.13.0.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: setuptools (82.0.1) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/.deps_siteforge_v812/anyio-4.13.0.dist-info/entry_points.txt b/.deps_siteforge_v812/anyio-4.13.0.dist-info/entry_points.txt new file mode 100644 index 0000000..44dd9bd --- /dev/null +++ b/.deps_siteforge_v812/anyio-4.13.0.dist-info/entry_points.txt @@ -0,0 +1,2 @@ +[pytest11] +anyio = anyio.pytest_plugin diff --git a/.deps_siteforge_v812/anyio-4.13.0.dist-info/licenses/LICENSE b/.deps_siteforge_v812/anyio-4.13.0.dist-info/licenses/LICENSE new file mode 100644 index 0000000..104eebf --- /dev/null +++ b/.deps_siteforge_v812/anyio-4.13.0.dist-info/licenses/LICENSE @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2018 Alex Grönholm + +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/.deps_siteforge_v812/anyio-4.13.0.dist-info/top_level.txt b/.deps_siteforge_v812/anyio-4.13.0.dist-info/top_level.txt new file mode 100644 index 0000000..c77c069 --- /dev/null +++ b/.deps_siteforge_v812/anyio-4.13.0.dist-info/top_level.txt @@ -0,0 +1 @@ +anyio diff --git a/.deps_siteforge_v812/anyio/__init__.py b/.deps_siteforge_v812/anyio/__init__.py new file mode 100644 index 0000000..d23c5a5 --- /dev/null +++ b/.deps_siteforge_v812/anyio/__init__.py @@ -0,0 +1,111 @@ +from __future__ import annotations + +from ._core._contextmanagers import AsyncContextManagerMixin as AsyncContextManagerMixin +from ._core._contextmanagers import ContextManagerMixin as ContextManagerMixin +from ._core._eventloop import current_time as current_time +from ._core._eventloop import get_all_backends as get_all_backends +from ._core._eventloop import get_available_backends as get_available_backends +from ._core._eventloop import get_cancelled_exc_class as get_cancelled_exc_class +from ._core._eventloop import run as run +from ._core._eventloop import sleep as sleep +from ._core._eventloop import sleep_forever as sleep_forever +from ._core._eventloop import sleep_until as sleep_until +from ._core._exceptions import BrokenResourceError as BrokenResourceError +from ._core._exceptions import BrokenWorkerInterpreter as BrokenWorkerInterpreter +from ._core._exceptions import BrokenWorkerProcess as BrokenWorkerProcess +from ._core._exceptions import BusyResourceError as BusyResourceError +from ._core._exceptions import ClosedResourceError as ClosedResourceError +from ._core._exceptions import ConnectionFailed as ConnectionFailed +from ._core._exceptions import DelimiterNotFound as DelimiterNotFound +from ._core._exceptions import EndOfStream as EndOfStream +from ._core._exceptions import IncompleteRead as IncompleteRead +from ._core._exceptions import NoEventLoopError as NoEventLoopError +from ._core._exceptions import RunFinishedError as RunFinishedError +from ._core._exceptions import TypedAttributeLookupError as TypedAttributeLookupError +from ._core._exceptions import WouldBlock as WouldBlock +from ._core._fileio import AsyncFile as AsyncFile +from ._core._fileio import Path as Path +from ._core._fileio import open_file as open_file +from ._core._fileio import wrap_file as wrap_file +from ._core._resources import aclose_forcefully as aclose_forcefully +from ._core._signals import open_signal_receiver as open_signal_receiver +from ._core._sockets import TCPConnectable as TCPConnectable +from ._core._sockets import UNIXConnectable as UNIXConnectable +from ._core._sockets import as_connectable as as_connectable +from ._core._sockets import connect_tcp as connect_tcp +from ._core._sockets import connect_unix as connect_unix +from ._core._sockets import create_connected_udp_socket as create_connected_udp_socket +from ._core._sockets import ( + create_connected_unix_datagram_socket as create_connected_unix_datagram_socket, +) +from ._core._sockets import create_tcp_listener as create_tcp_listener +from ._core._sockets import create_udp_socket as create_udp_socket +from ._core._sockets import create_unix_datagram_socket as create_unix_datagram_socket +from ._core._sockets import create_unix_listener as create_unix_listener +from ._core._sockets import getaddrinfo as getaddrinfo +from ._core._sockets import getnameinfo as getnameinfo +from ._core._sockets import notify_closing as notify_closing +from ._core._sockets import wait_readable as wait_readable +from ._core._sockets import wait_socket_readable as wait_socket_readable +from ._core._sockets import wait_socket_writable as wait_socket_writable +from ._core._sockets import wait_writable as wait_writable +from ._core._streams import create_memory_object_stream as create_memory_object_stream +from ._core._subprocesses import open_process as open_process +from ._core._subprocesses import run_process as run_process +from ._core._synchronization import CapacityLimiter as CapacityLimiter +from ._core._synchronization import ( + CapacityLimiterStatistics as CapacityLimiterStatistics, +) +from ._core._synchronization import Condition as Condition +from ._core._synchronization import ConditionStatistics as ConditionStatistics +from ._core._synchronization import Event as Event +from ._core._synchronization import EventStatistics as EventStatistics +from ._core._synchronization import Lock as Lock +from ._core._synchronization import LockStatistics as LockStatistics +from ._core._synchronization import ResourceGuard as ResourceGuard +from ._core._synchronization import Semaphore as Semaphore +from ._core._synchronization import SemaphoreStatistics as SemaphoreStatistics +from ._core._tasks import TASK_STATUS_IGNORED as TASK_STATUS_IGNORED +from ._core._tasks import CancelScope as CancelScope +from ._core._tasks import create_task_group as create_task_group +from ._core._tasks import current_effective_deadline as current_effective_deadline +from ._core._tasks import fail_after as fail_after +from ._core._tasks import move_on_after as move_on_after +from ._core._tempfile import NamedTemporaryFile as NamedTemporaryFile +from ._core._tempfile import SpooledTemporaryFile as SpooledTemporaryFile +from ._core._tempfile import TemporaryDirectory as TemporaryDirectory +from ._core._tempfile import TemporaryFile as TemporaryFile +from ._core._tempfile import gettempdir as gettempdir +from ._core._tempfile import gettempdirb as gettempdirb +from ._core._tempfile import mkdtemp as mkdtemp +from ._core._tempfile import mkstemp as mkstemp +from ._core._testing import TaskInfo as TaskInfo +from ._core._testing import get_current_task as get_current_task +from ._core._testing import get_running_tasks as get_running_tasks +from ._core._testing import wait_all_tasks_blocked as wait_all_tasks_blocked +from ._core._typedattr import TypedAttributeProvider as TypedAttributeProvider +from ._core._typedattr import TypedAttributeSet as TypedAttributeSet +from ._core._typedattr import typed_attribute as typed_attribute + +# Re-export imports so they look like they live directly in this package +for __value in list(locals().values()): + if getattr(__value, "__module__", "").startswith("anyio."): + __value.__module__ = __name__ + + +del __value + + +def __getattr__(attr: str) -> type[BrokenWorkerInterpreter]: + """Support deprecated aliases.""" + if attr == "BrokenWorkerIntepreter": + import warnings + + warnings.warn( + "The 'BrokenWorkerIntepreter' alias is deprecated, use 'BrokenWorkerInterpreter' instead.", + DeprecationWarning, + stacklevel=2, + ) + return BrokenWorkerInterpreter + + raise AttributeError(f"module {__name__!r} has no attribute {attr!r}") diff --git a/.deps_siteforge_v812/anyio/__pycache__/__init__.cpython-312.pyc b/.deps_siteforge_v812/anyio/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1f62a459ad01ac7812fa9a6ffb909f1e7bc380b1 GIT binary patch literal 4623 zcmZ{n%THX_9mmhW!0>*XmxDdAjWIUHFKmn-m^a48fEh64829Ef_W;-2$LQQUwn1qX zRavA`q$=tnk*Z2rbW^2^{t4N1QFT$bQQBnMks`HSWK~y5HeL1m`^{W1t~(mO^EtoY z?>z2#{Lba~wY60OpMU-KtH+(~Lj0Wy(Wkrsv2Vk;5FZIkSTRSqF&T>qtHdgGO59Re z${YvBWt_RpDRawZIdeHUArs6AaD}X3t^ilcO6E#%m8@c}0$0mw=4x<_tYNMJ*UDPv zTBpvfm-Wnb;0D>iTn}!Pjm!<;CfUT?2yT|m%uV1H*}~imJ|qt@w}4w^EAt_6n`~ol z1-Hv~<~DGL>|kyOcgjxY4)9@ln7Pw=!|jq?%!k28xG;0Zawd;>fwCz)@8r{om#FnC%{Gv5N=mbaNloEi6yyu&;S zzANuCkAY|9Eb}<{p1j9A0iKg{%#+}GInO-hEV%dOedcNKqFiLY4Nl1v^9*=NE-~K$ zFUw`-yWj`%0rM<)MXoU41Fy0#HZ9+UcE`(I5x1p4^Y(4n2wBULC zhy7X+Su0V!Aof9*+EU7}Bi{$zaq&=D(pr5lE+6l_)%)-pa`nb?)u!k9p&8n~7koqg z!qz?-?0D&M-wV~V@Sf?Jn@ZoapV=OK&967tq_bKpFEm2iRS0X?RH0!yj!mdTHf2$}-fmw2^kqfGU;hEOlEJI7(#zwG76FuhkQ!VW=(` zvR-IAC^nC29~~~Lz|ZQmn$+5-sJ4Q6#n;yfc~(zE6_QlVC+i(=i3m-gL^ zgOMyMlL8v%{K+Sn$64RcL}PDQ%z9I{X9ru#ifkQAI~iq-hM~6Cv!O!xc6P5q)ry~W ztTD$=Z==adUd<^C3x)WC8E%o$&nVB>C>SfBYBLiR8qG94Bh7GHZDbv12jMLo9@v|n z=@?q2m0g(px~1`j;vYZf5nMsm=_HbYX&uLDj{g5vFNPYocP-v!9`SIF0!U zRa6N(B5P%g0FR?WxV!(?je3SXU}>nxEFM^&meaE=XgqmUwMT_0}W+cPxHVUevRYd`@YDr~$ zAE*CSd()m_!p7kVu}}AG^B|OqhVefIu zjUI{~6`_r2#K4Ms*nI}sI1lrdqvXsMW1SKO=d9L)Sp~`PiES0m%t23z4`O{tNyspx zER?G<4A-}^NbW{X?3SC}j_voSi?Wj`ddV z9!_O5SiLZ5DO?3<UB}LEp!Rmo7-Jo+EU4$*N5~J8cEv0OAYrW zv%za>6z`y(r0rorO{@PKY9Gs16@<7p9o%rvZlW-w$=#;pmKh{HKe;#G$=>o@#Zyz$ z>IS(uQLbsC_&7MCD+QNsK&!dBy~~2{kX(gfP^L1B-jZCLn$(XF7SITi$^R^lUt3!a zln&M|ogW$+TpP_~CXip(aBNLyjdt!@#?G7#O^qFP_R8j^i@Up*gEhxqU(4)-TfVo( zi+|2AaB+o((VyALRT~C^sVl?K7Wzq3hcl3O#n*}I=k+~*O7#4-qV4O7hM(5{r1s02 zjlb0#`LeD3MM6}zekbCURo_*KhMwmYC;w5^{_)^1uYS_^ynp1gmd`4lcg%cVb?15f z&bMex%=O0gI(~Dd$bz|Y-1AxO?RL>-efa946_?;q?>24TjVwYjC7QNit`!%MR{eAh z(Tk8*DKC zpp#^-E7mMr@PU5wfRXRrKp%rqA166M(oJ%bq=y8DSm<7o($bR>HX$5+KjLp%Qvg^g`N(K5d)wx3MXmRLGbxZr6z02>gdXOTnk_?etBe_m; zgXAX3Fv%^F5t30zuBO-!!gcCx>IL%LCs`y(QB1T#oLc!Fpy|j+|{eL9YCX>10)q zOBe=IE>S#UoI`XNJhj6uZBp$O5;}Ij5?T?{@yk`*DCC!6{Ua19RR{QwOuQ(K#bSAJ z?4M#aFXr;%T3(DXEab(hyy(k|8+maxFJ|)MKIm3&_=V$`R?>riMscneq8g(`saz>za$3!m>BrY>KBROFT1+mPkd3)`JxO->P3|( OtNE_HCU!T*ll@=+YG|tf literal 0 HcmV?d00001 diff --git a/.deps_siteforge_v812/anyio/__pycache__/from_thread.cpython-312.pyc b/.deps_siteforge_v812/anyio/__pycache__/from_thread.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5e3b06d5b32026f3f9d145b436b8695b600abe8d GIT binary patch literal 25824 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 literal 0 HcmV?d00001 diff --git a/.deps_siteforge_v812/anyio/__pycache__/functools.cpython-312.pyc b/.deps_siteforge_v812/anyio/__pycache__/functools.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f6193452dac8402a935999ccbf2b56c6a5a427ac GIT binary patch literal 16566 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 literal 0 HcmV?d00001 diff --git a/.deps_siteforge_v812/anyio/__pycache__/lowlevel.cpython-312.pyc b/.deps_siteforge_v812/anyio/__pycache__/lowlevel.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..59b7b367a6da1098b44b320b3244ebb6767279bb GIT binary patch literal 7994 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 literal 0 HcmV?d00001 diff --git a/.deps_siteforge_v812/anyio/__pycache__/pytest_plugin.cpython-312.pyc b/.deps_siteforge_v812/anyio/__pycache__/pytest_plugin.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8f487093d61cdfeece9f1695368ea6cf67ef5523 GIT binary patch literal 16774 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 WXEe;Rb*Y9Sl_URmfUb_x`Tq;?juX@X literal 0 HcmV?d00001 diff --git a/.deps_siteforge_v812/anyio/__pycache__/to_interpreter.cpython-312.pyc b/.deps_siteforge_v812/anyio/__pycache__/to_interpreter.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0057c8b7ee3cbb79431f2412353eaf8c7b2f5226 GIT binary patch literal 10326 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 UvrAKZlOR2Q$EX@#HMINx14~V(0{{R3 literal 0 HcmV?d00001 diff --git a/.deps_siteforge_v812/anyio/_backends/__init__.py b/.deps_siteforge_v812/anyio/_backends/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/.deps_siteforge_v812/anyio/_backends/__pycache__/__init__.cpython-312.pyc b/.deps_siteforge_v812/anyio/_backends/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1fbf081fbc9c9644e3899d331358a7285ca7dfc7 GIT binary patch literal 189 zcmX@j%ge<81n-aD%mUGmK?FMZ%mNgd&QQsq$>_I|p@<2{`wUX^E5_L>CbT%Us5r*h zz|zt@#<8HlC9xzi#wR~HF()P@HMbz9AhSTXB(W$xwM5q}-PowA%CtBpCo?Igpt2+* zKQAURuQD@VKRzijIXg8krC2{cJ~J<~BtBlRpz;=nO>TZlX-=wL5i8I#Mj$Q*F+MUg KGBOr116cs1kTT!^ literal 0 HcmV?d00001 diff --git a/.deps_siteforge_v812/anyio/_backends/__pycache__/_asyncio.cpython-312.pyc b/.deps_siteforge_v812/anyio/_backends/__pycache__/_asyncio.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..32990e6486d277d06a7172e0d7f20b9ff9aec569 GIT binary patch literal 138620 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 VqmM;7`i(q+ePN59Ok*tO{{^l}n*;y= literal 0 HcmV?d00001 diff --git a/.deps_siteforge_v812/anyio/_backends/__pycache__/_trio.cpython-312.pyc b/.deps_siteforge_v812/anyio/_backends/__pycache__/_trio.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d5a585299824889aac2bb8bb42379e09f535bfb3 GIT binary patch literal 71477 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 WbM*SD77zu`#AfV)E5E87GyX5NCWlo3 literal 0 HcmV?d00001 diff --git a/.deps_siteforge_v812/anyio/_backends/_asyncio.py b/.deps_siteforge_v812/anyio/_backends/_asyncio.py new file mode 100644 index 0000000..9f1ddc2 --- /dev/null +++ b/.deps_siteforge_v812/anyio/_backends/_asyncio.py @@ -0,0 +1,2996 @@ +from __future__ import annotations + +import array +import asyncio +import concurrent.futures +import contextvars +import math +import os +import socket +import sys +import threading +import weakref +from asyncio import ( + AbstractEventLoop, + CancelledError, + all_tasks, + create_task, + current_task, + get_running_loop, + sleep, +) +from asyncio.base_events import _run_until_complete_cb # type: ignore[attr-defined] +from collections import OrderedDict, deque +from collections.abc import ( + AsyncGenerator, + AsyncIterator, + Awaitable, + Callable, + Collection, + Coroutine, + Iterable, + Sequence, +) +from concurrent.futures import Future +from contextlib import AbstractContextManager, suppress +from contextvars import Context, copy_context +from dataclasses import dataclass, field +from functools import partial, wraps +from inspect import ( + CORO_RUNNING, + CORO_SUSPENDED, + getcoroutinestate, + iscoroutine, +) +from io import IOBase +from os import PathLike +from queue import Queue +from signal import Signals +from socket import AddressFamily, SocketKind +from threading import Thread +from types import CodeType, TracebackType +from typing import ( + IO, + TYPE_CHECKING, + Any, + ParamSpec, + TypeVar, + cast, +) +from weakref import WeakKeyDictionary + +from .. import ( + CapacityLimiterStatistics, + EventStatistics, + LockStatistics, + TaskInfo, + abc, +) +from .._core._eventloop import ( + claim_worker_thread, + set_current_async_library, + threadlocals, +) +from .._core._exceptions import ( + BrokenResourceError, + BusyResourceError, + ClosedResourceError, + EndOfStream, + RunFinishedError, + WouldBlock, +) +from .._core._sockets import convert_ipv6_sockaddr +from .._core._streams import create_memory_object_stream +from .._core._synchronization import ( + CapacityLimiter as BaseCapacityLimiter, +) +from .._core._synchronization import Event as BaseEvent +from .._core._synchronization import Lock as BaseLock +from .._core._synchronization import ( + ResourceGuard, + SemaphoreStatistics, +) +from .._core._synchronization import Semaphore as BaseSemaphore +from .._core._tasks import CancelScope as BaseCancelScope +from ..abc import ( + AsyncBackend, + IPSockAddrType, + SocketListener, + UDPPacketType, + UNIXDatagramPacketType, +) +from ..abc._eventloop import StrOrBytesPath +from ..lowlevel import RunVar +from ..streams.memory import MemoryObjectReceiveStream, MemoryObjectSendStream + +if TYPE_CHECKING: + from _typeshed import FileDescriptorLike +else: + FileDescriptorLike = object + +if sys.version_info >= (3, 11): + from asyncio import Runner + from typing import TypeVarTuple, Unpack +else: + import contextvars + import enum + import signal + from asyncio import coroutines, events, exceptions, tasks + + from exceptiongroup import BaseExceptionGroup + from typing_extensions import TypeVarTuple, Unpack + + class _State(enum.Enum): + CREATED = "created" + INITIALIZED = "initialized" + CLOSED = "closed" + + class Runner: + # Copied from CPython 3.11 + def __init__( + self, + *, + debug: bool | None = None, + loop_factory: Callable[[], AbstractEventLoop] | None = None, + ): + self._state = _State.CREATED + self._debug = debug + self._loop_factory = loop_factory + self._loop: AbstractEventLoop | None = None + self._context = None + self._interrupt_count = 0 + self._set_event_loop = False + + def __enter__(self) -> Runner: + self._lazy_init() + return self + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + self.close() + + def close(self) -> None: + """Shutdown and close event loop.""" + loop = self._loop + if self._state is not _State.INITIALIZED or loop is None: + return + try: + _cancel_all_tasks(loop) + loop.run_until_complete(loop.shutdown_asyncgens()) + if hasattr(loop, "shutdown_default_executor"): + loop.run_until_complete(loop.shutdown_default_executor()) + else: + loop.run_until_complete(_shutdown_default_executor(loop)) + finally: + if self._set_event_loop: + events.set_event_loop(None) + loop.close() + self._loop = None + self._state = _State.CLOSED + + def get_loop(self) -> AbstractEventLoop: + """Return embedded event loop.""" + self._lazy_init() + return self._loop + + def run(self, coro: Coroutine[T_Retval], *, context=None) -> T_Retval: + """Run a coroutine inside the embedded event loop.""" + if not coroutines.iscoroutine(coro): + raise ValueError(f"a coroutine was expected, got {coro!r}") + + if events._get_running_loop() is not None: + # fail fast with short traceback + raise RuntimeError( + "Runner.run() cannot be called from a running event loop" + ) + + self._lazy_init() + + if context is None: + context = self._context + task = context.run(self._loop.create_task, coro) + + if ( + threading.current_thread() is threading.main_thread() + and signal.getsignal(signal.SIGINT) is signal.default_int_handler + ): + sigint_handler = partial(self._on_sigint, main_task=task) + try: + signal.signal(signal.SIGINT, sigint_handler) + except ValueError: + # `signal.signal` may throw if `threading.main_thread` does + # not support signals (e.g. embedded interpreter with signals + # not registered - see gh-91880) + sigint_handler = None + else: + sigint_handler = None + + self._interrupt_count = 0 + try: + return self._loop.run_until_complete(task) + except exceptions.CancelledError: + if self._interrupt_count > 0: + uncancel = getattr(task, "uncancel", None) + if uncancel is not None and uncancel() == 0: + raise KeyboardInterrupt # noqa: B904 + raise # CancelledError + finally: + if ( + sigint_handler is not None + and signal.getsignal(signal.SIGINT) is sigint_handler + ): + signal.signal(signal.SIGINT, signal.default_int_handler) + + def _lazy_init(self) -> None: + if self._state is _State.CLOSED: + raise RuntimeError("Runner is closed") + if self._state is _State.INITIALIZED: + return + if self._loop_factory is None: + self._loop = events.new_event_loop() + if not self._set_event_loop: + # Call set_event_loop only once to avoid calling + # attach_loop multiple times on child watchers + events.set_event_loop(self._loop) + self._set_event_loop = True + else: + self._loop = self._loop_factory() + if self._debug is not None: + self._loop.set_debug(self._debug) + self._context = contextvars.copy_context() + self._state = _State.INITIALIZED + + def _on_sigint(self, signum, frame, main_task: asyncio.Task) -> None: + self._interrupt_count += 1 + if self._interrupt_count == 1 and not main_task.done(): + main_task.cancel() + # wakeup loop if it is blocked by select() with long timeout + self._loop.call_soon_threadsafe(lambda: None) + return + raise KeyboardInterrupt() + + def _cancel_all_tasks(loop: AbstractEventLoop) -> None: + to_cancel = tasks.all_tasks(loop) + if not to_cancel: + return + + for task in to_cancel: + task.cancel() + + loop.run_until_complete(tasks.gather(*to_cancel, return_exceptions=True)) + + for task in to_cancel: + if task.cancelled(): + continue + if task.exception() is not None: + loop.call_exception_handler( + { + "message": "unhandled exception during asyncio.run() shutdown", + "exception": task.exception(), + "task": task, + } + ) + + async def _shutdown_default_executor(loop: AbstractEventLoop) -> None: + """Schedule the shutdown of the default executor.""" + + def _do_shutdown(future: asyncio.futures.Future) -> None: + try: + loop._default_executor.shutdown(wait=True) # type: ignore[attr-defined] + loop.call_soon_threadsafe(future.set_result, None) + except Exception as ex: + loop.call_soon_threadsafe(future.set_exception, ex) + + loop._executor_shutdown_called = True + if loop._default_executor is None: + return + future = loop.create_future() + thread = threading.Thread(target=_do_shutdown, args=(future,)) + thread.start() + try: + await future + finally: + thread.join() + + +T_Retval = TypeVar("T_Retval") +T_contra = TypeVar("T_contra", contravariant=True) +PosArgsT = TypeVarTuple("PosArgsT") +P = ParamSpec("P") + +_root_task: RunVar[asyncio.Task | None] = RunVar("_root_task") + + +def find_root_task() -> asyncio.Task: + root_task = _root_task.get(None) + if root_task is not None and not root_task.done(): + return root_task + + # Look for a task that has been started via run_until_complete() + for task in all_tasks(): + if task._callbacks and not task.done(): + callbacks = [cb for cb, context in task._callbacks] + for cb in callbacks: + if ( + cb is _run_until_complete_cb + or getattr(cb, "__module__", None) == "uvloop.loop" + ): + _root_task.set(task) + return task + + # Look up the topmost task in the AnyIO task tree, if possible + task = cast(asyncio.Task, current_task()) + state = _task_states.get(task) + if state: + cancel_scope = state.cancel_scope + while cancel_scope and cancel_scope._parent_scope is not None: + cancel_scope = cancel_scope._parent_scope + + if cancel_scope is not None: + return cast(asyncio.Task, cancel_scope._host_task) + + return task + + +def get_callable_name(func: Callable) -> str: + module = getattr(func, "__module__", None) + qualname = getattr(func, "__qualname__", None) + return ".".join([x for x in (module, qualname) if x]) + + +# +# Event loop +# + +_run_vars: WeakKeyDictionary[asyncio.AbstractEventLoop, Any] = WeakKeyDictionary() + + +def _task_started(task: asyncio.Task) -> bool: + """Return ``True`` if the task has been started and has not finished.""" + # The task coro should never be None here, as we never add finished tasks to the + # task list + coro = task.get_coro() + assert coro is not None + try: + return getcoroutinestate(coro) in (CORO_RUNNING, CORO_SUSPENDED) + except AttributeError: + # task coro is async_genenerator_asend https://bugs.python.org/issue37771 + raise Exception(f"Cannot determine if task {task} has started or not") from None + + +# +# Timeouts and cancellation +# + + +def is_anyio_cancellation(exc: CancelledError) -> bool: + # Sometimes third party frameworks catch a CancelledError and raise a new one, so as + # a workaround we have to look at the previous ones in __context__ too for a + # matching cancel message + while True: + if ( + exc.args + and isinstance(exc.args[0], str) + and exc.args[0].startswith("Cancelled via cancel scope ") + ): + return True + + if isinstance(exc.__context__, CancelledError): + exc = exc.__context__ + continue + + return False + + +class CancelScope(BaseCancelScope): + def __new__( + cls, *, deadline: float = math.inf, shield: bool = False + ) -> CancelScope: + return object.__new__(cls) + + def __init__(self, deadline: float = math.inf, shield: bool = False): + self._deadline = deadline + self._shield = shield + self._parent_scope: CancelScope | None = None + self._child_scopes: set[CancelScope] = set() + self._cancel_called = False + self._cancel_reason: str | None = None + self._cancelled_caught = False + self._active = False + self._timeout_handle: asyncio.TimerHandle | None = None + self._cancel_handle: asyncio.Handle | None = None + self._tasks: set[asyncio.Task] = set() + self._host_task: asyncio.Task | None = None + if sys.version_info >= (3, 11): + self._pending_uncancellations: int | None = 0 + else: + self._pending_uncancellations = None + + def __enter__(self) -> CancelScope: + if self._active: + raise RuntimeError( + "Each CancelScope may only be used for a single 'with' block" + ) + + self._host_task = host_task = cast(asyncio.Task, current_task()) + self._tasks.add(host_task) + try: + task_state = _task_states[host_task] + except KeyError: + task_state = TaskState(None, self) + _task_states[host_task] = task_state + else: + self._parent_scope = task_state.cancel_scope + task_state.cancel_scope = self + if self._parent_scope is not None: + # If using an eager task factory, the parent scope may not even contain + # the host task + self._parent_scope._child_scopes.add(self) + self._parent_scope._tasks.discard(host_task) + + self._timeout() + self._active = True + + # Start cancelling the host task if the scope was cancelled before entering + if self._cancel_called: + self._deliver_cancellation(self) + + return self + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> bool: + del exc_tb + + if not self._active: + raise RuntimeError("This cancel scope is not active") + if current_task() is not self._host_task: + raise RuntimeError( + "Attempted to exit cancel scope in a different task than it was " + "entered in" + ) + + assert self._host_task is not None + host_task_state = _task_states.get(self._host_task) + if host_task_state is None or host_task_state.cancel_scope is not self: + raise RuntimeError( + "Attempted to exit a cancel scope that isn't the current tasks's " + "current cancel scope" + ) + + try: + self._active = False + if self._timeout_handle: + self._timeout_handle.cancel() + self._timeout_handle = None + + self._tasks.remove(self._host_task) + if self._parent_scope is not None: + self._parent_scope._child_scopes.remove(self) + self._parent_scope._tasks.add(self._host_task) + + host_task_state.cancel_scope = self._parent_scope + + # Restart the cancellation effort in the closest visible, cancelled parent + # scope if necessary + self._restart_cancellation_in_parent() + + # We only swallow the exception iff it was an AnyIO CancelledError, either + # directly as exc_val or inside an exception group and there are no cancelled + # parent cancel scopes visible to us here + if self._cancel_called and not self._parent_cancellation_is_visible_to_us: + # For each level-cancel() call made on the host task, call uncancel() + while self._pending_uncancellations: + self._host_task.uncancel() + self._pending_uncancellations -= 1 + + # Update cancelled_caught and check for exceptions we must not swallow + if isinstance(exc_val, BaseExceptionGroup): + cancelleds_caught, remaining = exc_val.split( + lambda exc: ( + isinstance(exc, CancelledError) + and is_anyio_cancellation(exc) + ) + ) + + if cancelleds_caught is None: + return False + + self._cancelled_caught = True + + if remaining is None: + return True + + context = remaining.__context__ + try: + # Preserve __cause__ and __suppress_context__ by avoiding `raise + # ... from ...` + raise remaining + finally: + # Preserve __context__ + remaining.__context__ = context + del context + else: + if isinstance(exc_val, CancelledError) and is_anyio_cancellation( + exc_val + ): + self._cancelled_caught = True + return True + else: + return False + else: + if self._pending_uncancellations: + assert self._parent_scope is not None + assert self._parent_scope._pending_uncancellations is not None + self._parent_scope._pending_uncancellations += ( + self._pending_uncancellations + ) + self._pending_uncancellations = 0 + + return False + finally: + self._host_task = None + del exc_val + + @property + def _effectively_cancelled(self) -> bool: + cancel_scope: CancelScope | None = self + while cancel_scope is not None: + if cancel_scope._cancel_called: + return True + + if cancel_scope.shield: + return False + + cancel_scope = cancel_scope._parent_scope + + return False + + @property + def _parent_cancellation_is_visible_to_us(self) -> bool: + return ( + self._parent_scope is not None + and not self.shield + and self._parent_scope._effectively_cancelled + ) + + def _timeout(self) -> None: + if self._deadline != math.inf: + loop = get_running_loop() + if loop.time() >= self._deadline: + self.cancel("deadline exceeded") + else: + self._timeout_handle = loop.call_at(self._deadline, self._timeout) + + def _deliver_cancellation(self, origin: CancelScope) -> bool: + """ + Deliver cancellation to directly contained tasks and nested cancel scopes. + + Schedule another run at the end if we still have tasks eligible for + cancellation. + + :param origin: the cancel scope that originated the cancellation + :return: ``True`` if the delivery needs to be retried on the next cycle + + """ + should_retry = False + current = current_task() + for task in self._tasks: + should_retry = True + if task._must_cancel: # type: ignore[attr-defined] + continue + + # The task is eligible for cancellation if it has started + if task is not current and (task is self._host_task or _task_started(task)): + waiter = task._fut_waiter # type: ignore[attr-defined] + if not isinstance(waiter, asyncio.Future) or not waiter.done(): + task.cancel(origin._cancel_reason) + if ( + task is origin._host_task + and origin._pending_uncancellations is not None + ): + origin._pending_uncancellations += 1 + + # Deliver cancellation to child scopes that aren't shielded or running their own + # cancellation callbacks + for scope in self._child_scopes: + if not scope._shield and not scope.cancel_called: + should_retry = scope._deliver_cancellation(origin) or should_retry + + # Schedule another callback if there are still tasks left + if origin is self: + if should_retry: + self._cancel_handle = get_running_loop().call_soon( + self._deliver_cancellation, origin + ) + else: + self._cancel_handle = None + + return should_retry + + def _restart_cancellation_in_parent(self) -> None: + """ + Restart the cancellation effort in the closest directly cancelled parent scope. + + """ + scope = self._parent_scope + while scope is not None: + if scope._cancel_called: + if scope._cancel_handle is None: + scope._deliver_cancellation(scope) + + break + + # No point in looking beyond any shielded scope + if scope._shield: + break + + scope = scope._parent_scope + + def cancel(self, reason: str | None = None) -> None: + if not self._cancel_called: + if self._timeout_handle: + self._timeout_handle.cancel() + self._timeout_handle = None + + self._cancel_called = True + self._cancel_reason = f"Cancelled via cancel scope {id(self):x}" + if task := current_task(): + self._cancel_reason += f" by {task}" + + if reason: + self._cancel_reason += f"; reason: {reason}" + + if self._host_task is not None: + self._deliver_cancellation(self) + + @property + def deadline(self) -> float: + return self._deadline + + @deadline.setter + def deadline(self, value: float) -> None: + self._deadline = float(value) + if self._timeout_handle is not None: + self._timeout_handle.cancel() + self._timeout_handle = None + + if self._active and not self._cancel_called: + self._timeout() + + @property + def cancel_called(self) -> bool: + return self._cancel_called + + @property + def cancelled_caught(self) -> bool: + return self._cancelled_caught + + @property + def shield(self) -> bool: + return self._shield + + @shield.setter + def shield(self, value: bool) -> None: + if self._shield != value: + self._shield = value + if not value: + self._restart_cancellation_in_parent() + + +# +# Task states +# + + +class TaskState: + """ + Encapsulates auxiliary task information that cannot be added to the Task instance + itself because there are no guarantees about its implementation. + """ + + __slots__ = "parent_id", "cancel_scope", "__weakref__" + + def __init__(self, parent_id: int | None, cancel_scope: CancelScope | None): + self.parent_id = parent_id + self.cancel_scope = cancel_scope + + +_task_states: WeakKeyDictionary[asyncio.Task, TaskState] = WeakKeyDictionary() + + +# +# Task groups +# + + +class _AsyncioTaskStatus(abc.TaskStatus): + def __init__(self, future: asyncio.Future, parent_id: int): + self._future = future + self._parent_id = parent_id + + def started(self, value: T_contra | None = None) -> None: + try: + self._future.set_result(value) + except asyncio.InvalidStateError: + if not self._future.cancelled(): + raise RuntimeError( + "called 'started' twice on the same task status" + ) from None + + task = cast(asyncio.Task, current_task()) + _task_states[task].parent_id = self._parent_id + + +if sys.version_info >= (3, 12): + _eager_task_factory_code: CodeType | None = asyncio.eager_task_factory.__code__ +else: + _eager_task_factory_code = None + + +class TaskGroup(abc.TaskGroup): + def __init__(self) -> None: + self.cancel_scope: CancelScope = CancelScope() + self._active = False + self._exceptions: list[BaseException] = [] + self._tasks: set[asyncio.Task] = set() + self._on_completed_fut: asyncio.Future[None] | None = None + + async def __aenter__(self) -> TaskGroup: + self.cancel_scope.__enter__() + self._active = True + return self + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> bool: + try: + if exc_val is not None: + self.cancel_scope.cancel() + if not isinstance(exc_val, CancelledError): + self._exceptions.append(exc_val) + + loop = get_running_loop() + try: + if self._tasks: + with CancelScope() as wait_scope: + while self._tasks: + self._on_completed_fut = loop.create_future() + + try: + await self._on_completed_fut + except CancelledError as exc: + # Shield the scope against further cancellation attempts, + # as they're not productive (#695) + wait_scope.shield = True + self.cancel_scope.cancel() + + # Set exc_val from the cancellation exception if it was + # previously unset. However, we should not replace a native + # cancellation exception with one raise by a cancel scope. + if exc_val is None or ( + isinstance(exc_val, CancelledError) + and not is_anyio_cancellation(exc) + ): + exc_val = exc + + self._on_completed_fut = None + else: + # If there are no child tasks to wait on, run at least one checkpoint + # anyway + await AsyncIOBackend.cancel_shielded_checkpoint() + + self._active = False + if self._exceptions: + # The exception that got us here should already have been + # added to self._exceptions so it's ok to break exception + # chaining and avoid adding a "During handling of above..." + # for each nesting level. + raise BaseExceptionGroup( + "unhandled errors in a TaskGroup", self._exceptions + ) from None + elif exc_val: + raise exc_val + except BaseException as exc: + if self.cancel_scope.__exit__(type(exc), exc, exc.__traceback__): + return True + + raise + + return self.cancel_scope.__exit__(exc_type, exc_val, exc_tb) + finally: + del exc_val, exc_tb, self._exceptions + + def _spawn( + self, + func: Callable[[Unpack[PosArgsT]], Awaitable[Any]], + args: tuple[Unpack[PosArgsT]], + name: object, + task_status_future: asyncio.Future | None = None, + ) -> asyncio.Task: + def task_done(_task: asyncio.Task) -> None: + if sys.version_info >= (3, 14) and self.cancel_scope._host_task is not None: + asyncio.future_discard_from_awaited_by( + _task, self.cancel_scope._host_task + ) + + task_state = _task_states[_task] + assert task_state.cancel_scope is not None + assert _task in task_state.cancel_scope._tasks + task_state.cancel_scope._tasks.remove(_task) + self._tasks.remove(task) + del _task_states[_task] + + if self._on_completed_fut is not None and not self._tasks: + try: + self._on_completed_fut.set_result(None) + except asyncio.InvalidStateError: + pass + + try: + exc = _task.exception() + except CancelledError as e: + while isinstance(e.__context__, CancelledError): + e = e.__context__ + + exc = e + + if exc is not None: + # The future can only be in the cancelled state if the host task was + # cancelled, so return immediately instead of adding one more + # CancelledError to the exceptions list + if task_status_future is not None and task_status_future.cancelled(): + return + + if task_status_future is None or task_status_future.done(): + if not isinstance(exc, CancelledError): + self._exceptions.append(exc) + + if not self.cancel_scope._effectively_cancelled: + self.cancel_scope.cancel() + else: + task_status_future.set_exception(exc) + elif task_status_future is not None and not task_status_future.done(): + task_status_future.set_exception( + RuntimeError("Child exited without calling task_status.started()") + ) + + if not self._active: + raise RuntimeError( + "This task group is not active; no new tasks can be started." + ) + + kwargs = {} + if task_status_future: + parent_id = id(current_task()) + kwargs["task_status"] = _AsyncioTaskStatus( + task_status_future, id(self.cancel_scope._host_task) + ) + else: + parent_id = id(self.cancel_scope._host_task) + + coro = func(*args, **kwargs) + if not iscoroutine(coro): + prefix = f"{func.__module__}." if hasattr(func, "__module__") else "" + raise TypeError( + f"Expected {prefix}{func.__qualname__}() to return a coroutine, but " + f"the return value ({coro!r}) is not a coroutine object" + ) + + name = get_callable_name(func) if name is None else str(name) + loop = asyncio.get_running_loop() + if ( + (factory := loop.get_task_factory()) + and getattr(factory, "__code__", None) is _eager_task_factory_code + and (closure := getattr(factory, "__closure__", None)) + ): + custom_task_constructor = closure[0].cell_contents + task = custom_task_constructor(coro, loop=loop, name=name) + else: + task = create_task(coro, name=name) + + # Make the spawned task inherit the task group's cancel scope + _task_states[task] = TaskState( + parent_id=parent_id, cancel_scope=self.cancel_scope + ) + self.cancel_scope._tasks.add(task) + self._tasks.add(task) + if sys.version_info >= (3, 14) and self.cancel_scope._host_task is not None: + asyncio.future_add_to_awaited_by(task, self.cancel_scope._host_task) + + task.add_done_callback(task_done) + return task + + def start_soon( + self, + func: Callable[[Unpack[PosArgsT]], Awaitable[Any]], + *args: Unpack[PosArgsT], + name: object = None, + ) -> None: + self._spawn(func, args, name) + + async def start( + self, func: Callable[..., Awaitable[Any]], *args: object, name: object = None + ) -> Any: + future: asyncio.Future = asyncio.Future() + task = self._spawn(func, args, name, future) + + # If the task raises an exception after sending a start value without a switch + # point between, the task group is cancelled and this method never proceeds to + # process the completed future. That's why we have to have a shielded cancel + # scope here. + try: + return await future + except CancelledError: + # Cancel the task and wait for it to exit before returning + task.cancel() + with CancelScope(shield=True), suppress(CancelledError): + await task + + raise + + +# +# Threads +# + +_Retval_Queue_Type = tuple[T_Retval | None, BaseException | None] + + +class WorkerThread(Thread): + MAX_IDLE_TIME = 10 # seconds + + def __init__( + self, + root_task: asyncio.Task, + workers: set[WorkerThread], + idle_workers: deque[WorkerThread], + ): + super().__init__(name="AnyIO worker thread") + self.root_task = root_task + self.workers = workers + self.idle_workers = idle_workers + self.loop = root_task._loop + self.queue: Queue[ + tuple[Context, Callable, tuple, asyncio.Future, CancelScope] | None + ] = Queue(2) + self.idle_since = AsyncIOBackend.current_time() + self.stopping = False + + def _report_result( + self, future: asyncio.Future, result: Any, exc: BaseException | None + ) -> None: + self.idle_since = AsyncIOBackend.current_time() + if not self.stopping: + self.idle_workers.append(self) + + if not future.cancelled(): + if exc is not None: + if isinstance(exc, StopIteration): + new_exc = RuntimeError("coroutine raised StopIteration") + new_exc.__cause__ = exc + exc = new_exc + + future.set_exception(exc) + else: + future.set_result(result) + + def run(self) -> None: + with claim_worker_thread(AsyncIOBackend, self.loop): + while True: + item = self.queue.get() + if item is None: + # Shutdown command received + return + + context, func, args, future, cancel_scope = item + if not future.cancelled(): + result = None + exception: BaseException | None = None + threadlocals.current_cancel_scope = cancel_scope + try: + result = context.run(func, *args) + except BaseException as exc: + exception = exc + finally: + del threadlocals.current_cancel_scope + + if not self.loop.is_closed(): + self.loop.call_soon_threadsafe( + self._report_result, future, result, exception + ) + + del result, exception + + self.queue.task_done() + del item, context, func, args, future, cancel_scope + + def stop(self, f: asyncio.Task | None = None) -> None: + self.stopping = True + self.queue.put_nowait(None) + self.workers.discard(self) + try: + self.idle_workers.remove(self) + except ValueError: + pass + + +_threadpool_idle_workers: RunVar[deque[WorkerThread]] = RunVar( + "_threadpool_idle_workers" +) +_threadpool_workers: RunVar[set[WorkerThread]] = RunVar("_threadpool_workers") + + +# +# Subprocesses +# + + +@dataclass(eq=False) +class StreamReaderWrapper(abc.ByteReceiveStream): + _stream: asyncio.StreamReader + + async def receive(self, max_bytes: int = 65536) -> bytes: + data = await self._stream.read(max_bytes) + if data: + return data + else: + raise EndOfStream + + async def aclose(self) -> None: + self._stream.set_exception(ClosedResourceError()) + await AsyncIOBackend.checkpoint() + + +@dataclass(eq=False) +class StreamWriterWrapper(abc.ByteSendStream): + _stream: asyncio.StreamWriter + _closed: bool = field(init=False, default=False) + + async def send(self, item: bytes) -> None: + await AsyncIOBackend.checkpoint_if_cancelled() + stream_paused = self._stream._protocol._paused # type: ignore[attr-defined] + try: + self._stream.write(item) + await self._stream.drain() + except (ConnectionResetError, BrokenPipeError, RuntimeError) as exc: + # If closed by us and/or the peer: + # * on stdlib, drain() raises ConnectionResetError or BrokenPipeError + # * on uvloop and Winloop, write() eventually starts raising RuntimeError + if self._closed: + raise ClosedResourceError from exc + elif self._stream.is_closing(): + raise BrokenResourceError from exc + + raise + + if not stream_paused: + await AsyncIOBackend.cancel_shielded_checkpoint() + + async def aclose(self) -> None: + self._closed = True + self._stream.close() + await AsyncIOBackend.checkpoint() + + +@dataclass(eq=False) +class Process(abc.Process): + _process: asyncio.subprocess.Process + _stdin: StreamWriterWrapper | None + _stdout: StreamReaderWrapper | None + _stderr: StreamReaderWrapper | None + + async def aclose(self) -> None: + with CancelScope(shield=True) as scope: + if self._stdin: + await self._stdin.aclose() + if self._stdout: + await self._stdout.aclose() + if self._stderr: + await self._stderr.aclose() + + scope.shield = False + try: + await self.wait() + except BaseException: + scope.shield = True + self.kill() + await self.wait() + raise + + async def wait(self) -> int: + return await self._process.wait() + + def terminate(self) -> None: + self._process.terminate() + + def kill(self) -> None: + self._process.kill() + + def send_signal(self, signal: int) -> None: + self._process.send_signal(signal) + + @property + def pid(self) -> int: + return self._process.pid + + @property + def returncode(self) -> int | None: + return self._process.returncode + + @property + def stdin(self) -> abc.ByteSendStream | None: + return self._stdin + + @property + def stdout(self) -> abc.ByteReceiveStream | None: + return self._stdout + + @property + def stderr(self) -> abc.ByteReceiveStream | None: + return self._stderr + + +def _forcibly_shutdown_process_pool_on_exit( + workers: set[Process], _task: object +) -> None: + """ + Forcibly shuts down worker processes belonging to this event loop.""" + child_watcher: asyncio.AbstractChildWatcher | None = None # type: ignore[name-defined] + if sys.version_info < (3, 12): + try: + child_watcher = asyncio.get_event_loop_policy().get_child_watcher() + except NotImplementedError: + pass + + # Close as much as possible (w/o async/await) to avoid warnings + for process in workers.copy(): + if process.returncode is not None: + continue + + process._stdin._stream._transport.close() # type: ignore[union-attr] + process._stdout._stream._transport.close() # type: ignore[union-attr] + process._stderr._stream._transport.close() # type: ignore[union-attr] + process.kill() + if child_watcher: + child_watcher.remove_child_handler(process.pid) + + +async def _shutdown_process_pool_on_exit(workers: set[abc.Process]) -> None: + """ + Shuts down worker processes belonging to this event loop. + + NOTE: this only works when the event loop was started using asyncio.run() or + anyio.run(). + + """ + process: abc.Process + try: + await sleep(math.inf) + except asyncio.CancelledError: + workers = workers.copy() + for process in workers: + if process.returncode is None: + process.kill() + + for process in workers: + await process.aclose() + + +# +# Sockets and networking +# + + +class StreamProtocol(asyncio.Protocol): + read_queue: deque[bytes] + read_event: asyncio.Event + write_event: asyncio.Event + exception: Exception | None = None + is_at_eof: bool = False + + def connection_made(self, transport: asyncio.BaseTransport) -> None: + self.read_queue = deque() + self.read_event = asyncio.Event() + self.write_event = asyncio.Event() + self.write_event.set() + cast(asyncio.Transport, transport).set_write_buffer_limits(0) + + def connection_lost(self, exc: Exception | None) -> None: + if exc: + self.exception = exc + + self.read_event.set() + self.write_event.set() + + def data_received(self, data: bytes) -> None: + # ProactorEventloop sometimes sends bytearray instead of bytes + self.read_queue.append(bytes(data)) + self.read_event.set() + + def eof_received(self) -> bool | None: + self.is_at_eof = True + self.read_event.set() + return True + + def pause_writing(self) -> None: + self.write_event = asyncio.Event() + + def resume_writing(self) -> None: + self.write_event.set() + + +class DatagramProtocol(asyncio.DatagramProtocol): + read_queue: deque[tuple[bytes, IPSockAddrType]] + read_event: asyncio.Event + write_event: asyncio.Event + exception: Exception | None = None + + def connection_made(self, transport: asyncio.BaseTransport) -> None: + self.read_queue = deque(maxlen=100) # arbitrary value + self.read_event = asyncio.Event() + self.write_event = asyncio.Event() + self.write_event.set() + + def connection_lost(self, exc: Exception | None) -> None: + self.read_event.set() + self.write_event.set() + + def datagram_received(self, data: bytes, addr: IPSockAddrType) -> None: + addr = convert_ipv6_sockaddr(addr) + self.read_queue.append((data, addr)) + self.read_event.set() + + def error_received(self, exc: Exception) -> None: + self.exception = exc + + def pause_writing(self) -> None: + self.write_event.clear() + + def resume_writing(self) -> None: + self.write_event.set() + + +class SocketStream(abc.SocketStream): + def __init__(self, transport: asyncio.Transport, protocol: StreamProtocol): + self._transport = transport + self._protocol = protocol + self._receive_guard = ResourceGuard("reading from") + self._send_guard = ResourceGuard("writing to") + self._closed = False + + @property + def _raw_socket(self) -> socket.socket: + return self._transport.get_extra_info("socket") + + async def receive(self, max_bytes: int = 65536) -> bytes: + with self._receive_guard: + if ( + not self._protocol.read_event.is_set() + and not self._transport.is_closing() + and not self._protocol.is_at_eof + ): + self._transport.resume_reading() + await self._protocol.read_event.wait() + self._transport.pause_reading() + else: + await AsyncIOBackend.checkpoint() + + try: + chunk = self._protocol.read_queue.popleft() + except IndexError: + if self._closed: + raise ClosedResourceError from None + elif self._protocol.exception: + raise BrokenResourceError from self._protocol.exception + else: + raise EndOfStream from None + + if len(chunk) > max_bytes: + # Split the oversized chunk + chunk, leftover = chunk[:max_bytes], chunk[max_bytes:] + self._protocol.read_queue.appendleft(leftover) + + # If the read queue is empty, clear the flag so that the next call will + # block until data is available + if not self._protocol.read_queue: + self._protocol.read_event.clear() + + return chunk + + async def send(self, item: bytes) -> None: + with self._send_guard: + await AsyncIOBackend.checkpoint() + + if self._closed: + raise ClosedResourceError + elif self._protocol.exception is not None: + raise BrokenResourceError from self._protocol.exception + + try: + self._transport.write(item) + except RuntimeError as exc: + if self._transport.is_closing(): + raise BrokenResourceError from exc + else: + raise + + await self._protocol.write_event.wait() + + async def send_eof(self) -> None: + try: + self._transport.write_eof() + except OSError: + pass + + async def aclose(self) -> None: + self._closed = True + if not self._transport.is_closing(): + try: + self._transport.write_eof() + except OSError: + pass + + self._transport.close() + await sleep(0) + self._transport.abort() + + +class _RawSocketMixin: + _receive_future: asyncio.Future | None = None + _send_future: asyncio.Future | None = None + _closing = False + + def __init__(self, raw_socket: socket.socket): + self.__raw_socket = raw_socket + self._receive_guard = ResourceGuard("reading from") + self._send_guard = ResourceGuard("writing to") + + @property + def _raw_socket(self) -> socket.socket: + return self.__raw_socket + + def _wait_until_readable(self, loop: asyncio.AbstractEventLoop) -> asyncio.Future: + def callback(f: object) -> None: + del self._receive_future + loop.remove_reader(self.__raw_socket) + + f = self._receive_future = asyncio.Future() + loop.add_reader(self.__raw_socket, f.set_result, None) + f.add_done_callback(callback) + return f + + def _wait_until_writable(self, loop: asyncio.AbstractEventLoop) -> asyncio.Future: + def callback(f: object) -> None: + del self._send_future + loop.remove_writer(self.__raw_socket) + + f = self._send_future = asyncio.Future() + loop.add_writer(self.__raw_socket, f.set_result, None) + f.add_done_callback(callback) + return f + + async def aclose(self) -> None: + if not self._closing: + self._closing = True + if self.__raw_socket.fileno() != -1: + self.__raw_socket.close() + + if self._receive_future: + self._receive_future.set_result(None) + if self._send_future: + self._send_future.set_result(None) + + +class UNIXSocketStream(_RawSocketMixin, abc.UNIXSocketStream): + async def send_eof(self) -> None: + with self._send_guard: + self._raw_socket.shutdown(socket.SHUT_WR) + + async def receive(self, max_bytes: int = 65536) -> bytes: + loop = get_running_loop() + await AsyncIOBackend.checkpoint() + with self._receive_guard: + while True: + try: + data = self._raw_socket.recv(max_bytes) + except BlockingIOError: + await self._wait_until_readable(loop) + except OSError as exc: + if self._closing: + raise ClosedResourceError from None + else: + raise BrokenResourceError from exc + else: + if not data: + raise EndOfStream + + return data + + async def send(self, item: bytes) -> None: + loop = get_running_loop() + await AsyncIOBackend.checkpoint() + with self._send_guard: + view = memoryview(item) + while view: + try: + bytes_sent = self._raw_socket.send(view) + except BlockingIOError: + await self._wait_until_writable(loop) + except OSError as exc: + if self._closing: + raise ClosedResourceError from None + else: + raise BrokenResourceError from exc + else: + view = view[bytes_sent:] + + async def receive_fds(self, msglen: int, maxfds: int) -> tuple[bytes, list[int]]: + if not isinstance(msglen, int) or msglen < 0: + raise ValueError("msglen must be a non-negative integer") + if not isinstance(maxfds, int) or maxfds < 1: + raise ValueError("maxfds must be a positive integer") + + loop = get_running_loop() + fds = array.array("i") + await AsyncIOBackend.checkpoint() + with self._receive_guard: + while True: + try: + message, ancdata, flags, addr = self._raw_socket.recvmsg( + msglen, socket.CMSG_LEN(maxfds * fds.itemsize) + ) + except BlockingIOError: + await self._wait_until_readable(loop) + except OSError as exc: + if self._closing: + raise ClosedResourceError from None + else: + raise BrokenResourceError from exc + else: + if not message and not ancdata: + raise EndOfStream + + break + + for cmsg_level, cmsg_type, cmsg_data in ancdata: + if cmsg_level != socket.SOL_SOCKET or cmsg_type != socket.SCM_RIGHTS: + raise RuntimeError( + f"Received unexpected ancillary data; message = {message!r}, " + f"cmsg_level = {cmsg_level}, cmsg_type = {cmsg_type}" + ) + + fds.frombytes(cmsg_data[: len(cmsg_data) - (len(cmsg_data) % fds.itemsize)]) + + return message, list(fds) + + async def send_fds(self, message: bytes, fds: Collection[int | IOBase]) -> None: + if not message: + raise ValueError("message must not be empty") + if not fds: + raise ValueError("fds must not be empty") + + loop = get_running_loop() + filenos: list[int] = [] + for fd in fds: + if isinstance(fd, int): + filenos.append(fd) + elif isinstance(fd, IOBase): + filenos.append(fd.fileno()) + + fdarray = array.array("i", filenos) + await AsyncIOBackend.checkpoint() + with self._send_guard: + while True: + try: + # The ignore can be removed after mypy picks up + # https://github.com/python/typeshed/pull/5545 + self._raw_socket.sendmsg( + [message], [(socket.SOL_SOCKET, socket.SCM_RIGHTS, fdarray)] + ) + break + except BlockingIOError: + await self._wait_until_writable(loop) + except OSError as exc: + if self._closing: + raise ClosedResourceError from None + else: + raise BrokenResourceError from exc + + +class TCPSocketListener(abc.SocketListener): + _accept_scope: CancelScope | None = None + _closed = False + + def __init__(self, raw_socket: socket.socket): + self.__raw_socket = raw_socket + self._loop = cast(asyncio.BaseEventLoop, get_running_loop()) + self._accept_guard = ResourceGuard("accepting connections from") + + @property + def _raw_socket(self) -> socket.socket: + return self.__raw_socket + + async def accept(self) -> abc.SocketStream: + if self._closed: + raise ClosedResourceError + + with self._accept_guard: + await AsyncIOBackend.checkpoint() + with CancelScope() as self._accept_scope: + try: + client_sock, _addr = await self._loop.sock_accept(self._raw_socket) + except asyncio.CancelledError: + # Workaround for https://bugs.python.org/issue41317 + try: + self._loop.remove_reader(self._raw_socket) + except (ValueError, NotImplementedError): + pass + + if self._closed: + raise ClosedResourceError from None + + raise + finally: + self._accept_scope = None + + client_sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) + transport, protocol = await self._loop.connect_accepted_socket( + StreamProtocol, client_sock + ) + return SocketStream(transport, protocol) + + async def aclose(self) -> None: + if self._closed: + return + + self._closed = True + if self._accept_scope: + # Workaround for https://bugs.python.org/issue41317 + try: + self._loop.remove_reader(self._raw_socket) + except (ValueError, NotImplementedError): + pass + + self._accept_scope.cancel() + await sleep(0) + + self._raw_socket.close() + + +class UNIXSocketListener(abc.SocketListener): + def __init__(self, raw_socket: socket.socket): + self.__raw_socket = raw_socket + self._loop = get_running_loop() + self._accept_guard = ResourceGuard("accepting connections from") + self._closed = False + + async def accept(self) -> abc.SocketStream: + await AsyncIOBackend.checkpoint() + with self._accept_guard: + while True: + try: + client_sock, _ = self.__raw_socket.accept() + client_sock.setblocking(False) + return UNIXSocketStream(client_sock) + except BlockingIOError: + f: asyncio.Future = asyncio.Future() + self._loop.add_reader(self.__raw_socket, f.set_result, None) + f.add_done_callback( + lambda _: self._loop.remove_reader(self.__raw_socket) + ) + await f + except OSError as exc: + if self._closed: + raise ClosedResourceError from None + else: + raise BrokenResourceError from exc + + async def aclose(self) -> None: + self._closed = True + self.__raw_socket.close() + + @property + def _raw_socket(self) -> socket.socket: + return self.__raw_socket + + +class UDPSocket(abc.UDPSocket): + def __init__( + self, transport: asyncio.DatagramTransport, protocol: DatagramProtocol + ): + self._transport = transport + self._protocol = protocol + self._receive_guard = ResourceGuard("reading from") + self._send_guard = ResourceGuard("writing to") + self._closed = False + + @property + def _raw_socket(self) -> socket.socket: + return self._transport.get_extra_info("socket") + + async def aclose(self) -> None: + self._closed = True + if not self._transport.is_closing(): + self._transport.close() + + async def receive(self) -> tuple[bytes, IPSockAddrType]: + with self._receive_guard: + await AsyncIOBackend.checkpoint() + + # If the buffer is empty, ask for more data + if not self._protocol.read_queue and not self._transport.is_closing(): + self._protocol.read_event.clear() + await self._protocol.read_event.wait() + + try: + return self._protocol.read_queue.popleft() + except IndexError: + if self._closed: + raise ClosedResourceError from None + else: + raise BrokenResourceError from None + + async def send(self, item: UDPPacketType) -> None: + with self._send_guard: + await AsyncIOBackend.checkpoint() + await self._protocol.write_event.wait() + if self._closed: + raise ClosedResourceError + elif self._transport.is_closing(): + raise BrokenResourceError + else: + self._transport.sendto(*item) + + +class ConnectedUDPSocket(abc.ConnectedUDPSocket): + def __init__( + self, transport: asyncio.DatagramTransport, protocol: DatagramProtocol + ): + self._transport = transport + self._protocol = protocol + self._receive_guard = ResourceGuard("reading from") + self._send_guard = ResourceGuard("writing to") + self._closed = False + + @property + def _raw_socket(self) -> socket.socket: + return self._transport.get_extra_info("socket") + + async def aclose(self) -> None: + self._closed = True + if not self._transport.is_closing(): + self._transport.close() + + async def receive(self) -> bytes: + with self._receive_guard: + await AsyncIOBackend.checkpoint() + + # If the buffer is empty, ask for more data + if not self._protocol.read_queue and not self._transport.is_closing(): + self._protocol.read_event.clear() + await self._protocol.read_event.wait() + + try: + packet = self._protocol.read_queue.popleft() + except IndexError: + if self._closed: + raise ClosedResourceError from None + else: + raise BrokenResourceError from None + + return packet[0] + + async def send(self, item: bytes) -> None: + with self._send_guard: + await AsyncIOBackend.checkpoint() + await self._protocol.write_event.wait() + if self._closed: + raise ClosedResourceError + elif self._transport.is_closing(): + raise BrokenResourceError + else: + self._transport.sendto(item) + + +class UNIXDatagramSocket(_RawSocketMixin, abc.UNIXDatagramSocket): + async def receive(self) -> UNIXDatagramPacketType: + loop = get_running_loop() + await AsyncIOBackend.checkpoint() + with self._receive_guard: + while True: + try: + data = self._raw_socket.recvfrom(65536) + except BlockingIOError: + await self._wait_until_readable(loop) + except OSError as exc: + if self._closing: + raise ClosedResourceError from None + else: + raise BrokenResourceError from exc + else: + return data + + async def send(self, item: UNIXDatagramPacketType) -> None: + loop = get_running_loop() + await AsyncIOBackend.checkpoint() + with self._send_guard: + while True: + try: + self._raw_socket.sendto(*item) + except BlockingIOError: + await self._wait_until_writable(loop) + except OSError as exc: + if self._closing: + raise ClosedResourceError from None + else: + raise BrokenResourceError from exc + else: + return + + +class ConnectedUNIXDatagramSocket(_RawSocketMixin, abc.ConnectedUNIXDatagramSocket): + async def receive(self) -> bytes: + loop = get_running_loop() + await AsyncIOBackend.checkpoint() + with self._receive_guard: + while True: + try: + data = self._raw_socket.recv(65536) + except BlockingIOError: + await self._wait_until_readable(loop) + except OSError as exc: + if self._closing: + raise ClosedResourceError from None + else: + raise BrokenResourceError from exc + else: + return data + + async def send(self, item: bytes) -> None: + loop = get_running_loop() + await AsyncIOBackend.checkpoint() + with self._send_guard: + while True: + try: + self._raw_socket.send(item) + except BlockingIOError: + await self._wait_until_writable(loop) + except OSError as exc: + if self._closing: + raise ClosedResourceError from None + else: + raise BrokenResourceError from exc + else: + return + + +_read_events: RunVar[dict[int, asyncio.Future[bool]]] = RunVar("read_events") +_write_events: RunVar[dict[int, asyncio.Future[bool]]] = RunVar("write_events") + + +# +# Synchronization +# + + +class Event(BaseEvent): + def __new__(cls) -> Event: + return object.__new__(cls) + + def __init__(self) -> None: + self._event = asyncio.Event() + + def set(self) -> None: + self._event.set() + + def is_set(self) -> bool: + return self._event.is_set() + + async def wait(self) -> None: + if self.is_set(): + await AsyncIOBackend.checkpoint() + else: + await self._event.wait() + + def statistics(self) -> EventStatistics: + return EventStatistics(len(self._event._waiters)) + + +class Lock(BaseLock): + def __new__(cls, *, fast_acquire: bool = False) -> Lock: + return object.__new__(cls) + + def __init__(self, *, fast_acquire: bool = False) -> None: + self._fast_acquire = fast_acquire + self._owner_task: asyncio.Task | None = None + self._waiters: deque[tuple[asyncio.Task, asyncio.Future]] = deque() + + async def acquire(self) -> None: + task = cast(asyncio.Task, current_task()) + if self._owner_task is None and not self._waiters: + await AsyncIOBackend.checkpoint_if_cancelled() + self._owner_task = task + + # Unless on the "fast path", yield control of the event loop so that other + # tasks can run too + if not self._fast_acquire: + try: + await AsyncIOBackend.cancel_shielded_checkpoint() + except CancelledError: + self.release() + raise + + return + + if self._owner_task == task: + raise RuntimeError("Attempted to acquire an already held Lock") + + fut: asyncio.Future[None] = asyncio.Future() + item = task, fut + self._waiters.append(item) + try: + await fut + except CancelledError: + self._waiters.remove(item) + if self._owner_task is task: + self.release() + + raise + + self._waiters.remove(item) + + def acquire_nowait(self) -> None: + task = cast(asyncio.Task, current_task()) + if self._owner_task is None and not self._waiters: + self._owner_task = task + return + + if self._owner_task is task: + raise RuntimeError("Attempted to acquire an already held Lock") + + raise WouldBlock + + def locked(self) -> bool: + return self._owner_task is not None + + def release(self) -> None: + if self._owner_task != current_task(): + raise RuntimeError("The current task is not holding this lock") + + for task, fut in self._waiters: + if not fut.cancelled(): + self._owner_task = task + fut.set_result(None) + return + + self._owner_task = None + + def statistics(self) -> LockStatistics: + task_info = AsyncIOTaskInfo(self._owner_task) if self._owner_task else None + return LockStatistics(self.locked(), task_info, len(self._waiters)) + + +class Semaphore(BaseSemaphore): + def __new__( + cls, + initial_value: int, + *, + max_value: int | None = None, + fast_acquire: bool = False, + ) -> Semaphore: + return object.__new__(cls) + + def __init__( + self, + initial_value: int, + *, + max_value: int | None = None, + fast_acquire: bool = False, + ): + super().__init__(initial_value, max_value=max_value) + self._value = initial_value + self._max_value = max_value + self._fast_acquire = fast_acquire + self._waiters: deque[asyncio.Future[None]] = deque() + + async def acquire(self) -> None: + if self._value > 0 and not self._waiters: + await AsyncIOBackend.checkpoint_if_cancelled() + self._value -= 1 + + # Unless on the "fast path", yield control of the event loop so that other + # tasks can run too + if not self._fast_acquire: + try: + await AsyncIOBackend.cancel_shielded_checkpoint() + except CancelledError: + self.release() + raise + + return + + fut: asyncio.Future[None] = asyncio.Future() + self._waiters.append(fut) + try: + await fut + except CancelledError: + try: + self._waiters.remove(fut) + except ValueError: + self.release() + + raise + + def acquire_nowait(self) -> None: + if self._value == 0: + raise WouldBlock + + self._value -= 1 + + def release(self) -> None: + if self._max_value is not None and self._value == self._max_value: + raise ValueError("semaphore released too many times") + + for fut in self._waiters: + if not fut.cancelled(): + fut.set_result(None) + self._waiters.remove(fut) + return + + self._value += 1 + + @property + def value(self) -> int: + return self._value + + @property + def max_value(self) -> int | None: + return self._max_value + + def statistics(self) -> SemaphoreStatistics: + return SemaphoreStatistics(len(self._waiters)) + + +class CapacityLimiter(BaseCapacityLimiter): + _total_tokens: float = 0 + + def __new__(cls, total_tokens: float) -> CapacityLimiter: + return object.__new__(cls) + + def __init__(self, total_tokens: float): + self._borrowers: set[Any] = set() + self._wait_queue: OrderedDict[Any, asyncio.Event] = OrderedDict() + self.total_tokens = total_tokens + + async def __aenter__(self) -> None: + await self.acquire() + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + self.release() + + @property + def total_tokens(self) -> float: + return self._total_tokens + + @total_tokens.setter + def total_tokens(self, value: float) -> None: + if not isinstance(value, int) and not math.isinf(value): + raise TypeError("total_tokens must be an int or math.inf") + + if value < 0: + raise ValueError("total_tokens must be >= 0") + + waiters_to_notify = max(value - self._total_tokens, 0) + self._total_tokens = value + + # Notify waiting tasks that they have acquired the limiter + while self._wait_queue and waiters_to_notify: + event = self._wait_queue.popitem(last=False)[1] + event.set() + waiters_to_notify -= 1 + + @property + def borrowed_tokens(self) -> int: + return len(self._borrowers) + + @property + def available_tokens(self) -> float: + return self._total_tokens - len(self._borrowers) + + def _notify_next_waiter(self) -> None: + """Notify the next task in line if this limiter has free capacity now.""" + if self._wait_queue and len(self._borrowers) < self._total_tokens: + event = self._wait_queue.popitem(last=False)[1] + event.set() + + def acquire_nowait(self) -> None: + self.acquire_on_behalf_of_nowait(current_task()) + + def acquire_on_behalf_of_nowait(self, borrower: object) -> None: + if borrower in self._borrowers: + raise RuntimeError( + "this borrower is already holding one of this CapacityLimiter's tokens" + ) + + if self._wait_queue or len(self._borrowers) >= self._total_tokens: + raise WouldBlock + + self._borrowers.add(borrower) + + async def acquire(self) -> None: + return await self.acquire_on_behalf_of(current_task()) + + async def acquire_on_behalf_of(self, borrower: object) -> None: + await AsyncIOBackend.checkpoint_if_cancelled() + try: + self.acquire_on_behalf_of_nowait(borrower) + except WouldBlock: + event = asyncio.Event() + self._wait_queue[borrower] = event + try: + await event.wait() + except BaseException: + self._wait_queue.pop(borrower, None) + if event.is_set(): + self._notify_next_waiter() + + raise + + self._borrowers.add(borrower) + else: + try: + await AsyncIOBackend.cancel_shielded_checkpoint() + except BaseException: + self.release() + raise + + def release(self) -> None: + self.release_on_behalf_of(current_task()) + + def release_on_behalf_of(self, borrower: object) -> None: + try: + self._borrowers.remove(borrower) + except KeyError: + raise RuntimeError( + "this borrower isn't holding any of this CapacityLimiter's tokens" + ) from None + + self._notify_next_waiter() + + def statistics(self) -> CapacityLimiterStatistics: + return CapacityLimiterStatistics( + self.borrowed_tokens, + self.total_tokens, + tuple(self._borrowers), + len(self._wait_queue), + ) + + +_default_thread_limiter: RunVar[CapacityLimiter] = RunVar("_default_thread_limiter") + + +# +# Operating system signals +# + + +class _SignalReceiver: + def __init__(self, signals: tuple[Signals, ...]): + self._signals = signals + self._loop = get_running_loop() + self._signal_queue: deque[Signals] = deque() + self._future: asyncio.Future = asyncio.Future() + self._handled_signals: set[Signals] = set() + + def _deliver(self, signum: Signals) -> None: + self._signal_queue.append(signum) + if not self._future.done(): + self._future.set_result(None) + + def __enter__(self) -> _SignalReceiver: + for sig in set(self._signals): + self._loop.add_signal_handler(sig, self._deliver, sig) + self._handled_signals.add(sig) + + return self + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + for sig in self._handled_signals: + self._loop.remove_signal_handler(sig) + + def __aiter__(self) -> _SignalReceiver: + return self + + async def __anext__(self) -> Signals: + await AsyncIOBackend.checkpoint() + if not self._signal_queue: + self._future = asyncio.Future() + await self._future + + return self._signal_queue.popleft() + + +# +# Testing and debugging +# + + +class AsyncIOTaskInfo(TaskInfo): + def __init__(self, task: asyncio.Task): + task_state = _task_states.get(task) + if task_state is None: + parent_id = None + else: + parent_id = task_state.parent_id + + coro = task.get_coro() + assert coro is not None, "created TaskInfo from a completed Task" + super().__init__(id(task), parent_id, task.get_name(), coro) + self._task = weakref.ref(task) + + def has_pending_cancellation(self) -> bool: + if not (task := self._task()): + # If the task isn't around anymore, it won't have a pending cancellation + return False + + if task._must_cancel: # type: ignore[attr-defined] + return True + elif ( + isinstance(task._fut_waiter, asyncio.Future) # type: ignore[attr-defined] + and task._fut_waiter.cancelled() # type: ignore[attr-defined] + ): + return True + + if task_state := _task_states.get(task): + if cancel_scope := task_state.cancel_scope: + return cancel_scope._effectively_cancelled + + return False + + +class TestRunner(abc.TestRunner): + _send_stream: MemoryObjectSendStream[tuple[Awaitable[Any], asyncio.Future[Any]]] + + def __init__( + self, + *, + debug: bool | None = None, + use_uvloop: bool = False, + loop_factory: Callable[[], AbstractEventLoop] | None = None, + ) -> None: + if use_uvloop and loop_factory is None: + if sys.platform != "win32": + import uvloop + + loop_factory = uvloop.new_event_loop + else: + import winloop + + loop_factory = winloop.new_event_loop + + self._runner = Runner(debug=debug, loop_factory=loop_factory) + self._exceptions: list[BaseException] = [] + self._runner_task: asyncio.Task | None = None + + def __enter__(self) -> TestRunner: + self._runner.__enter__() + self.get_loop().set_exception_handler(self._exception_handler) + return self + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + self._runner.__exit__(exc_type, exc_val, exc_tb) + + def get_loop(self) -> AbstractEventLoop: + return self._runner.get_loop() + + def _exception_handler( + self, loop: asyncio.AbstractEventLoop, context: dict[str, Any] + ) -> None: + if isinstance(context.get("exception"), Exception): + self._exceptions.append(context["exception"]) + else: + loop.default_exception_handler(context) + + def _raise_async_exceptions(self) -> None: + # Re-raise any exceptions raised in asynchronous callbacks + if self._exceptions: + exceptions, self._exceptions = self._exceptions, [] + if len(exceptions) == 1: + raise exceptions[0] + elif exceptions: + raise BaseExceptionGroup( + "Multiple exceptions occurred in asynchronous callbacks", exceptions + ) + + async def _run_tests_and_fixtures( + self, + receive_stream: MemoryObjectReceiveStream[ + tuple[Awaitable[T_Retval], asyncio.Future[T_Retval]] + ], + ) -> None: + from _pytest.outcomes import OutcomeException + + with receive_stream, self._send_stream: + async for coro, future in receive_stream: + try: + retval = await coro + except CancelledError as exc: + if not future.cancelled(): + future.cancel(*exc.args) + + raise + except BaseException as exc: + if not future.cancelled(): + future.set_exception(exc) + + if not isinstance(exc, (Exception, OutcomeException)): + raise + else: + if not future.cancelled(): + future.set_result(retval) + + async def _call_in_runner_task( + self, + func: Callable[P, Awaitable[T_Retval]], + /, + *args: P.args, + **kwargs: P.kwargs, + ) -> T_Retval: + if not self._runner_task: + self._send_stream, receive_stream = create_memory_object_stream[ + tuple[Awaitable[Any], asyncio.Future] + ](1) + self._runner_task = self.get_loop().create_task( + self._run_tests_and_fixtures(receive_stream) + ) + + coro = func(*args, **kwargs) + future: asyncio.Future[T_Retval] = self.get_loop().create_future() + self._send_stream.send_nowait((coro, future)) + return await future + + def run_asyncgen_fixture( + self, + fixture_func: Callable[..., AsyncGenerator[T_Retval, Any]], + kwargs: dict[str, Any], + ) -> Iterable[T_Retval]: + asyncgen = fixture_func(**kwargs) + fixturevalue: T_Retval = self.get_loop().run_until_complete( + self._call_in_runner_task(asyncgen.asend, None) + ) + self._raise_async_exceptions() + + yield fixturevalue + + try: + self.get_loop().run_until_complete( + self._call_in_runner_task(asyncgen.asend, None) + ) + except StopAsyncIteration: + self._raise_async_exceptions() + else: + self.get_loop().run_until_complete(asyncgen.aclose()) + raise RuntimeError("Async generator fixture did not stop") + + def run_fixture( + self, + fixture_func: Callable[..., Coroutine[Any, Any, T_Retval]], + kwargs: dict[str, Any], + ) -> T_Retval: + retval = self.get_loop().run_until_complete( + self._call_in_runner_task(fixture_func, **kwargs) + ) + self._raise_async_exceptions() + return retval + + def run_test( + self, test_func: Callable[..., Coroutine[Any, Any, Any]], kwargs: dict[str, Any] + ) -> None: + try: + self.get_loop().run_until_complete( + self._call_in_runner_task(test_func, **kwargs) + ) + except Exception as exc: + self._exceptions.append(exc) + + self._raise_async_exceptions() + + +class AsyncIOBackend(AsyncBackend): + @classmethod + def run( + cls, + func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval]], + args: tuple[Unpack[PosArgsT]], + kwargs: dict[str, Any], + options: dict[str, Any], + ) -> T_Retval: + @wraps(func) + async def wrapper() -> T_Retval: + task = cast(asyncio.Task, current_task()) + task.set_name(get_callable_name(func)) + _task_states[task] = TaskState(None, None) + + try: + return await func(*args) + finally: + del _task_states[task] + + debug = options.get("debug", None) + loop_factory = options.get("loop_factory", None) + if loop_factory is None and options.get("use_uvloop", False): + if sys.platform != "win32": + import uvloop + + loop_factory = uvloop.new_event_loop + else: + import winloop + + loop_factory = winloop.new_event_loop + + with Runner(debug=debug, loop_factory=loop_factory) as runner: + return runner.run(wrapper()) + + @classmethod + def current_token(cls) -> object: + return get_running_loop() + + @classmethod + def current_time(cls) -> float: + return get_running_loop().time() + + @classmethod + def cancelled_exception_class(cls) -> type[BaseException]: + return CancelledError + + @classmethod + async def checkpoint(cls) -> None: + await sleep(0) + + @classmethod + async def checkpoint_if_cancelled(cls) -> None: + task = current_task() + if task is None: + return + + try: + cancel_scope = _task_states[task].cancel_scope + except KeyError: + return + + while cancel_scope: + if cancel_scope.cancel_called: + await sleep(0) + elif cancel_scope.shield: + break + else: + cancel_scope = cancel_scope._parent_scope + + @classmethod + async def cancel_shielded_checkpoint(cls) -> None: + with CancelScope(shield=True): + await sleep(0) + + @classmethod + async def sleep(cls, delay: float) -> None: + await sleep(delay) + + @classmethod + def create_cancel_scope( + cls, *, deadline: float = math.inf, shield: bool = False + ) -> CancelScope: + return CancelScope(deadline=deadline, shield=shield) + + @classmethod + def current_effective_deadline(cls) -> float: + if (task := current_task()) is None: + return math.inf + + try: + cancel_scope = _task_states[task].cancel_scope + except KeyError: + return math.inf + + deadline = math.inf + while cancel_scope: + deadline = min(deadline, cancel_scope.deadline) + if cancel_scope._cancel_called: + deadline = -math.inf + break + elif cancel_scope.shield: + break + else: + cancel_scope = cancel_scope._parent_scope + + return deadline + + @classmethod + def create_task_group(cls) -> abc.TaskGroup: + return TaskGroup() + + @classmethod + def create_event(cls) -> abc.Event: + return Event() + + @classmethod + def create_lock(cls, *, fast_acquire: bool) -> abc.Lock: + return Lock(fast_acquire=fast_acquire) + + @classmethod + def create_semaphore( + cls, + initial_value: int, + *, + max_value: int | None = None, + fast_acquire: bool = False, + ) -> abc.Semaphore: + return Semaphore(initial_value, max_value=max_value, fast_acquire=fast_acquire) + + @classmethod + def create_capacity_limiter(cls, total_tokens: float) -> abc.CapacityLimiter: + return CapacityLimiter(total_tokens) + + @classmethod + async def run_sync_in_worker_thread( # type: ignore[return] + cls, + func: Callable[[Unpack[PosArgsT]], T_Retval], + args: tuple[Unpack[PosArgsT]], + abandon_on_cancel: bool = False, + limiter: abc.CapacityLimiter | None = None, + ) -> T_Retval: + await cls.checkpoint() + + # If this is the first run in this event loop thread, set up the necessary + # variables + try: + idle_workers = _threadpool_idle_workers.get() + workers = _threadpool_workers.get() + except LookupError: + idle_workers = deque() + workers = set() + _threadpool_idle_workers.set(idle_workers) + _threadpool_workers.set(workers) + + async with limiter or cls.current_default_thread_limiter(): + with CancelScope(shield=not abandon_on_cancel) as scope: + future = asyncio.Future[T_Retval]() + root_task = find_root_task() + if not idle_workers: + worker = WorkerThread(root_task, workers, idle_workers) + worker.start() + workers.add(worker) + root_task.add_done_callback( + worker.stop, context=contextvars.Context() + ) + else: + worker = idle_workers.pop() + + # Prune any other workers that have been idle for MAX_IDLE_TIME + # seconds or longer + now = cls.current_time() + while idle_workers: + if ( + now - idle_workers[0].idle_since + < WorkerThread.MAX_IDLE_TIME + ): + break + + expired_worker = idle_workers.popleft() + expired_worker.root_task.remove_done_callback( + expired_worker.stop + ) + expired_worker.stop() + + context = copy_context() + context.run(set_current_async_library, None) + if abandon_on_cancel or scope._parent_scope is None: + worker_scope = scope + else: + worker_scope = scope._parent_scope + + worker.queue.put_nowait((context, func, args, future, worker_scope)) + return await future + + @classmethod + def check_cancelled(cls) -> None: + scope: CancelScope | None = threadlocals.current_cancel_scope + while scope is not None: + if scope.cancel_called: + raise CancelledError(f"Cancelled by cancel scope {id(scope):x}") + + if scope.shield: + return + + scope = scope._parent_scope + + @classmethod + def run_async_from_thread( + cls, + func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval]], + args: tuple[Unpack[PosArgsT]], + token: object, + ) -> T_Retval: + async def task_wrapper() -> T_Retval: + __tracebackhide__ = True + if scope is not None: + task = cast(asyncio.Task, current_task()) + _task_states[task] = TaskState(None, scope) + scope._tasks.add(task) + try: + return await func(*args) + except CancelledError as exc: + raise concurrent.futures.CancelledError(str(exc)) from None + finally: + if scope is not None: + scope._tasks.discard(task) + + loop = cast( + "AbstractEventLoop", token or threadlocals.current_token.native_token + ) + if loop.is_closed(): + raise RunFinishedError + + context = copy_context() + context.run(set_current_async_library, "asyncio") + scope = getattr(threadlocals, "current_cancel_scope", None) + f: concurrent.futures.Future[T_Retval] = context.run( + asyncio.run_coroutine_threadsafe, task_wrapper(), loop=loop + ) + return f.result() + + @classmethod + def run_sync_from_thread( + cls, + func: Callable[[Unpack[PosArgsT]], T_Retval], + args: tuple[Unpack[PosArgsT]], + token: object, + ) -> T_Retval: + @wraps(func) + def wrapper() -> None: + try: + set_current_async_library("asyncio") + f.set_result(func(*args)) + except BaseException as exc: + f.set_exception(exc) + if not isinstance(exc, Exception): + raise + + loop = cast( + "AbstractEventLoop", token or threadlocals.current_token.native_token + ) + if loop.is_closed(): + raise RunFinishedError + + f: concurrent.futures.Future[T_Retval] = Future() + loop.call_soon_threadsafe(wrapper) + return f.result() + + @classmethod + async def open_process( + cls, + command: StrOrBytesPath | Sequence[StrOrBytesPath], + *, + stdin: int | IO[Any] | None, + stdout: int | IO[Any] | None, + stderr: int | IO[Any] | None, + **kwargs: Any, + ) -> Process: + await cls.checkpoint() + if isinstance(command, PathLike): + command = os.fspath(command) + + if isinstance(command, (str, bytes)): + process = await asyncio.create_subprocess_shell( + command, + stdin=stdin, + stdout=stdout, + stderr=stderr, + **kwargs, + ) + else: + process = await asyncio.create_subprocess_exec( + *command, + stdin=stdin, + stdout=stdout, + stderr=stderr, + **kwargs, + ) + + stdin_stream = StreamWriterWrapper(process.stdin) if process.stdin else None + stdout_stream = StreamReaderWrapper(process.stdout) if process.stdout else None + stderr_stream = StreamReaderWrapper(process.stderr) if process.stderr else None + return Process(process, stdin_stream, stdout_stream, stderr_stream) + + @classmethod + def setup_process_pool_exit_at_shutdown(cls, workers: set[abc.Process]) -> None: + create_task( + _shutdown_process_pool_on_exit(workers), + name="AnyIO process pool shutdown task", + ) + find_root_task().add_done_callback( + partial(_forcibly_shutdown_process_pool_on_exit, workers) # type:ignore[arg-type] + ) + + @classmethod + async def connect_tcp( + cls, host: str, port: int, local_address: IPSockAddrType | None = None + ) -> abc.SocketStream: + transport, protocol = cast( + tuple[asyncio.Transport, StreamProtocol], + await get_running_loop().create_connection( + StreamProtocol, host, port, local_addr=local_address + ), + ) + transport.pause_reading() + return SocketStream(transport, protocol) + + @classmethod + async def connect_unix(cls, path: str | bytes) -> abc.UNIXSocketStream: + await cls.checkpoint() + loop = get_running_loop() + raw_socket = socket.socket(socket.AF_UNIX) + raw_socket.setblocking(False) + while True: + try: + raw_socket.connect(path) + except BlockingIOError: + f: asyncio.Future = asyncio.Future() + loop.add_writer(raw_socket, f.set_result, None) + f.add_done_callback(lambda _: loop.remove_writer(raw_socket)) + await f + except BaseException: + raw_socket.close() + raise + else: + return UNIXSocketStream(raw_socket) + + @classmethod + def create_tcp_listener(cls, sock: socket.socket) -> SocketListener: + return TCPSocketListener(sock) + + @classmethod + def create_unix_listener(cls, sock: socket.socket) -> SocketListener: + return UNIXSocketListener(sock) + + @classmethod + async def create_udp_socket( + cls, + family: AddressFamily, + local_address: IPSockAddrType | None, + remote_address: IPSockAddrType | None, + reuse_port: bool, + ) -> UDPSocket | ConnectedUDPSocket: + transport, protocol = await get_running_loop().create_datagram_endpoint( + DatagramProtocol, + local_addr=local_address, + remote_addr=remote_address, + family=family, + reuse_port=reuse_port, + ) + if protocol.exception: + transport.close() + raise protocol.exception + + if not remote_address: + return UDPSocket(transport, protocol) + else: + return ConnectedUDPSocket(transport, protocol) + + @classmethod + async def create_unix_datagram_socket( # type: ignore[override] + cls, raw_socket: socket.socket, remote_path: str | bytes | None + ) -> abc.UNIXDatagramSocket | abc.ConnectedUNIXDatagramSocket: + await cls.checkpoint() + loop = get_running_loop() + + if remote_path: + while True: + try: + raw_socket.connect(remote_path) + except BlockingIOError: + f: asyncio.Future = asyncio.Future() + loop.add_writer(raw_socket, f.set_result, None) + f.add_done_callback(lambda _: loop.remove_writer(raw_socket)) + await f + except BaseException: + raw_socket.close() + raise + else: + return ConnectedUNIXDatagramSocket(raw_socket) + else: + return UNIXDatagramSocket(raw_socket) + + @classmethod + async def getaddrinfo( + cls, + host: bytes | str | None, + port: str | int | None, + *, + family: int | AddressFamily = 0, + type: int | SocketKind = 0, + proto: int = 0, + flags: int = 0, + ) -> Sequence[ + tuple[ + AddressFamily, + SocketKind, + int, + str, + tuple[str, int] | tuple[str, int, int, int] | tuple[int, bytes], + ] + ]: + return await get_running_loop().getaddrinfo( + host, port, family=family, type=type, proto=proto, flags=flags + ) + + @classmethod + async def getnameinfo( + cls, sockaddr: IPSockAddrType, flags: int = 0 + ) -> tuple[str, str]: + return await get_running_loop().getnameinfo(sockaddr, flags) + + @classmethod + async def wait_readable(cls, obj: FileDescriptorLike) -> None: + try: + read_events = _read_events.get() + except LookupError: + read_events = {} + _read_events.set(read_events) + + fd = obj if isinstance(obj, int) else obj.fileno() + if read_events.get(fd): + raise BusyResourceError("reading from") + + loop = get_running_loop() + fut: asyncio.Future[bool] = loop.create_future() + + def cb() -> None: + try: + del read_events[fd] + except KeyError: + pass + else: + remove_reader(fd) + + try: + fut.set_result(True) + except asyncio.InvalidStateError: + pass + + try: + loop.add_reader(fd, cb) + except NotImplementedError: + from anyio._core._asyncio_selector_thread import get_selector + + selector = get_selector() + selector.add_reader(fd, cb) + remove_reader = selector.remove_reader + else: + remove_reader = loop.remove_reader + + read_events[fd] = fut + try: + success = await fut + finally: + try: + del read_events[fd] + except KeyError: + pass + else: + remove_reader(fd) + + if not success: + raise ClosedResourceError + + @classmethod + async def wait_writable(cls, obj: FileDescriptorLike) -> None: + try: + write_events = _write_events.get() + except LookupError: + write_events = {} + _write_events.set(write_events) + + fd = obj if isinstance(obj, int) else obj.fileno() + if write_events.get(fd): + raise BusyResourceError("writing to") + + loop = get_running_loop() + fut: asyncio.Future[bool] = loop.create_future() + + def cb() -> None: + try: + del write_events[fd] + except KeyError: + pass + else: + remove_writer(fd) + + try: + fut.set_result(True) + except asyncio.InvalidStateError: + pass + + try: + loop.add_writer(fd, cb) + except NotImplementedError: + from anyio._core._asyncio_selector_thread import get_selector + + selector = get_selector() + selector.add_writer(fd, cb) + remove_writer = selector.remove_writer + else: + remove_writer = loop.remove_writer + + write_events[fd] = fut + try: + success = await fut + finally: + try: + del write_events[fd] + except KeyError: + pass + else: + remove_writer(fd) + + if not success: + raise ClosedResourceError + + @classmethod + def notify_closing(cls, obj: FileDescriptorLike) -> None: + fd = obj if isinstance(obj, int) else obj.fileno() + loop = get_running_loop() + + try: + write_events = _write_events.get() + except LookupError: + pass + else: + try: + fut = write_events.pop(fd) + except KeyError: + pass + else: + try: + fut.set_result(False) + except asyncio.InvalidStateError: + pass + + try: + loop.remove_writer(fd) + except NotImplementedError: + from anyio._core._asyncio_selector_thread import get_selector + + get_selector().remove_writer(fd) + + try: + read_events = _read_events.get() + except LookupError: + pass + else: + try: + fut = read_events.pop(fd) + except KeyError: + pass + else: + try: + fut.set_result(False) + except asyncio.InvalidStateError: + pass + + try: + loop.remove_reader(fd) + except NotImplementedError: + from anyio._core._asyncio_selector_thread import get_selector + + get_selector().remove_reader(fd) + + @classmethod + async def wrap_listener_socket(cls, sock: socket.socket) -> SocketListener: + return TCPSocketListener(sock) + + @classmethod + async def wrap_stream_socket(cls, sock: socket.socket) -> SocketStream: + transport, protocol = await get_running_loop().create_connection( + StreamProtocol, sock=sock + ) + return SocketStream(transport, protocol) + + @classmethod + async def wrap_unix_stream_socket(cls, sock: socket.socket) -> UNIXSocketStream: + return UNIXSocketStream(sock) + + @classmethod + async def wrap_udp_socket(cls, sock: socket.socket) -> UDPSocket: + transport, protocol = await get_running_loop().create_datagram_endpoint( + DatagramProtocol, sock=sock + ) + return UDPSocket(transport, protocol) + + @classmethod + async def wrap_connected_udp_socket(cls, sock: socket.socket) -> ConnectedUDPSocket: + transport, protocol = await get_running_loop().create_datagram_endpoint( + DatagramProtocol, sock=sock + ) + return ConnectedUDPSocket(transport, protocol) + + @classmethod + async def wrap_unix_datagram_socket(cls, sock: socket.socket) -> UNIXDatagramSocket: + return UNIXDatagramSocket(sock) + + @classmethod + async def wrap_connected_unix_datagram_socket( + cls, sock: socket.socket + ) -> ConnectedUNIXDatagramSocket: + return ConnectedUNIXDatagramSocket(sock) + + @classmethod + def current_default_thread_limiter(cls) -> CapacityLimiter: + try: + return _default_thread_limiter.get() + except LookupError: + limiter = CapacityLimiter(40) + _default_thread_limiter.set(limiter) + return limiter + + @classmethod + def open_signal_receiver( + cls, *signals: Signals + ) -> AbstractContextManager[AsyncIterator[Signals]]: + return _SignalReceiver(signals) + + @classmethod + def get_current_task(cls) -> TaskInfo: + return AsyncIOTaskInfo(current_task()) # type: ignore[arg-type] + + @classmethod + def get_running_tasks(cls) -> Sequence[TaskInfo]: + return [AsyncIOTaskInfo(task) for task in all_tasks() if not task.done()] + + @classmethod + async def wait_all_tasks_blocked(cls) -> None: + await cls.checkpoint() + this_task = current_task() + while True: + for task in all_tasks(): + if task is this_task: + continue + + waiter = task._fut_waiter # type: ignore[attr-defined] + if waiter is None or waiter.done(): + await sleep(0.1) + break + else: + return + + @classmethod + def create_test_runner(cls, options: dict[str, Any]) -> TestRunner: + return TestRunner(**options) + + +backend_class = AsyncIOBackend diff --git a/.deps_siteforge_v812/anyio/_backends/_trio.py b/.deps_siteforge_v812/anyio/_backends/_trio.py new file mode 100644 index 0000000..b85a10a --- /dev/null +++ b/.deps_siteforge_v812/anyio/_backends/_trio.py @@ -0,0 +1,1343 @@ +from __future__ import annotations + +import array +import math +import os +import socket +import sys +import types +import weakref +from collections.abc import ( + AsyncGenerator, + AsyncIterator, + Awaitable, + Callable, + Collection, + Coroutine, + Iterable, + Sequence, +) +from contextlib import AbstractContextManager +from dataclasses import dataclass +from io import IOBase +from os import PathLike +from signal import Signals +from socket import AddressFamily, SocketKind +from types import TracebackType +from typing import ( + IO, + TYPE_CHECKING, + Any, + Generic, + NoReturn, + ParamSpec, + TypeVar, + cast, + overload, +) + +import trio.from_thread +import trio.lowlevel +from outcome import Error, Outcome, Value +from trio.lowlevel import ( + current_root_task, + current_task, + notify_closing, + wait_readable, + wait_writable, +) +from trio.socket import SocketType as TrioSocketType +from trio.to_thread import run_sync + +from .. import ( + CapacityLimiterStatistics, + EventStatistics, + LockStatistics, + RunFinishedError, + TaskInfo, + WouldBlock, + abc, +) +from .._core._eventloop import claim_worker_thread +from .._core._exceptions import ( + BrokenResourceError, + BusyResourceError, + ClosedResourceError, + EndOfStream, +) +from .._core._sockets import convert_ipv6_sockaddr +from .._core._streams import create_memory_object_stream +from .._core._synchronization import ( + CapacityLimiter as BaseCapacityLimiter, +) +from .._core._synchronization import Event as BaseEvent +from .._core._synchronization import Lock as BaseLock +from .._core._synchronization import ( + ResourceGuard, + SemaphoreStatistics, +) +from .._core._synchronization import Semaphore as BaseSemaphore +from .._core._tasks import CancelScope as BaseCancelScope +from ..abc import IPSockAddrType, UDPPacketType, UNIXDatagramPacketType +from ..abc._eventloop import AsyncBackend, StrOrBytesPath +from ..streams.memory import MemoryObjectSendStream + +if TYPE_CHECKING: + from _typeshed import FileDescriptorLike + +if sys.version_info >= (3, 11): + from typing import TypeVarTuple, Unpack +else: + from exceptiongroup import BaseExceptionGroup + from typing_extensions import TypeVarTuple, Unpack + +T = TypeVar("T") +T_Retval = TypeVar("T_Retval") +T_SockAddr = TypeVar("T_SockAddr", str, IPSockAddrType) +PosArgsT = TypeVarTuple("PosArgsT") +P = ParamSpec("P") + + +# +# Event loop +# + +RunVar = trio.lowlevel.RunVar + + +# +# Timeouts and cancellation +# + + +class CancelScope(BaseCancelScope): + def __new__( + cls, original: trio.CancelScope | None = None, **kwargs: object + ) -> CancelScope: + return object.__new__(cls) + + def __init__(self, original: trio.CancelScope | None = None, **kwargs: Any) -> None: + self.__original = original or trio.CancelScope(**kwargs) + + def __enter__(self) -> CancelScope: + self.__original.__enter__() + return self + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> bool: + return self.__original.__exit__(exc_type, exc_val, exc_tb) + + def cancel(self, reason: str | None = None) -> None: + self.__original.cancel(reason) + + @property + def deadline(self) -> float: + return self.__original.deadline + + @deadline.setter + def deadline(self, value: float) -> None: + self.__original.deadline = value + + @property + def cancel_called(self) -> bool: + return self.__original.cancel_called + + @property + def cancelled_caught(self) -> bool: + return self.__original.cancelled_caught + + @property + def shield(self) -> bool: + return self.__original.shield + + @shield.setter + def shield(self, value: bool) -> None: + self.__original.shield = value + + +# +# Task groups +# + + +class TaskGroup(abc.TaskGroup): + def __init__(self) -> None: + self._active = False + self._nursery_manager = trio.open_nursery(strict_exception_groups=True) + self.cancel_scope = None # type: ignore[assignment] + + async def __aenter__(self) -> TaskGroup: + self._active = True + self._nursery = await self._nursery_manager.__aenter__() + self.cancel_scope = CancelScope(self._nursery.cancel_scope) + return self + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> bool: + try: + # trio.Nursery.__exit__ returns bool; .open_nursery has wrong type + return await self._nursery_manager.__aexit__(exc_type, exc_val, exc_tb) # type: ignore[return-value] + except BaseExceptionGroup as exc: + if not exc.split(trio.Cancelled)[1]: + raise trio.Cancelled._create() from exc + + raise + finally: + del exc_val, exc_tb + self._active = False + + def start_soon( + self, + func: Callable[[Unpack[PosArgsT]], Awaitable[Any]], + *args: Unpack[PosArgsT], + name: object = None, + ) -> None: + if not self._active: + raise RuntimeError( + "This task group is not active; no new tasks can be started." + ) + + self._nursery.start_soon(func, *args, name=name) + + async def start( + self, func: Callable[..., Awaitable[Any]], *args: object, name: object = None + ) -> Any: + if not self._active: + raise RuntimeError( + "This task group is not active; no new tasks can be started." + ) + + return await self._nursery.start(func, *args, name=name) + + +# +# Subprocesses +# + + +@dataclass(eq=False) +class ReceiveStreamWrapper(abc.ByteReceiveStream): + _stream: trio.abc.ReceiveStream + + async def receive(self, max_bytes: int | None = None) -> bytes: + try: + data = await self._stream.receive_some(max_bytes) + except trio.ClosedResourceError as exc: + raise ClosedResourceError from exc.__cause__ + except trio.BrokenResourceError as exc: + raise BrokenResourceError from exc.__cause__ + + if data: + return bytes(data) + else: + raise EndOfStream + + async def aclose(self) -> None: + await self._stream.aclose() + + +@dataclass(eq=False) +class SendStreamWrapper(abc.ByteSendStream): + _stream: trio.abc.SendStream + + async def send(self, item: bytes) -> None: + try: + await self._stream.send_all(item) + except trio.ClosedResourceError as exc: + raise ClosedResourceError from exc.__cause__ + except trio.BrokenResourceError as exc: + raise BrokenResourceError from exc.__cause__ + + async def aclose(self) -> None: + await self._stream.aclose() + + +@dataclass(eq=False) +class Process(abc.Process): + _process: trio.Process + _stdin: abc.ByteSendStream | None + _stdout: abc.ByteReceiveStream | None + _stderr: abc.ByteReceiveStream | None + + async def aclose(self) -> None: + with CancelScope(shield=True): + if self._stdin: + await self._stdin.aclose() + if self._stdout: + await self._stdout.aclose() + if self._stderr: + await self._stderr.aclose() + + try: + await self.wait() + except BaseException: + self.kill() + with CancelScope(shield=True): + await self.wait() + raise + + async def wait(self) -> int: + return await self._process.wait() + + def terminate(self) -> None: + self._process.terminate() + + def kill(self) -> None: + self._process.kill() + + def send_signal(self, signal: Signals) -> None: + self._process.send_signal(signal) + + @property + def pid(self) -> int: + return self._process.pid + + @property + def returncode(self) -> int | None: + return self._process.returncode + + @property + def stdin(self) -> abc.ByteSendStream | None: + return self._stdin + + @property + def stdout(self) -> abc.ByteReceiveStream | None: + return self._stdout + + @property + def stderr(self) -> abc.ByteReceiveStream | None: + return self._stderr + + +class _ProcessPoolShutdownInstrument(trio.abc.Instrument): + def after_run(self) -> None: + super().after_run() + + +current_default_worker_process_limiter: trio.lowlevel.RunVar = RunVar( + "current_default_worker_process_limiter" +) + + +async def _shutdown_process_pool(workers: set[abc.Process]) -> None: + try: + await trio.sleep(math.inf) + except trio.Cancelled: + for process in workers: + if process.returncode is None: + process.kill() + + with CancelScope(shield=True): + for process in workers: + await process.aclose() + + +# +# Sockets and networking +# + + +class _TrioSocketMixin(Generic[T_SockAddr]): + def __init__(self, trio_socket: TrioSocketType) -> None: + self._trio_socket = trio_socket + self._closed = False + + def _check_closed(self) -> None: + if self._closed: + raise ClosedResourceError + if self._trio_socket.fileno() < 0: + raise BrokenResourceError + + @property + def _raw_socket(self) -> socket.socket: + return self._trio_socket._sock # type: ignore[attr-defined] + + async def aclose(self) -> None: + if self._trio_socket.fileno() >= 0: + self._closed = True + self._trio_socket.close() + + def _convert_socket_error(self, exc: BaseException) -> NoReturn: + if isinstance(exc, trio.ClosedResourceError): + raise ClosedResourceError from exc + elif self._trio_socket.fileno() < 0 and self._closed: + raise ClosedResourceError from None + elif isinstance(exc, OSError): + raise BrokenResourceError from exc + else: + raise exc + + +class SocketStream(_TrioSocketMixin, abc.SocketStream): + def __init__(self, trio_socket: TrioSocketType) -> None: + super().__init__(trio_socket) + self._receive_guard = ResourceGuard("reading from") + self._send_guard = ResourceGuard("writing to") + + async def receive(self, max_bytes: int = 65536) -> bytes: + with self._receive_guard: + try: + data = await self._trio_socket.recv(max_bytes) + except BaseException as exc: + self._convert_socket_error(exc) + + if data: + return data + else: + raise EndOfStream + + async def send(self, item: bytes) -> None: + with self._send_guard: + view = memoryview(item) + while view: + try: + bytes_sent = await self._trio_socket.send(view) + except BaseException as exc: + self._convert_socket_error(exc) + + view = view[bytes_sent:] + + async def send_eof(self) -> None: + self._trio_socket.shutdown(socket.SHUT_WR) + + +class UNIXSocketStream(SocketStream, abc.UNIXSocketStream): + async def receive_fds(self, msglen: int, maxfds: int) -> tuple[bytes, list[int]]: + if not isinstance(msglen, int) or msglen < 0: + raise ValueError("msglen must be a non-negative integer") + if not isinstance(maxfds, int) or maxfds < 1: + raise ValueError("maxfds must be a positive integer") + + fds = array.array("i") + await trio.lowlevel.checkpoint() + with self._receive_guard: + while True: + try: + message, ancdata, flags, addr = await self._trio_socket.recvmsg( + msglen, socket.CMSG_LEN(maxfds * fds.itemsize) + ) + except BaseException as exc: + self._convert_socket_error(exc) + else: + if not message and not ancdata: + raise EndOfStream + + break + + for cmsg_level, cmsg_type, cmsg_data in ancdata: + if cmsg_level != socket.SOL_SOCKET or cmsg_type != socket.SCM_RIGHTS: + raise RuntimeError( + f"Received unexpected ancillary data; message = {message!r}, " + f"cmsg_level = {cmsg_level}, cmsg_type = {cmsg_type}" + ) + + fds.frombytes(cmsg_data[: len(cmsg_data) - (len(cmsg_data) % fds.itemsize)]) + + return message, list(fds) + + async def send_fds(self, message: bytes, fds: Collection[int | IOBase]) -> None: + if not message: + raise ValueError("message must not be empty") + if not fds: + raise ValueError("fds must not be empty") + + filenos: list[int] = [] + for fd in fds: + if isinstance(fd, int): + filenos.append(fd) + elif isinstance(fd, IOBase): + filenos.append(fd.fileno()) + + fdarray = array.array("i", filenos) + await trio.lowlevel.checkpoint() + with self._send_guard: + while True: + try: + await self._trio_socket.sendmsg( + [message], + [ + ( + socket.SOL_SOCKET, + socket.SCM_RIGHTS, + fdarray, + ) + ], + ) + break + except BaseException as exc: + self._convert_socket_error(exc) + + +class TCPSocketListener(_TrioSocketMixin, abc.SocketListener): + def __init__(self, raw_socket: socket.socket): + super().__init__(trio.socket.from_stdlib_socket(raw_socket)) + self._accept_guard = ResourceGuard("accepting connections from") + + async def accept(self) -> SocketStream: + with self._accept_guard: + try: + trio_socket, _addr = await self._trio_socket.accept() + except BaseException as exc: + self._convert_socket_error(exc) + + trio_socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) + return SocketStream(trio_socket) + + +class UNIXSocketListener(_TrioSocketMixin, abc.SocketListener): + def __init__(self, raw_socket: socket.socket): + super().__init__(trio.socket.from_stdlib_socket(raw_socket)) + self._accept_guard = ResourceGuard("accepting connections from") + + async def accept(self) -> UNIXSocketStream: + with self._accept_guard: + try: + trio_socket, _addr = await self._trio_socket.accept() + except BaseException as exc: + self._convert_socket_error(exc) + + return UNIXSocketStream(trio_socket) + + +class UDPSocket(_TrioSocketMixin[IPSockAddrType], abc.UDPSocket): + def __init__(self, trio_socket: TrioSocketType) -> None: + super().__init__(trio_socket) + self._receive_guard = ResourceGuard("reading from") + self._send_guard = ResourceGuard("writing to") + + async def receive(self) -> tuple[bytes, IPSockAddrType]: + with self._receive_guard: + try: + data, addr = await self._trio_socket.recvfrom(65536) + return data, convert_ipv6_sockaddr(addr) + except BaseException as exc: + self._convert_socket_error(exc) + + async def send(self, item: UDPPacketType) -> None: + with self._send_guard: + try: + await self._trio_socket.sendto(*item) + except BaseException as exc: + self._convert_socket_error(exc) + + +class ConnectedUDPSocket(_TrioSocketMixin[IPSockAddrType], abc.ConnectedUDPSocket): + def __init__(self, trio_socket: TrioSocketType) -> None: + super().__init__(trio_socket) + self._receive_guard = ResourceGuard("reading from") + self._send_guard = ResourceGuard("writing to") + + async def receive(self) -> bytes: + with self._receive_guard: + try: + return await self._trio_socket.recv(65536) + except BaseException as exc: + self._convert_socket_error(exc) + + async def send(self, item: bytes) -> None: + with self._send_guard: + try: + await self._trio_socket.send(item) + except BaseException as exc: + self._convert_socket_error(exc) + + +class UNIXDatagramSocket(_TrioSocketMixin[str], abc.UNIXDatagramSocket): + def __init__(self, trio_socket: TrioSocketType) -> None: + super().__init__(trio_socket) + self._receive_guard = ResourceGuard("reading from") + self._send_guard = ResourceGuard("writing to") + + async def receive(self) -> UNIXDatagramPacketType: + with self._receive_guard: + try: + data, addr = await self._trio_socket.recvfrom(65536) + return data, addr + except BaseException as exc: + self._convert_socket_error(exc) + + async def send(self, item: UNIXDatagramPacketType) -> None: + with self._send_guard: + try: + await self._trio_socket.sendto(*item) + except BaseException as exc: + self._convert_socket_error(exc) + + +class ConnectedUNIXDatagramSocket( + _TrioSocketMixin[str], abc.ConnectedUNIXDatagramSocket +): + def __init__(self, trio_socket: TrioSocketType) -> None: + super().__init__(trio_socket) + self._receive_guard = ResourceGuard("reading from") + self._send_guard = ResourceGuard("writing to") + + async def receive(self) -> bytes: + with self._receive_guard: + try: + return await self._trio_socket.recv(65536) + except BaseException as exc: + self._convert_socket_error(exc) + + async def send(self, item: bytes) -> None: + with self._send_guard: + try: + await self._trio_socket.send(item) + except BaseException as exc: + self._convert_socket_error(exc) + + +# +# Synchronization +# + + +class Event(BaseEvent): + def __new__(cls) -> Event: + return object.__new__(cls) + + def __init__(self) -> None: + self.__original = trio.Event() + + def is_set(self) -> bool: + return self.__original.is_set() + + async def wait(self) -> None: + return await self.__original.wait() + + def statistics(self) -> EventStatistics: + orig_statistics = self.__original.statistics() + return EventStatistics(tasks_waiting=orig_statistics.tasks_waiting) + + def set(self) -> None: + self.__original.set() + + +class Lock(BaseLock): + def __new__(cls, *, fast_acquire: bool = False) -> Lock: + return object.__new__(cls) + + def __init__(self, *, fast_acquire: bool = False) -> None: + self._fast_acquire = fast_acquire + self.__original = trio.Lock() + + @staticmethod + def _convert_runtime_error_msg(exc: RuntimeError) -> None: + if exc.args == ("attempt to re-acquire an already held Lock",): + exc.args = ("Attempted to acquire an already held Lock",) + + async def acquire(self) -> None: + if not self._fast_acquire: + try: + await self.__original.acquire() + except RuntimeError as exc: + self._convert_runtime_error_msg(exc) + raise + + return + + # This is the "fast path" where we don't let other tasks run + await trio.lowlevel.checkpoint_if_cancelled() + try: + self.__original.acquire_nowait() + except trio.WouldBlock: + await self.__original._lot.park() + except RuntimeError as exc: + self._convert_runtime_error_msg(exc) + raise + + def acquire_nowait(self) -> None: + try: + self.__original.acquire_nowait() + except trio.WouldBlock: + raise WouldBlock from None + except RuntimeError as exc: + self._convert_runtime_error_msg(exc) + raise + + def locked(self) -> bool: + return self.__original.locked() + + def release(self) -> None: + self.__original.release() + + def statistics(self) -> LockStatistics: + orig_statistics = self.__original.statistics() + owner = TrioTaskInfo(orig_statistics.owner) if orig_statistics.owner else None + return LockStatistics( + orig_statistics.locked, owner, orig_statistics.tasks_waiting + ) + + +class Semaphore(BaseSemaphore): + def __new__( + cls, + initial_value: int, + *, + max_value: int | None = None, + fast_acquire: bool = False, + ) -> Semaphore: + return object.__new__(cls) + + def __init__( + self, + initial_value: int, + *, + max_value: int | None = None, + fast_acquire: bool = False, + ) -> None: + super().__init__(initial_value, max_value=max_value, fast_acquire=fast_acquire) + self.__original = trio.Semaphore(initial_value, max_value=max_value) + + async def acquire(self) -> None: + if not self._fast_acquire: + await self.__original.acquire() + return + + # This is the "fast path" where we don't let other tasks run + await trio.lowlevel.checkpoint_if_cancelled() + try: + self.__original.acquire_nowait() + except trio.WouldBlock: + await self.__original._lot.park() + + def acquire_nowait(self) -> None: + try: + self.__original.acquire_nowait() + except trio.WouldBlock: + raise WouldBlock from None + + @property + def max_value(self) -> int | None: + return self.__original.max_value + + @property + def value(self) -> int: + return self.__original.value + + def release(self) -> None: + self.__original.release() + + def statistics(self) -> SemaphoreStatistics: + orig_statistics = self.__original.statistics() + return SemaphoreStatistics(orig_statistics.tasks_waiting) + + +class CapacityLimiter(BaseCapacityLimiter): + def __new__( + cls, + total_tokens: float | None = None, + *, + original: trio.CapacityLimiter | None = None, + ) -> CapacityLimiter: + return object.__new__(cls) + + def __init__( + self, + total_tokens: float | None = None, + *, + original: trio.CapacityLimiter | None = None, + ) -> None: + if original is not None: + self.__original = original + else: + assert total_tokens is not None + self.__original = trio.CapacityLimiter(total_tokens) + + async def __aenter__(self) -> None: + return await self.__original.__aenter__() + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + await self.__original.__aexit__(exc_type, exc_val, exc_tb) + + @property + def total_tokens(self) -> float: + return self.__original.total_tokens + + @total_tokens.setter + def total_tokens(self, value: float) -> None: + self.__original.total_tokens = value + + @property + def borrowed_tokens(self) -> int: + return self.__original.borrowed_tokens + + @property + def available_tokens(self) -> float: + return self.__original.available_tokens + + def acquire_nowait(self) -> None: + self.__original.acquire_nowait() + + def acquire_on_behalf_of_nowait(self, borrower: object) -> None: + self.__original.acquire_on_behalf_of_nowait(borrower) + + async def acquire(self) -> None: + await self.__original.acquire() + + async def acquire_on_behalf_of(self, borrower: object) -> None: + await self.__original.acquire_on_behalf_of(borrower) + + def release(self) -> None: + return self.__original.release() + + def release_on_behalf_of(self, borrower: object) -> None: + return self.__original.release_on_behalf_of(borrower) + + def statistics(self) -> CapacityLimiterStatistics: + orig = self.__original.statistics() + return CapacityLimiterStatistics( + borrowed_tokens=orig.borrowed_tokens, + total_tokens=orig.total_tokens, + borrowers=tuple(orig.borrowers), + tasks_waiting=orig.tasks_waiting, + ) + + +_capacity_limiter_wrapper: trio.lowlevel.RunVar = RunVar("_capacity_limiter_wrapper") + + +# +# Signal handling +# + + +class _SignalReceiver: + _iterator: AsyncIterator[int] + + def __init__(self, signals: tuple[Signals, ...]): + self._signals = signals + + def __enter__(self) -> _SignalReceiver: + self._cm = trio.open_signal_receiver(*self._signals) + self._iterator = self._cm.__enter__() + return self + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> bool | None: + return self._cm.__exit__(exc_type, exc_val, exc_tb) + + def __aiter__(self) -> _SignalReceiver: + return self + + async def __anext__(self) -> Signals: + signum = await self._iterator.__anext__() + return Signals(signum) + + +# +# Testing and debugging +# + + +class TestRunner(abc.TestRunner): + def __init__(self, **options: Any) -> None: + from queue import Queue + + self._call_queue: Queue[Callable[[], object]] = Queue() + self._send_stream: MemoryObjectSendStream | None = None + self._options = options + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: types.TracebackType | None, + ) -> None: + if self._send_stream: + self._send_stream.close() + while self._send_stream is not None: + self._call_queue.get()() + + async def _run_tests_and_fixtures(self) -> None: + self._send_stream, receive_stream = create_memory_object_stream(1) + with receive_stream: + async for coro, outcome_holder in receive_stream: + try: + retval = await coro + except BaseException as exc: + outcome_holder.append(Error(exc)) + else: + outcome_holder.append(Value(retval)) + + def _main_task_finished(self, outcome: object) -> None: + self._send_stream = None + + def _call_in_runner_task( + self, + func: Callable[P, Awaitable[T_Retval]], + /, + *args: P.args, + **kwargs: P.kwargs, + ) -> T_Retval: + if self._send_stream is None: + trio.lowlevel.start_guest_run( + self._run_tests_and_fixtures, + run_sync_soon_threadsafe=self._call_queue.put, + done_callback=self._main_task_finished, + **self._options, + ) + while self._send_stream is None: + self._call_queue.get()() + + outcome_holder: list[Outcome] = [] + self._send_stream.send_nowait((func(*args, **kwargs), outcome_holder)) + while not outcome_holder: + self._call_queue.get()() + + return outcome_holder[0].unwrap() + + def run_asyncgen_fixture( + self, + fixture_func: Callable[..., AsyncGenerator[T_Retval, Any]], + kwargs: dict[str, Any], + ) -> Iterable[T_Retval]: + asyncgen = fixture_func(**kwargs) + fixturevalue: T_Retval = self._call_in_runner_task(asyncgen.asend, None) + + yield fixturevalue + + try: + self._call_in_runner_task(asyncgen.asend, None) + except StopAsyncIteration: + pass + else: + self._call_in_runner_task(asyncgen.aclose) + raise RuntimeError("Async generator fixture did not stop") + + def run_fixture( + self, + fixture_func: Callable[..., Coroutine[Any, Any, T_Retval]], + kwargs: dict[str, Any], + ) -> T_Retval: + return self._call_in_runner_task(fixture_func, **kwargs) + + def run_test( + self, test_func: Callable[..., Coroutine[Any, Any, Any]], kwargs: dict[str, Any] + ) -> None: + self._call_in_runner_task(test_func, **kwargs) + + +class TrioTaskInfo(TaskInfo): + def __init__(self, task: trio.lowlevel.Task): + parent_id = None + if task.parent_nursery and task.parent_nursery.parent_task: + parent_id = id(task.parent_nursery.parent_task) + + super().__init__(id(task), parent_id, task.name, task.coro) + self._task = weakref.proxy(task) + + def has_pending_cancellation(self) -> bool: + try: + return self._task._cancel_status.effectively_cancelled + except ReferenceError: + # If the task is no longer around, it surely doesn't have a cancellation + # pending + return False + + +class TrioBackend(AsyncBackend): + @classmethod + def run( + cls, + func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval]], + args: tuple[Unpack[PosArgsT]], + kwargs: dict[str, Any], + options: dict[str, Any], + ) -> T_Retval: + return trio.run(func, *args) + + @classmethod + def current_token(cls) -> object: + return trio.lowlevel.current_trio_token() + + @classmethod + def current_time(cls) -> float: + return trio.current_time() + + @classmethod + def cancelled_exception_class(cls) -> type[BaseException]: + return trio.Cancelled + + @classmethod + async def checkpoint(cls) -> None: + await trio.lowlevel.checkpoint() + + @classmethod + async def checkpoint_if_cancelled(cls) -> None: + await trio.lowlevel.checkpoint_if_cancelled() + + @classmethod + async def cancel_shielded_checkpoint(cls) -> None: + await trio.lowlevel.cancel_shielded_checkpoint() + + @classmethod + async def sleep(cls, delay: float) -> None: + await trio.sleep(delay) + + @classmethod + def create_cancel_scope( + cls, *, deadline: float = math.inf, shield: bool = False + ) -> abc.CancelScope: + return CancelScope(deadline=deadline, shield=shield) + + @classmethod + def current_effective_deadline(cls) -> float: + return trio.current_effective_deadline() + + @classmethod + def create_task_group(cls) -> abc.TaskGroup: + return TaskGroup() + + @classmethod + def create_event(cls) -> abc.Event: + return Event() + + @classmethod + def create_lock(cls, *, fast_acquire: bool) -> Lock: + return Lock(fast_acquire=fast_acquire) + + @classmethod + def create_semaphore( + cls, + initial_value: int, + *, + max_value: int | None = None, + fast_acquire: bool = False, + ) -> abc.Semaphore: + return Semaphore(initial_value, max_value=max_value, fast_acquire=fast_acquire) + + @classmethod + def create_capacity_limiter(cls, total_tokens: float) -> CapacityLimiter: + return CapacityLimiter(total_tokens) + + @classmethod + async def run_sync_in_worker_thread( + cls, + func: Callable[[Unpack[PosArgsT]], T_Retval], + args: tuple[Unpack[PosArgsT]], + abandon_on_cancel: bool = False, + limiter: abc.CapacityLimiter | None = None, + ) -> T_Retval: + def wrapper() -> T_Retval: + with claim_worker_thread(TrioBackend, token): + return func(*args) + + token = TrioBackend.current_token() + return await run_sync( + wrapper, + abandon_on_cancel=abandon_on_cancel, + limiter=cast(trio.CapacityLimiter, limiter), + ) + + @classmethod + def check_cancelled(cls) -> None: + trio.from_thread.check_cancelled() + + @classmethod + def run_async_from_thread( + cls, + func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval]], + args: tuple[Unpack[PosArgsT]], + token: object, + ) -> T_Retval: + trio_token = cast("trio.lowlevel.TrioToken | None", token) + try: + return trio.from_thread.run(func, *args, trio_token=trio_token) + except trio.RunFinishedError: + raise RunFinishedError from None + + @classmethod + def run_sync_from_thread( + cls, + func: Callable[[Unpack[PosArgsT]], T_Retval], + args: tuple[Unpack[PosArgsT]], + token: object, + ) -> T_Retval: + trio_token = cast("trio.lowlevel.TrioToken | None", token) + try: + return trio.from_thread.run_sync(func, *args, trio_token=trio_token) + except trio.RunFinishedError: + raise RunFinishedError from None + + @classmethod + async def open_process( + cls, + command: StrOrBytesPath | Sequence[StrOrBytesPath], + *, + stdin: int | IO[Any] | None, + stdout: int | IO[Any] | None, + stderr: int | IO[Any] | None, + **kwargs: Any, + ) -> Process: + def convert_item(item: StrOrBytesPath) -> str: + str_or_bytes = os.fspath(item) + if isinstance(str_or_bytes, str): + return str_or_bytes + else: + return os.fsdecode(str_or_bytes) + + if isinstance(command, (str, bytes, PathLike)): + process = await trio.lowlevel.open_process( + convert_item(command), + stdin=stdin, + stdout=stdout, + stderr=stderr, + shell=True, + **kwargs, + ) + else: + process = await trio.lowlevel.open_process( + [convert_item(item) for item in command], + stdin=stdin, + stdout=stdout, + stderr=stderr, + shell=False, + **kwargs, + ) + + stdin_stream = SendStreamWrapper(process.stdin) if process.stdin else None + stdout_stream = ReceiveStreamWrapper(process.stdout) if process.stdout else None + stderr_stream = ReceiveStreamWrapper(process.stderr) if process.stderr else None + return Process(process, stdin_stream, stdout_stream, stderr_stream) + + @classmethod + def setup_process_pool_exit_at_shutdown(cls, workers: set[abc.Process]) -> None: + trio.lowlevel.spawn_system_task(_shutdown_process_pool, workers) + + @classmethod + async def connect_tcp( + cls, host: str, port: int, local_address: IPSockAddrType | None = None + ) -> SocketStream: + family = socket.AF_INET6 if ":" in host else socket.AF_INET + trio_socket = trio.socket.socket(family) + trio_socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) + if local_address: + await trio_socket.bind(local_address) + + try: + await trio_socket.connect((host, port)) + except BaseException: + trio_socket.close() + raise + + return SocketStream(trio_socket) + + @classmethod + async def connect_unix(cls, path: str | bytes) -> abc.UNIXSocketStream: + trio_socket = trio.socket.socket(socket.AF_UNIX) + try: + await trio_socket.connect(path) + except BaseException: + trio_socket.close() + raise + + return UNIXSocketStream(trio_socket) + + @classmethod + def create_tcp_listener(cls, sock: socket.socket) -> abc.SocketListener: + return TCPSocketListener(sock) + + @classmethod + def create_unix_listener(cls, sock: socket.socket) -> abc.SocketListener: + return UNIXSocketListener(sock) + + @classmethod + async def create_udp_socket( + cls, + family: socket.AddressFamily, + local_address: IPSockAddrType | None, + remote_address: IPSockAddrType | None, + reuse_port: bool, + ) -> UDPSocket | ConnectedUDPSocket: + trio_socket = trio.socket.socket(family=family, type=socket.SOCK_DGRAM) + + if reuse_port: + trio_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1) + + if local_address: + await trio_socket.bind(local_address) + + if remote_address: + await trio_socket.connect(remote_address) + return ConnectedUDPSocket(trio_socket) + else: + return UDPSocket(trio_socket) + + @classmethod + @overload + async def create_unix_datagram_socket( + cls, raw_socket: socket.socket, remote_path: None + ) -> abc.UNIXDatagramSocket: ... + + @classmethod + @overload + async def create_unix_datagram_socket( + cls, raw_socket: socket.socket, remote_path: str | bytes + ) -> abc.ConnectedUNIXDatagramSocket: ... + + @classmethod + async def create_unix_datagram_socket( + cls, raw_socket: socket.socket, remote_path: str | bytes | None + ) -> abc.UNIXDatagramSocket | abc.ConnectedUNIXDatagramSocket: + trio_socket = trio.socket.from_stdlib_socket(raw_socket) + + if remote_path: + await trio_socket.connect(remote_path) + return ConnectedUNIXDatagramSocket(trio_socket) + else: + return UNIXDatagramSocket(trio_socket) + + @classmethod + async def getaddrinfo( + cls, + host: bytes | str | None, + port: str | int | None, + *, + family: int | AddressFamily = 0, + type: int | SocketKind = 0, + proto: int = 0, + flags: int = 0, + ) -> Sequence[ + tuple[ + AddressFamily, + SocketKind, + int, + str, + tuple[str, int] | tuple[str, int, int, int] | tuple[int, bytes], + ] + ]: + return await trio.socket.getaddrinfo(host, port, family, type, proto, flags) + + @classmethod + async def getnameinfo( + cls, sockaddr: IPSockAddrType, flags: int = 0 + ) -> tuple[str, str]: + return await trio.socket.getnameinfo(sockaddr, flags) + + @classmethod + async def wait_readable(cls, obj: FileDescriptorLike) -> None: + try: + await wait_readable(obj) + except trio.ClosedResourceError as exc: + raise ClosedResourceError().with_traceback(exc.__traceback__) from None + except trio.BusyResourceError: + raise BusyResourceError("reading from") from None + + @classmethod + async def wait_writable(cls, obj: FileDescriptorLike) -> None: + try: + await wait_writable(obj) + except trio.ClosedResourceError as exc: + raise ClosedResourceError().with_traceback(exc.__traceback__) from None + except trio.BusyResourceError: + raise BusyResourceError("writing to") from None + + @classmethod + def notify_closing(cls, obj: FileDescriptorLike) -> None: + notify_closing(obj) + + @classmethod + async def wrap_listener_socket(cls, sock: socket.socket) -> abc.SocketListener: + return TCPSocketListener(sock) + + @classmethod + async def wrap_stream_socket(cls, sock: socket.socket) -> SocketStream: + trio_sock = trio.socket.from_stdlib_socket(sock) + return SocketStream(trio_sock) + + @classmethod + async def wrap_unix_stream_socket(cls, sock: socket.socket) -> UNIXSocketStream: + trio_sock = trio.socket.from_stdlib_socket(sock) + return UNIXSocketStream(trio_sock) + + @classmethod + async def wrap_udp_socket(cls, sock: socket.socket) -> UDPSocket: + trio_sock = trio.socket.from_stdlib_socket(sock) + return UDPSocket(trio_sock) + + @classmethod + async def wrap_connected_udp_socket(cls, sock: socket.socket) -> ConnectedUDPSocket: + trio_sock = trio.socket.from_stdlib_socket(sock) + return ConnectedUDPSocket(trio_sock) + + @classmethod + async def wrap_unix_datagram_socket(cls, sock: socket.socket) -> UNIXDatagramSocket: + trio_sock = trio.socket.from_stdlib_socket(sock) + return UNIXDatagramSocket(trio_sock) + + @classmethod + async def wrap_connected_unix_datagram_socket( + cls, sock: socket.socket + ) -> ConnectedUNIXDatagramSocket: + trio_sock = trio.socket.from_stdlib_socket(sock) + return ConnectedUNIXDatagramSocket(trio_sock) + + @classmethod + def current_default_thread_limiter(cls) -> CapacityLimiter: + try: + return _capacity_limiter_wrapper.get() + except LookupError: + limiter = CapacityLimiter( + original=trio.to_thread.current_default_thread_limiter() + ) + _capacity_limiter_wrapper.set(limiter) + return limiter + + @classmethod + def open_signal_receiver( + cls, *signals: Signals + ) -> AbstractContextManager[AsyncIterator[Signals]]: + return _SignalReceiver(signals) + + @classmethod + def get_current_task(cls) -> TaskInfo: + task = current_task() + return TrioTaskInfo(task) + + @classmethod + def get_running_tasks(cls) -> Sequence[TaskInfo]: + root_task = current_root_task() + assert root_task + task_infos = [TrioTaskInfo(root_task)] + nurseries = root_task.child_nurseries + while nurseries: + new_nurseries: list[trio.Nursery] = [] + for nursery in nurseries: + for task in nursery.child_tasks: + task_infos.append(TrioTaskInfo(task)) + new_nurseries.extend(task.child_nurseries) + + nurseries = new_nurseries + + return task_infos + + @classmethod + async def wait_all_tasks_blocked(cls) -> None: + from trio.testing import wait_all_tasks_blocked + + await wait_all_tasks_blocked() + + @classmethod + def create_test_runner(cls, options: dict[str, Any]) -> TestRunner: + return TestRunner(**options) + + +backend_class = TrioBackend diff --git a/.deps_siteforge_v812/anyio/_core/__init__.py b/.deps_siteforge_v812/anyio/_core/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/.deps_siteforge_v812/anyio/_core/__pycache__/__init__.cpython-312.pyc b/.deps_siteforge_v812/anyio/_core/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b914209a54a6b84d7890d219569eb3537e7a45aa GIT binary patch literal 185 zcmX@j%ge<81n-aD%mUGmK?FMZ%mNgd&QQsq$>_I|p@<2{`wUX^E5g|-CbT%Us5r*h zz|zt@#<8HlC9xzi#wR~HF()P@HMbz9AhSTXB(W$xwM5q}-PowA%CtBpCo?Igpt2+* zKQAURuQD@VKR!9XC{;f`J~J<~BtBlRpz;=nO>TZlX-=wL5i8IlMj$Q*F+MUgGBOr1 G16csPelbb_ literal 0 HcmV?d00001 diff --git a/.deps_siteforge_v812/anyio/_core/__pycache__/_asyncio_selector_thread.cpython-312.pyc b/.deps_siteforge_v812/anyio/_core/__pycache__/_asyncio_selector_thread.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d9a93ffdec510e0502a4b331498eaddede0a2b8f GIT binary patch literal 8400 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 literal 0 HcmV?d00001 diff --git a/.deps_siteforge_v812/anyio/_core/__pycache__/_contextmanagers.cpython-312.pyc b/.deps_siteforge_v812/anyio/_core/__pycache__/_contextmanagers.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f482e34a8e7cb042160c129a2f8b940fd4d0c070 GIT binary patch literal 8995 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 N#fZ5t8Ez8y{|gi?8yNrq literal 0 HcmV?d00001 diff --git a/.deps_siteforge_v812/anyio/_core/__pycache__/_fileio.cpython-312.pyc b/.deps_siteforge_v812/anyio/_core/__pycache__/_fileio.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cf6fe90c8d22847fbab0f6112a3be84678e10c4e GIT binary patch literal 43300 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 literal 0 HcmV?d00001 diff --git a/.deps_siteforge_v812/anyio/_core/__pycache__/_resources.cpython-312.pyc b/.deps_siteforge_v812/anyio/_core/__pycache__/_resources.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0c9c58dfab4ea366d2342e264a5c9ac57b8de5ab GIT binary patch literal 927 zcmY*XO=#3W6rR~^vfFHTL8bLnHu!VMp+8z`=_1O~-jp8vIdlnxX)@hr&1S+(!n(DB zSP=`A-mE9@Qaz~PReJE`Metw^6a*3MNudRcc<{~cro|VSeDmh}-XHHnv8W@kpYOjk z-e82j$xCXPF2H+Z05*_|T z+{8(N1Obak&Z9ne2fHk5~|3VB>Czs+r>YdvgZ-Kqnu%0P&cAQPZ*92A^_7PWy^~eSsWQH;zg3kAX3lIUh5GpUrKJgB`QE%0LY*wIpk9xS+79D%)K0UAk+7W{ z>t?qX80^JD?E7tAf~>MaQGw$JI=F}Sk0X6xZRqLQ?R;^)f3<(Dv|SopXRGW}sr)%# z{+KWSR*`=4(a_g?`Je~?3KQh_3)Lcie5Q((SNO8_It$=!AGqFOKwDU?>bWf~2aZ`Y z%L^dja$ORQ(+Y2#xsBLq(Z;3+YogUSWh`1#>GwqcA9PI%E@uKAA mFE*JQ`}7hYgsWU(aRQEC3dVQ`o%)W-JE**`6|lhv!2b`FN9$t% literal 0 HcmV?d00001 diff --git a/.deps_siteforge_v812/anyio/_core/__pycache__/_signals.cpython-312.pyc b/.deps_siteforge_v812/anyio/_core/__pycache__/_signals.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..efb0572fa026b15d59b656779333aea60f667a2d GIT binary patch literal 1381 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