From 6e018ddbf12924a606f32d7038acfe40bc9744d4 Mon Sep 17 00:00:00 2001 From: websitehn Date: Fri, 5 Dec 2025 10:07:57 -0600 Subject: [PATCH 1/2] Solution --- src/app/main.py | 1 + .../__pycache__/py.cpython-312.pyc | Bin 0 -> 490 bytes venv/Lib/site-packages/_pytest/__init__.py | 13 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 478 bytes .../__pycache__/_argcomplete.cpython-312.pyc | Bin 0 -> 4828 bytes .../__pycache__/_version.cpython-312.pyc | Bin 0 -> 787 bytes .../__pycache__/cacheprovider.cpython-312.pyc | Bin 0 -> 31689 bytes .../__pycache__/capture.cpython-312.pyc | Bin 0 -> 55728 bytes .../__pycache__/compat.cpython-312.pyc | Bin 0 -> 13008 bytes .../__pycache__/debugging.cpython-312.pyc | Bin 0 -> 18286 bytes .../__pycache__/deprecated.cpython-312.pyc | Bin 0 -> 3027 bytes .../__pycache__/doctest.cpython-312.pyc | Bin 0 -> 32650 bytes .../__pycache__/faulthandler.cpython-312.pyc | Bin 0 -> 5077 bytes .../__pycache__/fixtures.cpython-312.pyc | Bin 0 -> 82888 bytes .../freeze_support.cpython-312.pyc | Bin 0 -> 1831 bytes .../__pycache__/helpconfig.cpython-312.pyc | Bin 0 -> 13151 bytes .../__pycache__/hookspec.cpython-312.pyc | Bin 0 -> 45764 bytes .../__pycache__/junitxml.cpython-312.pyc | Bin 0 -> 33345 bytes .../__pycache__/legacypath.cpython-312.pyc | Bin 0 -> 24163 bytes .../__pycache__/logging.cpython-312.pyc | Bin 0 -> 45968 bytes .../_pytest/__pycache__/main.cpython-312.pyc | Bin 0 -> 47339 bytes .../__pycache__/monkeypatch.cpython-312.pyc | Bin 0 -> 17212 bytes .../_pytest/__pycache__/nodes.cpython-312.pyc | Bin 0 -> 30841 bytes .../__pycache__/outcomes.cpython-312.pyc | Bin 0 -> 11719 bytes .../__pycache__/pastebin.cpython-312.pyc | Bin 0 -> 5886 bytes .../__pycache__/pathlib.cpython-312.pyc | Bin 0 -> 41806 bytes .../__pycache__/pytester.cpython-312.pyc | Bin 0 -> 85010 bytes .../pytester_assertions.cpython-312.pyc | Bin 0 -> 2487 bytes .../__pycache__/python.cpython-312.pyc | Bin 0 -> 75209 bytes .../__pycache__/python_api.cpython-312.pyc | Bin 0 -> 35389 bytes .../__pycache__/raises.cpython-312.pyc | Bin 0 -> 60036 bytes .../__pycache__/recwarn.cpython-312.pyc | Bin 0 -> 16099 bytes .../__pycache__/reports.cpython-312.pyc | Bin 0 -> 27004 bytes .../__pycache__/runner.cpython-312.pyc | Bin 0 -> 24208 bytes .../_pytest/__pycache__/scope.cpython-312.pyc | Bin 0 -> 3569 bytes .../__pycache__/setuponly.cpython-312.pyc | Bin 0 -> 5358 bytes .../__pycache__/setupplan.cpython-312.pyc | Bin 0 -> 1903 bytes .../__pycache__/skipping.cpython-312.pyc | Bin 0 -> 14069 bytes .../_pytest/__pycache__/stash.cpython-312.pyc | Bin 0 -> 4366 bytes .../__pycache__/stepwise.cpython-312.pyc | Bin 0 -> 9614 bytes .../__pycache__/subtests.cpython-312.pyc | Bin 0 -> 17728 bytes .../__pycache__/terminal.cpython-312.pyc | Bin 0 -> 84500 bytes .../threadexception.cpython-312.pyc | Bin 0 -> 5751 bytes .../__pycache__/timing.cpython-312.pyc | Bin 0 -> 4785 bytes .../__pycache__/tmpdir.cpython-312.pyc | Bin 0 -> 12715 bytes .../__pycache__/tracemalloc.cpython-312.pyc | Bin 0 -> 984 bytes .../__pycache__/unittest.cpython-312.pyc | Bin 0 -> 25870 bytes .../unraisableexception.cpython-312.pyc | Bin 0 -> 6252 bytes .../__pycache__/warning_types.cpython-312.pyc | Bin 0 -> 6939 bytes .../__pycache__/warnings.cpython-312.pyc | Bin 0 -> 6849 bytes .../Lib/site-packages/_pytest/_argcomplete.py | 117 + .../site-packages/_pytest/_code/__init__.py | 26 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 684 bytes .../_code/__pycache__/code.cpython-312.pyc | Bin 0 -> 67979 bytes .../_code/__pycache__/source.cpython-312.pyc | Bin 0 -> 11944 bytes venv/Lib/site-packages/_pytest/_code/code.py | 1565 +++ .../Lib/site-packages/_pytest/_code/source.py | 225 + .../Lib/site-packages/_pytest/_io/__init__.py | 10 + .../_io/__pycache__/__init__.cpython-312.pyc | Bin 0 -> 375 bytes .../_io/__pycache__/pprint.cpython-312.pyc | Bin 0 -> 24430 bytes .../_io/__pycache__/saferepr.cpython-312.pyc | Bin 0 -> 5821 bytes .../terminalwriter.cpython-312.pyc | Bin 0 -> 11244 bytes .../_io/__pycache__/wcwidth.cpython-312.pyc | Bin 0 -> 1686 bytes venv/Lib/site-packages/_pytest/_io/pprint.py | 673 ++ .../Lib/site-packages/_pytest/_io/saferepr.py | 130 + .../_pytest/_io/terminalwriter.py | 258 + venv/Lib/site-packages/_pytest/_io/wcwidth.py | 57 + .../Lib/site-packages/_pytest/_py/__init__.py | 0 .../_py/__pycache__/__init__.cpython-312.pyc | Bin 0 -> 183 bytes .../_py/__pycache__/error.cpython-312.pyc | Bin 0 -> 4951 bytes .../_py/__pycache__/path.cpython-312.pyc | Bin 0 -> 68847 bytes venv/Lib/site-packages/_pytest/_py/error.py | 119 + venv/Lib/site-packages/_pytest/_py/path.py | 1475 +++ venv/Lib/site-packages/_pytest/_version.py | 34 + .../_pytest/assertion/__init__.py | 208 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 10118 bytes .../__pycache__/rewrite.cpython-312.pyc | Bin 0 -> 61295 bytes .../__pycache__/truncate.cpython-312.pyc | Bin 0 -> 4192 bytes .../__pycache__/util.cpython-312.pyc | Bin 0 -> 23846 bytes .../_pytest/assertion/rewrite.py | 1202 +++ .../_pytest/assertion/truncate.py | 137 + .../site-packages/_pytest/assertion/util.py | 615 ++ .../site-packages/_pytest/cacheprovider.py | 646 ++ venv/Lib/site-packages/_pytest/capture.py | 1144 +++ venv/Lib/site-packages/_pytest/compat.py | 314 + .../site-packages/_pytest/config/__init__.py | 2166 +++++ .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 89848 bytes .../__pycache__/argparsing.cpython-312.pyc | Bin 0 -> 26411 bytes .../config/__pycache__/compat.cpython-312.pyc | Bin 0 -> 3581 bytes .../__pycache__/exceptions.cpython-312.pyc | Bin 0 -> 811 bytes .../__pycache__/findpaths.cpython-312.pyc | Bin 0 -> 13259 bytes .../_pytest/config/argparsing.py | 578 ++ .../site-packages/_pytest/config/compat.py | 85 + .../_pytest/config/exceptions.py | 15 + .../site-packages/_pytest/config/findpaths.py | 350 + venv/Lib/site-packages/_pytest/debugging.py | 407 + venv/Lib/site-packages/_pytest/deprecated.py | 99 + venv/Lib/site-packages/_pytest/doctest.py | 736 ++ .../Lib/site-packages/_pytest/faulthandler.py | 119 + venv/Lib/site-packages/_pytest/fixtures.py | 2047 ++++ .../site-packages/_pytest/freeze_support.py | 45 + venv/Lib/site-packages/_pytest/helpconfig.py | 293 + venv/Lib/site-packages/_pytest/hookspec.py | 1342 +++ venv/Lib/site-packages/_pytest/junitxml.py | 695 ++ venv/Lib/site-packages/_pytest/legacypath.py | 468 + venv/Lib/site-packages/_pytest/logging.py | 960 ++ venv/Lib/site-packages/_pytest/main.py | 1203 +++ .../site-packages/_pytest/mark/__init__.py | 301 + .../mark/__pycache__/__init__.cpython-312.pyc | Bin 0 -> 13072 bytes .../__pycache__/expression.cpython-312.pyc | Bin 0 -> 17359 bytes .../__pycache__/structures.cpython-312.pyc | Bin 0 -> 27494 bytes .../site-packages/_pytest/mark/expression.py | 353 + .../site-packages/_pytest/mark/structures.py | 664 ++ venv/Lib/site-packages/_pytest/monkeypatch.py | 435 + venv/Lib/site-packages/_pytest/nodes.py | 772 ++ venv/Lib/site-packages/_pytest/outcomes.py | 308 + venv/Lib/site-packages/_pytest/pastebin.py | 117 + venv/Lib/site-packages/_pytest/pathlib.py | 1063 ++ venv/Lib/site-packages/_pytest/py.typed | 0 venv/Lib/site-packages/_pytest/pytester.py | 1791 ++++ .../_pytest/pytester_assertions.py | 74 + venv/Lib/site-packages/_pytest/python.py | 1772 ++++ venv/Lib/site-packages/_pytest/python_api.py | 820 ++ venv/Lib/site-packages/_pytest/raises.py | 1517 +++ venv/Lib/site-packages/_pytest/recwarn.py | 367 + venv/Lib/site-packages/_pytest/reports.py | 694 ++ venv/Lib/site-packages/_pytest/runner.py | 580 ++ venv/Lib/site-packages/_pytest/scope.py | 91 + venv/Lib/site-packages/_pytest/setuponly.py | 98 + venv/Lib/site-packages/_pytest/setupplan.py | 39 + venv/Lib/site-packages/_pytest/skipping.py | 321 + venv/Lib/site-packages/_pytest/stash.py | 116 + venv/Lib/site-packages/_pytest/stepwise.py | 209 + venv/Lib/site-packages/_pytest/subtests.py | 411 + venv/Lib/site-packages/_pytest/terminal.py | 1770 ++++ .../site-packages/_pytest/threadexception.py | 152 + venv/Lib/site-packages/_pytest/timing.py | 95 + venv/Lib/site-packages/_pytest/tmpdir.py | 312 + venv/Lib/site-packages/_pytest/tracemalloc.py | 24 + venv/Lib/site-packages/_pytest/unittest.py | 614 ++ .../_pytest/unraisableexception.py | 163 + .../site-packages/_pytest/warning_types.py | 172 + venv/Lib/site-packages/_pytest/warnings.py | 151 + .../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 + venv/Lib/site-packages/colorama/__init__.py | 7 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 480 bytes .../colorama/__pycache__/ansi.cpython-312.pyc | Bin 0 -> 3938 bytes .../__pycache__/ansitowin32.cpython-312.pyc | Bin 0 -> 16409 bytes .../__pycache__/initialise.cpython-312.pyc | Bin 0 -> 3538 bytes .../__pycache__/win32.cpython-312.pyc | Bin 0 -> 8114 bytes .../__pycache__/winterm.cpython-312.pyc | Bin 0 -> 9076 bytes venv/Lib/site-packages/colorama/ansi.py | 102 + .../Lib/site-packages/colorama/ansitowin32.py | 277 + venv/Lib/site-packages/colorama/initialise.py | 121 + .../site-packages/colorama/tests/__init__.py | 1 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 186 bytes .../__pycache__/ansi_test.cpython-312.pyc | Bin 0 -> 5455 bytes .../ansitowin32_test.cpython-312.pyc | Bin 0 -> 18091 bytes .../initialise_test.cpython-312.pyc | Bin 0 -> 11736 bytes .../__pycache__/isatty_test.cpython-312.pyc | Bin 0 -> 4892 bytes .../tests/__pycache__/utils.cpython-312.pyc | Bin 0 -> 2476 bytes .../__pycache__/winterm_test.cpython-312.pyc | Bin 0 -> 6600 bytes .../site-packages/colorama/tests/ansi_test.py | 76 + .../colorama/tests/ansitowin32_test.py | 294 + .../colorama/tests/initialise_test.py | 189 + .../colorama/tests/isatty_test.py | 57 + .../Lib/site-packages/colorama/tests/utils.py | 49 + .../colorama/tests/winterm_test.py | 131 + venv/Lib/site-packages/colorama/win32.py | 180 + venv/Lib/site-packages/colorama/winterm.py | 195 + .../coverage-7.12.0.dist-info/INSTALLER | 1 + .../coverage-7.12.0.dist-info/METADATA | 221 + .../coverage-7.12.0.dist-info/RECORD | 106 + .../coverage-7.12.0.dist-info/WHEEL | 5 + .../entry_points.txt | 4 + .../licenses/LICENSE.txt | 177 + .../coverage-7.12.0.dist-info/top_level.txt | 1 + venv/Lib/site-packages/coverage/__init__.py | 40 + venv/Lib/site-packages/coverage/__main__.py | 12 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 758 bytes .../__pycache__/__main__.cpython-312.pyc | Bin 0 -> 410 bytes .../__pycache__/annotate.cpython-312.pyc | Bin 0 -> 5156 bytes .../__pycache__/bytecode.cpython-312.pyc | Bin 0 -> 7791 bytes .../__pycache__/cmdline.cpython-312.pyc | Bin 0 -> 38180 bytes .../__pycache__/collector.cpython-312.pyc | Bin 0 -> 19340 bytes .../__pycache__/config.cpython-312.pyc | Bin 0 -> 27221 bytes .../__pycache__/context.cpython-312.pyc | Bin 0 -> 3085 bytes .../__pycache__/control.cpython-312.pyc | Bin 0 -> 62597 bytes .../coverage/__pycache__/core.cpython-312.pyc | Bin 0 -> 5644 bytes .../coverage/__pycache__/data.cpython-312.pyc | Bin 0 -> 9906 bytes .../__pycache__/debug.cpython-312.pyc | Bin 0 -> 32402 bytes .../__pycache__/disposition.cpython-312.pyc | Bin 0 -> 2410 bytes .../coverage/__pycache__/env.cpython-312.pyc | Bin 0 -> 3223 bytes .../__pycache__/exceptions.cpython-312.pyc | Bin 0 -> 2958 bytes .../__pycache__/execfile.cpython-312.pyc | Bin 0 -> 13366 bytes .../__pycache__/files.cpython-312.pyc | Bin 0 -> 22942 bytes .../coverage/__pycache__/html.cpython-312.pyc | Bin 0 -> 40485 bytes .../__pycache__/inorout.cpython-312.pyc | Bin 0 -> 25173 bytes .../__pycache__/jsonreport.cpython-312.pyc | Bin 0 -> 9553 bytes .../__pycache__/lcovreport.cpython-312.pyc | Bin 0 -> 9588 bytes .../coverage/__pycache__/misc.cpython-312.pyc | Bin 0 -> 16753 bytes .../__pycache__/multiproc.cpython-312.pyc | Bin 0 -> 5254 bytes .../__pycache__/numbits.cpython-312.pyc | Bin 0 -> 6435 bytes .../__pycache__/parser.cpython-312.pyc | Bin 0 -> 53463 bytes .../__pycache__/patch.cpython-312.pyc | Bin 0 -> 7486 bytes .../__pycache__/phystokens.cpython-312.pyc | Bin 0 -> 7183 bytes .../__pycache__/plugin.cpython-312.pyc | Bin 0 -> 24593 bytes .../plugin_support.cpython-312.pyc | Bin 0 -> 17961 bytes .../__pycache__/python.cpython-312.pyc | Bin 0 -> 11852 bytes .../__pycache__/pytracer.cpython-312.pyc | Bin 0 -> 11218 bytes .../__pycache__/regions.cpython-312.pyc | Bin 0 -> 6595 bytes .../__pycache__/report.cpython-312.pyc | Bin 0 -> 14788 bytes .../__pycache__/report_core.cpython-312.pyc | Bin 0 -> 4787 bytes .../__pycache__/results.cpython-312.pyc | Bin 0 -> 22930 bytes .../__pycache__/sqldata.cpython-312.pyc | Bin 0 -> 57675 bytes .../__pycache__/sqlitedb.cpython-312.pyc | Bin 0 -> 12765 bytes .../__pycache__/sysmon.cpython-312.pyc | Bin 0 -> 22079 bytes .../__pycache__/templite.cpython-312.pyc | Bin 0 -> 13685 bytes .../__pycache__/tomlconfig.cpython-312.pyc | Bin 0 -> 10502 bytes .../__pycache__/types.cpython-312.pyc | Bin 0 -> 7651 bytes .../__pycache__/version.cpython-312.pyc | Bin 0 -> 1217 bytes .../__pycache__/xmlreport.cpython-312.pyc | Bin 0 -> 13267 bytes venv/Lib/site-packages/coverage/annotate.py | 114 + venv/Lib/site-packages/coverage/bytecode.py | 196 + venv/Lib/site-packages/coverage/cmdline.py | 1184 +++ venv/Lib/site-packages/coverage/collector.py | 486 + venv/Lib/site-packages/coverage/config.py | 731 ++ venv/Lib/site-packages/coverage/context.py | 74 + venv/Lib/site-packages/coverage/control.py | 1481 +++ venv/Lib/site-packages/coverage/core.py | 139 + venv/Lib/site-packages/coverage/data.py | 227 + venv/Lib/site-packages/coverage/debug.py | 669 ++ .../Lib/site-packages/coverage/disposition.py | 59 + venv/Lib/site-packages/coverage/env.py | 135 + venv/Lib/site-packages/coverage/exceptions.py | 85 + venv/Lib/site-packages/coverage/execfile.py | 329 + venv/Lib/site-packages/coverage/files.py | 553 ++ venv/Lib/site-packages/coverage/html.py | 860 ++ .../coverage/htmlfiles/coverage_html.js | 735 ++ .../coverage/htmlfiles/favicon_32.png | Bin 0 -> 1732 bytes .../coverage/htmlfiles/index.html | 199 + .../coverage/htmlfiles/keybd_closed.png | Bin 0 -> 9004 bytes .../coverage/htmlfiles/pyfile.html | 149 + .../coverage/htmlfiles/style.css | 385 + .../coverage/htmlfiles/style.scss | 842 ++ venv/Lib/site-packages/coverage/inorout.py | 614 ++ venv/Lib/site-packages/coverage/jsonreport.py | 192 + venv/Lib/site-packages/coverage/lcovreport.py | 219 + venv/Lib/site-packages/coverage/misc.py | 373 + venv/Lib/site-packages/coverage/multiproc.py | 120 + venv/Lib/site-packages/coverage/numbits.py | 146 + venv/Lib/site-packages/coverage/parser.py | 1215 +++ venv/Lib/site-packages/coverage/patch.py | 166 + venv/Lib/site-packages/coverage/phystokens.py | 197 + venv/Lib/site-packages/coverage/plugin.py | 617 ++ .../site-packages/coverage/plugin_support.py | 299 + venv/Lib/site-packages/coverage/py.typed | 1 + venv/Lib/site-packages/coverage/python.py | 272 + venv/Lib/site-packages/coverage/pytracer.py | 369 + venv/Lib/site-packages/coverage/regions.py | 127 + venv/Lib/site-packages/coverage/report.py | 298 + .../Lib/site-packages/coverage/report_core.py | 117 + venv/Lib/site-packages/coverage/results.py | 502 + venv/Lib/site-packages/coverage/sqldata.py | 1153 +++ venv/Lib/site-packages/coverage/sqlitedb.py | 239 + venv/Lib/site-packages/coverage/sysmon.py | 513 + venv/Lib/site-packages/coverage/templite.py | 318 + venv/Lib/site-packages/coverage/tomlconfig.py | 210 + .../coverage/tracer.cp312-win_amd64.pyd | Bin 0 -> 22528 bytes venv/Lib/site-packages/coverage/tracer.pyi | 43 + venv/Lib/site-packages/coverage/types.py | 206 + venv/Lib/site-packages/coverage/version.py | 35 + venv/Lib/site-packages/coverage/xmlreport.py | 264 + .../iniconfig-2.3.0.dist-info/INSTALLER | 1 + .../iniconfig-2.3.0.dist-info/METADATA | 79 + .../iniconfig-2.3.0.dist-info/RECORD | 15 + .../iniconfig-2.3.0.dist-info/WHEEL | 5 + .../licenses/LICENSE | 21 + .../iniconfig-2.3.0.dist-info/top_level.txt | 1 + venv/Lib/site-packages/iniconfig/__init__.py | 249 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 10874 bytes .../__pycache__/_parse.cpython-312.pyc | Bin 0 -> 5574 bytes .../__pycache__/_version.cpython-312.pyc | Bin 0 -> 789 bytes .../__pycache__/exceptions.cpython-312.pyc | Bin 0 -> 1193 bytes venv/Lib/site-packages/iniconfig/_parse.py | 163 + venv/Lib/site-packages/iniconfig/_version.py | 34 + .../Lib/site-packages/iniconfig/exceptions.py | 16 + venv/Lib/site-packages/iniconfig/py.typed | 0 .../jinja2-3.1.6.dist-info/INSTALLER | 1 + .../jinja2-3.1.6.dist-info/METADATA | 84 + .../jinja2-3.1.6.dist-info/RECORD | 57 + .../jinja2-3.1.6.dist-info/WHEEL | 4 + .../jinja2-3.1.6.dist-info/entry_points.txt | 3 + .../licenses/LICENSE.txt | 28 + venv/Lib/site-packages/jinja2/__init__.py | 38 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 1647 bytes .../__pycache__/_identifier.cpython-312.pyc | Bin 0 -> 2128 bytes .../__pycache__/async_utils.cpython-312.pyc | Bin 0 -> 4968 bytes .../__pycache__/bccache.cpython-312.pyc | Bin 0 -> 19343 bytes .../__pycache__/compiler.cpython-312.pyc | Bin 0 -> 104054 bytes .../__pycache__/constants.cpython-312.pyc | Bin 0 -> 1550 bytes .../jinja2/__pycache__/debug.cpython-312.pyc | Bin 0 -> 6575 bytes .../__pycache__/defaults.cpython-312.pyc | Bin 0 -> 1600 bytes .../__pycache__/environment.cpython-312.pyc | Bin 0 -> 76676 bytes .../__pycache__/exceptions.cpython-312.pyc | Bin 0 -> 7714 bytes .../jinja2/__pycache__/ext.cpython-312.pyc | Bin 0 -> 41913 bytes .../__pycache__/filters.cpython-312.pyc | Bin 0 -> 72324 bytes .../__pycache__/idtracking.cpython-312.pyc | Bin 0 -> 19189 bytes .../jinja2/__pycache__/lexer.cpython-312.pyc | Bin 0 -> 32070 bytes .../__pycache__/loaders.cpython-312.pyc | Bin 0 -> 32349 bytes .../jinja2/__pycache__/meta.cpython-312.pyc | Bin 0 -> 5484 bytes .../__pycache__/nativetypes.cpython-312.pyc | Bin 0 -> 7005 bytes .../jinja2/__pycache__/nodes.cpython-312.pyc | Bin 0 -> 58267 bytes .../__pycache__/optimizer.cpython-312.pyc | Bin 0 -> 2683 bytes .../jinja2/__pycache__/parser.cpython-312.pyc | Bin 0 -> 61197 bytes .../__pycache__/runtime.cpython-312.pyc | Bin 0 -> 48885 bytes .../__pycache__/sandbox.cpython-312.pyc | Bin 0 -> 18101 bytes .../jinja2/__pycache__/tests.cpython-312.pyc | Bin 0 -> 9044 bytes .../jinja2/__pycache__/utils.cpython-312.pyc | Bin 0 -> 34857 bytes .../__pycache__/visitor.cpython-312.pyc | Bin 0 -> 5362 bytes venv/Lib/site-packages/jinja2/_identifier.py | 6 + venv/Lib/site-packages/jinja2/async_utils.py | 99 + venv/Lib/site-packages/jinja2/bccache.py | 408 + venv/Lib/site-packages/jinja2/compiler.py | 1998 ++++ venv/Lib/site-packages/jinja2/constants.py | 20 + venv/Lib/site-packages/jinja2/debug.py | 191 + venv/Lib/site-packages/jinja2/defaults.py | 48 + venv/Lib/site-packages/jinja2/environment.py | 1672 ++++ venv/Lib/site-packages/jinja2/exceptions.py | 166 + venv/Lib/site-packages/jinja2/ext.py | 870 ++ venv/Lib/site-packages/jinja2/filters.py | 1873 ++++ venv/Lib/site-packages/jinja2/idtracking.py | 318 + venv/Lib/site-packages/jinja2/lexer.py | 868 ++ venv/Lib/site-packages/jinja2/loaders.py | 693 ++ venv/Lib/site-packages/jinja2/meta.py | 112 + venv/Lib/site-packages/jinja2/nativetypes.py | 130 + venv/Lib/site-packages/jinja2/nodes.py | 1206 +++ venv/Lib/site-packages/jinja2/optimizer.py | 48 + venv/Lib/site-packages/jinja2/parser.py | 1049 ++ venv/Lib/site-packages/jinja2/py.typed | 0 venv/Lib/site-packages/jinja2/runtime.py | 1062 ++ venv/Lib/site-packages/jinja2/sandbox.py | 436 + venv/Lib/site-packages/jinja2/tests.py | 256 + venv/Lib/site-packages/jinja2/utils.py | 766 ++ venv/Lib/site-packages/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 | 14 + .../markupsafe-3.0.3.dist-info/WHEEL | 5 + .../licenses/LICENSE.txt | 28 + .../markupsafe-3.0.3.dist-info/top_level.txt | 1 + venv/Lib/site-packages/markupsafe/__init__.py | 396 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 20963 bytes .../__pycache__/_native.cpython-312.pyc | Bin 0 -> 609 bytes venv/Lib/site-packages/markupsafe/_native.py | 8 + venv/Lib/site-packages/markupsafe/_speedups.c | 200 + .../markupsafe/_speedups.cp312-win_amd64.pyd | Bin 0 -> 13312 bytes .../site-packages/markupsafe/_speedups.pyi | 1 + venv/Lib/site-packages/markupsafe/py.typed | 0 .../packaging-25.0.dist-info/INSTALLER | 1 + .../packaging-25.0.dist-info/METADATA | 105 + .../packaging-25.0.dist-info/RECORD | 40 + .../packaging-25.0.dist-info/WHEEL | 4 + .../packaging-25.0.dist-info/licenses/LICENSE | 3 + .../licenses/LICENSE.APACHE | 177 + .../licenses/LICENSE.BSD | 23 + venv/Lib/site-packages/packaging/__init__.py | 15 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 546 bytes .../__pycache__/_elffile.cpython-312.pyc | Bin 0 -> 5011 bytes .../__pycache__/_manylinux.cpython-312.pyc | Bin 0 -> 9739 bytes .../__pycache__/_musllinux.cpython-312.pyc | Bin 0 -> 4557 bytes .../__pycache__/_parser.cpython-312.pyc | Bin 0 -> 13986 bytes .../__pycache__/_structures.cpython-312.pyc | Bin 0 -> 3229 bytes .../__pycache__/_tokenizer.cpython-312.pyc | Bin 0 -> 7936 bytes .../__pycache__/markers.cpython-312.pyc | Bin 0 -> 12753 bytes .../__pycache__/metadata.cpython-312.pyc | Bin 0 -> 27238 bytes .../__pycache__/requirements.cpython-312.pyc | Bin 0 -> 4398 bytes .../__pycache__/specifiers.cpython-312.pyc | Bin 0 -> 39028 bytes .../__pycache__/tags.cpython-312.pyc | Bin 0 -> 24804 bytes .../__pycache__/utils.cpython-312.pyc | Bin 0 -> 6623 bytes .../__pycache__/version.cpython-312.pyc | Bin 0 -> 20456 bytes venv/Lib/site-packages/packaging/_elffile.py | 109 + .../Lib/site-packages/packaging/_manylinux.py | 262 + .../Lib/site-packages/packaging/_musllinux.py | 85 + venv/Lib/site-packages/packaging/_parser.py | 353 + .../site-packages/packaging/_structures.py | 61 + .../Lib/site-packages/packaging/_tokenizer.py | 195 + .../packaging/licenses/__init__.py | 145 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 4105 bytes .../__pycache__/_spdx.cpython-312.pyc | Bin 0 -> 47352 bytes .../site-packages/packaging/licenses/_spdx.py | 759 ++ venv/Lib/site-packages/packaging/markers.py | 362 + venv/Lib/site-packages/packaging/metadata.py | 862 ++ venv/Lib/site-packages/packaging/py.typed | 0 .../site-packages/packaging/requirements.py | 91 + .../Lib/site-packages/packaging/specifiers.py | 1019 ++ venv/Lib/site-packages/packaging/tags.py | 656 ++ venv/Lib/site-packages/packaging/utils.py | 163 + venv/Lib/site-packages/packaging/version.py | 582 ++ .../pip-24.0.dist-info/AUTHORS.txt | 760 ++ .../pip-24.0.dist-info/INSTALLER | 1 + .../pip-24.0.dist-info/LICENSE.txt | 20 + .../site-packages/pip-24.0.dist-info/METADATA | 88 + .../site-packages/pip-24.0.dist-info/RECORD | 1024 ++ .../pip-24.0.dist-info/REQUESTED | 0 .../site-packages/pip-24.0.dist-info/WHEEL | 5 + .../pip-24.0.dist-info/entry_points.txt | 4 + .../pip-24.0.dist-info/top_level.txt | 1 + venv/Lib/site-packages/pip/__init__.py | 13 + venv/Lib/site-packages/pip/__main__.py | 24 + venv/Lib/site-packages/pip/__pip-runner__.py | 50 + .../pip/__pycache__/__init__.cpython-312.pyc | Bin 0 -> 684 bytes .../pip/__pycache__/__main__.cpython-312.pyc | Bin 0 -> 840 bytes .../__pip-runner__.cpython-312.pyc | Bin 0 -> 2203 bytes .../site-packages/pip/_internal/__init__.py | 18 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 786 bytes .../__pycache__/build_env.cpython-312.pyc | Bin 0 -> 14293 bytes .../__pycache__/cache.cpython-312.pyc | Bin 0 -> 12664 bytes .../__pycache__/configuration.cpython-312.pyc | Bin 0 -> 17665 bytes .../__pycache__/exceptions.cpython-312.pyc | Bin 0 -> 33283 bytes .../__pycache__/main.cpython-312.pyc | Bin 0 -> 669 bytes .../__pycache__/pyproject.cpython-312.pyc | Bin 0 -> 4970 bytes .../self_outdated_check.cpython-312.pyc | Bin 0 -> 10551 bytes .../__pycache__/wheel_builder.cpython-312.pyc | Bin 0 -> 13648 bytes .../site-packages/pip/_internal/build_env.py | 311 + venv/Lib/site-packages/pip/_internal/cache.py | 290 + .../pip/_internal/cli/__init__.py | 4 + .../cli/__pycache__/__init__.cpython-312.pyc | Bin 0 -> 277 bytes .../autocompletion.cpython-312.pyc | Bin 0 -> 8464 bytes .../__pycache__/base_command.cpython-312.pyc | Bin 0 -> 10454 bytes .../__pycache__/cmdoptions.cpython-312.pyc | Bin 0 -> 30373 bytes .../command_context.cpython-312.pyc | Bin 0 -> 1780 bytes .../cli/__pycache__/main.cpython-312.pyc | Bin 0 -> 2297 bytes .../__pycache__/main_parser.cpython-312.pyc | Bin 0 -> 4904 bytes .../cli/__pycache__/parser.cpython-312.pyc | Bin 0 -> 15021 bytes .../__pycache__/progress_bars.cpython-312.pyc | Bin 0 -> 2619 bytes .../__pycache__/req_command.cpython-312.pyc | Bin 0 -> 18851 bytes .../cli/__pycache__/spinners.cpython-312.pyc | Bin 0 -> 7839 bytes .../__pycache__/status_codes.cpython-312.pyc | Bin 0 -> 374 bytes .../pip/_internal/cli/autocompletion.py | 172 + .../pip/_internal/cli/base_command.py | 236 + .../pip/_internal/cli/cmdoptions.py | 1074 ++ .../pip/_internal/cli/command_context.py | 27 + .../site-packages/pip/_internal/cli/main.py | 79 + .../pip/_internal/cli/main_parser.py | 134 + .../site-packages/pip/_internal/cli/parser.py | 294 + .../pip/_internal/cli/progress_bars.py | 68 + .../pip/_internal/cli/req_command.py | 505 + .../pip/_internal/cli/spinners.py | 159 + .../pip/_internal/cli/status_codes.py | 6 + .../pip/_internal/commands/__init__.py | 132 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 4001 bytes .../__pycache__/cache.cpython-312.pyc | Bin 0 -> 9710 bytes .../__pycache__/check.cpython-312.pyc | Bin 0 -> 2089 bytes .../__pycache__/completion.cpython-312.pyc | Bin 0 -> 5191 bytes .../__pycache__/configuration.cpython-312.pyc | Bin 0 -> 13211 bytes .../__pycache__/debug.cpython-312.pyc | Bin 0 -> 10160 bytes .../__pycache__/download.cpython-312.pyc | Bin 0 -> 7588 bytes .../__pycache__/freeze.cpython-312.pyc | Bin 0 -> 4355 bytes .../commands/__pycache__/hash.cpython-312.pyc | Bin 0 -> 2982 bytes .../commands/__pycache__/help.cpython-312.pyc | Bin 0 -> 1672 bytes .../__pycache__/index.cpython-312.pyc | Bin 0 -> 6719 bytes .../__pycache__/inspect.cpython-312.pyc | Bin 0 -> 3974 bytes .../__pycache__/install.cpython-312.pyc | Bin 0 -> 28912 bytes .../commands/__pycache__/list.cpython-312.pyc | Bin 0 -> 15399 bytes .../__pycache__/search.cpython-312.pyc | Bin 0 -> 7620 bytes .../commands/__pycache__/show.cpython-312.pyc | Bin 0 -> 9727 bytes .../__pycache__/uninstall.cpython-312.pyc | Bin 0 -> 4725 bytes .../__pycache__/wheel.cpython-312.pyc | Bin 0 -> 8955 bytes .../pip/_internal/commands/cache.py | 225 + .../pip/_internal/commands/check.py | 54 + .../pip/_internal/commands/completion.py | 130 + .../pip/_internal/commands/configuration.py | 280 + .../pip/_internal/commands/debug.py | 201 + .../pip/_internal/commands/download.py | 147 + .../pip/_internal/commands/freeze.py | 108 + .../pip/_internal/commands/hash.py | 59 + .../pip/_internal/commands/help.py | 41 + .../pip/_internal/commands/index.py | 139 + .../pip/_internal/commands/inspect.py | 92 + .../pip/_internal/commands/install.py | 774 ++ .../pip/_internal/commands/list.py | 368 + .../pip/_internal/commands/search.py | 174 + .../pip/_internal/commands/show.py | 189 + .../pip/_internal/commands/uninstall.py | 113 + .../pip/_internal/commands/wheel.py | 183 + .../pip/_internal/configuration.py | 383 + .../pip/_internal/distributions/__init__.py | 21 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 940 bytes .../__pycache__/base.cpython-312.pyc | Bin 0 -> 2861 bytes .../__pycache__/installed.cpython-312.pyc | Bin 0 -> 1699 bytes .../__pycache__/sdist.cpython-312.pyc | Bin 0 -> 8487 bytes .../__pycache__/wheel.cpython-312.pyc | Bin 0 -> 2247 bytes .../pip/_internal/distributions/base.py | 51 + .../pip/_internal/distributions/installed.py | 29 + .../pip/_internal/distributions/sdist.py | 156 + .../pip/_internal/distributions/wheel.py | 40 + .../site-packages/pip/_internal/exceptions.py | 728 ++ .../pip/_internal/index/__init__.py | 2 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 231 bytes .../__pycache__/collector.cpython-312.pyc | Bin 0 -> 21885 bytes .../package_finder.cpython-312.pyc | Bin 0 -> 40734 bytes .../index/__pycache__/sources.cpython-312.pyc | Bin 0 -> 12603 bytes .../pip/_internal/index/collector.py | 507 + .../pip/_internal/index/package_finder.py | 1027 ++ .../pip/_internal/index/sources.py | 285 + .../pip/_internal/locations/__init__.py | 467 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 16775 bytes .../__pycache__/_distutils.cpython-312.pyc | Bin 0 -> 6855 bytes .../__pycache__/_sysconfig.cpython-312.pyc | Bin 0 -> 8010 bytes .../__pycache__/base.cpython-312.pyc | Bin 0 -> 3780 bytes .../pip/_internal/locations/_distutils.py | 172 + .../pip/_internal/locations/_sysconfig.py | 213 + .../pip/_internal/locations/base.py | 81 + venv/Lib/site-packages/pip/_internal/main.py | 12 + .../pip/_internal/metadata/__init__.py | 128 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 5881 bytes .../__pycache__/_json.cpython-312.pyc | Bin 0 -> 2874 bytes .../metadata/__pycache__/base.cpython-312.pyc | Bin 0 -> 35711 bytes .../__pycache__/pkg_resources.cpython-312.pyc | Bin 0 -> 15789 bytes .../pip/_internal/metadata/_json.py | 84 + .../pip/_internal/metadata/base.py | 702 ++ .../_internal/metadata/importlib/__init__.py | 6 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 357 bytes .../__pycache__/_compat.cpython-312.pyc | Bin 0 -> 3332 bytes .../__pycache__/_dists.cpython-312.pyc | Bin 0 -> 13424 bytes .../__pycache__/_envs.cpython-312.pyc | Bin 0 -> 11179 bytes .../_internal/metadata/importlib/_compat.py | 55 + .../_internal/metadata/importlib/_dists.py | 227 + .../pip/_internal/metadata/importlib/_envs.py | 189 + .../pip/_internal/metadata/pkg_resources.py | 278 + .../pip/_internal/models/__init__.py | 2 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 265 bytes .../__pycache__/candidate.cpython-312.pyc | Bin 0 -> 1904 bytes .../__pycache__/direct_url.cpython-312.pyc | Bin 0 -> 11198 bytes .../format_control.cpython-312.pyc | Bin 0 -> 4226 bytes .../models/__pycache__/index.cpython-312.pyc | Bin 0 -> 1693 bytes .../installation_report.cpython-312.pyc | Bin 0 -> 2271 bytes .../models/__pycache__/link.cpython-312.pyc | Bin 0 -> 26001 bytes .../models/__pycache__/scheme.cpython-312.pyc | Bin 0 -> 1168 bytes .../__pycache__/search_scope.cpython-312.pyc | Bin 0 -> 5087 bytes .../selection_prefs.cpython-312.pyc | Bin 0 -> 1850 bytes .../__pycache__/target_python.cpython-312.pyc | Bin 0 -> 4953 bytes .../models/__pycache__/wheel.cpython-312.pyc | Bin 0 -> 5779 bytes .../pip/_internal/models/candidate.py | 30 + .../pip/_internal/models/direct_url.py | 235 + .../pip/_internal/models/format_control.py | 78 + .../pip/_internal/models/index.py | 28 + .../_internal/models/installation_report.py | 56 + .../pip/_internal/models/link.py | 579 ++ .../pip/_internal/models/scheme.py | 31 + .../pip/_internal/models/search_scope.py | 132 + .../pip/_internal/models/selection_prefs.py | 51 + .../pip/_internal/models/target_python.py | 122 + .../pip/_internal/models/wheel.py | 92 + .../pip/_internal/network/__init__.py | 2 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 253 bytes .../network/__pycache__/auth.cpython-312.pyc | Bin 0 -> 21995 bytes .../network/__pycache__/cache.cpython-312.pyc | Bin 0 -> 6517 bytes .../__pycache__/download.cpython-312.pyc | Bin 0 -> 8552 bytes .../__pycache__/lazy_wheel.cpython-312.pyc | Bin 0 -> 11662 bytes .../__pycache__/session.cpython-312.pyc | Bin 0 -> 18773 bytes .../network/__pycache__/utils.cpython-312.pyc | Bin 0 -> 2252 bytes .../__pycache__/xmlrpc.cpython-312.pyc | Bin 0 -> 2948 bytes .../pip/_internal/network/auth.py | 561 ++ .../pip/_internal/network/cache.py | 106 + .../pip/_internal/network/download.py | 186 + .../pip/_internal/network/lazy_wheel.py | 210 + .../pip/_internal/network/session.py | 520 + .../pip/_internal/network/utils.py | 96 + .../pip/_internal/network/xmlrpc.py | 62 + .../pip/_internal/operations/__init__.py | 0 .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 196 bytes .../__pycache__/check.cpython-312.pyc | Bin 0 -> 7578 bytes .../__pycache__/freeze.cpython-312.pyc | Bin 0 -> 10116 bytes .../__pycache__/prepare.cpython-312.pyc | Bin 0 -> 25746 bytes .../_internal/operations/build/__init__.py | 0 .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 202 bytes .../__pycache__/build_tracker.cpython-312.pyc | Bin 0 -> 7822 bytes .../__pycache__/metadata.cpython-312.pyc | Bin 0 -> 1879 bytes .../metadata_editable.cpython-312.pyc | Bin 0 -> 1913 bytes .../metadata_legacy.cpython-312.pyc | Bin 0 -> 3064 bytes .../build/__pycache__/wheel.cpython-312.pyc | Bin 0 -> 1683 bytes .../wheel_editable.cpython-312.pyc | Bin 0 -> 2024 bytes .../__pycache__/wheel_legacy.cpython-312.pyc | Bin 0 -> 3928 bytes .../operations/build/build_tracker.py | 139 + .../_internal/operations/build/metadata.py | 39 + .../operations/build/metadata_editable.py | 41 + .../operations/build/metadata_legacy.py | 74 + .../pip/_internal/operations/build/wheel.py | 37 + .../operations/build/wheel_editable.py | 46 + .../operations/build/wheel_legacy.py | 102 + .../pip/_internal/operations/check.py | 187 + .../pip/_internal/operations/freeze.py | 255 + .../_internal/operations/install/__init__.py | 2 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 265 bytes .../editable_legacy.cpython-312.pyc | Bin 0 -> 1816 bytes .../install/__pycache__/wheel.cpython-312.pyc | Bin 0 -> 33858 bytes .../operations/install/editable_legacy.py | 46 + .../pip/_internal/operations/install/wheel.py | 734 ++ .../pip/_internal/operations/prepare.py | 730 ++ .../site-packages/pip/_internal/pyproject.py | 179 + .../pip/_internal/req/__init__.py | 92 + .../req/__pycache__/__init__.cpython-312.pyc | Bin 0 -> 3742 bytes .../__pycache__/constructors.cpython-312.pyc | Bin 0 -> 21581 bytes .../req/__pycache__/req_file.cpython-312.pyc | Bin 0 -> 21460 bytes .../__pycache__/req_install.cpython-312.pyc | Bin 0 -> 38413 bytes .../req/__pycache__/req_set.cpython-312.pyc | Bin 0 -> 7217 bytes .../__pycache__/req_uninstall.cpython-312.pyc | Bin 0 -> 32976 bytes .../pip/_internal/req/constructors.py | 576 ++ .../pip/_internal/req/req_file.py | 554 ++ .../pip/_internal/req/req_install.py | 923 ++ .../pip/_internal/req/req_set.py | 119 + .../pip/_internal/req/req_uninstall.py | 649 ++ .../pip/_internal/resolution/__init__.py | 0 .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 196 bytes .../__pycache__/base.cpython-312.pyc | Bin 0 -> 1184 bytes .../pip/_internal/resolution/base.py | 20 + .../_internal/resolution/legacy/__init__.py | 0 .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 203 bytes .../__pycache__/resolver.cpython-312.pyc | Bin 0 -> 22438 bytes .../_internal/resolution/legacy/resolver.py | 598 ++ .../resolution/resolvelib/__init__.py | 0 .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 207 bytes .../__pycache__/base.cpython-312.pyc | Bin 0 -> 8336 bytes .../__pycache__/candidates.cpython-312.pyc | Bin 0 -> 30397 bytes .../__pycache__/factory.cpython-312.pyc | Bin 0 -> 32113 bytes .../found_candidates.cpython-312.pyc | Bin 0 -> 6207 bytes .../__pycache__/provider.cpython-312.pyc | Bin 0 -> 10377 bytes .../__pycache__/reporter.cpython-312.pyc | Bin 0 -> 4934 bytes .../__pycache__/requirements.cpython-312.pyc | Bin 0 -> 11428 bytes .../__pycache__/resolver.cpython-312.pyc | Bin 0 -> 12350 bytes .../_internal/resolution/resolvelib/base.py | 141 + .../resolution/resolvelib/candidates.py | 597 ++ .../resolution/resolvelib/factory.py | 812 ++ .../resolution/resolvelib/found_candidates.py | 155 + .../resolution/resolvelib/provider.py | 255 + .../resolution/resolvelib/reporter.py | 80 + .../resolution/resolvelib/requirements.py | 166 + .../resolution/resolvelib/resolver.py | 317 + .../pip/_internal/self_outdated_check.py | 248 + .../pip/_internal/utils/__init__.py | 0 .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 191 bytes .../__pycache__/_jaraco_text.cpython-312.pyc | Bin 0 -> 4532 bytes .../utils/__pycache__/_log.cpython-312.pyc | Bin 0 -> 1862 bytes .../utils/__pycache__/appdirs.cpython-312.pyc | Bin 0 -> 2406 bytes .../utils/__pycache__/compat.cpython-312.pyc | Bin 0 -> 2209 bytes .../compatibility_tags.cpython-312.pyc | Bin 0 -> 5557 bytes .../__pycache__/datetime.cpython-312.pyc | Bin 0 -> 680 bytes .../__pycache__/deprecation.cpython-312.pyc | Bin 0 -> 4182 bytes .../direct_url_helpers.cpython-312.pyc | Bin 0 -> 3559 bytes .../__pycache__/egg_link.cpython-312.pyc | Bin 0 -> 3222 bytes .../__pycache__/encoding.cpython-312.pyc | Bin 0 -> 2154 bytes .../__pycache__/entrypoints.cpython-312.pyc | Bin 0 -> 3989 bytes .../__pycache__/filesystem.cpython-312.pyc | Bin 0 -> 7454 bytes .../__pycache__/filetypes.cpython-312.pyc | Bin 0 -> 1160 bytes .../utils/__pycache__/glibc.cpython-312.pyc | Bin 0 -> 2338 bytes .../utils/__pycache__/hashes.cpython-312.pyc | Bin 0 -> 7550 bytes .../utils/__pycache__/logging.cpython-312.pyc | Bin 0 -> 13553 bytes .../utils/__pycache__/misc.cpython-312.pyc | Bin 0 -> 34117 bytes .../utils/__pycache__/models.cpython-312.pyc | Bin 0 -> 2708 bytes .../__pycache__/packaging.cpython-312.pyc | Bin 0 -> 2579 bytes .../setuptools_build.cpython-312.pyc | Bin 0 -> 4546 bytes .../__pycache__/subprocess.cpython-312.pyc | Bin 0 -> 8714 bytes .../__pycache__/temp_dir.cpython-312.pyc | Bin 0 -> 12058 bytes .../__pycache__/unpacking.cpython-312.pyc | Bin 0 -> 11104 bytes .../utils/__pycache__/urls.cpython-312.pyc | Bin 0 -> 2401 bytes .../__pycache__/virtualenv.cpython-312.pyc | Bin 0 -> 4476 bytes .../utils/__pycache__/wheel.cpython-312.pyc | Bin 0 -> 5922 bytes .../pip/_internal/utils/_jaraco_text.py | 109 + .../site-packages/pip/_internal/utils/_log.py | 38 + .../pip/_internal/utils/appdirs.py | 52 + .../pip/_internal/utils/compat.py | 63 + .../pip/_internal/utils/compatibility_tags.py | 165 + .../pip/_internal/utils/datetime.py | 11 + .../pip/_internal/utils/deprecation.py | 120 + .../pip/_internal/utils/direct_url_helpers.py | 87 + .../pip/_internal/utils/egg_link.py | 80 + .../pip/_internal/utils/encoding.py | 36 + .../pip/_internal/utils/entrypoints.py | 84 + .../pip/_internal/utils/filesystem.py | 153 + .../pip/_internal/utils/filetypes.py | 27 + .../pip/_internal/utils/glibc.py | 88 + .../pip/_internal/utils/hashes.py | 151 + .../pip/_internal/utils/logging.py | 348 + .../site-packages/pip/_internal/utils/misc.py | 783 ++ .../pip/_internal/utils/models.py | 39 + .../pip/_internal/utils/packaging.py | 57 + .../pip/_internal/utils/setuptools_build.py | 146 + .../pip/_internal/utils/subprocess.py | 260 + .../pip/_internal/utils/temp_dir.py | 296 + .../pip/_internal/utils/unpacking.py | 257 + .../site-packages/pip/_internal/utils/urls.py | 62 + .../pip/_internal/utils/virtualenv.py | 104 + .../pip/_internal/utils/wheel.py | 134 + .../pip/_internal/vcs/__init__.py | 15 + .../vcs/__pycache__/__init__.cpython-312.pyc | Bin 0 -> 530 bytes .../vcs/__pycache__/bazaar.cpython-312.pyc | Bin 0 -> 5022 bytes .../vcs/__pycache__/git.cpython-312.pyc | Bin 0 -> 18991 bytes .../vcs/__pycache__/mercurial.cpython-312.pyc | Bin 0 -> 7611 bytes .../__pycache__/subversion.cpython-312.pyc | Bin 0 -> 12483 bytes .../versioncontrol.cpython-312.pyc | Bin 0 -> 29009 bytes .../site-packages/pip/_internal/vcs/bazaar.py | 112 + .../site-packages/pip/_internal/vcs/git.py | 526 + .../pip/_internal/vcs/mercurial.py | 163 + .../pip/_internal/vcs/subversion.py | 324 + .../pip/_internal/vcs/versioncontrol.py | 705 ++ .../pip/_internal/wheel_builder.py | 354 + .../Lib/site-packages/pip/_vendor/__init__.py | 121 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 4692 bytes .../_vendor/__pycache__/six.cpython-312.pyc | Bin 0 -> 41269 bytes .../typing_extensions.cpython-312.pyc | Bin 0 -> 122049 bytes .../pip/_vendor/cachecontrol/__init__.py | 28 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 902 bytes .../__pycache__/_cmd.cpython-312.pyc | Bin 0 -> 2646 bytes .../__pycache__/adapter.cpython-312.pyc | Bin 0 -> 6464 bytes .../__pycache__/cache.cpython-312.pyc | Bin 0 -> 3809 bytes .../__pycache__/controller.cpython-312.pyc | Bin 0 -> 16167 bytes .../__pycache__/filewrapper.cpython-312.pyc | Bin 0 -> 4347 bytes .../__pycache__/heuristics.cpython-312.pyc | Bin 0 -> 6694 bytes .../__pycache__/serialize.cpython-312.pyc | Bin 0 -> 6405 bytes .../__pycache__/wrapper.cpython-312.pyc | Bin 0 -> 1674 bytes .../pip/_vendor/cachecontrol/_cmd.py | 70 + .../pip/_vendor/cachecontrol/adapter.py | 161 + .../pip/_vendor/cachecontrol/cache.py | 74 + .../_vendor/cachecontrol/caches/__init__.py | 8 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 435 bytes .../__pycache__/file_cache.cpython-312.pyc | Bin 0 -> 7710 bytes .../__pycache__/redis_cache.cpython-312.pyc | Bin 0 -> 2738 bytes .../_vendor/cachecontrol/caches/file_cache.py | 181 + .../cachecontrol/caches/redis_cache.py | 48 + .../pip/_vendor/cachecontrol/controller.py | 494 + .../pip/_vendor/cachecontrol/filewrapper.py | 119 + .../pip/_vendor/cachecontrol/heuristics.py | 154 + .../pip/_vendor/cachecontrol/py.typed | 0 .../pip/_vendor/cachecontrol/serialize.py | 206 + .../pip/_vendor/cachecontrol/wrapper.py | 43 + .../pip/_vendor/certifi/__init__.py | 4 + .../pip/_vendor/certifi/__main__.py | 12 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 318 bytes .../__pycache__/__main__.cpython-312.pyc | Bin 0 -> 645 bytes .../certifi/__pycache__/core.cpython-312.pyc | Bin 0 -> 2849 bytes .../pip/_vendor/certifi/cacert.pem | 4635 +++++++++ .../site-packages/pip/_vendor/certifi/core.py | 108 + .../pip/_vendor/certifi/py.typed | 0 .../pip/_vendor/chardet/__init__.py | 115 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 4568 bytes .../__pycache__/big5freq.cpython-312.pyc | Bin 0 -> 27199 bytes .../__pycache__/big5prober.cpython-312.pyc | Bin 0 -> 1387 bytes .../chardistribution.cpython-312.pyc | Bin 0 -> 9638 bytes .../charsetgroupprober.cpython-312.pyc | Bin 0 -> 4122 bytes .../__pycache__/charsetprober.cpython-312.pyc | Bin 0 -> 5018 bytes .../codingstatemachine.cpython-312.pyc | Bin 0 -> 3878 bytes .../codingstatemachinedict.cpython-312.pyc | Bin 0 -> 789 bytes .../__pycache__/cp949prober.cpython-312.pyc | Bin 0 -> 1396 bytes .../chardet/__pycache__/enums.cpython-312.pyc | Bin 0 -> 2996 bytes .../__pycache__/escprober.cpython-312.pyc | Bin 0 -> 4566 bytes .../chardet/__pycache__/escsm.cpython-312.pyc | Bin 0 -> 15310 bytes .../__pycache__/eucjpprober.cpython-312.pyc | Bin 0 -> 4383 bytes .../__pycache__/euckrfreq.cpython-312.pyc | Bin 0 -> 12082 bytes .../__pycache__/euckrprober.cpython-312.pyc | Bin 0 -> 1390 bytes .../__pycache__/euctwfreq.cpython-312.pyc | Bin 0 -> 27204 bytes .../__pycache__/euctwprober.cpython-312.pyc | Bin 0 -> 1390 bytes .../__pycache__/gb2312freq.cpython-312.pyc | Bin 0 -> 19126 bytes .../__pycache__/gb2312prober.cpython-312.pyc | Bin 0 -> 1403 bytes .../__pycache__/hebrewprober.cpython-312.pyc | Bin 0 -> 5822 bytes .../__pycache__/jisfreq.cpython-312.pyc | Bin 0 -> 22155 bytes .../__pycache__/johabfreq.cpython-312.pyc | Bin 0 -> 83003 bytes .../__pycache__/johabprober.cpython-312.pyc | Bin 0 -> 1394 bytes .../__pycache__/jpcntx.cpython-312.pyc | Bin 0 -> 39549 bytes .../langbulgarianmodel.cpython-312.pyc | Bin 0 -> 83122 bytes .../langgreekmodel.cpython-312.pyc | Bin 0 -> 76988 bytes .../langhebrewmodel.cpython-312.pyc | Bin 0 -> 77499 bytes .../langhungarianmodel.cpython-312.pyc | Bin 0 -> 83076 bytes .../langrussianmodel.cpython-312.pyc | Bin 0 -> 105251 bytes .../__pycache__/langthaimodel.cpython-312.pyc | Bin 0 -> 77677 bytes .../langturkishmodel.cpython-312.pyc | Bin 0 -> 77516 bytes .../__pycache__/latin1prober.cpython-312.pyc | Bin 0 -> 7002 bytes .../macromanprober.cpython-312.pyc | Bin 0 -> 7182 bytes .../mbcharsetprober.cpython-312.pyc | Bin 0 -> 3903 bytes .../mbcsgroupprober.cpython-312.pyc | Bin 0 -> 1588 bytes .../__pycache__/mbcssm.cpython-312.pyc | Bin 0 -> 38645 bytes .../__pycache__/resultdict.cpython-312.pyc | Bin 0 -> 632 bytes .../sbcharsetprober.cpython-312.pyc | Bin 0 -> 6387 bytes .../sbcsgroupprober.cpython-312.pyc | Bin 0 -> 2357 bytes .../__pycache__/sjisprober.cpython-312.pyc | Bin 0 -> 4495 bytes .../universaldetector.cpython-312.pyc | Bin 0 -> 12269 bytes .../__pycache__/utf1632prober.cpython-312.pyc | Bin 0 -> 9979 bytes .../__pycache__/utf8prober.cpython-312.pyc | Bin 0 -> 3175 bytes .../__pycache__/version.cpython-312.pyc | Bin 0 -> 488 bytes .../pip/_vendor/chardet/big5freq.py | 386 + .../pip/_vendor/chardet/big5prober.py | 47 + .../pip/_vendor/chardet/chardistribution.py | 261 + .../pip/_vendor/chardet/charsetgroupprober.py | 106 + .../pip/_vendor/chardet/charsetprober.py | 147 + .../pip/_vendor/chardet/cli/__init__.py | 0 .../cli/__pycache__/__init__.cpython-312.pyc | Bin 0 -> 195 bytes .../__pycache__/chardetect.cpython-312.pyc | Bin 0 -> 4012 bytes .../pip/_vendor/chardet/cli/chardetect.py | 112 + .../pip/_vendor/chardet/codingstatemachine.py | 90 + .../_vendor/chardet/codingstatemachinedict.py | 19 + .../pip/_vendor/chardet/cp949prober.py | 49 + .../pip/_vendor/chardet/enums.py | 85 + .../pip/_vendor/chardet/escprober.py | 102 + .../pip/_vendor/chardet/escsm.py | 261 + .../pip/_vendor/chardet/eucjpprober.py | 102 + .../pip/_vendor/chardet/euckrfreq.py | 196 + .../pip/_vendor/chardet/euckrprober.py | 47 + .../pip/_vendor/chardet/euctwfreq.py | 388 + .../pip/_vendor/chardet/euctwprober.py | 47 + .../pip/_vendor/chardet/gb2312freq.py | 284 + .../pip/_vendor/chardet/gb2312prober.py | 47 + .../pip/_vendor/chardet/hebrewprober.py | 316 + .../pip/_vendor/chardet/jisfreq.py | 325 + .../pip/_vendor/chardet/johabfreq.py | 2382 +++++ .../pip/_vendor/chardet/johabprober.py | 47 + .../pip/_vendor/chardet/jpcntx.py | 238 + .../pip/_vendor/chardet/langbulgarianmodel.py | 4649 +++++++++ .../pip/_vendor/chardet/langgreekmodel.py | 4397 +++++++++ .../pip/_vendor/chardet/langhebrewmodel.py | 4380 +++++++++ .../pip/_vendor/chardet/langhungarianmodel.py | 4649 +++++++++ .../pip/_vendor/chardet/langrussianmodel.py | 5725 +++++++++++ .../pip/_vendor/chardet/langthaimodel.py | 4380 +++++++++ .../pip/_vendor/chardet/langturkishmodel.py | 4380 +++++++++ .../pip/_vendor/chardet/latin1prober.py | 147 + .../pip/_vendor/chardet/macromanprober.py | 162 + .../pip/_vendor/chardet/mbcharsetprober.py | 95 + .../pip/_vendor/chardet/mbcsgroupprober.py | 57 + .../pip/_vendor/chardet/mbcssm.py | 661 ++ .../pip/_vendor/chardet/metadata/__init__.py | 0 .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 200 bytes .../__pycache__/languages.cpython-312.pyc | Bin 0 -> 9755 bytes .../pip/_vendor/chardet/metadata/languages.py | 352 + .../pip/_vendor/chardet/py.typed | 0 .../pip/_vendor/chardet/resultdict.py | 16 + .../pip/_vendor/chardet/sbcharsetprober.py | 162 + .../pip/_vendor/chardet/sbcsgroupprober.py | 88 + .../pip/_vendor/chardet/sjisprober.py | 105 + .../pip/_vendor/chardet/universaldetector.py | 362 + .../pip/_vendor/chardet/utf1632prober.py | 225 + .../pip/_vendor/chardet/utf8prober.py | 82 + .../pip/_vendor/chardet/version.py | 9 + .../pip/_vendor/colorama/__init__.py | 7 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 492 bytes .../colorama/__pycache__/ansi.cpython-312.pyc | Bin 0 -> 3950 bytes .../__pycache__/ansitowin32.cpython-312.pyc | Bin 0 -> 16421 bytes .../__pycache__/initialise.cpython-312.pyc | Bin 0 -> 3550 bytes .../__pycache__/win32.cpython-312.pyc | Bin 0 -> 8126 bytes .../__pycache__/winterm.cpython-312.pyc | Bin 0 -> 9088 bytes .../pip/_vendor/colorama/ansi.py | 102 + .../pip/_vendor/colorama/ansitowin32.py | 277 + .../pip/_vendor/colorama/initialise.py | 121 + .../pip/_vendor/colorama/tests/__init__.py | 1 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 198 bytes .../__pycache__/ansi_test.cpython-312.pyc | Bin 0 -> 5467 bytes .../ansitowin32_test.cpython-312.pyc | Bin 0 -> 18103 bytes .../initialise_test.cpython-312.pyc | Bin 0 -> 11748 bytes .../__pycache__/isatty_test.cpython-312.pyc | Bin 0 -> 4904 bytes .../tests/__pycache__/utils.cpython-312.pyc | Bin 0 -> 2488 bytes .../__pycache__/winterm_test.cpython-312.pyc | Bin 0 -> 6612 bytes .../pip/_vendor/colorama/tests/ansi_test.py | 76 + .../colorama/tests/ansitowin32_test.py | 294 + .../_vendor/colorama/tests/initialise_test.py | 189 + .../pip/_vendor/colorama/tests/isatty_test.py | 57 + .../pip/_vendor/colorama/tests/utils.py | 49 + .../_vendor/colorama/tests/winterm_test.py | 131 + .../pip/_vendor/colorama/win32.py | 180 + .../pip/_vendor/colorama/winterm.py | 195 + .../pip/_vendor/distlib/__init__.py | 33 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 1269 bytes .../__pycache__/compat.cpython-312.pyc | Bin 0 -> 45605 bytes .../__pycache__/database.cpython-312.pyc | Bin 0 -> 66027 bytes .../distlib/__pycache__/index.cpython-312.pyc | Bin 0 -> 24366 bytes .../__pycache__/locators.cpython-312.pyc | Bin 0 -> 60158 bytes .../__pycache__/manifest.cpython-312.pyc | Bin 0 -> 15125 bytes .../__pycache__/markers.cpython-312.pyc | Bin 0 -> 7682 bytes .../__pycache__/metadata.cpython-312.pyc | Bin 0 -> 41799 bytes .../__pycache__/resources.cpython-312.pyc | Bin 0 -> 17325 bytes .../__pycache__/scripts.cpython-312.pyc | Bin 0 -> 19580 bytes .../distlib/__pycache__/util.cpython-312.pyc | Bin 0 -> 88256 bytes .../__pycache__/version.cpython-312.pyc | Bin 0 -> 30366 bytes .../distlib/__pycache__/wheel.cpython-312.pyc | Bin 0 -> 51861 bytes .../pip/_vendor/distlib/compat.py | 1138 +++ .../pip/_vendor/distlib/database.py | 1359 +++ .../pip/_vendor/distlib/index.py | 508 + .../pip/_vendor/distlib/locators.py | 1303 +++ .../pip/_vendor/distlib/manifest.py | 384 + .../pip/_vendor/distlib/markers.py | 167 + .../pip/_vendor/distlib/metadata.py | 1068 ++ .../pip/_vendor/distlib/resources.py | 358 + .../pip/_vendor/distlib/scripts.py | 452 + .../site-packages/pip/_vendor/distlib/t32.exe | Bin 0 -> 97792 bytes .../pip/_vendor/distlib/t64-arm.exe | Bin 0 -> 182784 bytes .../site-packages/pip/_vendor/distlib/t64.exe | Bin 0 -> 108032 bytes .../site-packages/pip/_vendor/distlib/util.py | 2025 ++++ .../pip/_vendor/distlib/version.py | 751 ++ .../site-packages/pip/_vendor/distlib/w32.exe | Bin 0 -> 91648 bytes .../pip/_vendor/distlib/w64-arm.exe | Bin 0 -> 168448 bytes .../site-packages/pip/_vendor/distlib/w64.exe | Bin 0 -> 101888 bytes .../pip/_vendor/distlib/wheel.py | 1099 +++ .../pip/_vendor/distro/__init__.py | 54 + .../pip/_vendor/distro/__main__.py | 4 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 960 bytes .../__pycache__/__main__.cpython-312.pyc | Bin 0 -> 292 bytes .../distro/__pycache__/distro.cpython-312.pyc | Bin 0 -> 53754 bytes .../pip/_vendor/distro/distro.py | 1399 +++ .../site-packages/pip/_vendor/distro/py.typed | 0 .../pip/_vendor/idna/__init__.py | 44 + .../idna/__pycache__/__init__.cpython-312.pyc | Bin 0 -> 881 bytes .../idna/__pycache__/codec.cpython-312.pyc | Bin 0 -> 4633 bytes .../idna/__pycache__/compat.cpython-312.pyc | Bin 0 -> 887 bytes .../idna/__pycache__/core.cpython-312.pyc | Bin 0 -> 16282 bytes .../idna/__pycache__/idnadata.cpython-312.pyc | Bin 0 -> 38382 bytes .../__pycache__/intranges.cpython-312.pyc | Bin 0 -> 2638 bytes .../__pycache__/package_data.cpython-312.pyc | Bin 0 -> 216 bytes .../__pycache__/uts46data.cpython-312.pyc | Bin 0 -> 158870 bytes .../site-packages/pip/_vendor/idna/codec.py | 112 + .../site-packages/pip/_vendor/idna/compat.py | 13 + .../site-packages/pip/_vendor/idna/core.py | 400 + .../pip/_vendor/idna/idnadata.py | 2151 +++++ .../pip/_vendor/idna/intranges.py | 54 + .../pip/_vendor/idna/package_data.py | 2 + .../site-packages/pip/_vendor/idna/py.typed | 0 .../pip/_vendor/idna/uts46data.py | 8600 +++++++++++++++++ .../pip/_vendor/msgpack/__init__.py | 57 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 1831 bytes .../__pycache__/exceptions.cpython-312.pyc | Bin 0 -> 2025 bytes .../msgpack/__pycache__/ext.cpython-312.pyc | Bin 0 -> 8668 bytes .../__pycache__/fallback.cpython-312.pyc | Bin 0 -> 43576 bytes .../pip/_vendor/msgpack/exceptions.py | 48 + .../site-packages/pip/_vendor/msgpack/ext.py | 193 + .../pip/_vendor/msgpack/fallback.py | 1010 ++ .../pip/_vendor/packaging/__about__.py | 26 + .../pip/_vendor/packaging/__init__.py | 25 + .../__pycache__/__about__.cpython-312.pyc | Bin 0 -> 630 bytes .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 466 bytes .../__pycache__/_manylinux.cpython-312.pyc | Bin 0 -> 12076 bytes .../__pycache__/_musllinux.cpython-312.pyc | Bin 0 -> 6910 bytes .../__pycache__/_structures.cpython-312.pyc | Bin 0 -> 3241 bytes .../__pycache__/markers.cpython-312.pyc | Bin 0 -> 14058 bytes .../__pycache__/requirements.cpython-312.pyc | Bin 0 -> 6946 bytes .../__pycache__/specifiers.cpython-312.pyc | Bin 0 -> 31247 bytes .../__pycache__/tags.cpython-312.pyc | Bin 0 -> 18956 bytes .../__pycache__/utils.cpython-312.pyc | Bin 0 -> 5868 bytes .../__pycache__/version.cpython-312.pyc | Bin 0 -> 19939 bytes .../pip/_vendor/packaging/_manylinux.py | 301 + .../pip/_vendor/packaging/_musllinux.py | 136 + .../pip/_vendor/packaging/_structures.py | 61 + .../pip/_vendor/packaging/markers.py | 304 + .../pip/_vendor/packaging/py.typed | 0 .../pip/_vendor/packaging/requirements.py | 146 + .../pip/_vendor/packaging/specifiers.py | 802 ++ .../pip/_vendor/packaging/tags.py | 487 + .../pip/_vendor/packaging/utils.py | 136 + .../pip/_vendor/packaging/version.py | 504 + .../pip/_vendor/pkg_resources/__init__.py | 3361 +++++++ .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 146474 bytes .../pip/_vendor/platformdirs/__init__.py | 566 ++ .../pip/_vendor/platformdirs/__main__.py | 53 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 18029 bytes .../__pycache__/__main__.cpython-312.pyc | Bin 0 -> 1946 bytes .../__pycache__/android.cpython-312.pyc | Bin 0 -> 9444 bytes .../__pycache__/api.cpython-312.pyc | Bin 0 -> 9672 bytes .../__pycache__/macos.cpython-312.pyc | Bin 0 -> 5637 bytes .../__pycache__/unix.cpython-312.pyc | Bin 0 -> 12441 bytes .../__pycache__/version.cpython-312.pyc | Bin 0 -> 311 bytes .../__pycache__/windows.cpython-312.pyc | Bin 0 -> 12999 bytes .../pip/_vendor/platformdirs/android.py | 210 + .../pip/_vendor/platformdirs/api.py | 223 + .../pip/_vendor/platformdirs/macos.py | 91 + .../pip/_vendor/platformdirs/py.typed | 0 .../pip/_vendor/platformdirs/unix.py | 223 + .../pip/_vendor/platformdirs/version.py | 4 + .../pip/_vendor/platformdirs/windows.py | 255 + .../pip/_vendor/pygments/__init__.py | 82 + .../pip/_vendor/pygments/__main__.py | 17 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 3489 bytes .../__pycache__/__main__.cpython-312.pyc | Bin 0 -> 735 bytes .../__pycache__/cmdline.cpython-312.pyc | Bin 0 -> 26606 bytes .../__pycache__/console.cpython-312.pyc | Bin 0 -> 2627 bytes .../__pycache__/filter.cpython-312.pyc | Bin 0 -> 3233 bytes .../__pycache__/formatter.cpython-312.pyc | Bin 0 -> 4570 bytes .../__pycache__/lexer.cpython-312.pyc | Bin 0 -> 38330 bytes .../__pycache__/modeline.cpython-312.pyc | Bin 0 -> 1569 bytes .../__pycache__/plugin.cpython-312.pyc | Bin 0 -> 3397 bytes .../__pycache__/regexopt.cpython-312.pyc | Bin 0 -> 4082 bytes .../__pycache__/scanner.cpython-312.pyc | Bin 0 -> 4757 bytes .../__pycache__/sphinxext.cpython-312.pyc | Bin 0 -> 11047 bytes .../__pycache__/style.cpython-312.pyc | Bin 0 -> 6675 bytes .../__pycache__/token.cpython-312.pyc | Bin 0 -> 8143 bytes .../__pycache__/unistring.cpython-312.pyc | Bin 0 -> 32989 bytes .../pygments/__pycache__/util.cpython-312.pyc | Bin 0 -> 13982 bytes .../pip/_vendor/pygments/cmdline.py | 668 ++ .../pip/_vendor/pygments/console.py | 70 + .../pip/_vendor/pygments/filter.py | 71 + .../pip/_vendor/pygments/filters/__init__.py | 940 ++ .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 37937 bytes .../pip/_vendor/pygments/formatter.py | 124 + .../_vendor/pygments/formatters/__init__.py | 158 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 6927 bytes .../__pycache__/_mapping.cpython-312.pyc | Bin 0 -> 4216 bytes .../__pycache__/bbcode.cpython-312.pyc | Bin 0 -> 4195 bytes .../__pycache__/groff.cpython-312.pyc | Bin 0 -> 7265 bytes .../__pycache__/html.cpython-312.pyc | Bin 0 -> 40573 bytes .../__pycache__/img.cpython-312.pyc | Bin 0 -> 27044 bytes .../__pycache__/irc.cpython-312.pyc | Bin 0 -> 6066 bytes .../__pycache__/latex.cpython-312.pyc | Bin 0 -> 19955 bytes .../__pycache__/other.cpython-312.pyc | Bin 0 -> 6885 bytes .../__pycache__/pangomarkup.cpython-312.pyc | Bin 0 -> 2931 bytes .../__pycache__/rtf.cpython-312.pyc | Bin 0 -> 6127 bytes .../__pycache__/svg.cpython-312.pyc | Bin 0 -> 9067 bytes .../__pycache__/terminal.cpython-312.pyc | Bin 0 -> 5830 bytes .../__pycache__/terminal256.cpython-312.pyc | Bin 0 -> 15158 bytes .../_vendor/pygments/formatters/_mapping.py | 23 + .../pip/_vendor/pygments/formatters/bbcode.py | 108 + .../pip/_vendor/pygments/formatters/groff.py | 170 + .../pip/_vendor/pygments/formatters/html.py | 989 ++ .../pip/_vendor/pygments/formatters/img.py | 645 ++ .../pip/_vendor/pygments/formatters/irc.py | 154 + .../pip/_vendor/pygments/formatters/latex.py | 521 + .../pip/_vendor/pygments/formatters/other.py | 161 + .../pygments/formatters/pangomarkup.py | 83 + .../pip/_vendor/pygments/formatters/rtf.py | 146 + .../pip/_vendor/pygments/formatters/svg.py | 188 + .../_vendor/pygments/formatters/terminal.py | 127 + .../pygments/formatters/terminal256.py | 338 + .../pip/_vendor/pygments/lexer.py | 943 ++ .../pip/_vendor/pygments/lexers/__init__.py | 362 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 14653 bytes .../__pycache__/_mapping.cpython-312.pyc | Bin 0 -> 64405 bytes .../lexers/__pycache__/python.cpython-312.pyc | Bin 0 -> 42640 bytes .../pip/_vendor/pygments/lexers/_mapping.py | 559 ++ .../pip/_vendor/pygments/lexers/python.py | 1198 +++ .../pip/_vendor/pygments/modeline.py | 43 + .../pip/_vendor/pygments/plugin.py | 88 + .../pip/_vendor/pygments/regexopt.py | 91 + .../pip/_vendor/pygments/scanner.py | 104 + .../pip/_vendor/pygments/sphinxext.py | 217 + .../pip/_vendor/pygments/style.py | 197 + .../pip/_vendor/pygments/styles/__init__.py | 103 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 4449 bytes .../pip/_vendor/pygments/token.py | 213 + .../pip/_vendor/pygments/unistring.py | 153 + .../pip/_vendor/pygments/util.py | 330 + .../pip/_vendor/pyparsing/__init__.py | 322 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 7912 bytes .../__pycache__/actions.cpython-312.pyc | Bin 0 -> 8396 bytes .../__pycache__/common.cpython-312.pyc | Bin 0 -> 13415 bytes .../__pycache__/core.cpython-312.pyc | Bin 0 -> 267709 bytes .../__pycache__/exceptions.cpython-312.pyc | Bin 0 -> 12995 bytes .../__pycache__/helpers.cpython-312.pyc | Bin 0 -> 48502 bytes .../__pycache__/results.cpython-312.pyc | Bin 0 -> 34111 bytes .../__pycache__/testing.cpython-312.pyc | Bin 0 -> 17189 bytes .../__pycache__/unicode.cpython-312.pyc | Bin 0 -> 13185 bytes .../__pycache__/util.cpython-312.pyc | Bin 0 -> 14905 bytes .../pip/_vendor/pyparsing/actions.py | 217 + .../pip/_vendor/pyparsing/common.py | 432 + .../pip/_vendor/pyparsing/core.py | 6115 ++++++++++++ .../pip/_vendor/pyparsing/diagram/__init__.py | 656 ++ .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 26814 bytes .../pip/_vendor/pyparsing/exceptions.py | 299 + .../pip/_vendor/pyparsing/helpers.py | 1100 +++ .../pip/_vendor/pyparsing/py.typed | 0 .../pip/_vendor/pyparsing/results.py | 796 ++ .../pip/_vendor/pyparsing/testing.py | 331 + .../pip/_vendor/pyparsing/unicode.py | 361 + .../pip/_vendor/pyparsing/util.py | 284 + .../pip/_vendor/pyproject_hooks/__init__.py | 23 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 614 bytes .../__pycache__/_compat.cpython-312.pyc | Bin 0 -> 375 bytes .../__pycache__/_impl.cpython-312.pyc | Bin 0 -> 14726 bytes .../pip/_vendor/pyproject_hooks/_compat.py | 8 + .../pip/_vendor/pyproject_hooks/_impl.py | 330 + .../pyproject_hooks/_in_process/__init__.py | 18 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 1081 bytes .../__pycache__/_in_process.cpython-312.pyc | Bin 0 -> 14398 bytes .../_in_process/_in_process.py | 353 + .../pip/_vendor/requests/__init__.py | 182 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 5454 bytes .../__pycache__/__version__.cpython-312.pyc | Bin 0 -> 585 bytes .../_internal_utils.cpython-312.pyc | Bin 0 -> 2025 bytes .../__pycache__/adapters.cpython-312.pyc | Bin 0 -> 21281 bytes .../requests/__pycache__/api.cpython-312.pyc | Bin 0 -> 7205 bytes .../requests/__pycache__/auth.cpython-312.pyc | Bin 0 -> 13924 bytes .../__pycache__/certs.cpython-312.pyc | Bin 0 -> 923 bytes .../__pycache__/compat.cpython-312.pyc | Bin 0 -> 1508 bytes .../__pycache__/cookies.cpython-312.pyc | Bin 0 -> 25247 bytes .../__pycache__/exceptions.cpython-312.pyc | Bin 0 -> 7048 bytes .../requests/__pycache__/help.cpython-312.pyc | Bin 0 -> 4313 bytes .../__pycache__/hooks.cpython-312.pyc | Bin 0 -> 1053 bytes .../__pycache__/models.cpython-312.pyc | Bin 0 -> 35449 bytes .../__pycache__/packages.cpython-312.pyc | Bin 0 -> 773 bytes .../__pycache__/sessions.cpython-312.pyc | Bin 0 -> 27758 bytes .../__pycache__/status_codes.cpython-312.pyc | Bin 0 -> 5960 bytes .../__pycache__/structures.cpython-312.pyc | Bin 0 -> 5618 bytes .../__pycache__/utils.cpython-312.pyc | Bin 0 -> 36270 bytes .../pip/_vendor/requests/__version__.py | 14 + .../pip/_vendor/requests/_internal_utils.py | 50 + .../pip/_vendor/requests/adapters.py | 538 ++ .../site-packages/pip/_vendor/requests/api.py | 157 + .../pip/_vendor/requests/auth.py | 315 + .../pip/_vendor/requests/certs.py | 24 + .../pip/_vendor/requests/compat.py | 67 + .../pip/_vendor/requests/cookies.py | 561 ++ .../pip/_vendor/requests/exceptions.py | 141 + .../pip/_vendor/requests/help.py | 131 + .../pip/_vendor/requests/hooks.py | 33 + .../pip/_vendor/requests/models.py | 1034 ++ .../pip/_vendor/requests/packages.py | 16 + .../pip/_vendor/requests/sessions.py | 833 ++ .../pip/_vendor/requests/status_codes.py | 128 + .../pip/_vendor/requests/structures.py | 99 + .../pip/_vendor/requests/utils.py | 1094 +++ .../pip/_vendor/resolvelib/__init__.py | 26 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 635 bytes .../__pycache__/providers.cpython-312.pyc | Bin 0 -> 6852 bytes .../__pycache__/reporters.cpython-312.pyc | Bin 0 -> 2655 bytes .../__pycache__/resolvers.cpython-312.pyc | Bin 0 -> 25898 bytes .../__pycache__/structs.cpython-312.pyc | Bin 0 -> 10507 bytes .../pip/_vendor/resolvelib/compat/__init__.py | 0 .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 201 bytes .../collections_abc.cpython-312.pyc | Bin 0 -> 421 bytes .../resolvelib/compat/collections_abc.py | 6 + .../pip/_vendor/resolvelib/providers.py | 133 + .../pip/_vendor/resolvelib/py.typed | 0 .../pip/_vendor/resolvelib/reporters.py | 43 + .../pip/_vendor/resolvelib/resolvers.py | 547 ++ .../pip/_vendor/resolvelib/structs.py | 170 + .../pip/_vendor/rich/__init__.py | 177 + .../pip/_vendor/rich/__main__.py | 274 + .../rich/__pycache__/__init__.cpython-312.pyc | Bin 0 -> 7016 bytes .../rich/__pycache__/__main__.cpython-312.pyc | Bin 0 -> 10305 bytes .../__pycache__/_cell_widths.cpython-312.pyc | Bin 0 -> 7822 bytes .../__pycache__/_emoji_codes.cpython-312.pyc | Bin 0 -> 205977 bytes .../_emoji_replace.cpython-312.pyc | Bin 0 -> 1730 bytes .../_export_format.cpython-312.pyc | Bin 0 -> 2322 bytes .../__pycache__/_extension.cpython-312.pyc | Bin 0 -> 538 bytes .../rich/__pycache__/_fileno.cpython-312.pyc | Bin 0 -> 856 bytes .../rich/__pycache__/_inspect.cpython-312.pyc | Bin 0 -> 12078 bytes .../__pycache__/_log_render.cpython-312.pyc | Bin 0 -> 4148 bytes .../rich/__pycache__/_loop.cpython-312.pyc | Bin 0 -> 1886 bytes .../__pycache__/_null_file.cpython-312.pyc | Bin 0 -> 3621 bytes .../__pycache__/_palettes.cpython-312.pyc | Bin 0 -> 5161 bytes .../rich/__pycache__/_pick.cpython-312.pyc | Bin 0 -> 727 bytes .../rich/__pycache__/_ratio.cpython-312.pyc | Bin 0 -> 6580 bytes .../__pycache__/_spinners.cpython-312.pyc | Bin 0 -> 13180 bytes .../rich/__pycache__/_stack.cpython-312.pyc | Bin 0 -> 966 bytes .../rich/__pycache__/_timer.cpython-312.pyc | Bin 0 -> 866 bytes .../_win32_console.cpython-312.pyc | Bin 0 -> 28977 bytes .../rich/__pycache__/_windows.cpython-312.pyc | Bin 0 -> 2491 bytes .../_windows_renderer.cpython-312.pyc | Bin 0 -> 3574 bytes .../rich/__pycache__/_wrap.cpython-312.pyc | Bin 0 -> 2361 bytes .../rich/__pycache__/abc.cpython-312.pyc | Bin 0 -> 1609 bytes .../rich/__pycache__/align.cpython-312.pyc | Bin 0 -> 12323 bytes .../rich/__pycache__/ansi.cpython-312.pyc | Bin 0 -> 9107 bytes .../rich/__pycache__/bar.cpython-312.pyc | Bin 0 -> 4273 bytes .../rich/__pycache__/box.cpython-312.pyc | Bin 0 -> 11859 bytes .../rich/__pycache__/cells.cpython-312.pyc | Bin 0 -> 5619 bytes .../rich/__pycache__/color.cpython-312.pyc | Bin 0 -> 26571 bytes .../__pycache__/color_triplet.cpython-312.pyc | Bin 0 -> 1702 bytes .../rich/__pycache__/columns.cpython-312.pyc | Bin 0 -> 8588 bytes .../rich/__pycache__/console.cpython-312.pyc | Bin 0 -> 113794 bytes .../__pycache__/constrain.cpython-312.pyc | Bin 0 -> 2259 bytes .../__pycache__/containers.cpython-312.pyc | Bin 0 -> 9227 bytes .../rich/__pycache__/control.cpython-312.pyc | Bin 0 -> 10930 bytes .../default_styles.cpython-312.pyc | Bin 0 -> 10374 bytes .../rich/__pycache__/diagnose.cpython-312.pyc | Bin 0 -> 1488 bytes .../rich/__pycache__/emoji.cpython-312.pyc | Bin 0 -> 4210 bytes .../rich/__pycache__/errors.cpython-312.pyc | Bin 0 -> 1846 bytes .../__pycache__/file_proxy.cpython-312.pyc | Bin 0 -> 3578 bytes .../rich/__pycache__/filesize.cpython-312.pyc | Bin 0 -> 3083 bytes .../__pycache__/highlighter.cpython-312.pyc | Bin 0 -> 9899 bytes .../rich/__pycache__/json.cpython-312.pyc | Bin 0 -> 6036 bytes .../rich/__pycache__/jupyter.cpython-312.pyc | Bin 0 -> 5210 bytes .../rich/__pycache__/layout.cpython-312.pyc | Bin 0 -> 20221 bytes .../rich/__pycache__/live.cpython-312.pyc | Bin 0 -> 19144 bytes .../__pycache__/live_render.cpython-312.pyc | Bin 0 -> 4895 bytes .../rich/__pycache__/logging.cpython-312.pyc | Bin 0 -> 13555 bytes .../rich/__pycache__/markup.cpython-312.pyc | Bin 0 -> 9299 bytes .../rich/__pycache__/measure.cpython-312.pyc | Bin 0 -> 6377 bytes .../rich/__pycache__/padding.cpython-312.pyc | Bin 0 -> 7135 bytes .../rich/__pycache__/pager.cpython-312.pyc | Bin 0 -> 1821 bytes .../rich/__pycache__/palette.cpython-312.pyc | Bin 0 -> 5315 bytes .../rich/__pycache__/panel.cpython-312.pyc | Bin 0 -> 12098 bytes .../rich/__pycache__/pretty.cpython-312.pyc | Bin 0 -> 40057 bytes .../rich/__pycache__/progress.cpython-312.pyc | Bin 0 -> 75079 bytes .../__pycache__/progress_bar.cpython-312.pyc | Bin 0 -> 10390 bytes .../rich/__pycache__/prompt.cpython-312.pyc | Bin 0 -> 14782 bytes .../rich/__pycache__/protocol.cpython-312.pyc | Bin 0 -> 1793 bytes .../rich/__pycache__/region.cpython-312.pyc | Bin 0 -> 568 bytes .../rich/__pycache__/repr.cpython-312.pyc | Bin 0 -> 6627 bytes .../rich/__pycache__/rule.cpython-312.pyc | Bin 0 -> 6569 bytes .../rich/__pycache__/scope.cpython-312.pyc | Bin 0 -> 3831 bytes .../rich/__pycache__/screen.cpython-312.pyc | Bin 0 -> 2485 bytes .../rich/__pycache__/segment.cpython-312.pyc | Bin 0 -> 28162 bytes .../rich/__pycache__/spinner.cpython-312.pyc | Bin 0 -> 6065 bytes .../rich/__pycache__/status.cpython-312.pyc | Bin 0 -> 6069 bytes .../rich/__pycache__/style.cpython-312.pyc | Bin 0 -> 33515 bytes .../rich/__pycache__/styled.cpython-312.pyc | Bin 0 -> 2140 bytes .../rich/__pycache__/syntax.cpython-312.pyc | Bin 0 -> 39613 bytes .../rich/__pycache__/table.cpython-312.pyc | Bin 0 -> 43585 bytes .../terminal_theme.cpython-312.pyc | Bin 0 -> 3349 bytes .../rich/__pycache__/text.cpython-312.pyc | Bin 0 -> 58964 bytes .../rich/__pycache__/theme.cpython-312.pyc | Bin 0 -> 6341 bytes .../rich/__pycache__/themes.cpython-312.pyc | Bin 0 -> 315 bytes .../__pycache__/traceback.cpython-312.pyc | Bin 0 -> 31549 bytes .../rich/__pycache__/tree.cpython-312.pyc | Bin 0 -> 11440 bytes .../pip/_vendor/rich/_cell_widths.py | 451 + .../pip/_vendor/rich/_emoji_codes.py | 3610 +++++++ .../pip/_vendor/rich/_emoji_replace.py | 32 + .../pip/_vendor/rich/_export_format.py | 76 + .../pip/_vendor/rich/_extension.py | 10 + .../site-packages/pip/_vendor/rich/_fileno.py | 24 + .../pip/_vendor/rich/_inspect.py | 270 + .../pip/_vendor/rich/_log_render.py | 94 + .../site-packages/pip/_vendor/rich/_loop.py | 43 + .../pip/_vendor/rich/_null_file.py | 69 + .../pip/_vendor/rich/_palettes.py | 309 + .../site-packages/pip/_vendor/rich/_pick.py | 17 + .../site-packages/pip/_vendor/rich/_ratio.py | 160 + .../pip/_vendor/rich/_spinners.py | 482 + .../site-packages/pip/_vendor/rich/_stack.py | 16 + .../site-packages/pip/_vendor/rich/_timer.py | 19 + .../pip/_vendor/rich/_win32_console.py | 662 ++ .../pip/_vendor/rich/_windows.py | 72 + .../pip/_vendor/rich/_windows_renderer.py | 56 + .../site-packages/pip/_vendor/rich/_wrap.py | 56 + .../Lib/site-packages/pip/_vendor/rich/abc.py | 33 + .../site-packages/pip/_vendor/rich/align.py | 311 + .../site-packages/pip/_vendor/rich/ansi.py | 240 + .../Lib/site-packages/pip/_vendor/rich/bar.py | 94 + .../Lib/site-packages/pip/_vendor/rich/box.py | 517 + .../site-packages/pip/_vendor/rich/cells.py | 154 + .../site-packages/pip/_vendor/rich/color.py | 622 ++ .../pip/_vendor/rich/color_triplet.py | 38 + .../site-packages/pip/_vendor/rich/columns.py | 187 + .../site-packages/pip/_vendor/rich/console.py | 2633 +++++ .../pip/_vendor/rich/constrain.py | 37 + .../pip/_vendor/rich/containers.py | 167 + .../site-packages/pip/_vendor/rich/control.py | 225 + .../pip/_vendor/rich/default_styles.py | 190 + .../pip/_vendor/rich/diagnose.py | 37 + .../site-packages/pip/_vendor/rich/emoji.py | 96 + .../site-packages/pip/_vendor/rich/errors.py | 34 + .../pip/_vendor/rich/file_proxy.py | 57 + .../pip/_vendor/rich/filesize.py | 89 + .../pip/_vendor/rich/highlighter.py | 232 + .../site-packages/pip/_vendor/rich/json.py | 140 + .../site-packages/pip/_vendor/rich/jupyter.py | 101 + .../site-packages/pip/_vendor/rich/layout.py | 443 + .../site-packages/pip/_vendor/rich/live.py | 375 + .../pip/_vendor/rich/live_render.py | 113 + .../site-packages/pip/_vendor/rich/logging.py | 289 + .../site-packages/pip/_vendor/rich/markup.py | 246 + .../site-packages/pip/_vendor/rich/measure.py | 151 + .../site-packages/pip/_vendor/rich/padding.py | 141 + .../site-packages/pip/_vendor/rich/pager.py | 34 + .../site-packages/pip/_vendor/rich/palette.py | 100 + .../site-packages/pip/_vendor/rich/panel.py | 308 + .../site-packages/pip/_vendor/rich/pretty.py | 994 ++ .../pip/_vendor/rich/progress.py | 1702 ++++ .../pip/_vendor/rich/progress_bar.py | 224 + .../site-packages/pip/_vendor/rich/prompt.py | 376 + .../pip/_vendor/rich/protocol.py | 42 + .../site-packages/pip/_vendor/rich/py.typed | 0 .../site-packages/pip/_vendor/rich/region.py | 10 + .../site-packages/pip/_vendor/rich/repr.py | 149 + .../site-packages/pip/_vendor/rich/rule.py | 130 + .../site-packages/pip/_vendor/rich/scope.py | 86 + .../site-packages/pip/_vendor/rich/screen.py | 54 + .../site-packages/pip/_vendor/rich/segment.py | 739 ++ .../site-packages/pip/_vendor/rich/spinner.py | 137 + .../site-packages/pip/_vendor/rich/status.py | 132 + .../site-packages/pip/_vendor/rich/style.py | 796 ++ .../site-packages/pip/_vendor/rich/styled.py | 42 + .../site-packages/pip/_vendor/rich/syntax.py | 948 ++ .../site-packages/pip/_vendor/rich/table.py | 1002 ++ .../pip/_vendor/rich/terminal_theme.py | 153 + .../site-packages/pip/_vendor/rich/text.py | 1307 +++ .../site-packages/pip/_vendor/rich/theme.py | 115 + .../site-packages/pip/_vendor/rich/themes.py | 5 + .../pip/_vendor/rich/traceback.py | 756 ++ .../site-packages/pip/_vendor/rich/tree.py | 251 + venv/Lib/site-packages/pip/_vendor/six.py | 998 ++ .../pip/_vendor/tenacity/__init__.py | 608 ++ .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 27087 bytes .../__pycache__/_asyncio.cpython-312.pyc | Bin 0 -> 4807 bytes .../__pycache__/_utils.cpython-312.pyc | Bin 0 -> 2316 bytes .../__pycache__/after.cpython-312.pyc | Bin 0 -> 1625 bytes .../__pycache__/before.cpython-312.pyc | Bin 0 -> 1465 bytes .../__pycache__/before_sleep.cpython-312.pyc | Bin 0 -> 2303 bytes .../tenacity/__pycache__/nap.cpython-312.pyc | Bin 0 -> 1413 bytes .../__pycache__/retry.cpython-312.pyc | Bin 0 -> 14282 bytes .../tenacity/__pycache__/stop.cpython-312.pyc | Bin 0 -> 5569 bytes .../__pycache__/tornadoweb.cpython-312.pyc | Bin 0 -> 2587 bytes .../tenacity/__pycache__/wait.cpython-312.pyc | Bin 0 -> 12414 bytes .../pip/_vendor/tenacity/_asyncio.py | 94 + .../pip/_vendor/tenacity/_utils.py | 76 + .../pip/_vendor/tenacity/after.py | 51 + .../pip/_vendor/tenacity/before.py | 46 + .../pip/_vendor/tenacity/before_sleep.py | 71 + .../site-packages/pip/_vendor/tenacity/nap.py | 43 + .../pip/_vendor/tenacity/py.typed | 0 .../pip/_vendor/tenacity/retry.py | 272 + .../pip/_vendor/tenacity/stop.py | 103 + .../pip/_vendor/tenacity/tornadoweb.py | 59 + .../pip/_vendor/tenacity/wait.py | 228 + .../pip/_vendor/tomli/__init__.py | 11 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 385 bytes .../tomli/__pycache__/_parser.cpython-312.pyc | Bin 0 -> 26928 bytes .../tomli/__pycache__/_re.cpython-312.pyc | Bin 0 -> 3909 bytes .../tomli/__pycache__/_types.cpython-312.pyc | Bin 0 -> 367 bytes .../pip/_vendor/tomli/_parser.py | 691 ++ .../site-packages/pip/_vendor/tomli/_re.py | 107 + .../site-packages/pip/_vendor/tomli/_types.py | 10 + .../site-packages/pip/_vendor/tomli/py.typed | 1 + .../pip/_vendor/truststore/__init__.py | 13 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 619 bytes .../__pycache__/_api.cpython-312.pyc | Bin 0 -> 15798 bytes .../__pycache__/_macos.cpython-312.pyc | Bin 0 -> 16663 bytes .../__pycache__/_openssl.cpython-312.pyc | Bin 0 -> 2216 bytes .../_ssl_constants.cpython-312.pyc | Bin 0 -> 1100 bytes .../__pycache__/_windows.cpython-312.pyc | Bin 0 -> 15507 bytes .../pip/_vendor/truststore/_api.py | 302 + .../pip/_vendor/truststore/_macos.py | 501 + .../pip/_vendor/truststore/_openssl.py | 66 + .../pip/_vendor/truststore/_ssl_constants.py | 31 + .../pip/_vendor/truststore/_windows.py | 554 ++ .../pip/_vendor/truststore/py.typed | 0 .../pip/_vendor/typing_extensions.py | 3072 ++++++ .../pip/_vendor/urllib3/__init__.py | 102 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 3406 bytes .../__pycache__/_collections.cpython-312.pyc | Bin 0 -> 15932 bytes .../__pycache__/_version.cpython-312.pyc | Bin 0 -> 219 bytes .../__pycache__/connection.cpython-312.pyc | Bin 0 -> 20408 bytes .../connectionpool.cpython-312.pyc | Bin 0 -> 36280 bytes .../__pycache__/exceptions.cpython-312.pyc | Bin 0 -> 13494 bytes .../__pycache__/fields.cpython-312.pyc | Bin 0 -> 10410 bytes .../__pycache__/filepost.cpython-312.pyc | Bin 0 -> 4019 bytes .../__pycache__/poolmanager.cpython-312.pyc | Bin 0 -> 20303 bytes .../__pycache__/request.cpython-312.pyc | Bin 0 -> 7295 bytes .../__pycache__/response.cpython-312.pyc | Bin 0 -> 33969 bytes .../pip/_vendor/urllib3/_collections.py | 337 + .../pip/_vendor/urllib3/_version.py | 2 + .../pip/_vendor/urllib3/connection.py | 572 ++ .../pip/_vendor/urllib3/connectionpool.py | 1132 +++ .../pip/_vendor/urllib3/contrib/__init__.py | 0 .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 199 bytes .../_appengine_environ.cpython-312.pyc | Bin 0 -> 1849 bytes .../__pycache__/appengine.cpython-312.pyc | Bin 0 -> 11565 bytes .../__pycache__/ntlmpool.cpython-312.pyc | Bin 0 -> 5720 bytes .../__pycache__/pyopenssl.cpython-312.pyc | Bin 0 -> 24451 bytes .../securetransport.cpython-312.pyc | Bin 0 -> 35557 bytes .../contrib/__pycache__/socks.cpython-312.pyc | Bin 0 -> 7512 bytes .../urllib3/contrib/_appengine_environ.py | 36 + .../contrib/_securetransport/__init__.py | 0 .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 216 bytes .../__pycache__/bindings.cpython-312.pyc | Bin 0 -> 17428 bytes .../__pycache__/low_level.cpython-312.pyc | Bin 0 -> 14802 bytes .../contrib/_securetransport/bindings.py | 519 + .../contrib/_securetransport/low_level.py | 397 + .../pip/_vendor/urllib3/contrib/appengine.py | 314 + .../pip/_vendor/urllib3/contrib/ntlmpool.py | 130 + .../pip/_vendor/urllib3/contrib/pyopenssl.py | 518 + .../urllib3/contrib/securetransport.py | 921 ++ .../pip/_vendor/urllib3/contrib/socks.py | 216 + .../pip/_vendor/urllib3/exceptions.py | 323 + .../pip/_vendor/urllib3/fields.py | 274 + .../pip/_vendor/urllib3/filepost.py | 98 + .../pip/_vendor/urllib3/packages/__init__.py | 0 .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 200 bytes .../packages/__pycache__/six.cpython-312.pyc | Bin 0 -> 41320 bytes .../urllib3/packages/backports/__init__.py | 0 .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 210 bytes .../__pycache__/makefile.cpython-312.pyc | Bin 0 -> 1826 bytes .../weakref_finalize.cpython-312.pyc | Bin 0 -> 7332 bytes .../urllib3/packages/backports/makefile.py | 51 + .../packages/backports/weakref_finalize.py | 155 + .../pip/_vendor/urllib3/packages/six.py | 1076 +++ .../pip/_vendor/urllib3/poolmanager.py | 537 + .../pip/_vendor/urllib3/request.py | 191 + .../pip/_vendor/urllib3/response.py | 879 ++ .../pip/_vendor/urllib3/util/__init__.py | 49 + .../util/__pycache__/__init__.cpython-312.pyc | Bin 0 -> 1147 bytes .../__pycache__/connection.cpython-312.pyc | Bin 0 -> 4757 bytes .../util/__pycache__/proxy.cpython-312.pyc | Bin 0 -> 1553 bytes .../util/__pycache__/queue.cpython-312.pyc | Bin 0 -> 1353 bytes .../util/__pycache__/request.cpython-312.pyc | Bin 0 -> 4184 bytes .../util/__pycache__/response.cpython-312.pyc | Bin 0 -> 2990 bytes .../util/__pycache__/retry.cpython-312.pyc | Bin 0 -> 21698 bytes .../util/__pycache__/ssl_.cpython-312.pyc | Bin 0 -> 15104 bytes .../ssl_match_hostname.cpython-312.pyc | Bin 0 -> 5072 bytes .../__pycache__/ssltransport.cpython-312.pyc | Bin 0 -> 10773 bytes .../util/__pycache__/timeout.cpython-312.pyc | Bin 0 -> 11140 bytes .../util/__pycache__/url.cpython-312.pyc | Bin 0 -> 15796 bytes .../util/__pycache__/wait.cpython-312.pyc | Bin 0 -> 4404 bytes .../pip/_vendor/urllib3/util/connection.py | 149 + .../pip/_vendor/urllib3/util/proxy.py | 57 + .../pip/_vendor/urllib3/util/queue.py | 22 + .../pip/_vendor/urllib3/util/request.py | 137 + .../pip/_vendor/urllib3/util/response.py | 107 + .../pip/_vendor/urllib3/util/retry.py | 620 ++ .../pip/_vendor/urllib3/util/ssl_.py | 495 + .../urllib3/util/ssl_match_hostname.py | 159 + .../pip/_vendor/urllib3/util/ssltransport.py | 221 + .../pip/_vendor/urllib3/util/timeout.py | 271 + .../pip/_vendor/urllib3/util/url.py | 435 + .../pip/_vendor/urllib3/util/wait.py | 152 + venv/Lib/site-packages/pip/_vendor/vendor.txt | 24 + .../pip/_vendor/webencodings/__init__.py | 342 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 12002 bytes .../__pycache__/labels.cpython-312.pyc | Bin 0 -> 7133 bytes .../__pycache__/mklabels.cpython-312.pyc | Bin 0 -> 2700 bytes .../__pycache__/tests.cpython-312.pyc | Bin 0 -> 9252 bytes .../x_user_defined.cpython-312.pyc | Bin 0 -> 3296 bytes .../pip/_vendor/webencodings/labels.py | 231 + .../pip/_vendor/webencodings/mklabels.py | 59 + .../pip/_vendor/webencodings/tests.py | 153 + .../_vendor/webencodings/x_user_defined.py | 325 + venv/Lib/site-packages/pip/py.typed | 4 + .../pluggy-1.6.0.dist-info/INSTALLER | 1 + .../pluggy-1.6.0.dist-info/METADATA | 152 + .../pluggy-1.6.0.dist-info/RECORD | 23 + .../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 + venv/Lib/site-packages/pluggy/__init__.py | 30 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 854 bytes .../__pycache__/_callers.cpython-312.pyc | Bin 0 -> 7026 bytes .../pluggy/__pycache__/_hooks.cpython-312.pyc | Bin 0 -> 27433 bytes .../__pycache__/_manager.cpython-312.pyc | Bin 0 -> 25627 bytes .../__pycache__/_result.cpython-312.pyc | Bin 0 -> 4141 bytes .../__pycache__/_tracing.cpython-312.pyc | Bin 0 -> 4018 bytes .../__pycache__/_version.cpython-312.pyc | Bin 0 -> 639 bytes .../__pycache__/_warnings.cpython-312.pyc | Bin 0 -> 1292 bytes venv/Lib/site-packages/pluggy/_callers.py | 169 + venv/Lib/site-packages/pluggy/_hooks.py | 714 ++ venv/Lib/site-packages/pluggy/_manager.py | 523 + venv/Lib/site-packages/pluggy/_result.py | 107 + venv/Lib/site-packages/pluggy/_tracing.py | 72 + venv/Lib/site-packages/pluggy/_version.py | 21 + venv/Lib/site-packages/pluggy/_warnings.py | 27 + venv/Lib/site-packages/pluggy/py.typed | 0 venv/Lib/site-packages/py.py | 15 + .../pygments-2.19.2.dist-info/INSTALLER | 1 + .../pygments-2.19.2.dist-info/METADATA | 58 + .../pygments-2.19.2.dist-info/RECORD | 684 ++ .../pygments-2.19.2.dist-info/WHEEL | 4 + .../entry_points.txt | 2 + .../licenses/AUTHORS | 291 + .../licenses/LICENSE | 25 + venv/Lib/site-packages/pygments/__init__.py | 82 + venv/Lib/site-packages/pygments/__main__.py | 17 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 3453 bytes .../__pycache__/__main__.cpython-312.pyc | Bin 0 -> 772 bytes .../__pycache__/cmdline.cpython-312.pyc | Bin 0 -> 26537 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 -> 4685 bytes .../__pycache__/lexer.cpython-312.pyc | Bin 0 -> 38749 bytes .../__pycache__/modeline.cpython-312.pyc | Bin 0 -> 1558 bytes .../__pycache__/plugin.cpython-312.pyc | Bin 0 -> 2617 bytes .../__pycache__/regexopt.cpython-312.pyc | Bin 0 -> 4070 bytes .../__pycache__/scanner.cpython-312.pyc | Bin 0 -> 4745 bytes .../__pycache__/sphinxext.cpython-312.pyc | Bin 0 -> 12100 bytes .../__pycache__/style.cpython-312.pyc | Bin 0 -> 6694 bytes .../__pycache__/token.cpython-312.pyc | Bin 0 -> 8183 bytes .../__pycache__/unistring.cpython-312.pyc | Bin 0 -> 33000 bytes .../pygments/__pycache__/util.cpython-312.pyc | Bin 0 -> 14072 bytes venv/Lib/site-packages/pygments/cmdline.py | 668 ++ venv/Lib/site-packages/pygments/console.py | 70 + venv/Lib/site-packages/pygments/filter.py | 70 + .../pygments/filters/__init__.py | 940 ++ .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 37920 bytes venv/Lib/site-packages/pygments/formatter.py | 129 + .../pygments/formatters/__init__.py | 157 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 6906 bytes .../__pycache__/_mapping.cpython-312.pyc | Bin 0 -> 4204 bytes .../__pycache__/bbcode.cpython-312.pyc | Bin 0 -> 4197 bytes .../__pycache__/groff.cpython-312.pyc | Bin 0 -> 7297 bytes .../__pycache__/html.cpython-312.pyc | Bin 0 -> 41366 bytes .../__pycache__/img.cpython-312.pyc | Bin 0 -> 28678 bytes .../__pycache__/irc.cpython-312.pyc | Bin 0 -> 6018 bytes .../__pycache__/latex.cpython-312.pyc | Bin 0 -> 20112 bytes .../__pycache__/other.cpython-312.pyc | Bin 0 -> 6840 bytes .../__pycache__/pangomarkup.cpython-312.pyc | Bin 0 -> 2945 bytes .../__pycache__/rtf.cpython-312.pyc | Bin 0 -> 13777 bytes .../__pycache__/svg.cpython-312.pyc | Bin 0 -> 9104 bytes .../__pycache__/terminal.cpython-312.pyc | Bin 0 -> 5770 bytes .../__pycache__/terminal256.cpython-312.pyc | Bin 0 -> 15110 bytes .../pygments/formatters/_mapping.py | 23 + .../pygments/formatters/bbcode.py | 108 + .../pygments/formatters/groff.py | 170 + .../site-packages/pygments/formatters/html.py | 995 ++ .../site-packages/pygments/formatters/img.py | 686 ++ .../site-packages/pygments/formatters/irc.py | 154 + .../pygments/formatters/latex.py | 518 + .../pygments/formatters/other.py | 160 + .../pygments/formatters/pangomarkup.py | 83 + .../site-packages/pygments/formatters/rtf.py | 349 + .../site-packages/pygments/formatters/svg.py | 185 + .../pygments/formatters/terminal.py | 127 + .../pygments/formatters/terminal256.py | 338 + venv/Lib/site-packages/pygments/lexer.py | 961 ++ .../site-packages/pygments/lexers/__init__.py | 362 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 14676 bytes .../__pycache__/_ada_builtins.cpython-312.pyc | Bin 0 -> 1234 bytes .../__pycache__/_asy_builtins.cpython-312.pyc | Bin 0 -> 17627 bytes .../__pycache__/_cl_builtins.cpython-312.pyc | Bin 0 -> 11663 bytes .../_cocoa_builtins.cpython-312.pyc | Bin 0 -> 97567 bytes .../_csound_builtins.cpython-312.pyc | Bin 0 -> 16375 bytes .../__pycache__/_css_builtins.cpython-312.pyc | Bin 0 -> 9384 bytes .../_googlesql_builtins.cpython-312.pyc | Bin 0 -> 10822 bytes .../_julia_builtins.cpython-312.pyc | Bin 0 -> 8249 bytes .../_lasso_builtins.cpython-312.pyc | Bin 0 -> 76727 bytes .../_lilypond_builtins.cpython-312.pyc | Bin 0 -> 88405 bytes .../__pycache__/_lua_builtins.cpython-312.pyc | Bin 0 -> 8386 bytes .../_luau_builtins.cpython-312.pyc | Bin 0 -> 1048 bytes .../__pycache__/_mapping.cpython-312.pyc | Bin 0 -> 66950 bytes .../__pycache__/_mql_builtins.cpython-312.pyc | Bin 0 -> 18008 bytes .../_mysql_builtins.cpython-312.pyc | Bin 0 -> 19554 bytes .../_openedge_builtins.cpython-312.pyc | Bin 0 -> 34090 bytes .../__pycache__/_php_builtins.cpython-312.pyc | Bin 0 -> 65523 bytes .../_postgres_builtins.cpython-312.pyc | Bin 0 -> 11295 bytes .../_qlik_builtins.cpython-312.pyc | Bin 0 -> 6375 bytes .../_scheme_builtins.cpython-312.pyc | Bin 0 -> 23172 bytes .../_scilab_builtins.cpython-312.pyc | Bin 0 -> 35219 bytes .../_sourcemod_builtins.cpython-312.pyc | Bin 0 -> 21837 bytes .../__pycache__/_sql_builtins.cpython-312.pyc | Bin 0 -> 5566 bytes .../_stan_builtins.cpython-312.pyc | Bin 0 -> 9951 bytes .../_stata_builtins.cpython-312.pyc | Bin 0 -> 21235 bytes .../_tsql_builtins.cpython-312.pyc | Bin 0 -> 8867 bytes .../__pycache__/_usd_builtins.cpython-312.pyc | Bin 0 -> 1397 bytes .../_vbscript_builtins.cpython-312.pyc | Bin 0 -> 2926 bytes .../__pycache__/_vim_builtins.cpython-312.pyc | Bin 0 -> 30729 bytes .../__pycache__/actionscript.cpython-312.pyc | Bin 0 -> 11114 bytes .../lexers/__pycache__/ada.cpython-312.pyc | Bin 0 -> 5510 bytes .../lexers/__pycache__/agile.cpython-312.pyc | Bin 0 -> 1305 bytes .../__pycache__/algebra.cpython-312.pyc | Bin 0 -> 10994 bytes .../__pycache__/ambient.cpython-312.pyc | Bin 0 -> 3122 bytes .../lexers/__pycache__/amdgpu.cpython-312.pyc | Bin 0 -> 2245 bytes .../lexers/__pycache__/ampl.cpython-312.pyc | Bin 0 -> 4093 bytes .../__pycache__/apdlexer.cpython-312.pyc | Bin 0 -> 19034 bytes .../lexers/__pycache__/apl.cpython-312.pyc | Bin 0 -> 2514 bytes .../__pycache__/archetype.cpython-312.pyc | Bin 0 -> 9186 bytes .../lexers/__pycache__/arrow.cpython-312.pyc | Bin 0 -> 3564 bytes .../lexers/__pycache__/arturo.cpython-312.pyc | Bin 0 -> 9741 bytes .../lexers/__pycache__/asc.cpython-312.pyc | Bin 0 -> 2044 bytes .../lexers/__pycache__/asm.cpython-312.pyc | Bin 0 -> 36008 bytes .../lexers/__pycache__/asn1.cpython-312.pyc | Bin 0 -> 4485 bytes .../__pycache__/automation.cpython-312.pyc | Bin 0 -> 18412 bytes .../lexers/__pycache__/bare.cpython-312.pyc | Bin 0 -> 2871 bytes .../lexers/__pycache__/basic.cpython-312.pyc | Bin 0 -> 26968 bytes .../lexers/__pycache__/bdd.cpython-312.pyc | Bin 0 -> 2080 bytes .../lexers/__pycache__/berry.cpython-312.pyc | Bin 0 -> 3554 bytes .../lexers/__pycache__/bibtex.cpython-312.pyc | Bin 0 -> 5220 bytes .../__pycache__/blueprint.cpython-312.pyc | Bin 0 -> 5308 bytes .../lexers/__pycache__/boa.cpython-312.pyc | Bin 0 -> 3530 bytes .../lexers/__pycache__/bqn.cpython-312.pyc | Bin 0 -> 2544 bytes .../__pycache__/business.cpython-312.pyc | Bin 0 -> 22061 bytes .../lexers/__pycache__/c_cpp.cpython-312.pyc | Bin 0 -> 16092 bytes .../lexers/__pycache__/c_like.cpython-312.pyc | Bin 0 -> 27564 bytes .../__pycache__/capnproto.cpython-312.pyc | Bin 0 -> 2403 bytes .../lexers/__pycache__/carbon.cpython-312.pyc | Bin 0 -> 3541 bytes .../lexers/__pycache__/cddl.cpython-312.pyc | Bin 0 -> 4233 bytes .../lexers/__pycache__/chapel.cpython-312.pyc | Bin 0 -> 4267 bytes .../lexers/__pycache__/clean.cpython-312.pyc | Bin 0 -> 6076 bytes .../lexers/__pycache__/codeql.cpython-312.pyc | Bin 0 -> 2749 bytes .../lexers/__pycache__/comal.cpython-312.pyc | Bin 0 -> 3223 bytes .../__pycache__/compiled.cpython-312.pyc | Bin 0 -> 1996 bytes .../__pycache__/configs.cpython-312.pyc | Bin 0 -> 44514 bytes .../__pycache__/console.cpython-312.pyc | Bin 0 -> 4246 bytes .../lexers/__pycache__/cplint.cpython-312.pyc | Bin 0 -> 1758 bytes .../__pycache__/crystal.cpython-312.pyc | Bin 0 -> 15180 bytes .../lexers/__pycache__/csound.cpython-312.pyc | Bin 0 -> 14117 bytes .../lexers/__pycache__/css.cpython-312.pyc | Bin 0 -> 22103 bytes .../lexers/__pycache__/d.cpython-312.pyc | Bin 0 -> 8332 bytes .../lexers/__pycache__/dalvik.cpython-312.pyc | Bin 0 -> 4542 bytes .../lexers/__pycache__/data.cpython-312.pyc | Bin 0 -> 21166 bytes .../lexers/__pycache__/dax.cpython-312.pyc | Bin 0 -> 6243 bytes .../__pycache__/devicetree.cpython-312.pyc | Bin 0 -> 4043 bytes .../lexers/__pycache__/diff.cpython-312.pyc | Bin 0 -> 5678 bytes .../lexers/__pycache__/dns.cpython-312.pyc | Bin 0 -> 3777 bytes .../lexers/__pycache__/dotnet.cpython-312.pyc | Bin 0 -> 35225 bytes .../lexers/__pycache__/dsls.cpython-312.pyc | Bin 0 -> 33788 bytes .../lexers/__pycache__/dylan.cpython-312.pyc | Bin 0 -> 9732 bytes .../lexers/__pycache__/ecl.cpython-312.pyc | Bin 0 -> 5586 bytes .../lexers/__pycache__/eiffel.cpython-312.pyc | Bin 0 -> 2993 bytes .../lexers/__pycache__/elm.cpython-312.pyc | Bin 0 -> 3237 bytes .../lexers/__pycache__/elpi.cpython-312.pyc | Bin 0 -> 7235 bytes .../lexers/__pycache__/email.cpython-312.pyc | Bin 0 -> 5983 bytes .../lexers/__pycache__/erlang.cpython-312.pyc | Bin 0 -> 20548 bytes .../__pycache__/esoteric.cpython-312.pyc | Bin 0 -> 9765 bytes .../lexers/__pycache__/ezhil.cpython-312.pyc | Bin 0 -> 3859 bytes .../lexers/__pycache__/factor.cpython-312.pyc | Bin 0 -> 16926 bytes .../lexers/__pycache__/fantom.cpython-312.pyc | Bin 0 -> 7995 bytes .../lexers/__pycache__/felix.cpython-312.pyc | Bin 0 -> 8250 bytes .../lexers/__pycache__/fift.cpython-312.pyc | Bin 0 -> 1962 bytes .../__pycache__/floscript.cpython-312.pyc | Bin 0 -> 2980 bytes .../lexers/__pycache__/forth.cpython-312.pyc | Bin 0 -> 5356 bytes .../__pycache__/fortran.cpython-312.pyc | Bin 0 -> 8717 bytes .../lexers/__pycache__/foxpro.cpython-312.pyc | Bin 0 -> 20810 bytes .../__pycache__/freefem.cpython-312.pyc | Bin 0 -> 12785 bytes .../lexers/__pycache__/func.cpython-312.pyc | Bin 0 -> 3336 bytes .../__pycache__/functional.cpython-312.pyc | Bin 0 -> 1046 bytes .../__pycache__/futhark.cpython-312.pyc | Bin 0 -> 4078 bytes .../__pycache__/gcodelexer.cpython-312.pyc | Bin 0 -> 1318 bytes .../__pycache__/gdscript.cpython-312.pyc | Bin 0 -> 7212 bytes .../lexers/__pycache__/gleam.cpython-312.pyc | Bin 0 -> 2710 bytes .../lexers/__pycache__/go.cpython-312.pyc | Bin 0 -> 3385 bytes .../grammar_notation.cpython-312.pyc | Bin 0 -> 7715 bytes .../lexers/__pycache__/graph.cpython-312.pyc | Bin 0 -> 3812 bytes .../__pycache__/graphics.cpython-312.pyc | Bin 0 -> 29701 bytes .../__pycache__/graphql.cpython-312.pyc | Bin 0 -> 4445 bytes .../__pycache__/graphviz.cpython-312.pyc | Bin 0 -> 2219 bytes .../lexers/__pycache__/gsql.cpython-312.pyc | Bin 0 -> 3779 bytes .../lexers/__pycache__/hare.cpython-312.pyc | Bin 0 -> 2952 bytes .../__pycache__/haskell.cpython-312.pyc | Bin 0 -> 30498 bytes .../lexers/__pycache__/haxe.cpython-312.pyc | Bin 0 -> 22282 bytes .../lexers/__pycache__/hdl.cpython-312.pyc | Bin 0 -> 17477 bytes .../__pycache__/hexdump.cpython-312.pyc | Bin 0 -> 3653 bytes .../lexers/__pycache__/html.cpython-312.pyc | Bin 0 -> 20731 bytes .../lexers/__pycache__/idl.cpython-312.pyc | Bin 0 -> 12471 bytes .../lexers/__pycache__/igor.cpython-312.pyc | Bin 0 -> 25696 bytes .../__pycache__/inferno.cpython-312.pyc | Bin 0 -> 3249 bytes .../__pycache__/installers.cpython-312.pyc | Bin 0 -> 13778 bytes .../__pycache__/int_fiction.cpython-312.pyc | Bin 0 -> 48101 bytes .../lexers/__pycache__/iolang.cpython-312.pyc | Bin 0 -> 2213 bytes .../lexers/__pycache__/j.cpython-312.pyc | Bin 0 -> 4305 bytes .../__pycache__/javascript.cpython-312.pyc | Bin 0 -> 56997 bytes .../__pycache__/jmespath.cpython-312.pyc | Bin 0 -> 2403 bytes .../lexers/__pycache__/jslt.cpython-312.pyc | Bin 0 -> 3761 bytes .../lexers/__pycache__/json5.cpython-312.pyc | Bin 0 -> 2883 bytes .../__pycache__/jsonnet.cpython-312.pyc | Bin 0 -> 4859 bytes .../lexers/__pycache__/jsx.cpython-312.pyc | Bin 0 -> 2915 bytes .../lexers/__pycache__/julia.cpython-312.pyc | Bin 0 -> 10937 bytes .../lexers/__pycache__/jvm.cpython-312.pyc | Bin 0 -> 63874 bytes .../lexers/__pycache__/kuin.cpython-312.pyc | Bin 0 -> 9883 bytes .../lexers/__pycache__/kusto.cpython-312.pyc | Bin 0 -> 2845 bytes .../lexers/__pycache__/ldap.cpython-312.pyc | Bin 0 -> 6429 bytes .../lexers/__pycache__/lean.cpython-312.pyc | Bin 0 -> 7994 bytes .../__pycache__/lilypond.cpython-312.pyc | Bin 0 -> 8378 bytes .../lexers/__pycache__/lisp.cpython-312.pyc | Bin 0 -> 121643 bytes .../__pycache__/macaulay2.cpython-312.pyc | Bin 0 -> 23158 bytes .../lexers/__pycache__/make.cpython-312.pyc | Bin 0 -> 6672 bytes .../lexers/__pycache__/maple.cpython-312.pyc | Bin 0 -> 4981 bytes .../lexers/__pycache__/markup.cpython-312.pyc | Bin 0 -> 60419 bytes .../lexers/__pycache__/math.cpython-312.pyc | Bin 0 -> 1042 bytes .../lexers/__pycache__/matlab.cpython-312.pyc | Bin 0 -> 55741 bytes .../lexers/__pycache__/maxima.cpython-312.pyc | Bin 0 -> 3190 bytes .../lexers/__pycache__/meson.cpython-312.pyc | Bin 0 -> 3523 bytes .../lexers/__pycache__/mime.cpython-312.pyc | Bin 0 -> 10136 bytes .../__pycache__/minecraft.cpython-312.pyc | Bin 0 -> 10698 bytes .../lexers/__pycache__/mips.cpython-312.pyc | Bin 0 -> 3437 bytes .../lexers/__pycache__/ml.cpython-312.pyc | Bin 0 -> 26168 bytes .../__pycache__/modeling.cpython-312.pyc | Bin 0 -> 12072 bytes .../__pycache__/modula2.cpython-312.pyc | Bin 0 -> 26502 bytes .../lexers/__pycache__/mojo.cpython-312.pyc | Bin 0 -> 14363 bytes .../lexers/__pycache__/monte.cpython-312.pyc | Bin 0 -> 5115 bytes .../lexers/__pycache__/mosel.cpython-312.pyc | Bin 0 -> 6964 bytes .../lexers/__pycache__/ncl.cpython-312.pyc | Bin 0 -> 45916 bytes .../lexers/__pycache__/nimrod.cpython-312.pyc | Bin 0 -> 6462 bytes .../lexers/__pycache__/nit.cpython-312.pyc | Bin 0 -> 2758 bytes .../lexers/__pycache__/nix.cpython-312.pyc | Bin 0 -> 5467 bytes .../__pycache__/numbair.cpython-312.pyc | Bin 0 -> 2133 bytes .../lexers/__pycache__/oberon.cpython-312.pyc | Bin 0 -> 3747 bytes .../__pycache__/objective.cpython-312.pyc | Bin 0 -> 19471 bytes .../lexers/__pycache__/ooc.cpython-312.pyc | Bin 0 -> 3118 bytes .../__pycache__/openscad.cpython-312.pyc | Bin 0 -> 3730 bytes .../lexers/__pycache__/other.cpython-312.pyc | Bin 0 -> 2452 bytes .../__pycache__/parasail.cpython-312.pyc | Bin 0 -> 2887 bytes .../__pycache__/parsers.cpython-312.pyc | Bin 0 -> 24402 bytes .../lexers/__pycache__/pascal.cpython-312.pyc | Bin 0 -> 23942 bytes .../lexers/__pycache__/pawn.cpython-312.pyc | Bin 0 -> 7865 bytes .../lexers/__pycache__/pddl.cpython-312.pyc | Bin 0 -> 2813 bytes .../lexers/__pycache__/perl.cpython-312.pyc | Bin 0 -> 39018 bytes .../lexers/__pycache__/phix.cpython-312.pyc | Bin 0 -> 18424 bytes .../lexers/__pycache__/php.cpython-312.pyc | Bin 0 -> 14323 bytes .../__pycache__/pointless.cpython-312.pyc | Bin 0 -> 2297 bytes .../lexers/__pycache__/pony.cpython-312.pyc | Bin 0 -> 3426 bytes .../lexers/__pycache__/praat.cpython-312.pyc | Bin 0 -> 10288 bytes .../__pycache__/procfile.cpython-312.pyc | Bin 0 -> 1631 bytes .../lexers/__pycache__/prolog.cpython-312.pyc | Bin 0 -> 10533 bytes .../lexers/__pycache__/promql.cpython-312.pyc | Bin 0 -> 3336 bytes .../lexers/__pycache__/prql.cpython-312.pyc | Bin 0 -> 8384 bytes .../lexers/__pycache__/ptx.cpython-312.pyc | Bin 0 -> 3772 bytes .../lexers/__pycache__/python.cpython-312.pyc | Bin 0 -> 42915 bytes .../lexers/__pycache__/q.cpython-312.pyc | Bin 0 -> 5846 bytes .../lexers/__pycache__/qlik.cpython-312.pyc | Bin 0 -> 3512 bytes .../lexers/__pycache__/qvt.cpython-312.pyc | Bin 0 -> 5380 bytes .../lexers/__pycache__/r.cpython-312.pyc | Bin 0 -> 6090 bytes .../lexers/__pycache__/rdf.cpython-312.pyc | Bin 0 -> 12260 bytes .../lexers/__pycache__/rebol.cpython-312.pyc | Bin 0 -> 19251 bytes .../lexers/__pycache__/rego.cpython-312.pyc | Bin 0 -> 1876 bytes .../__pycache__/resource.cpython-312.pyc | Bin 0 -> 3588 bytes .../lexers/__pycache__/ride.cpython-312.pyc | Bin 0 -> 4490 bytes .../lexers/__pycache__/rita.cpython-312.pyc | Bin 0 -> 1471 bytes .../lexers/__pycache__/rnc.cpython-312.pyc | Bin 0 -> 2013 bytes .../__pycache__/roboconf.cpython-312.pyc | Bin 0 -> 2364 bytes .../robotframework.cpython-312.pyc | Bin 0 -> 29607 bytes .../lexers/__pycache__/ruby.cpython-312.pyc | Bin 0 -> 22591 bytes .../lexers/__pycache__/rust.cpython-312.pyc | Bin 0 -> 7306 bytes .../lexers/__pycache__/sas.cpython-312.pyc | Bin 0 -> 7041 bytes .../lexers/__pycache__/savi.cpython-312.pyc | Bin 0 -> 3982 bytes .../lexers/__pycache__/scdoc.cpython-312.pyc | Bin 0 -> 2820 bytes .../__pycache__/scripting.cpython-312.pyc | Bin 0 -> 71841 bytes .../lexers/__pycache__/sgf.cpython-312.pyc | Bin 0 -> 2081 bytes .../lexers/__pycache__/shell.cpython-312.pyc | Bin 0 -> 37102 bytes .../lexers/__pycache__/sieve.cpython-312.pyc | Bin 0 -> 2751 bytes .../lexers/__pycache__/slash.cpython-312.pyc | Bin 0 -> 8394 bytes .../__pycache__/smalltalk.cpython-312.pyc | Bin 0 -> 6703 bytes .../lexers/__pycache__/smithy.cpython-312.pyc | Bin 0 -> 3128 bytes .../lexers/__pycache__/smv.cpython-312.pyc | Bin 0 -> 2810 bytes .../lexers/__pycache__/snobol.cpython-312.pyc | Bin 0 -> 2506 bytes .../__pycache__/solidity.cpython-312.pyc | Bin 0 -> 3409 bytes .../lexers/__pycache__/soong.cpython-312.pyc | Bin 0 -> 2274 bytes .../lexers/__pycache__/sophia.cpython-312.pyc | Bin 0 -> 3854 bytes .../__pycache__/special.cpython-312.pyc | Bin 0 -> 5431 bytes .../lexers/__pycache__/spice.cpython-312.pyc | Bin 0 -> 3179 bytes .../lexers/__pycache__/sql.cpython-312.pyc | Bin 0 -> 40700 bytes .../__pycache__/srcinfo.cpython-312.pyc | Bin 0 -> 2009 bytes .../lexers/__pycache__/stata.cpython-312.pyc | Bin 0 -> 5161 bytes .../__pycache__/supercollider.cpython-312.pyc | Bin 0 -> 3911 bytes .../__pycache__/tablegen.cpython-312.pyc | Bin 0 -> 3353 bytes .../lexers/__pycache__/tact.cpython-312.pyc | Bin 0 -> 9038 bytes .../lexers/__pycache__/tal.cpython-312.pyc | Bin 0 -> 2975 bytes .../lexers/__pycache__/tcl.cpython-312.pyc | Bin 0 -> 5149 bytes .../lexers/__pycache__/teal.cpython-312.pyc | Bin 0 -> 3558 bytes .../__pycache__/templates.cpython-312.pyc | Bin 0 -> 83655 bytes .../__pycache__/teraterm.cpython-312.pyc | Bin 0 -> 5566 bytes .../__pycache__/testing.cpython-312.pyc | Bin 0 -> 10074 bytes .../lexers/__pycache__/text.cpython-312.pyc | Bin 0 -> 1556 bytes .../__pycache__/textedit.cpython-312.pyc | Bin 0 -> 8518 bytes .../__pycache__/textfmts.cpython-312.pyc | Bin 0 -> 15601 bytes .../__pycache__/theorem.cpython-312.pyc | Bin 0 -> 14909 bytes .../__pycache__/thingsdb.cpython-312.pyc | Bin 0 -> 5620 bytes .../lexers/__pycache__/tlb.cpython-312.pyc | Bin 0 -> 1873 bytes .../lexers/__pycache__/tls.cpython-312.pyc | Bin 0 -> 1931 bytes .../lexers/__pycache__/tnt.cpython-312.pyc | Bin 0 -> 13594 bytes .../__pycache__/trafficscript.cpython-312.pyc | Bin 0 -> 1850 bytes .../__pycache__/typoscript.cpython-312.pyc | Bin 0 -> 7366 bytes .../lexers/__pycache__/typst.cpython-312.pyc | Bin 0 -> 6911 bytes .../lexers/__pycache__/ul4.cpython-312.pyc | Bin 0 -> 8147 bytes .../lexers/__pycache__/unicon.cpython-312.pyc | Bin 0 -> 12514 bytes .../lexers/__pycache__/urbi.cpython-312.pyc | Bin 0 -> 5916 bytes .../lexers/__pycache__/usd.cpython-312.pyc | Bin 0 -> 4028 bytes .../__pycache__/varnish.cpython-312.pyc | Bin 0 -> 6951 bytes .../__pycache__/verification.cpython-312.pyc | Bin 0 -> 4035 bytes .../__pycache__/verifpal.cpython-312.pyc | Bin 0 -> 2977 bytes .../lexers/__pycache__/vip.cpython-312.pyc | Bin 0 -> 5700 bytes .../lexers/__pycache__/vyper.cpython-312.pyc | Bin 0 -> 4930 bytes .../lexers/__pycache__/web.cpython-312.pyc | Bin 0 -> 1320 bytes .../__pycache__/webassembly.cpython-312.pyc | Bin 0 -> 5826 bytes .../lexers/__pycache__/webidl.cpython-312.pyc | Bin 0 -> 8110 bytes .../__pycache__/webmisc.cpython-312.pyc | Bin 0 -> 43547 bytes .../lexers/__pycache__/wgsl.cpython-312.pyc | Bin 0 -> 10856 bytes .../lexers/__pycache__/whiley.cpython-312.pyc | Bin 0 -> 3641 bytes .../lexers/__pycache__/wowtoc.cpython-312.pyc | Bin 0 -> 3246 bytes .../lexers/__pycache__/wren.cpython-312.pyc | Bin 0 -> 3111 bytes .../lexers/__pycache__/x10.cpython-312.pyc | Bin 0 -> 2402 bytes .../lexers/__pycache__/xorg.cpython-312.pyc | Bin 0 -> 1391 bytes .../lexers/__pycache__/yang.cpython-312.pyc | Bin 0 -> 4155 bytes .../lexers/__pycache__/yara.cpython-312.pyc | Bin 0 -> 2737 bytes .../lexers/__pycache__/zig.cpython-312.pyc | Bin 0 -> 3900 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 | 4932 ++++++++++ .../pygments/lexers/_lua_builtins.py | 285 + .../pygments/lexers/_luau_builtins.py | 62 + .../site-packages/pygments/lexers/_mapping.py | 602 ++ .../pygments/lexers/_mql_builtins.py | 1171 +++ .../pygments/lexers/_mysql_builtins.py | 1335 +++ .../pygments/lexers/_openedge_builtins.py | 2600 +++++ .../pygments/lexers/_php_builtins.py | 3325 +++++++ .../pygments/lexers/_postgres_builtins.py | 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 | 648 ++ .../pygments/lexers/_stata_builtins.py | 457 + .../pygments/lexers/_tsql_builtins.py | 1003 ++ .../pygments/lexers/_usd_builtins.py | 112 + .../pygments/lexers/_vbscript_builtins.py | 279 + .../pygments/lexers/_vim_builtins.py | 1938 ++++ .../pygments/lexers/actionscript.py | 243 + venv/Lib/site-packages/pygments/lexers/ada.py | 144 + .../site-packages/pygments/lexers/agile.py | 25 + .../site-packages/pygments/lexers/algebra.py | 299 + .../site-packages/pygments/lexers/ambient.py | 75 + .../site-packages/pygments/lexers/amdgpu.py | 54 + .../Lib/site-packages/pygments/lexers/ampl.py | 87 + .../site-packages/pygments/lexers/apdlexer.py | 593 ++ venv/Lib/site-packages/pygments/lexers/apl.py | 103 + .../pygments/lexers/archetype.py | 315 + .../site-packages/pygments/lexers/arrow.py | 116 + .../site-packages/pygments/lexers/arturo.py | 249 + venv/Lib/site-packages/pygments/lexers/asc.py | 55 + venv/Lib/site-packages/pygments/lexers/asm.py | 1051 ++ .../Lib/site-packages/pygments/lexers/asn1.py | 178 + .../pygments/lexers/automation.py | 379 + .../Lib/site-packages/pygments/lexers/bare.py | 101 + .../site-packages/pygments/lexers/basic.py | 656 ++ venv/Lib/site-packages/pygments/lexers/bdd.py | 57 + .../site-packages/pygments/lexers/berry.py | 99 + .../site-packages/pygments/lexers/bibtex.py | 159 + .../pygments/lexers/blueprint.py | 173 + venv/Lib/site-packages/pygments/lexers/boa.py | 97 + venv/Lib/site-packages/pygments/lexers/bqn.py | 112 + .../site-packages/pygments/lexers/business.py | 625 ++ .../site-packages/pygments/lexers/c_cpp.py | 414 + .../site-packages/pygments/lexers/c_like.py | 738 ++ .../pygments/lexers/capnproto.py | 74 + .../site-packages/pygments/lexers/carbon.py | 95 + .../Lib/site-packages/pygments/lexers/cddl.py | 172 + .../site-packages/pygments/lexers/chapel.py | 139 + .../site-packages/pygments/lexers/clean.py | 180 + .../site-packages/pygments/lexers/codeql.py | 80 + .../site-packages/pygments/lexers/comal.py | 81 + .../site-packages/pygments/lexers/compiled.py | 35 + .../site-packages/pygments/lexers/configs.py | 1433 +++ .../site-packages/pygments/lexers/console.py | 114 + .../site-packages/pygments/lexers/cplint.py | 43 + .../site-packages/pygments/lexers/crystal.py | 364 + .../site-packages/pygments/lexers/csound.py | 466 + venv/Lib/site-packages/pygments/lexers/css.py | 602 ++ venv/Lib/site-packages/pygments/lexers/d.py | 259 + .../site-packages/pygments/lexers/dalvik.py | 126 + .../Lib/site-packages/pygments/lexers/data.py | 763 ++ venv/Lib/site-packages/pygments/lexers/dax.py | 135 + .../pygments/lexers/devicetree.py | 108 + .../Lib/site-packages/pygments/lexers/diff.py | 169 + venv/Lib/site-packages/pygments/lexers/dns.py | 109 + .../site-packages/pygments/lexers/dotnet.py | 873 ++ .../Lib/site-packages/pygments/lexers/dsls.py | 970 ++ .../site-packages/pygments/lexers/dylan.py | 279 + venv/Lib/site-packages/pygments/lexers/ecl.py | 144 + .../site-packages/pygments/lexers/eiffel.py | 68 + venv/Lib/site-packages/pygments/lexers/elm.py | 123 + .../Lib/site-packages/pygments/lexers/elpi.py | 175 + .../site-packages/pygments/lexers/email.py | 132 + .../site-packages/pygments/lexers/erlang.py | 526 + .../site-packages/pygments/lexers/esoteric.py | 300 + .../site-packages/pygments/lexers/ezhil.py | 76 + .../site-packages/pygments/lexers/factor.py | 363 + .../site-packages/pygments/lexers/fantom.py | 251 + .../site-packages/pygments/lexers/felix.py | 275 + .../Lib/site-packages/pygments/lexers/fift.py | 68 + .../pygments/lexers/floscript.py | 81 + .../site-packages/pygments/lexers/forth.py | 178 + .../site-packages/pygments/lexers/fortran.py | 212 + .../site-packages/pygments/lexers/foxpro.py | 427 + .../site-packages/pygments/lexers/freefem.py | 893 ++ .../Lib/site-packages/pygments/lexers/func.py | 110 + .../pygments/lexers/functional.py | 21 + .../site-packages/pygments/lexers/futhark.py | 105 + .../pygments/lexers/gcodelexer.py | 35 + .../site-packages/pygments/lexers/gdscript.py | 189 + .../site-packages/pygments/lexers/gleam.py | 74 + venv/Lib/site-packages/pygments/lexers/go.py | 97 + .../pygments/lexers/grammar_notation.py | 262 + .../site-packages/pygments/lexers/graph.py | 108 + .../site-packages/pygments/lexers/graphics.py | 794 ++ .../site-packages/pygments/lexers/graphql.py | 176 + .../site-packages/pygments/lexers/graphviz.py | 58 + .../Lib/site-packages/pygments/lexers/gsql.py | 103 + .../Lib/site-packages/pygments/lexers/hare.py | 73 + .../site-packages/pygments/lexers/haskell.py | 866 ++ .../Lib/site-packages/pygments/lexers/haxe.py | 935 ++ venv/Lib/site-packages/pygments/lexers/hdl.py | 466 + .../site-packages/pygments/lexers/hexdump.py | 102 + .../Lib/site-packages/pygments/lexers/html.py | 670 ++ venv/Lib/site-packages/pygments/lexers/idl.py | 284 + .../Lib/site-packages/pygments/lexers/igor.py | 435 + .../site-packages/pygments/lexers/inferno.py | 95 + .../pygments/lexers/installers.py | 352 + .../pygments/lexers/int_fiction.py | 1370 +++ .../site-packages/pygments/lexers/iolang.py | 61 + venv/Lib/site-packages/pygments/lexers/j.py | 151 + .../pygments/lexers/javascript.py | 1591 +++ .../site-packages/pygments/lexers/jmespath.py | 69 + .../Lib/site-packages/pygments/lexers/jslt.py | 94 + .../site-packages/pygments/lexers/json5.py | 83 + .../site-packages/pygments/lexers/jsonnet.py | 169 + venv/Lib/site-packages/pygments/lexers/jsx.py | 100 + .../site-packages/pygments/lexers/julia.py | 294 + venv/Lib/site-packages/pygments/lexers/jvm.py | 1802 ++++ .../Lib/site-packages/pygments/lexers/kuin.py | 332 + .../site-packages/pygments/lexers/kusto.py | 93 + .../Lib/site-packages/pygments/lexers/ldap.py | 155 + .../Lib/site-packages/pygments/lexers/lean.py | 241 + .../site-packages/pygments/lexers/lilypond.py | 225 + .../Lib/site-packages/pygments/lexers/lisp.py | 3146 ++++++ .../pygments/lexers/macaulay2.py | 1814 ++++ .../Lib/site-packages/pygments/lexers/make.py | 212 + .../site-packages/pygments/lexers/maple.py | 291 + .../site-packages/pygments/lexers/markup.py | 1654 ++++ .../Lib/site-packages/pygments/lexers/math.py | 21 + .../site-packages/pygments/lexers/matlab.py | 3307 +++++++ .../site-packages/pygments/lexers/maxima.py | 84 + .../site-packages/pygments/lexers/meson.py | 139 + .../Lib/site-packages/pygments/lexers/mime.py | 210 + .../pygments/lexers/minecraft.py | 391 + .../Lib/site-packages/pygments/lexers/mips.py | 130 + venv/Lib/site-packages/pygments/lexers/ml.py | 958 ++ .../site-packages/pygments/lexers/modeling.py | 366 + .../site-packages/pygments/lexers/modula2.py | 1579 +++ .../Lib/site-packages/pygments/lexers/mojo.py | 707 ++ .../site-packages/pygments/lexers/monte.py | 203 + .../site-packages/pygments/lexers/mosel.py | 447 + venv/Lib/site-packages/pygments/lexers/ncl.py | 894 ++ .../site-packages/pygments/lexers/nimrod.py | 199 + venv/Lib/site-packages/pygments/lexers/nit.py | 63 + venv/Lib/site-packages/pygments/lexers/nix.py | 144 + .../site-packages/pygments/lexers/numbair.py | 63 + .../site-packages/pygments/lexers/oberon.py | 120 + .../pygments/lexers/objective.py | 513 + venv/Lib/site-packages/pygments/lexers/ooc.py | 84 + .../site-packages/pygments/lexers/openscad.py | 96 + .../site-packages/pygments/lexers/other.py | 41 + .../site-packages/pygments/lexers/parasail.py | 78 + .../site-packages/pygments/lexers/parsers.py | 798 ++ .../site-packages/pygments/lexers/pascal.py | 644 ++ .../Lib/site-packages/pygments/lexers/pawn.py | 202 + .../Lib/site-packages/pygments/lexers/pddl.py | 82 + .../Lib/site-packages/pygments/lexers/perl.py | 733 ++ .../Lib/site-packages/pygments/lexers/phix.py | 363 + venv/Lib/site-packages/pygments/lexers/php.py | 334 + .../pygments/lexers/pointless.py | 70 + .../Lib/site-packages/pygments/lexers/pony.py | 93 + .../site-packages/pygments/lexers/praat.py | 303 + .../site-packages/pygments/lexers/procfile.py | 41 + .../site-packages/pygments/lexers/prolog.py | 318 + .../site-packages/pygments/lexers/promql.py | 176 + .../Lib/site-packages/pygments/lexers/prql.py | 251 + venv/Lib/site-packages/pygments/lexers/ptx.py | 119 + .../site-packages/pygments/lexers/python.py | 1201 +++ venv/Lib/site-packages/pygments/lexers/q.py | 187 + .../Lib/site-packages/pygments/lexers/qlik.py | 117 + venv/Lib/site-packages/pygments/lexers/qvt.py | 153 + venv/Lib/site-packages/pygments/lexers/r.py | 196 + venv/Lib/site-packages/pygments/lexers/rdf.py | 468 + .../site-packages/pygments/lexers/rebol.py | 419 + .../Lib/site-packages/pygments/lexers/rego.py | 57 + .../site-packages/pygments/lexers/resource.py | 83 + .../Lib/site-packages/pygments/lexers/ride.py | 138 + .../Lib/site-packages/pygments/lexers/rita.py | 42 + venv/Lib/site-packages/pygments/lexers/rnc.py | 66 + .../site-packages/pygments/lexers/roboconf.py | 81 + .../pygments/lexers/robotframework.py | 551 ++ .../Lib/site-packages/pygments/lexers/ruby.py | 518 + .../Lib/site-packages/pygments/lexers/rust.py | 222 + venv/Lib/site-packages/pygments/lexers/sas.py | 227 + .../Lib/site-packages/pygments/lexers/savi.py | 171 + .../site-packages/pygments/lexers/scdoc.py | 85 + .../pygments/lexers/scripting.py | 1616 ++++ venv/Lib/site-packages/pygments/lexers/sgf.py | 59 + .../site-packages/pygments/lexers/shell.py | 902 ++ .../site-packages/pygments/lexers/sieve.py | 78 + .../site-packages/pygments/lexers/slash.py | 183 + .../pygments/lexers/smalltalk.py | 194 + .../site-packages/pygments/lexers/smithy.py | 77 + venv/Lib/site-packages/pygments/lexers/smv.py | 78 + .../site-packages/pygments/lexers/snobol.py | 82 + .../site-packages/pygments/lexers/solidity.py | 87 + .../site-packages/pygments/lexers/soong.py | 78 + .../site-packages/pygments/lexers/sophia.py | 102 + .../site-packages/pygments/lexers/special.py | 122 + .../site-packages/pygments/lexers/spice.py | 70 + venv/Lib/site-packages/pygments/lexers/sql.py | 1109 +++ .../site-packages/pygments/lexers/srcinfo.py | 62 + .../site-packages/pygments/lexers/stata.py | 170 + .../pygments/lexers/supercollider.py | 94 + .../site-packages/pygments/lexers/tablegen.py | 177 + .../Lib/site-packages/pygments/lexers/tact.py | 303 + venv/Lib/site-packages/pygments/lexers/tal.py | 77 + venv/Lib/site-packages/pygments/lexers/tcl.py | 148 + .../Lib/site-packages/pygments/lexers/teal.py | 88 + .../pygments/lexers/templates.py | 2355 +++++ .../site-packages/pygments/lexers/teraterm.py | 325 + .../site-packages/pygments/lexers/testing.py | 209 + .../Lib/site-packages/pygments/lexers/text.py | 27 + .../site-packages/pygments/lexers/textedit.py | 205 + .../site-packages/pygments/lexers/textfmts.py | 436 + .../site-packages/pygments/lexers/theorem.py | 410 + .../site-packages/pygments/lexers/thingsdb.py | 140 + venv/Lib/site-packages/pygments/lexers/tlb.py | 59 + venv/Lib/site-packages/pygments/lexers/tls.py | 54 + venv/Lib/site-packages/pygments/lexers/tnt.py | 270 + .../pygments/lexers/trafficscript.py | 51 + .../pygments/lexers/typoscript.py | 216 + .../site-packages/pygments/lexers/typst.py | 160 + venv/Lib/site-packages/pygments/lexers/ul4.py | 309 + .../site-packages/pygments/lexers/unicon.py | 413 + .../Lib/site-packages/pygments/lexers/urbi.py | 145 + venv/Lib/site-packages/pygments/lexers/usd.py | 85 + .../site-packages/pygments/lexers/varnish.py | 189 + .../pygments/lexers/verification.py | 113 + .../site-packages/pygments/lexers/verifpal.py | 65 + venv/Lib/site-packages/pygments/lexers/vip.py | 150 + .../site-packages/pygments/lexers/vyper.py | 140 + venv/Lib/site-packages/pygments/lexers/web.py | 24 + .../pygments/lexers/webassembly.py | 119 + .../site-packages/pygments/lexers/webidl.py | 298 + .../site-packages/pygments/lexers/webmisc.py | 1006 ++ .../Lib/site-packages/pygments/lexers/wgsl.py | 406 + .../site-packages/pygments/lexers/whiley.py | 115 + .../site-packages/pygments/lexers/wowtoc.py | 120 + .../Lib/site-packages/pygments/lexers/wren.py | 98 + venv/Lib/site-packages/pygments/lexers/x10.py | 66 + .../Lib/site-packages/pygments/lexers/xorg.py | 38 + .../Lib/site-packages/pygments/lexers/yang.py | 103 + .../Lib/site-packages/pygments/lexers/yara.py | 69 + venv/Lib/site-packages/pygments/lexers/zig.py | 125 + venv/Lib/site-packages/pygments/modeline.py | 43 + venv/Lib/site-packages/pygments/plugin.py | 72 + venv/Lib/site-packages/pygments/regexopt.py | 91 + venv/Lib/site-packages/pygments/scanner.py | 104 + venv/Lib/site-packages/pygments/sphinxext.py | 247 + venv/Lib/site-packages/pygments/style.py | 203 + .../site-packages/pygments/styles/__init__.py | 61 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 2624 bytes .../__pycache__/_mapping.cpython-312.pyc | Bin 0 -> 3637 bytes .../styles/__pycache__/abap.cpython-312.pyc | Bin 0 -> 1072 bytes .../styles/__pycache__/algol.cpython-312.pyc | Bin 0 -> 2592 bytes .../__pycache__/algol_nu.cpython-312.pyc | Bin 0 -> 2607 bytes .../__pycache__/arduino.cpython-312.pyc | Bin 0 -> 4266 bytes .../styles/__pycache__/autumn.cpython-312.pyc | Bin 0 -> 2848 bytes .../__pycache__/borland.cpython-312.pyc | Bin 0 -> 2217 bytes .../styles/__pycache__/bw.cpython-312.pyc | Bin 0 -> 1894 bytes .../styles/__pycache__/coffee.cpython-312.pyc | Bin 0 -> 3438 bytes .../__pycache__/colorful.cpython-312.pyc | Bin 0 -> 3706 bytes .../__pycache__/default.cpython-312.pyc | Bin 0 -> 3208 bytes .../__pycache__/dracula.cpython-312.pyc | Bin 0 -> 3021 bytes .../styles/__pycache__/emacs.cpython-312.pyc | Bin 0 -> 3248 bytes .../__pycache__/friendly.cpython-312.pyc | Bin 0 -> 3344 bytes .../friendly_grayscale.cpython-312.pyc | Bin 0 -> 3554 bytes .../styles/__pycache__/fruity.cpython-312.pyc | Bin 0 -> 1923 bytes .../__pycache__/gh_dark.cpython-312.pyc | Bin 0 -> 4012 bytes .../__pycache__/gruvbox.cpython-312.pyc | Bin 0 -> 4360 bytes .../styles/__pycache__/igor.cpython-312.pyc | Bin 0 -> 1117 bytes .../styles/__pycache__/inkpot.cpython-312.pyc | Bin 0 -> 2979 bytes .../__pycache__/lightbulb.cpython-312.pyc | Bin 0 -> 4350 bytes .../__pycache__/lilypond.cpython-312.pyc | Bin 0 -> 3491 bytes .../__pycache__/lovelace.cpython-312.pyc | Bin 0 -> 4244 bytes .../styles/__pycache__/manni.cpython-312.pyc | Bin 0 -> 3479 bytes .../__pycache__/material.cpython-312.pyc | Bin 0 -> 4792 bytes .../__pycache__/monokai.cpython-312.pyc | Bin 0 -> 4773 bytes .../styles/__pycache__/murphy.cpython-312.pyc | Bin 0 -> 3664 bytes .../styles/__pycache__/native.cpython-312.pyc | Bin 0 -> 2941 bytes .../styles/__pycache__/nord.cpython-312.pyc | Bin 0 -> 5570 bytes .../__pycache__/onedark.cpython-312.pyc | Bin 0 -> 2247 bytes .../__pycache__/paraiso_dark.cpython-312.pyc | Bin 0 -> 5113 bytes .../__pycache__/paraiso_light.cpython-312.pyc | Bin 0 -> 5119 bytes .../styles/__pycache__/pastie.cpython-312.pyc | Bin 0 -> 3453 bytes .../__pycache__/perldoc.cpython-312.pyc | Bin 0 -> 3039 bytes .../__pycache__/rainbow_dash.cpython-312.pyc | Bin 0 -> 3685 bytes .../styles/__pycache__/rrt.cpython-312.pyc | Bin 0 -> 1447 bytes .../styles/__pycache__/sas.cpython-312.pyc | Bin 0 -> 1792 bytes .../__pycache__/solarized.cpython-312.pyc | Bin 0 -> 5744 bytes .../__pycache__/staroffice.cpython-312.pyc | Bin 0 -> 1095 bytes .../__pycache__/stata_dark.cpython-312.pyc | Bin 0 -> 1662 bytes .../__pycache__/stata_light.cpython-312.pyc | Bin 0 -> 1663 bytes .../styles/__pycache__/tango.cpython-312.pyc | Bin 0 -> 6116 bytes .../styles/__pycache__/trac.cpython-312.pyc | Bin 0 -> 2588 bytes .../styles/__pycache__/vim.cpython-312.pyc | Bin 0 -> 2471 bytes .../styles/__pycache__/vs.cpython-312.pyc | Bin 0 -> 1476 bytes .../styles/__pycache__/xcode.cpython-312.pyc | Bin 0 -> 1814 bytes .../__pycache__/zenburn.cpython-312.pyc | Bin 0 -> 3323 bytes .../site-packages/pygments/styles/_mapping.py | 54 + .../Lib/site-packages/pygments/styles/abap.py | 32 + .../site-packages/pygments/styles/algol.py | 65 + .../site-packages/pygments/styles/algol_nu.py | 65 + .../site-packages/pygments/styles/arduino.py | 100 + .../site-packages/pygments/styles/autumn.py | 67 + .../site-packages/pygments/styles/borland.py | 53 + venv/Lib/site-packages/pygments/styles/bw.py | 52 + .../site-packages/pygments/styles/coffee.py | 80 + .../site-packages/pygments/styles/colorful.py | 83 + .../site-packages/pygments/styles/default.py | 76 + .../site-packages/pygments/styles/dracula.py | 90 + .../site-packages/pygments/styles/emacs.py | 75 + .../site-packages/pygments/styles/friendly.py | 76 + .../pygments/styles/friendly_grayscale.py | 80 + .../site-packages/pygments/styles/fruity.py | 47 + .../site-packages/pygments/styles/gh_dark.py | 113 + .../site-packages/pygments/styles/gruvbox.py | 118 + .../Lib/site-packages/pygments/styles/igor.py | 32 + .../site-packages/pygments/styles/inkpot.py | 72 + .../pygments/styles/lightbulb.py | 110 + .../site-packages/pygments/styles/lilypond.py | 62 + .../site-packages/pygments/styles/lovelace.py | 100 + .../site-packages/pygments/styles/manni.py | 79 + .../site-packages/pygments/styles/material.py | 124 + .../site-packages/pygments/styles/monokai.py | 112 + .../site-packages/pygments/styles/murphy.py | 82 + .../site-packages/pygments/styles/native.py | 70 + .../Lib/site-packages/pygments/styles/nord.py | 156 + .../site-packages/pygments/styles/onedark.py | 63 + .../pygments/styles/paraiso_dark.py | 124 + .../pygments/styles/paraiso_light.py | 124 + .../site-packages/pygments/styles/pastie.py | 78 + .../site-packages/pygments/styles/perldoc.py | 73 + .../pygments/styles/rainbow_dash.py | 95 + venv/Lib/site-packages/pygments/styles/rrt.py | 40 + venv/Lib/site-packages/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 + .../site-packages/pygments/styles/tango.py | 143 + .../Lib/site-packages/pygments/styles/trac.py | 66 + venv/Lib/site-packages/pygments/styles/vim.py | 67 + venv/Lib/site-packages/pygments/styles/vs.py | 41 + .../site-packages/pygments/styles/xcode.py | 53 + .../site-packages/pygments/styles/zenburn.py | 83 + venv/Lib/site-packages/pygments/token.py | 214 + venv/Lib/site-packages/pygments/unistring.py | 153 + venv/Lib/site-packages/pygments/util.py | 324 + .../pytest-9.0.1.dist-info/INSTALLER | 1 + .../pytest-9.0.1.dist-info/METADATA | 212 + .../pytest-9.0.1.dist-info/RECORD | 158 + .../pytest-9.0.1.dist-info/REQUESTED | 0 .../pytest-9.0.1.dist-info/WHEEL | 5 + .../pytest-9.0.1.dist-info/entry_points.txt | 3 + .../pytest-9.0.1.dist-info/licenses/LICENSE | 21 + .../pytest-9.0.1.dist-info/top_level.txt | 3 + venv/Lib/site-packages/pytest/__init__.py | 186 + venv/Lib/site-packages/pytest/__main__.py | 9 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 4491 bytes .../__pycache__/__main__.cpython-312.pyc | Bin 0 -> 427 bytes venv/Lib/site-packages/pytest/py.typed | 0 .../pytest_cov-7.0.0.dist-info/INSTALLER | 1 + .../pytest_cov-7.0.0.dist-info/METADATA | 673 ++ .../pytest_cov-7.0.0.dist-info/RECORD | 14 + .../pytest_cov-7.0.0.dist-info/REQUESTED | 0 .../pytest_cov-7.0.0.dist-info/WHEEL | 4 + .../entry_points.txt | 2 + .../licenses/AUTHORS.rst | 67 + .../licenses/LICENSE | 21 + venv/Lib/site-packages/pytest_cov/__init__.py | 41 + .../__init__.cpython-312-pytest-9.0.1.pyc | Bin 0 -> 1823 bytes .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 1823 bytes .../__pycache__/engine.cpython-312.pyc | Bin 0 -> 23162 bytes .../plugin.cpython-312-pytest-9.0.1.pyc | Bin 0 -> 22963 bytes .../__pycache__/plugin.cpython-312.pyc | Bin 0 -> 22035 bytes venv/Lib/site-packages/pytest_cov/engine.py | 460 + venv/Lib/site-packages/pytest_cov/plugin.py | 468 + .../pytest_html-4.1.1.dist-info/INSTALLER | 1 + .../pytest_html-4.1.1.dist-info/METADATA | 92 + .../pytest_html-4.1.1.dist-info/RECORD | 32 + .../pytest_html-4.1.1.dist-info/REQUESTED | 0 .../pytest_html-4.1.1.dist-info/WHEEL | 4 + .../entry_points.txt | 3 + .../licenses/LICENSE | 3 + .../Lib/site-packages/pytest_html/__init__.py | 9 + .../__init__.cpython-312-pytest-9.0.1.pyc | Bin 0 -> 554 bytes .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 442 bytes .../__version.cpython-312-pytest-9.0.1.pyc | Bin 0 -> 697 bytes .../__pycache__/__version.cpython-312.pyc | Bin 0 -> 586 bytes .../basereport.cpython-312-pytest-9.0.1.pyc | Bin 0 -> 18758 bytes .../__pycache__/basereport.cpython-312.pyc | Bin 0 -> 18645 bytes .../extras.cpython-312-pytest-9.0.1.pyc | Bin 0 -> 2424 bytes .../__pycache__/extras.cpython-312.pyc | Bin 0 -> 2306 bytes .../fixtures.cpython-312-pytest-9.0.1.pyc | Bin 0 -> 1786 bytes .../__pycache__/fixtures.cpython-312.pyc | Bin 0 -> 1673 bytes .../hooks.cpython-312-pytest-9.0.1.pyc | Bin 0 -> 1381 bytes .../__pycache__/hooks.cpython-312.pyc | Bin 0 -> 1260 bytes .../plugin.cpython-312-pytest-9.0.1.pyc | Bin 0 -> 5812 bytes .../__pycache__/plugin.cpython-312.pyc | Bin 0 -> 5699 bytes .../report.cpython-312-pytest-9.0.1.pyc | Bin 0 -> 2965 bytes .../__pycache__/report.cpython-312.pyc | Bin 0 -> 2852 bytes .../report_data.cpython-312-pytest-9.0.1.pyc | Bin 0 -> 7284 bytes .../__pycache__/report_data.cpython-312.pyc | Bin 0 -> 7171 bytes ...tained_report.cpython-312-pytest-9.0.1.pyc | Bin 0 -> 2410 bytes .../selfcontained_report.cpython-312.pyc | Bin 0 -> 2297 bytes .../util.cpython-312-pytest-9.0.1.pyc | Bin 0 -> 2360 bytes .../__pycache__/util.cpython-312.pyc | Bin 0 -> 2246 bytes .../site-packages/pytest_html/__version.py | 16 + .../site-packages/pytest_html/basereport.py | 364 + venv/Lib/site-packages/pytest_html/extras.py | 60 + .../Lib/site-packages/pytest_html/fixtures.py | 47 + venv/Lib/site-packages/pytest_html/hooks.py | 27 + venv/Lib/site-packages/pytest_html/plugin.py | 140 + venv/Lib/site-packages/pytest_html/report.py | 41 + .../site-packages/pytest_html/report_data.py | 153 + .../pytest_html/resources/app.js | 655 ++ .../pytest_html/resources/index.jinja2 | 120 + .../pytest_html/resources/style.css | 319 + .../pytest_html/selfcontained_report.py | 39 + venv/Lib/site-packages/pytest_html/util.py | 56 + .../pytest_metadata-3.1.1.dist-info/INSTALLER | 1 + .../pytest_metadata-3.1.1.dist-info/METADATA | 235 + .../pytest_metadata-3.1.1.dist-info/RECORD | 33 + .../pytest_metadata-3.1.1.dist-info/WHEEL | 4 + .../entry_points.txt | 2 + .../licenses/AUTHORS | 8 + .../licenses/LICENSE | 3 + .../site-packages/pytest_metadata/__init__.py | 0 .../__init__.cpython-312-pytest-9.0.1.pyc | Bin 0 -> 187 bytes .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 187 bytes .../__pycache__/__version.cpython-312.pyc | Bin 0 -> 590 bytes .../hooks.cpython-312-pytest-9.0.1.pyc | Bin 0 -> 460 bytes .../__pycache__/hooks.cpython-312.pyc | Bin 0 -> 339 bytes .../plugin.cpython-312-pytest-9.0.1.pyc | Bin 0 -> 6154 bytes .../__pycache__/plugin.cpython-312.pyc | Bin 0 -> 6041 bytes .../pytest_metadata/__version.py | 16 + .../pytest_metadata/ci/__init__.py | 0 .../__init__.cpython-312-pytest-9.0.1.pyc | Bin 0 -> 190 bytes .../ci/__pycache__/__init__.cpython-312.pyc | Bin 0 -> 190 bytes .../appveyor.cpython-312-pytest-9.0.1.pyc | Bin 0 -> 1049 bytes .../ci/__pycache__/appveyor.cpython-312.pyc | Bin 0 -> 928 bytes .../bitbucket.cpython-312-pytest-9.0.1.pyc | Bin 0 -> 617 bytes .../ci/__pycache__/bitbucket.cpython-312.pyc | Bin 0 -> 496 bytes .../circleci.cpython-312-pytest-9.0.1.pyc | Bin 0 -> 752 bytes .../ci/__pycache__/circleci.cpython-312.pyc | Bin 0 -> 631 bytes .../codebuild.cpython-312-pytest-9.0.1.pyc | Bin 0 -> 494 bytes .../ci/__pycache__/codebuild.cpython-312.pyc | Bin 0 -> 373 bytes .../gitlab_ci.cpython-312-pytest-9.0.1.pyc | Bin 0 -> 966 bytes .../ci/__pycache__/gitlab_ci.cpython-312.pyc | Bin 0 -> 845 bytes .../jenkins.cpython-312-pytest-9.0.1.pyc | Bin 0 -> 526 bytes .../ci/__pycache__/jenkins.cpython-312.pyc | Bin 0 -> 405 bytes .../taskcluster.cpython-312-pytest-9.0.1.pyc | Bin 0 -> 369 bytes .../__pycache__/taskcluster.cpython-312.pyc | Bin 0 -> 251 bytes .../travis_ci.cpython-312-pytest-9.0.1.pyc | Bin 0 -> 674 bytes .../ci/__pycache__/travis_ci.cpython-312.pyc | Bin 0 -> 553 bytes .../pytest_metadata/ci/appveyor.py | 36 + .../pytest_metadata/ci/bitbucket.py | 19 + .../pytest_metadata/ci/circleci.py | 27 + .../pytest_metadata/ci/codebuild.py | 14 + .../pytest_metadata/ci/gitlab_ci.py | 42 + .../pytest_metadata/ci/jenkins.py | 21 + .../pytest_metadata/ci/taskcluster.py | 5 + .../pytest_metadata/ci/travis_ci.py | 23 + .../site-packages/pytest_metadata/hooks.py | 7 + .../site-packages/pytest_metadata/plugin.py | 131 + venv/Scripts/Activate.ps1 | 502 + venv/Scripts/activate | 70 + venv/Scripts/activate.bat | 34 + venv/Scripts/coverage-3.12.exe | Bin 0 -> 108416 bytes venv/Scripts/coverage.exe | Bin 0 -> 108416 bytes venv/Scripts/coverage3.exe | Bin 0 -> 108416 bytes venv/Scripts/deactivate.bat | 22 + venv/Scripts/pip.exe | Bin 0 -> 108422 bytes venv/Scripts/pip3.12.exe | Bin 0 -> 108422 bytes venv/Scripts/pip3.exe | Bin 0 -> 108422 bytes venv/Scripts/py.test.exe | Bin 0 -> 108422 bytes venv/Scripts/pygmentize.exe | Bin 0 -> 108416 bytes venv/Scripts/pytest.exe | Bin 0 -> 108422 bytes venv/Scripts/python.exe | Bin 0 -> 270104 bytes venv/Scripts/pythonw.exe | Bin 0 -> 258840 bytes venv/pyvenv.cfg | 5 + 2264 files changed, 403027 insertions(+) create mode 100644 src/app/main.py create mode 100644 venv/Lib/site-packages/__pycache__/py.cpython-312.pyc create mode 100644 venv/Lib/site-packages/_pytest/__init__.py create mode 100644 venv/Lib/site-packages/_pytest/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/_pytest/__pycache__/_argcomplete.cpython-312.pyc create mode 100644 venv/Lib/site-packages/_pytest/__pycache__/_version.cpython-312.pyc create mode 100644 venv/Lib/site-packages/_pytest/__pycache__/cacheprovider.cpython-312.pyc create mode 100644 venv/Lib/site-packages/_pytest/__pycache__/capture.cpython-312.pyc create mode 100644 venv/Lib/site-packages/_pytest/__pycache__/compat.cpython-312.pyc create mode 100644 venv/Lib/site-packages/_pytest/__pycache__/debugging.cpython-312.pyc create mode 100644 venv/Lib/site-packages/_pytest/__pycache__/deprecated.cpython-312.pyc create mode 100644 venv/Lib/site-packages/_pytest/__pycache__/doctest.cpython-312.pyc create mode 100644 venv/Lib/site-packages/_pytest/__pycache__/faulthandler.cpython-312.pyc create mode 100644 venv/Lib/site-packages/_pytest/__pycache__/fixtures.cpython-312.pyc create mode 100644 venv/Lib/site-packages/_pytest/__pycache__/freeze_support.cpython-312.pyc create mode 100644 venv/Lib/site-packages/_pytest/__pycache__/helpconfig.cpython-312.pyc create mode 100644 venv/Lib/site-packages/_pytest/__pycache__/hookspec.cpython-312.pyc create mode 100644 venv/Lib/site-packages/_pytest/__pycache__/junitxml.cpython-312.pyc create mode 100644 venv/Lib/site-packages/_pytest/__pycache__/legacypath.cpython-312.pyc create mode 100644 venv/Lib/site-packages/_pytest/__pycache__/logging.cpython-312.pyc create mode 100644 venv/Lib/site-packages/_pytest/__pycache__/main.cpython-312.pyc create mode 100644 venv/Lib/site-packages/_pytest/__pycache__/monkeypatch.cpython-312.pyc create mode 100644 venv/Lib/site-packages/_pytest/__pycache__/nodes.cpython-312.pyc create mode 100644 venv/Lib/site-packages/_pytest/__pycache__/outcomes.cpython-312.pyc create mode 100644 venv/Lib/site-packages/_pytest/__pycache__/pastebin.cpython-312.pyc create mode 100644 venv/Lib/site-packages/_pytest/__pycache__/pathlib.cpython-312.pyc create mode 100644 venv/Lib/site-packages/_pytest/__pycache__/pytester.cpython-312.pyc create mode 100644 venv/Lib/site-packages/_pytest/__pycache__/pytester_assertions.cpython-312.pyc create mode 100644 venv/Lib/site-packages/_pytest/__pycache__/python.cpython-312.pyc create mode 100644 venv/Lib/site-packages/_pytest/__pycache__/python_api.cpython-312.pyc create mode 100644 venv/Lib/site-packages/_pytest/__pycache__/raises.cpython-312.pyc create mode 100644 venv/Lib/site-packages/_pytest/__pycache__/recwarn.cpython-312.pyc create mode 100644 venv/Lib/site-packages/_pytest/__pycache__/reports.cpython-312.pyc create mode 100644 venv/Lib/site-packages/_pytest/__pycache__/runner.cpython-312.pyc create mode 100644 venv/Lib/site-packages/_pytest/__pycache__/scope.cpython-312.pyc create mode 100644 venv/Lib/site-packages/_pytest/__pycache__/setuponly.cpython-312.pyc create mode 100644 venv/Lib/site-packages/_pytest/__pycache__/setupplan.cpython-312.pyc create mode 100644 venv/Lib/site-packages/_pytest/__pycache__/skipping.cpython-312.pyc create mode 100644 venv/Lib/site-packages/_pytest/__pycache__/stash.cpython-312.pyc create mode 100644 venv/Lib/site-packages/_pytest/__pycache__/stepwise.cpython-312.pyc create mode 100644 venv/Lib/site-packages/_pytest/__pycache__/subtests.cpython-312.pyc create mode 100644 venv/Lib/site-packages/_pytest/__pycache__/terminal.cpython-312.pyc create mode 100644 venv/Lib/site-packages/_pytest/__pycache__/threadexception.cpython-312.pyc create mode 100644 venv/Lib/site-packages/_pytest/__pycache__/timing.cpython-312.pyc create mode 100644 venv/Lib/site-packages/_pytest/__pycache__/tmpdir.cpython-312.pyc create mode 100644 venv/Lib/site-packages/_pytest/__pycache__/tracemalloc.cpython-312.pyc create mode 100644 venv/Lib/site-packages/_pytest/__pycache__/unittest.cpython-312.pyc create mode 100644 venv/Lib/site-packages/_pytest/__pycache__/unraisableexception.cpython-312.pyc create mode 100644 venv/Lib/site-packages/_pytest/__pycache__/warning_types.cpython-312.pyc create mode 100644 venv/Lib/site-packages/_pytest/__pycache__/warnings.cpython-312.pyc create mode 100644 venv/Lib/site-packages/_pytest/_argcomplete.py create mode 100644 venv/Lib/site-packages/_pytest/_code/__init__.py create mode 100644 venv/Lib/site-packages/_pytest/_code/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/_pytest/_code/__pycache__/code.cpython-312.pyc create mode 100644 venv/Lib/site-packages/_pytest/_code/__pycache__/source.cpython-312.pyc create mode 100644 venv/Lib/site-packages/_pytest/_code/code.py create mode 100644 venv/Lib/site-packages/_pytest/_code/source.py create mode 100644 venv/Lib/site-packages/_pytest/_io/__init__.py create mode 100644 venv/Lib/site-packages/_pytest/_io/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/_pytest/_io/__pycache__/pprint.cpython-312.pyc create mode 100644 venv/Lib/site-packages/_pytest/_io/__pycache__/saferepr.cpython-312.pyc create mode 100644 venv/Lib/site-packages/_pytest/_io/__pycache__/terminalwriter.cpython-312.pyc create mode 100644 venv/Lib/site-packages/_pytest/_io/__pycache__/wcwidth.cpython-312.pyc create mode 100644 venv/Lib/site-packages/_pytest/_io/pprint.py create mode 100644 venv/Lib/site-packages/_pytest/_io/saferepr.py create mode 100644 venv/Lib/site-packages/_pytest/_io/terminalwriter.py create mode 100644 venv/Lib/site-packages/_pytest/_io/wcwidth.py create mode 100644 venv/Lib/site-packages/_pytest/_py/__init__.py create mode 100644 venv/Lib/site-packages/_pytest/_py/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/_pytest/_py/__pycache__/error.cpython-312.pyc create mode 100644 venv/Lib/site-packages/_pytest/_py/__pycache__/path.cpython-312.pyc create mode 100644 venv/Lib/site-packages/_pytest/_py/error.py create mode 100644 venv/Lib/site-packages/_pytest/_py/path.py create mode 100644 venv/Lib/site-packages/_pytest/_version.py create mode 100644 venv/Lib/site-packages/_pytest/assertion/__init__.py create mode 100644 venv/Lib/site-packages/_pytest/assertion/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/_pytest/assertion/__pycache__/rewrite.cpython-312.pyc create mode 100644 venv/Lib/site-packages/_pytest/assertion/__pycache__/truncate.cpython-312.pyc create mode 100644 venv/Lib/site-packages/_pytest/assertion/__pycache__/util.cpython-312.pyc create mode 100644 venv/Lib/site-packages/_pytest/assertion/rewrite.py create mode 100644 venv/Lib/site-packages/_pytest/assertion/truncate.py create mode 100644 venv/Lib/site-packages/_pytest/assertion/util.py create mode 100644 venv/Lib/site-packages/_pytest/cacheprovider.py create mode 100644 venv/Lib/site-packages/_pytest/capture.py create mode 100644 venv/Lib/site-packages/_pytest/compat.py create mode 100644 venv/Lib/site-packages/_pytest/config/__init__.py create mode 100644 venv/Lib/site-packages/_pytest/config/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/_pytest/config/__pycache__/argparsing.cpython-312.pyc create mode 100644 venv/Lib/site-packages/_pytest/config/__pycache__/compat.cpython-312.pyc create mode 100644 venv/Lib/site-packages/_pytest/config/__pycache__/exceptions.cpython-312.pyc create mode 100644 venv/Lib/site-packages/_pytest/config/__pycache__/findpaths.cpython-312.pyc create mode 100644 venv/Lib/site-packages/_pytest/config/argparsing.py create mode 100644 venv/Lib/site-packages/_pytest/config/compat.py create mode 100644 venv/Lib/site-packages/_pytest/config/exceptions.py create mode 100644 venv/Lib/site-packages/_pytest/config/findpaths.py create mode 100644 venv/Lib/site-packages/_pytest/debugging.py create mode 100644 venv/Lib/site-packages/_pytest/deprecated.py create mode 100644 venv/Lib/site-packages/_pytest/doctest.py create mode 100644 venv/Lib/site-packages/_pytest/faulthandler.py create mode 100644 venv/Lib/site-packages/_pytest/fixtures.py create mode 100644 venv/Lib/site-packages/_pytest/freeze_support.py create mode 100644 venv/Lib/site-packages/_pytest/helpconfig.py create mode 100644 venv/Lib/site-packages/_pytest/hookspec.py create mode 100644 venv/Lib/site-packages/_pytest/junitxml.py create mode 100644 venv/Lib/site-packages/_pytest/legacypath.py create mode 100644 venv/Lib/site-packages/_pytest/logging.py create mode 100644 venv/Lib/site-packages/_pytest/main.py create mode 100644 venv/Lib/site-packages/_pytest/mark/__init__.py create mode 100644 venv/Lib/site-packages/_pytest/mark/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/_pytest/mark/__pycache__/expression.cpython-312.pyc create mode 100644 venv/Lib/site-packages/_pytest/mark/__pycache__/structures.cpython-312.pyc create mode 100644 venv/Lib/site-packages/_pytest/mark/expression.py create mode 100644 venv/Lib/site-packages/_pytest/mark/structures.py create mode 100644 venv/Lib/site-packages/_pytest/monkeypatch.py create mode 100644 venv/Lib/site-packages/_pytest/nodes.py create mode 100644 venv/Lib/site-packages/_pytest/outcomes.py create mode 100644 venv/Lib/site-packages/_pytest/pastebin.py create mode 100644 venv/Lib/site-packages/_pytest/pathlib.py create mode 100644 venv/Lib/site-packages/_pytest/py.typed create mode 100644 venv/Lib/site-packages/_pytest/pytester.py create mode 100644 venv/Lib/site-packages/_pytest/pytester_assertions.py create mode 100644 venv/Lib/site-packages/_pytest/python.py create mode 100644 venv/Lib/site-packages/_pytest/python_api.py create mode 100644 venv/Lib/site-packages/_pytest/raises.py create mode 100644 venv/Lib/site-packages/_pytest/recwarn.py create mode 100644 venv/Lib/site-packages/_pytest/reports.py create mode 100644 venv/Lib/site-packages/_pytest/runner.py create mode 100644 venv/Lib/site-packages/_pytest/scope.py create mode 100644 venv/Lib/site-packages/_pytest/setuponly.py create mode 100644 venv/Lib/site-packages/_pytest/setupplan.py create mode 100644 venv/Lib/site-packages/_pytest/skipping.py create mode 100644 venv/Lib/site-packages/_pytest/stash.py create mode 100644 venv/Lib/site-packages/_pytest/stepwise.py create mode 100644 venv/Lib/site-packages/_pytest/subtests.py create mode 100644 venv/Lib/site-packages/_pytest/terminal.py create mode 100644 venv/Lib/site-packages/_pytest/threadexception.py create mode 100644 venv/Lib/site-packages/_pytest/timing.py create mode 100644 venv/Lib/site-packages/_pytest/tmpdir.py create mode 100644 venv/Lib/site-packages/_pytest/tracemalloc.py create mode 100644 venv/Lib/site-packages/_pytest/unittest.py create mode 100644 venv/Lib/site-packages/_pytest/unraisableexception.py create mode 100644 venv/Lib/site-packages/_pytest/warning_types.py create mode 100644 venv/Lib/site-packages/_pytest/warnings.py create mode 100644 venv/Lib/site-packages/colorama-0.4.6.dist-info/INSTALLER create mode 100644 venv/Lib/site-packages/colorama-0.4.6.dist-info/METADATA create mode 100644 venv/Lib/site-packages/colorama-0.4.6.dist-info/RECORD create mode 100644 venv/Lib/site-packages/colorama-0.4.6.dist-info/WHEEL create mode 100644 venv/Lib/site-packages/colorama-0.4.6.dist-info/licenses/LICENSE.txt create mode 100644 venv/Lib/site-packages/colorama/__init__.py create mode 100644 venv/Lib/site-packages/colorama/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/colorama/__pycache__/ansi.cpython-312.pyc create mode 100644 venv/Lib/site-packages/colorama/__pycache__/ansitowin32.cpython-312.pyc create mode 100644 venv/Lib/site-packages/colorama/__pycache__/initialise.cpython-312.pyc create mode 100644 venv/Lib/site-packages/colorama/__pycache__/win32.cpython-312.pyc create mode 100644 venv/Lib/site-packages/colorama/__pycache__/winterm.cpython-312.pyc create mode 100644 venv/Lib/site-packages/colorama/ansi.py create mode 100644 venv/Lib/site-packages/colorama/ansitowin32.py create mode 100644 venv/Lib/site-packages/colorama/initialise.py create mode 100644 venv/Lib/site-packages/colorama/tests/__init__.py create mode 100644 venv/Lib/site-packages/colorama/tests/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/colorama/tests/__pycache__/ansi_test.cpython-312.pyc create mode 100644 venv/Lib/site-packages/colorama/tests/__pycache__/ansitowin32_test.cpython-312.pyc create mode 100644 venv/Lib/site-packages/colorama/tests/__pycache__/initialise_test.cpython-312.pyc create mode 100644 venv/Lib/site-packages/colorama/tests/__pycache__/isatty_test.cpython-312.pyc create mode 100644 venv/Lib/site-packages/colorama/tests/__pycache__/utils.cpython-312.pyc create mode 100644 venv/Lib/site-packages/colorama/tests/__pycache__/winterm_test.cpython-312.pyc create mode 100644 venv/Lib/site-packages/colorama/tests/ansi_test.py create mode 100644 venv/Lib/site-packages/colorama/tests/ansitowin32_test.py create mode 100644 venv/Lib/site-packages/colorama/tests/initialise_test.py create mode 100644 venv/Lib/site-packages/colorama/tests/isatty_test.py create mode 100644 venv/Lib/site-packages/colorama/tests/utils.py create mode 100644 venv/Lib/site-packages/colorama/tests/winterm_test.py create mode 100644 venv/Lib/site-packages/colorama/win32.py create mode 100644 venv/Lib/site-packages/colorama/winterm.py create mode 100644 venv/Lib/site-packages/coverage-7.12.0.dist-info/INSTALLER create mode 100644 venv/Lib/site-packages/coverage-7.12.0.dist-info/METADATA create mode 100644 venv/Lib/site-packages/coverage-7.12.0.dist-info/RECORD create mode 100644 venv/Lib/site-packages/coverage-7.12.0.dist-info/WHEEL create mode 100644 venv/Lib/site-packages/coverage-7.12.0.dist-info/entry_points.txt create mode 100644 venv/Lib/site-packages/coverage-7.12.0.dist-info/licenses/LICENSE.txt create mode 100644 venv/Lib/site-packages/coverage-7.12.0.dist-info/top_level.txt create mode 100644 venv/Lib/site-packages/coverage/__init__.py create mode 100644 venv/Lib/site-packages/coverage/__main__.py create mode 100644 venv/Lib/site-packages/coverage/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/coverage/__pycache__/__main__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/coverage/__pycache__/annotate.cpython-312.pyc create mode 100644 venv/Lib/site-packages/coverage/__pycache__/bytecode.cpython-312.pyc create mode 100644 venv/Lib/site-packages/coverage/__pycache__/cmdline.cpython-312.pyc create mode 100644 venv/Lib/site-packages/coverage/__pycache__/collector.cpython-312.pyc create mode 100644 venv/Lib/site-packages/coverage/__pycache__/config.cpython-312.pyc create mode 100644 venv/Lib/site-packages/coverage/__pycache__/context.cpython-312.pyc create mode 100644 venv/Lib/site-packages/coverage/__pycache__/control.cpython-312.pyc create mode 100644 venv/Lib/site-packages/coverage/__pycache__/core.cpython-312.pyc create mode 100644 venv/Lib/site-packages/coverage/__pycache__/data.cpython-312.pyc create mode 100644 venv/Lib/site-packages/coverage/__pycache__/debug.cpython-312.pyc create mode 100644 venv/Lib/site-packages/coverage/__pycache__/disposition.cpython-312.pyc create mode 100644 venv/Lib/site-packages/coverage/__pycache__/env.cpython-312.pyc create mode 100644 venv/Lib/site-packages/coverage/__pycache__/exceptions.cpython-312.pyc create mode 100644 venv/Lib/site-packages/coverage/__pycache__/execfile.cpython-312.pyc create mode 100644 venv/Lib/site-packages/coverage/__pycache__/files.cpython-312.pyc create mode 100644 venv/Lib/site-packages/coverage/__pycache__/html.cpython-312.pyc create mode 100644 venv/Lib/site-packages/coverage/__pycache__/inorout.cpython-312.pyc create mode 100644 venv/Lib/site-packages/coverage/__pycache__/jsonreport.cpython-312.pyc create mode 100644 venv/Lib/site-packages/coverage/__pycache__/lcovreport.cpython-312.pyc create mode 100644 venv/Lib/site-packages/coverage/__pycache__/misc.cpython-312.pyc create mode 100644 venv/Lib/site-packages/coverage/__pycache__/multiproc.cpython-312.pyc create mode 100644 venv/Lib/site-packages/coverage/__pycache__/numbits.cpython-312.pyc create mode 100644 venv/Lib/site-packages/coverage/__pycache__/parser.cpython-312.pyc create mode 100644 venv/Lib/site-packages/coverage/__pycache__/patch.cpython-312.pyc create mode 100644 venv/Lib/site-packages/coverage/__pycache__/phystokens.cpython-312.pyc create mode 100644 venv/Lib/site-packages/coverage/__pycache__/plugin.cpython-312.pyc create mode 100644 venv/Lib/site-packages/coverage/__pycache__/plugin_support.cpython-312.pyc create mode 100644 venv/Lib/site-packages/coverage/__pycache__/python.cpython-312.pyc create mode 100644 venv/Lib/site-packages/coverage/__pycache__/pytracer.cpython-312.pyc create mode 100644 venv/Lib/site-packages/coverage/__pycache__/regions.cpython-312.pyc create mode 100644 venv/Lib/site-packages/coverage/__pycache__/report.cpython-312.pyc create mode 100644 venv/Lib/site-packages/coverage/__pycache__/report_core.cpython-312.pyc create mode 100644 venv/Lib/site-packages/coverage/__pycache__/results.cpython-312.pyc create mode 100644 venv/Lib/site-packages/coverage/__pycache__/sqldata.cpython-312.pyc create mode 100644 venv/Lib/site-packages/coverage/__pycache__/sqlitedb.cpython-312.pyc create mode 100644 venv/Lib/site-packages/coverage/__pycache__/sysmon.cpython-312.pyc create mode 100644 venv/Lib/site-packages/coverage/__pycache__/templite.cpython-312.pyc create mode 100644 venv/Lib/site-packages/coverage/__pycache__/tomlconfig.cpython-312.pyc create mode 100644 venv/Lib/site-packages/coverage/__pycache__/types.cpython-312.pyc create mode 100644 venv/Lib/site-packages/coverage/__pycache__/version.cpython-312.pyc create mode 100644 venv/Lib/site-packages/coverage/__pycache__/xmlreport.cpython-312.pyc create mode 100644 venv/Lib/site-packages/coverage/annotate.py create mode 100644 venv/Lib/site-packages/coverage/bytecode.py create mode 100644 venv/Lib/site-packages/coverage/cmdline.py create mode 100644 venv/Lib/site-packages/coverage/collector.py create mode 100644 venv/Lib/site-packages/coverage/config.py create mode 100644 venv/Lib/site-packages/coverage/context.py create mode 100644 venv/Lib/site-packages/coverage/control.py create mode 100644 venv/Lib/site-packages/coverage/core.py create mode 100644 venv/Lib/site-packages/coverage/data.py create mode 100644 venv/Lib/site-packages/coverage/debug.py create mode 100644 venv/Lib/site-packages/coverage/disposition.py create mode 100644 venv/Lib/site-packages/coverage/env.py create mode 100644 venv/Lib/site-packages/coverage/exceptions.py create mode 100644 venv/Lib/site-packages/coverage/execfile.py create mode 100644 venv/Lib/site-packages/coverage/files.py create mode 100644 venv/Lib/site-packages/coverage/html.py create mode 100644 venv/Lib/site-packages/coverage/htmlfiles/coverage_html.js create mode 100644 venv/Lib/site-packages/coverage/htmlfiles/favicon_32.png create mode 100644 venv/Lib/site-packages/coverage/htmlfiles/index.html create mode 100644 venv/Lib/site-packages/coverage/htmlfiles/keybd_closed.png create mode 100644 venv/Lib/site-packages/coverage/htmlfiles/pyfile.html create mode 100644 venv/Lib/site-packages/coverage/htmlfiles/style.css create mode 100644 venv/Lib/site-packages/coverage/htmlfiles/style.scss create mode 100644 venv/Lib/site-packages/coverage/inorout.py create mode 100644 venv/Lib/site-packages/coverage/jsonreport.py create mode 100644 venv/Lib/site-packages/coverage/lcovreport.py create mode 100644 venv/Lib/site-packages/coverage/misc.py create mode 100644 venv/Lib/site-packages/coverage/multiproc.py create mode 100644 venv/Lib/site-packages/coverage/numbits.py create mode 100644 venv/Lib/site-packages/coverage/parser.py create mode 100644 venv/Lib/site-packages/coverage/patch.py create mode 100644 venv/Lib/site-packages/coverage/phystokens.py create mode 100644 venv/Lib/site-packages/coverage/plugin.py create mode 100644 venv/Lib/site-packages/coverage/plugin_support.py create mode 100644 venv/Lib/site-packages/coverage/py.typed create mode 100644 venv/Lib/site-packages/coverage/python.py create mode 100644 venv/Lib/site-packages/coverage/pytracer.py create mode 100644 venv/Lib/site-packages/coverage/regions.py create mode 100644 venv/Lib/site-packages/coverage/report.py create mode 100644 venv/Lib/site-packages/coverage/report_core.py create mode 100644 venv/Lib/site-packages/coverage/results.py create mode 100644 venv/Lib/site-packages/coverage/sqldata.py create mode 100644 venv/Lib/site-packages/coverage/sqlitedb.py create mode 100644 venv/Lib/site-packages/coverage/sysmon.py create mode 100644 venv/Lib/site-packages/coverage/templite.py create mode 100644 venv/Lib/site-packages/coverage/tomlconfig.py create mode 100644 venv/Lib/site-packages/coverage/tracer.cp312-win_amd64.pyd create mode 100644 venv/Lib/site-packages/coverage/tracer.pyi create mode 100644 venv/Lib/site-packages/coverage/types.py create mode 100644 venv/Lib/site-packages/coverage/version.py create mode 100644 venv/Lib/site-packages/coverage/xmlreport.py create mode 100644 venv/Lib/site-packages/iniconfig-2.3.0.dist-info/INSTALLER create mode 100644 venv/Lib/site-packages/iniconfig-2.3.0.dist-info/METADATA create mode 100644 venv/Lib/site-packages/iniconfig-2.3.0.dist-info/RECORD create mode 100644 venv/Lib/site-packages/iniconfig-2.3.0.dist-info/WHEEL create mode 100644 venv/Lib/site-packages/iniconfig-2.3.0.dist-info/licenses/LICENSE create mode 100644 venv/Lib/site-packages/iniconfig-2.3.0.dist-info/top_level.txt create mode 100644 venv/Lib/site-packages/iniconfig/__init__.py create mode 100644 venv/Lib/site-packages/iniconfig/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/iniconfig/__pycache__/_parse.cpython-312.pyc create mode 100644 venv/Lib/site-packages/iniconfig/__pycache__/_version.cpython-312.pyc create mode 100644 venv/Lib/site-packages/iniconfig/__pycache__/exceptions.cpython-312.pyc create mode 100644 venv/Lib/site-packages/iniconfig/_parse.py create mode 100644 venv/Lib/site-packages/iniconfig/_version.py create mode 100644 venv/Lib/site-packages/iniconfig/exceptions.py create mode 100644 venv/Lib/site-packages/iniconfig/py.typed create mode 100644 venv/Lib/site-packages/jinja2-3.1.6.dist-info/INSTALLER create mode 100644 venv/Lib/site-packages/jinja2-3.1.6.dist-info/METADATA create mode 100644 venv/Lib/site-packages/jinja2-3.1.6.dist-info/RECORD create mode 100644 venv/Lib/site-packages/jinja2-3.1.6.dist-info/WHEEL create mode 100644 venv/Lib/site-packages/jinja2-3.1.6.dist-info/entry_points.txt create mode 100644 venv/Lib/site-packages/jinja2-3.1.6.dist-info/licenses/LICENSE.txt create mode 100644 venv/Lib/site-packages/jinja2/__init__.py create mode 100644 venv/Lib/site-packages/jinja2/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/jinja2/__pycache__/_identifier.cpython-312.pyc create mode 100644 venv/Lib/site-packages/jinja2/__pycache__/async_utils.cpython-312.pyc create mode 100644 venv/Lib/site-packages/jinja2/__pycache__/bccache.cpython-312.pyc create mode 100644 venv/Lib/site-packages/jinja2/__pycache__/compiler.cpython-312.pyc create mode 100644 venv/Lib/site-packages/jinja2/__pycache__/constants.cpython-312.pyc create mode 100644 venv/Lib/site-packages/jinja2/__pycache__/debug.cpython-312.pyc create mode 100644 venv/Lib/site-packages/jinja2/__pycache__/defaults.cpython-312.pyc create mode 100644 venv/Lib/site-packages/jinja2/__pycache__/environment.cpython-312.pyc create mode 100644 venv/Lib/site-packages/jinja2/__pycache__/exceptions.cpython-312.pyc create mode 100644 venv/Lib/site-packages/jinja2/__pycache__/ext.cpython-312.pyc create mode 100644 venv/Lib/site-packages/jinja2/__pycache__/filters.cpython-312.pyc create mode 100644 venv/Lib/site-packages/jinja2/__pycache__/idtracking.cpython-312.pyc create mode 100644 venv/Lib/site-packages/jinja2/__pycache__/lexer.cpython-312.pyc create mode 100644 venv/Lib/site-packages/jinja2/__pycache__/loaders.cpython-312.pyc create mode 100644 venv/Lib/site-packages/jinja2/__pycache__/meta.cpython-312.pyc create mode 100644 venv/Lib/site-packages/jinja2/__pycache__/nativetypes.cpython-312.pyc create mode 100644 venv/Lib/site-packages/jinja2/__pycache__/nodes.cpython-312.pyc create mode 100644 venv/Lib/site-packages/jinja2/__pycache__/optimizer.cpython-312.pyc create mode 100644 venv/Lib/site-packages/jinja2/__pycache__/parser.cpython-312.pyc create mode 100644 venv/Lib/site-packages/jinja2/__pycache__/runtime.cpython-312.pyc create mode 100644 venv/Lib/site-packages/jinja2/__pycache__/sandbox.cpython-312.pyc create mode 100644 venv/Lib/site-packages/jinja2/__pycache__/tests.cpython-312.pyc create mode 100644 venv/Lib/site-packages/jinja2/__pycache__/utils.cpython-312.pyc create mode 100644 venv/Lib/site-packages/jinja2/__pycache__/visitor.cpython-312.pyc create mode 100644 venv/Lib/site-packages/jinja2/_identifier.py create mode 100644 venv/Lib/site-packages/jinja2/async_utils.py create mode 100644 venv/Lib/site-packages/jinja2/bccache.py create mode 100644 venv/Lib/site-packages/jinja2/compiler.py create mode 100644 venv/Lib/site-packages/jinja2/constants.py create mode 100644 venv/Lib/site-packages/jinja2/debug.py create mode 100644 venv/Lib/site-packages/jinja2/defaults.py create mode 100644 venv/Lib/site-packages/jinja2/environment.py create mode 100644 venv/Lib/site-packages/jinja2/exceptions.py create mode 100644 venv/Lib/site-packages/jinja2/ext.py create mode 100644 venv/Lib/site-packages/jinja2/filters.py create mode 100644 venv/Lib/site-packages/jinja2/idtracking.py create mode 100644 venv/Lib/site-packages/jinja2/lexer.py create mode 100644 venv/Lib/site-packages/jinja2/loaders.py create mode 100644 venv/Lib/site-packages/jinja2/meta.py create mode 100644 venv/Lib/site-packages/jinja2/nativetypes.py create mode 100644 venv/Lib/site-packages/jinja2/nodes.py create mode 100644 venv/Lib/site-packages/jinja2/optimizer.py create mode 100644 venv/Lib/site-packages/jinja2/parser.py create mode 100644 venv/Lib/site-packages/jinja2/py.typed create mode 100644 venv/Lib/site-packages/jinja2/runtime.py create mode 100644 venv/Lib/site-packages/jinja2/sandbox.py create mode 100644 venv/Lib/site-packages/jinja2/tests.py create mode 100644 venv/Lib/site-packages/jinja2/utils.py create mode 100644 venv/Lib/site-packages/jinja2/visitor.py create mode 100644 venv/Lib/site-packages/markupsafe-3.0.3.dist-info/INSTALLER create mode 100644 venv/Lib/site-packages/markupsafe-3.0.3.dist-info/METADATA create mode 100644 venv/Lib/site-packages/markupsafe-3.0.3.dist-info/RECORD create mode 100644 venv/Lib/site-packages/markupsafe-3.0.3.dist-info/WHEEL create mode 100644 venv/Lib/site-packages/markupsafe-3.0.3.dist-info/licenses/LICENSE.txt create mode 100644 venv/Lib/site-packages/markupsafe-3.0.3.dist-info/top_level.txt create mode 100644 venv/Lib/site-packages/markupsafe/__init__.py create mode 100644 venv/Lib/site-packages/markupsafe/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/markupsafe/__pycache__/_native.cpython-312.pyc create mode 100644 venv/Lib/site-packages/markupsafe/_native.py create mode 100644 venv/Lib/site-packages/markupsafe/_speedups.c create mode 100644 venv/Lib/site-packages/markupsafe/_speedups.cp312-win_amd64.pyd create mode 100644 venv/Lib/site-packages/markupsafe/_speedups.pyi create mode 100644 venv/Lib/site-packages/markupsafe/py.typed create mode 100644 venv/Lib/site-packages/packaging-25.0.dist-info/INSTALLER create mode 100644 venv/Lib/site-packages/packaging-25.0.dist-info/METADATA create mode 100644 venv/Lib/site-packages/packaging-25.0.dist-info/RECORD create mode 100644 venv/Lib/site-packages/packaging-25.0.dist-info/WHEEL create mode 100644 venv/Lib/site-packages/packaging-25.0.dist-info/licenses/LICENSE create mode 100644 venv/Lib/site-packages/packaging-25.0.dist-info/licenses/LICENSE.APACHE create mode 100644 venv/Lib/site-packages/packaging-25.0.dist-info/licenses/LICENSE.BSD create mode 100644 venv/Lib/site-packages/packaging/__init__.py create mode 100644 venv/Lib/site-packages/packaging/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/packaging/__pycache__/_elffile.cpython-312.pyc create mode 100644 venv/Lib/site-packages/packaging/__pycache__/_manylinux.cpython-312.pyc create mode 100644 venv/Lib/site-packages/packaging/__pycache__/_musllinux.cpython-312.pyc create mode 100644 venv/Lib/site-packages/packaging/__pycache__/_parser.cpython-312.pyc create mode 100644 venv/Lib/site-packages/packaging/__pycache__/_structures.cpython-312.pyc create mode 100644 venv/Lib/site-packages/packaging/__pycache__/_tokenizer.cpython-312.pyc create mode 100644 venv/Lib/site-packages/packaging/__pycache__/markers.cpython-312.pyc create mode 100644 venv/Lib/site-packages/packaging/__pycache__/metadata.cpython-312.pyc create mode 100644 venv/Lib/site-packages/packaging/__pycache__/requirements.cpython-312.pyc create mode 100644 venv/Lib/site-packages/packaging/__pycache__/specifiers.cpython-312.pyc create mode 100644 venv/Lib/site-packages/packaging/__pycache__/tags.cpython-312.pyc create mode 100644 venv/Lib/site-packages/packaging/__pycache__/utils.cpython-312.pyc create mode 100644 venv/Lib/site-packages/packaging/__pycache__/version.cpython-312.pyc create mode 100644 venv/Lib/site-packages/packaging/_elffile.py create mode 100644 venv/Lib/site-packages/packaging/_manylinux.py create mode 100644 venv/Lib/site-packages/packaging/_musllinux.py create mode 100644 venv/Lib/site-packages/packaging/_parser.py create mode 100644 venv/Lib/site-packages/packaging/_structures.py create mode 100644 venv/Lib/site-packages/packaging/_tokenizer.py create mode 100644 venv/Lib/site-packages/packaging/licenses/__init__.py create mode 100644 venv/Lib/site-packages/packaging/licenses/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/packaging/licenses/__pycache__/_spdx.cpython-312.pyc create mode 100644 venv/Lib/site-packages/packaging/licenses/_spdx.py create mode 100644 venv/Lib/site-packages/packaging/markers.py create mode 100644 venv/Lib/site-packages/packaging/metadata.py create mode 100644 venv/Lib/site-packages/packaging/py.typed create mode 100644 venv/Lib/site-packages/packaging/requirements.py create mode 100644 venv/Lib/site-packages/packaging/specifiers.py create mode 100644 venv/Lib/site-packages/packaging/tags.py create mode 100644 venv/Lib/site-packages/packaging/utils.py create mode 100644 venv/Lib/site-packages/packaging/version.py create mode 100644 venv/Lib/site-packages/pip-24.0.dist-info/AUTHORS.txt create mode 100644 venv/Lib/site-packages/pip-24.0.dist-info/INSTALLER create mode 100644 venv/Lib/site-packages/pip-24.0.dist-info/LICENSE.txt create mode 100644 venv/Lib/site-packages/pip-24.0.dist-info/METADATA create mode 100644 venv/Lib/site-packages/pip-24.0.dist-info/RECORD create mode 100644 venv/Lib/site-packages/pip-24.0.dist-info/REQUESTED create mode 100644 venv/Lib/site-packages/pip-24.0.dist-info/WHEEL create mode 100644 venv/Lib/site-packages/pip-24.0.dist-info/entry_points.txt create mode 100644 venv/Lib/site-packages/pip-24.0.dist-info/top_level.txt create mode 100644 venv/Lib/site-packages/pip/__init__.py create mode 100644 venv/Lib/site-packages/pip/__main__.py create mode 100644 venv/Lib/site-packages/pip/__pip-runner__.py create mode 100644 venv/Lib/site-packages/pip/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/__pycache__/__main__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/__pycache__/__pip-runner__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/__init__.py create mode 100644 venv/Lib/site-packages/pip/_internal/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/__pycache__/build_env.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/__pycache__/cache.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/__pycache__/configuration.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/__pycache__/exceptions.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/__pycache__/main.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/__pycache__/pyproject.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/__pycache__/self_outdated_check.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/__pycache__/wheel_builder.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/build_env.py create mode 100644 venv/Lib/site-packages/pip/_internal/cache.py create mode 100644 venv/Lib/site-packages/pip/_internal/cli/__init__.py create mode 100644 venv/Lib/site-packages/pip/_internal/cli/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/cli/__pycache__/autocompletion.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/cli/__pycache__/base_command.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/cli/__pycache__/cmdoptions.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/cli/__pycache__/command_context.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/cli/__pycache__/main.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/cli/__pycache__/main_parser.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/cli/__pycache__/parser.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/cli/__pycache__/progress_bars.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/cli/__pycache__/req_command.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/cli/__pycache__/spinners.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/cli/__pycache__/status_codes.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/cli/autocompletion.py create mode 100644 venv/Lib/site-packages/pip/_internal/cli/base_command.py create mode 100644 venv/Lib/site-packages/pip/_internal/cli/cmdoptions.py create mode 100644 venv/Lib/site-packages/pip/_internal/cli/command_context.py create mode 100644 venv/Lib/site-packages/pip/_internal/cli/main.py create mode 100644 venv/Lib/site-packages/pip/_internal/cli/main_parser.py create mode 100644 venv/Lib/site-packages/pip/_internal/cli/parser.py create mode 100644 venv/Lib/site-packages/pip/_internal/cli/progress_bars.py create mode 100644 venv/Lib/site-packages/pip/_internal/cli/req_command.py create mode 100644 venv/Lib/site-packages/pip/_internal/cli/spinners.py create mode 100644 venv/Lib/site-packages/pip/_internal/cli/status_codes.py create mode 100644 venv/Lib/site-packages/pip/_internal/commands/__init__.py create mode 100644 venv/Lib/site-packages/pip/_internal/commands/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/commands/__pycache__/cache.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/commands/__pycache__/check.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/commands/__pycache__/completion.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/commands/__pycache__/configuration.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/commands/__pycache__/debug.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/commands/__pycache__/download.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/commands/__pycache__/freeze.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/commands/__pycache__/hash.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/commands/__pycache__/help.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/commands/__pycache__/index.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/commands/__pycache__/inspect.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/commands/__pycache__/install.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/commands/__pycache__/list.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/commands/__pycache__/search.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/commands/__pycache__/show.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/commands/__pycache__/uninstall.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/commands/__pycache__/wheel.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/commands/cache.py create mode 100644 venv/Lib/site-packages/pip/_internal/commands/check.py create mode 100644 venv/Lib/site-packages/pip/_internal/commands/completion.py create mode 100644 venv/Lib/site-packages/pip/_internal/commands/configuration.py create mode 100644 venv/Lib/site-packages/pip/_internal/commands/debug.py create mode 100644 venv/Lib/site-packages/pip/_internal/commands/download.py create mode 100644 venv/Lib/site-packages/pip/_internal/commands/freeze.py create mode 100644 venv/Lib/site-packages/pip/_internal/commands/hash.py create mode 100644 venv/Lib/site-packages/pip/_internal/commands/help.py create mode 100644 venv/Lib/site-packages/pip/_internal/commands/index.py create mode 100644 venv/Lib/site-packages/pip/_internal/commands/inspect.py create mode 100644 venv/Lib/site-packages/pip/_internal/commands/install.py create mode 100644 venv/Lib/site-packages/pip/_internal/commands/list.py create mode 100644 venv/Lib/site-packages/pip/_internal/commands/search.py create mode 100644 venv/Lib/site-packages/pip/_internal/commands/show.py create mode 100644 venv/Lib/site-packages/pip/_internal/commands/uninstall.py create mode 100644 venv/Lib/site-packages/pip/_internal/commands/wheel.py create mode 100644 venv/Lib/site-packages/pip/_internal/configuration.py create mode 100644 venv/Lib/site-packages/pip/_internal/distributions/__init__.py create mode 100644 venv/Lib/site-packages/pip/_internal/distributions/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/distributions/__pycache__/base.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/distributions/__pycache__/installed.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/distributions/__pycache__/sdist.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/distributions/__pycache__/wheel.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/distributions/base.py create mode 100644 venv/Lib/site-packages/pip/_internal/distributions/installed.py create mode 100644 venv/Lib/site-packages/pip/_internal/distributions/sdist.py create mode 100644 venv/Lib/site-packages/pip/_internal/distributions/wheel.py create mode 100644 venv/Lib/site-packages/pip/_internal/exceptions.py create mode 100644 venv/Lib/site-packages/pip/_internal/index/__init__.py create mode 100644 venv/Lib/site-packages/pip/_internal/index/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/index/__pycache__/collector.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/index/__pycache__/package_finder.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/index/__pycache__/sources.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/index/collector.py create mode 100644 venv/Lib/site-packages/pip/_internal/index/package_finder.py create mode 100644 venv/Lib/site-packages/pip/_internal/index/sources.py create mode 100644 venv/Lib/site-packages/pip/_internal/locations/__init__.py create mode 100644 venv/Lib/site-packages/pip/_internal/locations/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/locations/__pycache__/_distutils.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/locations/__pycache__/_sysconfig.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/locations/__pycache__/base.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/locations/_distutils.py create mode 100644 venv/Lib/site-packages/pip/_internal/locations/_sysconfig.py create mode 100644 venv/Lib/site-packages/pip/_internal/locations/base.py create mode 100644 venv/Lib/site-packages/pip/_internal/main.py create mode 100644 venv/Lib/site-packages/pip/_internal/metadata/__init__.py create mode 100644 venv/Lib/site-packages/pip/_internal/metadata/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/metadata/__pycache__/_json.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/metadata/__pycache__/base.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/metadata/__pycache__/pkg_resources.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/metadata/_json.py create mode 100644 venv/Lib/site-packages/pip/_internal/metadata/base.py create mode 100644 venv/Lib/site-packages/pip/_internal/metadata/importlib/__init__.py create mode 100644 venv/Lib/site-packages/pip/_internal/metadata/importlib/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/metadata/importlib/__pycache__/_compat.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/metadata/importlib/__pycache__/_dists.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/metadata/importlib/__pycache__/_envs.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/metadata/importlib/_compat.py create mode 100644 venv/Lib/site-packages/pip/_internal/metadata/importlib/_dists.py create mode 100644 venv/Lib/site-packages/pip/_internal/metadata/importlib/_envs.py create mode 100644 venv/Lib/site-packages/pip/_internal/metadata/pkg_resources.py create mode 100644 venv/Lib/site-packages/pip/_internal/models/__init__.py create mode 100644 venv/Lib/site-packages/pip/_internal/models/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/models/__pycache__/candidate.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/models/__pycache__/direct_url.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/models/__pycache__/format_control.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/models/__pycache__/index.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/models/__pycache__/installation_report.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/models/__pycache__/link.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/models/__pycache__/scheme.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/models/__pycache__/search_scope.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/models/__pycache__/selection_prefs.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/models/__pycache__/target_python.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/models/__pycache__/wheel.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/models/candidate.py create mode 100644 venv/Lib/site-packages/pip/_internal/models/direct_url.py create mode 100644 venv/Lib/site-packages/pip/_internal/models/format_control.py create mode 100644 venv/Lib/site-packages/pip/_internal/models/index.py create mode 100644 venv/Lib/site-packages/pip/_internal/models/installation_report.py create mode 100644 venv/Lib/site-packages/pip/_internal/models/link.py create mode 100644 venv/Lib/site-packages/pip/_internal/models/scheme.py create mode 100644 venv/Lib/site-packages/pip/_internal/models/search_scope.py create mode 100644 venv/Lib/site-packages/pip/_internal/models/selection_prefs.py create mode 100644 venv/Lib/site-packages/pip/_internal/models/target_python.py create mode 100644 venv/Lib/site-packages/pip/_internal/models/wheel.py create mode 100644 venv/Lib/site-packages/pip/_internal/network/__init__.py create mode 100644 venv/Lib/site-packages/pip/_internal/network/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/network/__pycache__/auth.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/network/__pycache__/cache.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/network/__pycache__/download.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/network/__pycache__/lazy_wheel.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/network/__pycache__/session.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/network/__pycache__/utils.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/network/__pycache__/xmlrpc.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/network/auth.py create mode 100644 venv/Lib/site-packages/pip/_internal/network/cache.py create mode 100644 venv/Lib/site-packages/pip/_internal/network/download.py create mode 100644 venv/Lib/site-packages/pip/_internal/network/lazy_wheel.py create mode 100644 venv/Lib/site-packages/pip/_internal/network/session.py create mode 100644 venv/Lib/site-packages/pip/_internal/network/utils.py create mode 100644 venv/Lib/site-packages/pip/_internal/network/xmlrpc.py create mode 100644 venv/Lib/site-packages/pip/_internal/operations/__init__.py create mode 100644 venv/Lib/site-packages/pip/_internal/operations/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/operations/__pycache__/check.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/operations/__pycache__/freeze.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/operations/__pycache__/prepare.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/operations/build/__init__.py create mode 100644 venv/Lib/site-packages/pip/_internal/operations/build/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/operations/build/__pycache__/build_tracker.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/operations/build/__pycache__/metadata.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/operations/build/__pycache__/metadata_editable.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/operations/build/__pycache__/metadata_legacy.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/operations/build/__pycache__/wheel.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/operations/build/__pycache__/wheel_editable.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/operations/build/__pycache__/wheel_legacy.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/operations/build/build_tracker.py create mode 100644 venv/Lib/site-packages/pip/_internal/operations/build/metadata.py create mode 100644 venv/Lib/site-packages/pip/_internal/operations/build/metadata_editable.py create mode 100644 venv/Lib/site-packages/pip/_internal/operations/build/metadata_legacy.py create mode 100644 venv/Lib/site-packages/pip/_internal/operations/build/wheel.py create mode 100644 venv/Lib/site-packages/pip/_internal/operations/build/wheel_editable.py create mode 100644 venv/Lib/site-packages/pip/_internal/operations/build/wheel_legacy.py create mode 100644 venv/Lib/site-packages/pip/_internal/operations/check.py create mode 100644 venv/Lib/site-packages/pip/_internal/operations/freeze.py create mode 100644 venv/Lib/site-packages/pip/_internal/operations/install/__init__.py create mode 100644 venv/Lib/site-packages/pip/_internal/operations/install/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/operations/install/__pycache__/editable_legacy.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/operations/install/__pycache__/wheel.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/operations/install/editable_legacy.py create mode 100644 venv/Lib/site-packages/pip/_internal/operations/install/wheel.py create mode 100644 venv/Lib/site-packages/pip/_internal/operations/prepare.py create mode 100644 venv/Lib/site-packages/pip/_internal/pyproject.py create mode 100644 venv/Lib/site-packages/pip/_internal/req/__init__.py create mode 100644 venv/Lib/site-packages/pip/_internal/req/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/req/__pycache__/constructors.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/req/__pycache__/req_file.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/req/__pycache__/req_install.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/req/__pycache__/req_set.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/req/__pycache__/req_uninstall.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/req/constructors.py create mode 100644 venv/Lib/site-packages/pip/_internal/req/req_file.py create mode 100644 venv/Lib/site-packages/pip/_internal/req/req_install.py create mode 100644 venv/Lib/site-packages/pip/_internal/req/req_set.py create mode 100644 venv/Lib/site-packages/pip/_internal/req/req_uninstall.py create mode 100644 venv/Lib/site-packages/pip/_internal/resolution/__init__.py create mode 100644 venv/Lib/site-packages/pip/_internal/resolution/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/resolution/__pycache__/base.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/resolution/base.py create mode 100644 venv/Lib/site-packages/pip/_internal/resolution/legacy/__init__.py create mode 100644 venv/Lib/site-packages/pip/_internal/resolution/legacy/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/resolution/legacy/__pycache__/resolver.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/resolution/legacy/resolver.py create mode 100644 venv/Lib/site-packages/pip/_internal/resolution/resolvelib/__init__.py create mode 100644 venv/Lib/site-packages/pip/_internal/resolution/resolvelib/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/resolution/resolvelib/__pycache__/base.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/resolution/resolvelib/__pycache__/candidates.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/resolution/resolvelib/__pycache__/factory.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/resolution/resolvelib/__pycache__/found_candidates.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/resolution/resolvelib/__pycache__/provider.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/resolution/resolvelib/__pycache__/reporter.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/resolution/resolvelib/__pycache__/requirements.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/resolution/resolvelib/__pycache__/resolver.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/resolution/resolvelib/base.py create mode 100644 venv/Lib/site-packages/pip/_internal/resolution/resolvelib/candidates.py create mode 100644 venv/Lib/site-packages/pip/_internal/resolution/resolvelib/factory.py create mode 100644 venv/Lib/site-packages/pip/_internal/resolution/resolvelib/found_candidates.py create mode 100644 venv/Lib/site-packages/pip/_internal/resolution/resolvelib/provider.py create mode 100644 venv/Lib/site-packages/pip/_internal/resolution/resolvelib/reporter.py create mode 100644 venv/Lib/site-packages/pip/_internal/resolution/resolvelib/requirements.py create mode 100644 venv/Lib/site-packages/pip/_internal/resolution/resolvelib/resolver.py create mode 100644 venv/Lib/site-packages/pip/_internal/self_outdated_check.py create mode 100644 venv/Lib/site-packages/pip/_internal/utils/__init__.py create mode 100644 venv/Lib/site-packages/pip/_internal/utils/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/utils/__pycache__/_jaraco_text.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/utils/__pycache__/_log.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/utils/__pycache__/appdirs.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/utils/__pycache__/compat.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/utils/__pycache__/compatibility_tags.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/utils/__pycache__/datetime.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/utils/__pycache__/deprecation.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/utils/__pycache__/direct_url_helpers.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/utils/__pycache__/egg_link.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/utils/__pycache__/encoding.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/utils/__pycache__/entrypoints.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/utils/__pycache__/filesystem.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/utils/__pycache__/filetypes.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/utils/__pycache__/glibc.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/utils/__pycache__/hashes.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/utils/__pycache__/logging.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/utils/__pycache__/misc.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/utils/__pycache__/models.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/utils/__pycache__/packaging.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/utils/__pycache__/setuptools_build.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/utils/__pycache__/subprocess.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/utils/__pycache__/temp_dir.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/utils/__pycache__/unpacking.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/utils/__pycache__/urls.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/utils/__pycache__/virtualenv.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/utils/__pycache__/wheel.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/utils/_jaraco_text.py create mode 100644 venv/Lib/site-packages/pip/_internal/utils/_log.py create mode 100644 venv/Lib/site-packages/pip/_internal/utils/appdirs.py create mode 100644 venv/Lib/site-packages/pip/_internal/utils/compat.py create mode 100644 venv/Lib/site-packages/pip/_internal/utils/compatibility_tags.py create mode 100644 venv/Lib/site-packages/pip/_internal/utils/datetime.py create mode 100644 venv/Lib/site-packages/pip/_internal/utils/deprecation.py create mode 100644 venv/Lib/site-packages/pip/_internal/utils/direct_url_helpers.py create mode 100644 venv/Lib/site-packages/pip/_internal/utils/egg_link.py create mode 100644 venv/Lib/site-packages/pip/_internal/utils/encoding.py create mode 100644 venv/Lib/site-packages/pip/_internal/utils/entrypoints.py create mode 100644 venv/Lib/site-packages/pip/_internal/utils/filesystem.py create mode 100644 venv/Lib/site-packages/pip/_internal/utils/filetypes.py create mode 100644 venv/Lib/site-packages/pip/_internal/utils/glibc.py create mode 100644 venv/Lib/site-packages/pip/_internal/utils/hashes.py create mode 100644 venv/Lib/site-packages/pip/_internal/utils/logging.py create mode 100644 venv/Lib/site-packages/pip/_internal/utils/misc.py create mode 100644 venv/Lib/site-packages/pip/_internal/utils/models.py create mode 100644 venv/Lib/site-packages/pip/_internal/utils/packaging.py create mode 100644 venv/Lib/site-packages/pip/_internal/utils/setuptools_build.py create mode 100644 venv/Lib/site-packages/pip/_internal/utils/subprocess.py create mode 100644 venv/Lib/site-packages/pip/_internal/utils/temp_dir.py create mode 100644 venv/Lib/site-packages/pip/_internal/utils/unpacking.py create mode 100644 venv/Lib/site-packages/pip/_internal/utils/urls.py create mode 100644 venv/Lib/site-packages/pip/_internal/utils/virtualenv.py create mode 100644 venv/Lib/site-packages/pip/_internal/utils/wheel.py create mode 100644 venv/Lib/site-packages/pip/_internal/vcs/__init__.py create mode 100644 venv/Lib/site-packages/pip/_internal/vcs/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/vcs/__pycache__/bazaar.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/vcs/__pycache__/git.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/vcs/__pycache__/mercurial.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/vcs/__pycache__/subversion.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/vcs/__pycache__/versioncontrol.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/vcs/bazaar.py create mode 100644 venv/Lib/site-packages/pip/_internal/vcs/git.py create mode 100644 venv/Lib/site-packages/pip/_internal/vcs/mercurial.py create mode 100644 venv/Lib/site-packages/pip/_internal/vcs/subversion.py create mode 100644 venv/Lib/site-packages/pip/_internal/vcs/versioncontrol.py create mode 100644 venv/Lib/site-packages/pip/_internal/wheel_builder.py create mode 100644 venv/Lib/site-packages/pip/_vendor/__init__.py create mode 100644 venv/Lib/site-packages/pip/_vendor/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/__pycache__/six.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/__pycache__/typing_extensions.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/cachecontrol/__init__.py create mode 100644 venv/Lib/site-packages/pip/_vendor/cachecontrol/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/cachecontrol/__pycache__/_cmd.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/cachecontrol/__pycache__/adapter.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/cachecontrol/__pycache__/cache.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/cachecontrol/__pycache__/controller.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/cachecontrol/__pycache__/filewrapper.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/cachecontrol/__pycache__/heuristics.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/cachecontrol/__pycache__/serialize.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/cachecontrol/__pycache__/wrapper.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/cachecontrol/_cmd.py create mode 100644 venv/Lib/site-packages/pip/_vendor/cachecontrol/adapter.py create mode 100644 venv/Lib/site-packages/pip/_vendor/cachecontrol/cache.py create mode 100644 venv/Lib/site-packages/pip/_vendor/cachecontrol/caches/__init__.py create mode 100644 venv/Lib/site-packages/pip/_vendor/cachecontrol/caches/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/cachecontrol/caches/__pycache__/file_cache.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/cachecontrol/caches/__pycache__/redis_cache.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/cachecontrol/caches/file_cache.py create mode 100644 venv/Lib/site-packages/pip/_vendor/cachecontrol/caches/redis_cache.py create mode 100644 venv/Lib/site-packages/pip/_vendor/cachecontrol/controller.py create mode 100644 venv/Lib/site-packages/pip/_vendor/cachecontrol/filewrapper.py create mode 100644 venv/Lib/site-packages/pip/_vendor/cachecontrol/heuristics.py create mode 100644 venv/Lib/site-packages/pip/_vendor/cachecontrol/py.typed create mode 100644 venv/Lib/site-packages/pip/_vendor/cachecontrol/serialize.py create mode 100644 venv/Lib/site-packages/pip/_vendor/cachecontrol/wrapper.py create mode 100644 venv/Lib/site-packages/pip/_vendor/certifi/__init__.py create mode 100644 venv/Lib/site-packages/pip/_vendor/certifi/__main__.py create mode 100644 venv/Lib/site-packages/pip/_vendor/certifi/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/certifi/__pycache__/__main__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/certifi/__pycache__/core.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/certifi/cacert.pem create mode 100644 venv/Lib/site-packages/pip/_vendor/certifi/core.py create mode 100644 venv/Lib/site-packages/pip/_vendor/certifi/py.typed create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/__init__.py create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/__pycache__/big5freq.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/__pycache__/big5prober.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/__pycache__/chardistribution.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/__pycache__/charsetgroupprober.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/__pycache__/charsetprober.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/__pycache__/codingstatemachine.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/__pycache__/codingstatemachinedict.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/__pycache__/cp949prober.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/__pycache__/enums.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/__pycache__/escprober.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/__pycache__/escsm.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/__pycache__/eucjpprober.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/__pycache__/euckrfreq.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/__pycache__/euckrprober.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/__pycache__/euctwfreq.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/__pycache__/euctwprober.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/__pycache__/gb2312freq.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/__pycache__/gb2312prober.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/__pycache__/hebrewprober.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/__pycache__/jisfreq.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/__pycache__/johabfreq.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/__pycache__/johabprober.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/__pycache__/jpcntx.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/__pycache__/langbulgarianmodel.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/__pycache__/langgreekmodel.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/__pycache__/langhebrewmodel.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/__pycache__/langhungarianmodel.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/__pycache__/langrussianmodel.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/__pycache__/langthaimodel.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/__pycache__/langturkishmodel.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/__pycache__/latin1prober.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/__pycache__/macromanprober.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/__pycache__/mbcharsetprober.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/__pycache__/mbcsgroupprober.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/__pycache__/mbcssm.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/__pycache__/resultdict.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/__pycache__/sbcharsetprober.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/__pycache__/sbcsgroupprober.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/__pycache__/sjisprober.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/__pycache__/universaldetector.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/__pycache__/utf1632prober.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/__pycache__/utf8prober.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/__pycache__/version.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/big5freq.py create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/big5prober.py create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/chardistribution.py create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/charsetgroupprober.py create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/charsetprober.py create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/cli/__init__.py create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/cli/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/cli/__pycache__/chardetect.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/cli/chardetect.py create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/codingstatemachine.py create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/codingstatemachinedict.py create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/cp949prober.py create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/enums.py create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/escprober.py create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/escsm.py create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/eucjpprober.py create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/euckrfreq.py create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/euckrprober.py create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/euctwfreq.py create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/euctwprober.py create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/gb2312freq.py create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/gb2312prober.py create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/hebrewprober.py create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/jisfreq.py create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/johabfreq.py create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/johabprober.py create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/jpcntx.py create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/langbulgarianmodel.py create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/langgreekmodel.py create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/langhebrewmodel.py create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/langhungarianmodel.py create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/langrussianmodel.py create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/langthaimodel.py create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/langturkishmodel.py create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/latin1prober.py create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/macromanprober.py create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/mbcharsetprober.py create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/mbcsgroupprober.py create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/mbcssm.py create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/metadata/__init__.py create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/metadata/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/metadata/__pycache__/languages.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/metadata/languages.py create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/py.typed create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/resultdict.py create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/sbcharsetprober.py create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/sbcsgroupprober.py create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/sjisprober.py create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/universaldetector.py create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/utf1632prober.py create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/utf8prober.py create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/version.py create mode 100644 venv/Lib/site-packages/pip/_vendor/colorama/__init__.py create mode 100644 venv/Lib/site-packages/pip/_vendor/colorama/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/colorama/__pycache__/ansi.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/colorama/__pycache__/ansitowin32.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/colorama/__pycache__/initialise.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/colorama/__pycache__/win32.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/colorama/__pycache__/winterm.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/colorama/ansi.py create mode 100644 venv/Lib/site-packages/pip/_vendor/colorama/ansitowin32.py create mode 100644 venv/Lib/site-packages/pip/_vendor/colorama/initialise.py create mode 100644 venv/Lib/site-packages/pip/_vendor/colorama/tests/__init__.py create mode 100644 venv/Lib/site-packages/pip/_vendor/colorama/tests/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/colorama/tests/__pycache__/ansi_test.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/colorama/tests/__pycache__/ansitowin32_test.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/colorama/tests/__pycache__/initialise_test.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/colorama/tests/__pycache__/isatty_test.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/colorama/tests/__pycache__/utils.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/colorama/tests/__pycache__/winterm_test.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/colorama/tests/ansi_test.py create mode 100644 venv/Lib/site-packages/pip/_vendor/colorama/tests/ansitowin32_test.py create mode 100644 venv/Lib/site-packages/pip/_vendor/colorama/tests/initialise_test.py create mode 100644 venv/Lib/site-packages/pip/_vendor/colorama/tests/isatty_test.py create mode 100644 venv/Lib/site-packages/pip/_vendor/colorama/tests/utils.py create mode 100644 venv/Lib/site-packages/pip/_vendor/colorama/tests/winterm_test.py create mode 100644 venv/Lib/site-packages/pip/_vendor/colorama/win32.py create mode 100644 venv/Lib/site-packages/pip/_vendor/colorama/winterm.py create mode 100644 venv/Lib/site-packages/pip/_vendor/distlib/__init__.py create mode 100644 venv/Lib/site-packages/pip/_vendor/distlib/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/distlib/__pycache__/compat.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/distlib/__pycache__/database.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/distlib/__pycache__/index.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/distlib/__pycache__/locators.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/distlib/__pycache__/manifest.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/distlib/__pycache__/markers.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/distlib/__pycache__/metadata.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/distlib/__pycache__/resources.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/distlib/__pycache__/scripts.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/distlib/__pycache__/util.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/distlib/__pycache__/version.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/distlib/__pycache__/wheel.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/distlib/compat.py create mode 100644 venv/Lib/site-packages/pip/_vendor/distlib/database.py create mode 100644 venv/Lib/site-packages/pip/_vendor/distlib/index.py create mode 100644 venv/Lib/site-packages/pip/_vendor/distlib/locators.py create mode 100644 venv/Lib/site-packages/pip/_vendor/distlib/manifest.py create mode 100644 venv/Lib/site-packages/pip/_vendor/distlib/markers.py create mode 100644 venv/Lib/site-packages/pip/_vendor/distlib/metadata.py create mode 100644 venv/Lib/site-packages/pip/_vendor/distlib/resources.py create mode 100644 venv/Lib/site-packages/pip/_vendor/distlib/scripts.py create mode 100644 venv/Lib/site-packages/pip/_vendor/distlib/t32.exe create mode 100644 venv/Lib/site-packages/pip/_vendor/distlib/t64-arm.exe create mode 100644 venv/Lib/site-packages/pip/_vendor/distlib/t64.exe create mode 100644 venv/Lib/site-packages/pip/_vendor/distlib/util.py create mode 100644 venv/Lib/site-packages/pip/_vendor/distlib/version.py create mode 100644 venv/Lib/site-packages/pip/_vendor/distlib/w32.exe create mode 100644 venv/Lib/site-packages/pip/_vendor/distlib/w64-arm.exe create mode 100644 venv/Lib/site-packages/pip/_vendor/distlib/w64.exe create mode 100644 venv/Lib/site-packages/pip/_vendor/distlib/wheel.py create mode 100644 venv/Lib/site-packages/pip/_vendor/distro/__init__.py create mode 100644 venv/Lib/site-packages/pip/_vendor/distro/__main__.py create mode 100644 venv/Lib/site-packages/pip/_vendor/distro/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/distro/__pycache__/__main__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/distro/__pycache__/distro.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/distro/distro.py create mode 100644 venv/Lib/site-packages/pip/_vendor/distro/py.typed create mode 100644 venv/Lib/site-packages/pip/_vendor/idna/__init__.py create mode 100644 venv/Lib/site-packages/pip/_vendor/idna/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/idna/__pycache__/codec.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/idna/__pycache__/compat.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/idna/__pycache__/core.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/idna/__pycache__/idnadata.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/idna/__pycache__/intranges.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/idna/__pycache__/package_data.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/idna/__pycache__/uts46data.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/idna/codec.py create mode 100644 venv/Lib/site-packages/pip/_vendor/idna/compat.py create mode 100644 venv/Lib/site-packages/pip/_vendor/idna/core.py create mode 100644 venv/Lib/site-packages/pip/_vendor/idna/idnadata.py create mode 100644 venv/Lib/site-packages/pip/_vendor/idna/intranges.py create mode 100644 venv/Lib/site-packages/pip/_vendor/idna/package_data.py create mode 100644 venv/Lib/site-packages/pip/_vendor/idna/py.typed create mode 100644 venv/Lib/site-packages/pip/_vendor/idna/uts46data.py create mode 100644 venv/Lib/site-packages/pip/_vendor/msgpack/__init__.py create mode 100644 venv/Lib/site-packages/pip/_vendor/msgpack/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/msgpack/__pycache__/exceptions.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/msgpack/__pycache__/ext.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/msgpack/__pycache__/fallback.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/msgpack/exceptions.py create mode 100644 venv/Lib/site-packages/pip/_vendor/msgpack/ext.py create mode 100644 venv/Lib/site-packages/pip/_vendor/msgpack/fallback.py create mode 100644 venv/Lib/site-packages/pip/_vendor/packaging/__about__.py create mode 100644 venv/Lib/site-packages/pip/_vendor/packaging/__init__.py create mode 100644 venv/Lib/site-packages/pip/_vendor/packaging/__pycache__/__about__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/packaging/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/packaging/__pycache__/_manylinux.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/packaging/__pycache__/_musllinux.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/packaging/__pycache__/_structures.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/packaging/__pycache__/markers.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/packaging/__pycache__/requirements.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/packaging/__pycache__/specifiers.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/packaging/__pycache__/tags.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/packaging/__pycache__/utils.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/packaging/__pycache__/version.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/packaging/_manylinux.py create mode 100644 venv/Lib/site-packages/pip/_vendor/packaging/_musllinux.py create mode 100644 venv/Lib/site-packages/pip/_vendor/packaging/_structures.py create mode 100644 venv/Lib/site-packages/pip/_vendor/packaging/markers.py create mode 100644 venv/Lib/site-packages/pip/_vendor/packaging/py.typed create mode 100644 venv/Lib/site-packages/pip/_vendor/packaging/requirements.py create mode 100644 venv/Lib/site-packages/pip/_vendor/packaging/specifiers.py create mode 100644 venv/Lib/site-packages/pip/_vendor/packaging/tags.py create mode 100644 venv/Lib/site-packages/pip/_vendor/packaging/utils.py create mode 100644 venv/Lib/site-packages/pip/_vendor/packaging/version.py create mode 100644 venv/Lib/site-packages/pip/_vendor/pkg_resources/__init__.py create mode 100644 venv/Lib/site-packages/pip/_vendor/pkg_resources/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/platformdirs/__init__.py create mode 100644 venv/Lib/site-packages/pip/_vendor/platformdirs/__main__.py create mode 100644 venv/Lib/site-packages/pip/_vendor/platformdirs/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/platformdirs/__pycache__/__main__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/platformdirs/__pycache__/android.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/platformdirs/__pycache__/api.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/platformdirs/__pycache__/macos.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/platformdirs/__pycache__/unix.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/platformdirs/__pycache__/version.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/platformdirs/__pycache__/windows.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/platformdirs/android.py create mode 100644 venv/Lib/site-packages/pip/_vendor/platformdirs/api.py create mode 100644 venv/Lib/site-packages/pip/_vendor/platformdirs/macos.py create mode 100644 venv/Lib/site-packages/pip/_vendor/platformdirs/py.typed create mode 100644 venv/Lib/site-packages/pip/_vendor/platformdirs/unix.py create mode 100644 venv/Lib/site-packages/pip/_vendor/platformdirs/version.py create mode 100644 venv/Lib/site-packages/pip/_vendor/platformdirs/windows.py create mode 100644 venv/Lib/site-packages/pip/_vendor/pygments/__init__.py create mode 100644 venv/Lib/site-packages/pip/_vendor/pygments/__main__.py create mode 100644 venv/Lib/site-packages/pip/_vendor/pygments/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/pygments/__pycache__/__main__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/pygments/__pycache__/cmdline.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/pygments/__pycache__/console.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/pygments/__pycache__/filter.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/pygments/__pycache__/formatter.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/pygments/__pycache__/lexer.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/pygments/__pycache__/modeline.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/pygments/__pycache__/plugin.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/pygments/__pycache__/regexopt.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/pygments/__pycache__/scanner.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/pygments/__pycache__/sphinxext.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/pygments/__pycache__/style.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/pygments/__pycache__/token.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/pygments/__pycache__/unistring.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/pygments/__pycache__/util.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/pygments/cmdline.py create mode 100644 venv/Lib/site-packages/pip/_vendor/pygments/console.py create mode 100644 venv/Lib/site-packages/pip/_vendor/pygments/filter.py create mode 100644 venv/Lib/site-packages/pip/_vendor/pygments/filters/__init__.py create mode 100644 venv/Lib/site-packages/pip/_vendor/pygments/filters/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/pygments/formatter.py create mode 100644 venv/Lib/site-packages/pip/_vendor/pygments/formatters/__init__.py create mode 100644 venv/Lib/site-packages/pip/_vendor/pygments/formatters/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/pygments/formatters/__pycache__/_mapping.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/pygments/formatters/__pycache__/bbcode.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/pygments/formatters/__pycache__/groff.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/pygments/formatters/__pycache__/html.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/pygments/formatters/__pycache__/img.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/pygments/formatters/__pycache__/irc.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/pygments/formatters/__pycache__/latex.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/pygments/formatters/__pycache__/other.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/pygments/formatters/__pycache__/pangomarkup.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/pygments/formatters/__pycache__/rtf.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/pygments/formatters/__pycache__/svg.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/pygments/formatters/__pycache__/terminal.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/pygments/formatters/__pycache__/terminal256.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/pygments/formatters/_mapping.py create mode 100644 venv/Lib/site-packages/pip/_vendor/pygments/formatters/bbcode.py create mode 100644 venv/Lib/site-packages/pip/_vendor/pygments/formatters/groff.py create mode 100644 venv/Lib/site-packages/pip/_vendor/pygments/formatters/html.py create mode 100644 venv/Lib/site-packages/pip/_vendor/pygments/formatters/img.py create mode 100644 venv/Lib/site-packages/pip/_vendor/pygments/formatters/irc.py create mode 100644 venv/Lib/site-packages/pip/_vendor/pygments/formatters/latex.py create mode 100644 venv/Lib/site-packages/pip/_vendor/pygments/formatters/other.py create mode 100644 venv/Lib/site-packages/pip/_vendor/pygments/formatters/pangomarkup.py create mode 100644 venv/Lib/site-packages/pip/_vendor/pygments/formatters/rtf.py create mode 100644 venv/Lib/site-packages/pip/_vendor/pygments/formatters/svg.py create mode 100644 venv/Lib/site-packages/pip/_vendor/pygments/formatters/terminal.py create mode 100644 venv/Lib/site-packages/pip/_vendor/pygments/formatters/terminal256.py create mode 100644 venv/Lib/site-packages/pip/_vendor/pygments/lexer.py create mode 100644 venv/Lib/site-packages/pip/_vendor/pygments/lexers/__init__.py create mode 100644 venv/Lib/site-packages/pip/_vendor/pygments/lexers/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/pygments/lexers/__pycache__/_mapping.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/pygments/lexers/__pycache__/python.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/pygments/lexers/_mapping.py create mode 100644 venv/Lib/site-packages/pip/_vendor/pygments/lexers/python.py create mode 100644 venv/Lib/site-packages/pip/_vendor/pygments/modeline.py create mode 100644 venv/Lib/site-packages/pip/_vendor/pygments/plugin.py create mode 100644 venv/Lib/site-packages/pip/_vendor/pygments/regexopt.py create mode 100644 venv/Lib/site-packages/pip/_vendor/pygments/scanner.py create mode 100644 venv/Lib/site-packages/pip/_vendor/pygments/sphinxext.py create mode 100644 venv/Lib/site-packages/pip/_vendor/pygments/style.py create mode 100644 venv/Lib/site-packages/pip/_vendor/pygments/styles/__init__.py create mode 100644 venv/Lib/site-packages/pip/_vendor/pygments/styles/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/pygments/token.py create mode 100644 venv/Lib/site-packages/pip/_vendor/pygments/unistring.py create mode 100644 venv/Lib/site-packages/pip/_vendor/pygments/util.py create mode 100644 venv/Lib/site-packages/pip/_vendor/pyparsing/__init__.py create mode 100644 venv/Lib/site-packages/pip/_vendor/pyparsing/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/pyparsing/__pycache__/actions.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/pyparsing/__pycache__/common.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/pyparsing/__pycache__/core.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/pyparsing/__pycache__/exceptions.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/pyparsing/__pycache__/helpers.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/pyparsing/__pycache__/results.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/pyparsing/__pycache__/testing.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/pyparsing/__pycache__/unicode.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/pyparsing/__pycache__/util.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/pyparsing/actions.py create mode 100644 venv/Lib/site-packages/pip/_vendor/pyparsing/common.py create mode 100644 venv/Lib/site-packages/pip/_vendor/pyparsing/core.py create mode 100644 venv/Lib/site-packages/pip/_vendor/pyparsing/diagram/__init__.py create mode 100644 venv/Lib/site-packages/pip/_vendor/pyparsing/diagram/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/pyparsing/exceptions.py create mode 100644 venv/Lib/site-packages/pip/_vendor/pyparsing/helpers.py create mode 100644 venv/Lib/site-packages/pip/_vendor/pyparsing/py.typed create mode 100644 venv/Lib/site-packages/pip/_vendor/pyparsing/results.py create mode 100644 venv/Lib/site-packages/pip/_vendor/pyparsing/testing.py create mode 100644 venv/Lib/site-packages/pip/_vendor/pyparsing/unicode.py create mode 100644 venv/Lib/site-packages/pip/_vendor/pyparsing/util.py create mode 100644 venv/Lib/site-packages/pip/_vendor/pyproject_hooks/__init__.py create mode 100644 venv/Lib/site-packages/pip/_vendor/pyproject_hooks/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/pyproject_hooks/__pycache__/_compat.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/pyproject_hooks/__pycache__/_impl.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/pyproject_hooks/_compat.py create mode 100644 venv/Lib/site-packages/pip/_vendor/pyproject_hooks/_impl.py create mode 100644 venv/Lib/site-packages/pip/_vendor/pyproject_hooks/_in_process/__init__.py create mode 100644 venv/Lib/site-packages/pip/_vendor/pyproject_hooks/_in_process/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/pyproject_hooks/_in_process/__pycache__/_in_process.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/pyproject_hooks/_in_process/_in_process.py create mode 100644 venv/Lib/site-packages/pip/_vendor/requests/__init__.py create mode 100644 venv/Lib/site-packages/pip/_vendor/requests/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/requests/__pycache__/__version__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/requests/__pycache__/_internal_utils.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/requests/__pycache__/adapters.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/requests/__pycache__/api.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/requests/__pycache__/auth.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/requests/__pycache__/certs.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/requests/__pycache__/compat.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/requests/__pycache__/cookies.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/requests/__pycache__/exceptions.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/requests/__pycache__/help.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/requests/__pycache__/hooks.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/requests/__pycache__/models.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/requests/__pycache__/packages.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/requests/__pycache__/sessions.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/requests/__pycache__/status_codes.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/requests/__pycache__/structures.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/requests/__pycache__/utils.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/requests/__version__.py create mode 100644 venv/Lib/site-packages/pip/_vendor/requests/_internal_utils.py create mode 100644 venv/Lib/site-packages/pip/_vendor/requests/adapters.py create mode 100644 venv/Lib/site-packages/pip/_vendor/requests/api.py create mode 100644 venv/Lib/site-packages/pip/_vendor/requests/auth.py create mode 100644 venv/Lib/site-packages/pip/_vendor/requests/certs.py create mode 100644 venv/Lib/site-packages/pip/_vendor/requests/compat.py create mode 100644 venv/Lib/site-packages/pip/_vendor/requests/cookies.py create mode 100644 venv/Lib/site-packages/pip/_vendor/requests/exceptions.py create mode 100644 venv/Lib/site-packages/pip/_vendor/requests/help.py create mode 100644 venv/Lib/site-packages/pip/_vendor/requests/hooks.py create mode 100644 venv/Lib/site-packages/pip/_vendor/requests/models.py create mode 100644 venv/Lib/site-packages/pip/_vendor/requests/packages.py create mode 100644 venv/Lib/site-packages/pip/_vendor/requests/sessions.py create mode 100644 venv/Lib/site-packages/pip/_vendor/requests/status_codes.py create mode 100644 venv/Lib/site-packages/pip/_vendor/requests/structures.py create mode 100644 venv/Lib/site-packages/pip/_vendor/requests/utils.py create mode 100644 venv/Lib/site-packages/pip/_vendor/resolvelib/__init__.py create mode 100644 venv/Lib/site-packages/pip/_vendor/resolvelib/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/resolvelib/__pycache__/providers.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/resolvelib/__pycache__/reporters.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/resolvelib/__pycache__/resolvers.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/resolvelib/__pycache__/structs.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/resolvelib/compat/__init__.py create mode 100644 venv/Lib/site-packages/pip/_vendor/resolvelib/compat/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/resolvelib/compat/__pycache__/collections_abc.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/resolvelib/compat/collections_abc.py create mode 100644 venv/Lib/site-packages/pip/_vendor/resolvelib/providers.py create mode 100644 venv/Lib/site-packages/pip/_vendor/resolvelib/py.typed create mode 100644 venv/Lib/site-packages/pip/_vendor/resolvelib/reporters.py create mode 100644 venv/Lib/site-packages/pip/_vendor/resolvelib/resolvers.py create mode 100644 venv/Lib/site-packages/pip/_vendor/resolvelib/structs.py create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/__init__.py create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/__main__.py create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/__pycache__/__main__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/__pycache__/_cell_widths.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/__pycache__/_emoji_codes.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/__pycache__/_emoji_replace.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/__pycache__/_export_format.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/__pycache__/_extension.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/__pycache__/_fileno.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/__pycache__/_inspect.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/__pycache__/_log_render.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/__pycache__/_loop.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/__pycache__/_null_file.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/__pycache__/_palettes.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/__pycache__/_pick.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/__pycache__/_ratio.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/__pycache__/_spinners.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/__pycache__/_stack.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/__pycache__/_timer.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/__pycache__/_win32_console.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/__pycache__/_windows.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/__pycache__/_windows_renderer.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/__pycache__/_wrap.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/__pycache__/abc.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/__pycache__/align.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/__pycache__/ansi.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/__pycache__/bar.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/__pycache__/box.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/__pycache__/cells.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/__pycache__/color.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/__pycache__/color_triplet.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/__pycache__/columns.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/__pycache__/console.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/__pycache__/constrain.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/__pycache__/containers.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/__pycache__/control.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/__pycache__/default_styles.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/__pycache__/diagnose.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/__pycache__/emoji.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/__pycache__/errors.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/__pycache__/file_proxy.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/__pycache__/filesize.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/__pycache__/highlighter.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/__pycache__/json.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/__pycache__/jupyter.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/__pycache__/layout.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/__pycache__/live.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/__pycache__/live_render.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/__pycache__/logging.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/__pycache__/markup.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/__pycache__/measure.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/__pycache__/padding.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/__pycache__/pager.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/__pycache__/palette.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/__pycache__/panel.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/__pycache__/pretty.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/__pycache__/progress.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/__pycache__/progress_bar.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/__pycache__/prompt.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/__pycache__/protocol.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/__pycache__/region.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/__pycache__/repr.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/__pycache__/rule.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/__pycache__/scope.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/__pycache__/screen.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/__pycache__/segment.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/__pycache__/spinner.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/__pycache__/status.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/__pycache__/style.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/__pycache__/styled.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/__pycache__/syntax.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/__pycache__/table.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/__pycache__/terminal_theme.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/__pycache__/text.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/__pycache__/theme.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/__pycache__/themes.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/__pycache__/traceback.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/__pycache__/tree.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/_cell_widths.py create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/_emoji_codes.py create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/_emoji_replace.py create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/_export_format.py create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/_extension.py create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/_fileno.py create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/_inspect.py create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/_log_render.py create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/_loop.py create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/_null_file.py create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/_palettes.py create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/_pick.py create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/_ratio.py create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/_spinners.py create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/_stack.py create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/_timer.py create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/_win32_console.py create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/_windows.py create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/_windows_renderer.py create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/_wrap.py create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/abc.py create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/align.py create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/ansi.py create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/bar.py create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/box.py create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/cells.py create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/color.py create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/color_triplet.py create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/columns.py create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/console.py create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/constrain.py create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/containers.py create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/control.py create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/default_styles.py create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/diagnose.py create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/emoji.py create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/errors.py create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/file_proxy.py create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/filesize.py create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/highlighter.py create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/json.py create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/jupyter.py create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/layout.py create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/live.py create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/live_render.py create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/logging.py create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/markup.py create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/measure.py create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/padding.py create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/pager.py create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/palette.py create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/panel.py create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/pretty.py create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/progress.py create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/progress_bar.py create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/prompt.py create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/protocol.py create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/py.typed create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/region.py create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/repr.py create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/rule.py create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/scope.py create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/screen.py create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/segment.py create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/spinner.py create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/status.py create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/style.py create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/styled.py create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/syntax.py create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/table.py create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/terminal_theme.py create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/text.py create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/theme.py create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/themes.py create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/traceback.py create mode 100644 venv/Lib/site-packages/pip/_vendor/rich/tree.py create mode 100644 venv/Lib/site-packages/pip/_vendor/six.py create mode 100644 venv/Lib/site-packages/pip/_vendor/tenacity/__init__.py create mode 100644 venv/Lib/site-packages/pip/_vendor/tenacity/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/tenacity/__pycache__/_asyncio.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/tenacity/__pycache__/_utils.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/tenacity/__pycache__/after.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/tenacity/__pycache__/before.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/tenacity/__pycache__/before_sleep.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/tenacity/__pycache__/nap.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/tenacity/__pycache__/retry.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/tenacity/__pycache__/stop.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/tenacity/__pycache__/tornadoweb.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/tenacity/__pycache__/wait.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/tenacity/_asyncio.py create mode 100644 venv/Lib/site-packages/pip/_vendor/tenacity/_utils.py create mode 100644 venv/Lib/site-packages/pip/_vendor/tenacity/after.py create mode 100644 venv/Lib/site-packages/pip/_vendor/tenacity/before.py create mode 100644 venv/Lib/site-packages/pip/_vendor/tenacity/before_sleep.py create mode 100644 venv/Lib/site-packages/pip/_vendor/tenacity/nap.py create mode 100644 venv/Lib/site-packages/pip/_vendor/tenacity/py.typed create mode 100644 venv/Lib/site-packages/pip/_vendor/tenacity/retry.py create mode 100644 venv/Lib/site-packages/pip/_vendor/tenacity/stop.py create mode 100644 venv/Lib/site-packages/pip/_vendor/tenacity/tornadoweb.py create mode 100644 venv/Lib/site-packages/pip/_vendor/tenacity/wait.py create mode 100644 venv/Lib/site-packages/pip/_vendor/tomli/__init__.py create mode 100644 venv/Lib/site-packages/pip/_vendor/tomli/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/tomli/__pycache__/_parser.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/tomli/__pycache__/_re.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/tomli/__pycache__/_types.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/tomli/_parser.py create mode 100644 venv/Lib/site-packages/pip/_vendor/tomli/_re.py create mode 100644 venv/Lib/site-packages/pip/_vendor/tomli/_types.py create mode 100644 venv/Lib/site-packages/pip/_vendor/tomli/py.typed create mode 100644 venv/Lib/site-packages/pip/_vendor/truststore/__init__.py create mode 100644 venv/Lib/site-packages/pip/_vendor/truststore/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/truststore/__pycache__/_api.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/truststore/__pycache__/_macos.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/truststore/__pycache__/_openssl.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/truststore/__pycache__/_ssl_constants.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/truststore/__pycache__/_windows.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/truststore/_api.py create mode 100644 venv/Lib/site-packages/pip/_vendor/truststore/_macos.py create mode 100644 venv/Lib/site-packages/pip/_vendor/truststore/_openssl.py create mode 100644 venv/Lib/site-packages/pip/_vendor/truststore/_ssl_constants.py create mode 100644 venv/Lib/site-packages/pip/_vendor/truststore/_windows.py create mode 100644 venv/Lib/site-packages/pip/_vendor/truststore/py.typed create mode 100644 venv/Lib/site-packages/pip/_vendor/typing_extensions.py create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/__init__.py create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/__pycache__/_collections.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/__pycache__/_version.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/__pycache__/connection.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/__pycache__/connectionpool.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/__pycache__/exceptions.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/__pycache__/fields.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/__pycache__/filepost.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/__pycache__/poolmanager.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/__pycache__/request.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/__pycache__/response.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/_collections.py create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/_version.py create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/connection.py create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/connectionpool.py create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/contrib/__init__.py create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/contrib/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/contrib/__pycache__/_appengine_environ.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/contrib/__pycache__/appengine.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/contrib/__pycache__/ntlmpool.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/contrib/__pycache__/pyopenssl.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/contrib/__pycache__/securetransport.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/contrib/__pycache__/socks.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/contrib/_appengine_environ.py create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/contrib/_securetransport/__init__.py create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/contrib/_securetransport/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/contrib/_securetransport/__pycache__/bindings.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/contrib/_securetransport/__pycache__/low_level.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/contrib/_securetransport/bindings.py create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/contrib/_securetransport/low_level.py create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/contrib/appengine.py create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/contrib/ntlmpool.py create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/contrib/pyopenssl.py create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/contrib/securetransport.py create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/contrib/socks.py create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/exceptions.py create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/fields.py create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/filepost.py create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/packages/__init__.py create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/packages/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/packages/__pycache__/six.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/packages/backports/__init__.py create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/packages/backports/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/packages/backports/__pycache__/makefile.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/packages/backports/__pycache__/weakref_finalize.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/packages/backports/makefile.py create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/packages/backports/weakref_finalize.py create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/packages/six.py create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/poolmanager.py create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/request.py create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/response.py create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/util/__init__.py create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/util/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/util/__pycache__/connection.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/util/__pycache__/proxy.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/util/__pycache__/queue.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/util/__pycache__/request.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/util/__pycache__/response.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/util/__pycache__/retry.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/util/__pycache__/ssl_.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/util/__pycache__/ssl_match_hostname.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/util/__pycache__/ssltransport.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/util/__pycache__/timeout.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/util/__pycache__/url.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/util/__pycache__/wait.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/util/connection.py create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/util/proxy.py create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/util/queue.py create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/util/request.py create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/util/response.py create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/util/retry.py create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/util/ssl_.py create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/util/ssl_match_hostname.py create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/util/ssltransport.py create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/util/timeout.py create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/util/url.py create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/util/wait.py create mode 100644 venv/Lib/site-packages/pip/_vendor/vendor.txt create mode 100644 venv/Lib/site-packages/pip/_vendor/webencodings/__init__.py create mode 100644 venv/Lib/site-packages/pip/_vendor/webencodings/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/webencodings/__pycache__/labels.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/webencodings/__pycache__/mklabels.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/webencodings/__pycache__/tests.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/webencodings/__pycache__/x_user_defined.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/webencodings/labels.py create mode 100644 venv/Lib/site-packages/pip/_vendor/webencodings/mklabels.py create mode 100644 venv/Lib/site-packages/pip/_vendor/webencodings/tests.py create mode 100644 venv/Lib/site-packages/pip/_vendor/webencodings/x_user_defined.py create mode 100644 venv/Lib/site-packages/pip/py.typed create mode 100644 venv/Lib/site-packages/pluggy-1.6.0.dist-info/INSTALLER create mode 100644 venv/Lib/site-packages/pluggy-1.6.0.dist-info/METADATA create mode 100644 venv/Lib/site-packages/pluggy-1.6.0.dist-info/RECORD create mode 100644 venv/Lib/site-packages/pluggy-1.6.0.dist-info/WHEEL create mode 100644 venv/Lib/site-packages/pluggy-1.6.0.dist-info/licenses/LICENSE create mode 100644 venv/Lib/site-packages/pluggy-1.6.0.dist-info/top_level.txt create mode 100644 venv/Lib/site-packages/pluggy/__init__.py create mode 100644 venv/Lib/site-packages/pluggy/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pluggy/__pycache__/_callers.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pluggy/__pycache__/_hooks.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pluggy/__pycache__/_manager.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pluggy/__pycache__/_result.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pluggy/__pycache__/_tracing.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pluggy/__pycache__/_version.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pluggy/__pycache__/_warnings.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pluggy/_callers.py create mode 100644 venv/Lib/site-packages/pluggy/_hooks.py create mode 100644 venv/Lib/site-packages/pluggy/_manager.py create mode 100644 venv/Lib/site-packages/pluggy/_result.py create mode 100644 venv/Lib/site-packages/pluggy/_tracing.py create mode 100644 venv/Lib/site-packages/pluggy/_version.py create mode 100644 venv/Lib/site-packages/pluggy/_warnings.py create mode 100644 venv/Lib/site-packages/pluggy/py.typed create mode 100644 venv/Lib/site-packages/py.py create mode 100644 venv/Lib/site-packages/pygments-2.19.2.dist-info/INSTALLER create mode 100644 venv/Lib/site-packages/pygments-2.19.2.dist-info/METADATA create mode 100644 venv/Lib/site-packages/pygments-2.19.2.dist-info/RECORD create mode 100644 venv/Lib/site-packages/pygments-2.19.2.dist-info/WHEEL create mode 100644 venv/Lib/site-packages/pygments-2.19.2.dist-info/entry_points.txt create mode 100644 venv/Lib/site-packages/pygments-2.19.2.dist-info/licenses/AUTHORS create mode 100644 venv/Lib/site-packages/pygments-2.19.2.dist-info/licenses/LICENSE create mode 100644 venv/Lib/site-packages/pygments/__init__.py create mode 100644 venv/Lib/site-packages/pygments/__main__.py create mode 100644 venv/Lib/site-packages/pygments/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/__pycache__/__main__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/__pycache__/cmdline.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/__pycache__/console.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/__pycache__/filter.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/__pycache__/formatter.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/__pycache__/lexer.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/__pycache__/modeline.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/__pycache__/plugin.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/__pycache__/regexopt.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/__pycache__/scanner.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/__pycache__/sphinxext.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/__pycache__/style.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/__pycache__/token.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/__pycache__/unistring.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/__pycache__/util.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/cmdline.py create mode 100644 venv/Lib/site-packages/pygments/console.py create mode 100644 venv/Lib/site-packages/pygments/filter.py create mode 100644 venv/Lib/site-packages/pygments/filters/__init__.py create mode 100644 venv/Lib/site-packages/pygments/filters/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/formatter.py create mode 100644 venv/Lib/site-packages/pygments/formatters/__init__.py create mode 100644 venv/Lib/site-packages/pygments/formatters/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/formatters/__pycache__/_mapping.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/formatters/__pycache__/bbcode.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/formatters/__pycache__/groff.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/formatters/__pycache__/html.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/formatters/__pycache__/img.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/formatters/__pycache__/irc.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/formatters/__pycache__/latex.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/formatters/__pycache__/other.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/formatters/__pycache__/pangomarkup.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/formatters/__pycache__/rtf.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/formatters/__pycache__/svg.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/formatters/__pycache__/terminal.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/formatters/__pycache__/terminal256.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/formatters/_mapping.py create mode 100644 venv/Lib/site-packages/pygments/formatters/bbcode.py create mode 100644 venv/Lib/site-packages/pygments/formatters/groff.py create mode 100644 venv/Lib/site-packages/pygments/formatters/html.py create mode 100644 venv/Lib/site-packages/pygments/formatters/img.py create mode 100644 venv/Lib/site-packages/pygments/formatters/irc.py create mode 100644 venv/Lib/site-packages/pygments/formatters/latex.py create mode 100644 venv/Lib/site-packages/pygments/formatters/other.py create mode 100644 venv/Lib/site-packages/pygments/formatters/pangomarkup.py create mode 100644 venv/Lib/site-packages/pygments/formatters/rtf.py create mode 100644 venv/Lib/site-packages/pygments/formatters/svg.py create mode 100644 venv/Lib/site-packages/pygments/formatters/terminal.py create mode 100644 venv/Lib/site-packages/pygments/formatters/terminal256.py create mode 100644 venv/Lib/site-packages/pygments/lexer.py create mode 100644 venv/Lib/site-packages/pygments/lexers/__init__.py create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/_ada_builtins.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/_asy_builtins.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/_cl_builtins.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/_cocoa_builtins.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/_csound_builtins.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/_css_builtins.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/_googlesql_builtins.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/_julia_builtins.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/_lasso_builtins.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/_lilypond_builtins.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/_lua_builtins.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/_luau_builtins.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/_mapping.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/_mql_builtins.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/_mysql_builtins.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/_openedge_builtins.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/_php_builtins.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/_postgres_builtins.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/_qlik_builtins.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/_scheme_builtins.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/_scilab_builtins.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/_sourcemod_builtins.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/_sql_builtins.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/_stan_builtins.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/_stata_builtins.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/_tsql_builtins.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/_usd_builtins.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/_vbscript_builtins.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/_vim_builtins.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/actionscript.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/ada.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/agile.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/algebra.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/ambient.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/amdgpu.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/ampl.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/apdlexer.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/apl.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/archetype.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/arrow.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/arturo.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/asc.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/asm.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/asn1.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/automation.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/bare.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/basic.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/bdd.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/berry.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/bibtex.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/blueprint.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/boa.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/bqn.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/business.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/c_cpp.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/c_like.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/capnproto.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/carbon.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/cddl.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/chapel.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/clean.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/codeql.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/comal.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/compiled.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/configs.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/console.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/cplint.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/crystal.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/csound.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/css.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/d.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/dalvik.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/data.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/dax.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/devicetree.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/diff.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/dns.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/dotnet.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/dsls.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/dylan.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/ecl.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/eiffel.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/elm.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/elpi.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/email.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/erlang.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/esoteric.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/ezhil.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/factor.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/fantom.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/felix.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/fift.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/floscript.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/forth.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/fortran.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/foxpro.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/freefem.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/func.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/functional.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/futhark.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/gcodelexer.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/gdscript.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/gleam.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/go.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/grammar_notation.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/graph.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/graphics.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/graphql.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/graphviz.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/gsql.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/hare.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/haskell.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/haxe.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/hdl.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/hexdump.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/html.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/idl.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/igor.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/inferno.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/installers.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/int_fiction.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/iolang.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/j.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/javascript.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/jmespath.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/jslt.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/json5.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/jsonnet.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/jsx.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/julia.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/jvm.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/kuin.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/kusto.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/ldap.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/lean.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/lilypond.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/lisp.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/macaulay2.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/make.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/maple.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/markup.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/math.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/matlab.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/maxima.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/meson.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/mime.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/minecraft.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/mips.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/ml.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/modeling.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/modula2.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/mojo.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/monte.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/mosel.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/ncl.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/nimrod.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/nit.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/nix.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/numbair.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/oberon.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/objective.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/ooc.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/openscad.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/other.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/parasail.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/parsers.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/pascal.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/pawn.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/pddl.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/perl.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/phix.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/php.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/pointless.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/pony.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/praat.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/procfile.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/prolog.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/promql.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/prql.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/ptx.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/python.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/q.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/qlik.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/qvt.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/r.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/rdf.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/rebol.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/rego.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/resource.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/ride.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/rita.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/rnc.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/roboconf.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/robotframework.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/ruby.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/rust.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/sas.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/savi.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/scdoc.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/scripting.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/sgf.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/shell.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/sieve.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/slash.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/smalltalk.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/smithy.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/smv.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/snobol.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/solidity.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/soong.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/sophia.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/special.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/spice.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/sql.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/srcinfo.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/stata.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/supercollider.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/tablegen.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/tact.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/tal.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/tcl.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/teal.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/templates.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/teraterm.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/testing.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/text.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/textedit.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/textfmts.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/theorem.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/thingsdb.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/tlb.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/tls.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/tnt.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/trafficscript.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/typoscript.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/typst.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/ul4.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/unicon.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/urbi.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/usd.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/varnish.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/verification.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/verifpal.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/vip.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/vyper.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/web.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/webassembly.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/webidl.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/webmisc.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/wgsl.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/whiley.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/wowtoc.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/wren.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/x10.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/xorg.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/yang.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/yara.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/__pycache__/zig.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/lexers/_ada_builtins.py create mode 100644 venv/Lib/site-packages/pygments/lexers/_asy_builtins.py create mode 100644 venv/Lib/site-packages/pygments/lexers/_cl_builtins.py create mode 100644 venv/Lib/site-packages/pygments/lexers/_cocoa_builtins.py create mode 100644 venv/Lib/site-packages/pygments/lexers/_csound_builtins.py create mode 100644 venv/Lib/site-packages/pygments/lexers/_css_builtins.py create mode 100644 venv/Lib/site-packages/pygments/lexers/_googlesql_builtins.py create mode 100644 venv/Lib/site-packages/pygments/lexers/_julia_builtins.py create mode 100644 venv/Lib/site-packages/pygments/lexers/_lasso_builtins.py create mode 100644 venv/Lib/site-packages/pygments/lexers/_lilypond_builtins.py create mode 100644 venv/Lib/site-packages/pygments/lexers/_lua_builtins.py create mode 100644 venv/Lib/site-packages/pygments/lexers/_luau_builtins.py create mode 100644 venv/Lib/site-packages/pygments/lexers/_mapping.py create mode 100644 venv/Lib/site-packages/pygments/lexers/_mql_builtins.py create mode 100644 venv/Lib/site-packages/pygments/lexers/_mysql_builtins.py create mode 100644 venv/Lib/site-packages/pygments/lexers/_openedge_builtins.py create mode 100644 venv/Lib/site-packages/pygments/lexers/_php_builtins.py create mode 100644 venv/Lib/site-packages/pygments/lexers/_postgres_builtins.py create mode 100644 venv/Lib/site-packages/pygments/lexers/_qlik_builtins.py create mode 100644 venv/Lib/site-packages/pygments/lexers/_scheme_builtins.py create mode 100644 venv/Lib/site-packages/pygments/lexers/_scilab_builtins.py create mode 100644 venv/Lib/site-packages/pygments/lexers/_sourcemod_builtins.py create mode 100644 venv/Lib/site-packages/pygments/lexers/_sql_builtins.py create mode 100644 venv/Lib/site-packages/pygments/lexers/_stan_builtins.py create mode 100644 venv/Lib/site-packages/pygments/lexers/_stata_builtins.py create mode 100644 venv/Lib/site-packages/pygments/lexers/_tsql_builtins.py create mode 100644 venv/Lib/site-packages/pygments/lexers/_usd_builtins.py create mode 100644 venv/Lib/site-packages/pygments/lexers/_vbscript_builtins.py create mode 100644 venv/Lib/site-packages/pygments/lexers/_vim_builtins.py create mode 100644 venv/Lib/site-packages/pygments/lexers/actionscript.py create mode 100644 venv/Lib/site-packages/pygments/lexers/ada.py create mode 100644 venv/Lib/site-packages/pygments/lexers/agile.py create mode 100644 venv/Lib/site-packages/pygments/lexers/algebra.py create mode 100644 venv/Lib/site-packages/pygments/lexers/ambient.py create mode 100644 venv/Lib/site-packages/pygments/lexers/amdgpu.py create mode 100644 venv/Lib/site-packages/pygments/lexers/ampl.py create mode 100644 venv/Lib/site-packages/pygments/lexers/apdlexer.py create mode 100644 venv/Lib/site-packages/pygments/lexers/apl.py create mode 100644 venv/Lib/site-packages/pygments/lexers/archetype.py create mode 100644 venv/Lib/site-packages/pygments/lexers/arrow.py create mode 100644 venv/Lib/site-packages/pygments/lexers/arturo.py create mode 100644 venv/Lib/site-packages/pygments/lexers/asc.py create mode 100644 venv/Lib/site-packages/pygments/lexers/asm.py create mode 100644 venv/Lib/site-packages/pygments/lexers/asn1.py create mode 100644 venv/Lib/site-packages/pygments/lexers/automation.py create mode 100644 venv/Lib/site-packages/pygments/lexers/bare.py create mode 100644 venv/Lib/site-packages/pygments/lexers/basic.py create mode 100644 venv/Lib/site-packages/pygments/lexers/bdd.py create mode 100644 venv/Lib/site-packages/pygments/lexers/berry.py create mode 100644 venv/Lib/site-packages/pygments/lexers/bibtex.py create mode 100644 venv/Lib/site-packages/pygments/lexers/blueprint.py create mode 100644 venv/Lib/site-packages/pygments/lexers/boa.py create mode 100644 venv/Lib/site-packages/pygments/lexers/bqn.py create mode 100644 venv/Lib/site-packages/pygments/lexers/business.py create mode 100644 venv/Lib/site-packages/pygments/lexers/c_cpp.py create mode 100644 venv/Lib/site-packages/pygments/lexers/c_like.py create mode 100644 venv/Lib/site-packages/pygments/lexers/capnproto.py create mode 100644 venv/Lib/site-packages/pygments/lexers/carbon.py create mode 100644 venv/Lib/site-packages/pygments/lexers/cddl.py create mode 100644 venv/Lib/site-packages/pygments/lexers/chapel.py create mode 100644 venv/Lib/site-packages/pygments/lexers/clean.py create mode 100644 venv/Lib/site-packages/pygments/lexers/codeql.py create mode 100644 venv/Lib/site-packages/pygments/lexers/comal.py create mode 100644 venv/Lib/site-packages/pygments/lexers/compiled.py create mode 100644 venv/Lib/site-packages/pygments/lexers/configs.py create mode 100644 venv/Lib/site-packages/pygments/lexers/console.py create mode 100644 venv/Lib/site-packages/pygments/lexers/cplint.py create mode 100644 venv/Lib/site-packages/pygments/lexers/crystal.py create mode 100644 venv/Lib/site-packages/pygments/lexers/csound.py create mode 100644 venv/Lib/site-packages/pygments/lexers/css.py create mode 100644 venv/Lib/site-packages/pygments/lexers/d.py create mode 100644 venv/Lib/site-packages/pygments/lexers/dalvik.py create mode 100644 venv/Lib/site-packages/pygments/lexers/data.py create mode 100644 venv/Lib/site-packages/pygments/lexers/dax.py create mode 100644 venv/Lib/site-packages/pygments/lexers/devicetree.py create mode 100644 venv/Lib/site-packages/pygments/lexers/diff.py create mode 100644 venv/Lib/site-packages/pygments/lexers/dns.py create mode 100644 venv/Lib/site-packages/pygments/lexers/dotnet.py create mode 100644 venv/Lib/site-packages/pygments/lexers/dsls.py create mode 100644 venv/Lib/site-packages/pygments/lexers/dylan.py create mode 100644 venv/Lib/site-packages/pygments/lexers/ecl.py create mode 100644 venv/Lib/site-packages/pygments/lexers/eiffel.py create mode 100644 venv/Lib/site-packages/pygments/lexers/elm.py create mode 100644 venv/Lib/site-packages/pygments/lexers/elpi.py create mode 100644 venv/Lib/site-packages/pygments/lexers/email.py create mode 100644 venv/Lib/site-packages/pygments/lexers/erlang.py create mode 100644 venv/Lib/site-packages/pygments/lexers/esoteric.py create mode 100644 venv/Lib/site-packages/pygments/lexers/ezhil.py create mode 100644 venv/Lib/site-packages/pygments/lexers/factor.py create mode 100644 venv/Lib/site-packages/pygments/lexers/fantom.py create mode 100644 venv/Lib/site-packages/pygments/lexers/felix.py create mode 100644 venv/Lib/site-packages/pygments/lexers/fift.py create mode 100644 venv/Lib/site-packages/pygments/lexers/floscript.py create mode 100644 venv/Lib/site-packages/pygments/lexers/forth.py create mode 100644 venv/Lib/site-packages/pygments/lexers/fortran.py create mode 100644 venv/Lib/site-packages/pygments/lexers/foxpro.py create mode 100644 venv/Lib/site-packages/pygments/lexers/freefem.py create mode 100644 venv/Lib/site-packages/pygments/lexers/func.py create mode 100644 venv/Lib/site-packages/pygments/lexers/functional.py create mode 100644 venv/Lib/site-packages/pygments/lexers/futhark.py create mode 100644 venv/Lib/site-packages/pygments/lexers/gcodelexer.py create mode 100644 venv/Lib/site-packages/pygments/lexers/gdscript.py create mode 100644 venv/Lib/site-packages/pygments/lexers/gleam.py create mode 100644 venv/Lib/site-packages/pygments/lexers/go.py create mode 100644 venv/Lib/site-packages/pygments/lexers/grammar_notation.py create mode 100644 venv/Lib/site-packages/pygments/lexers/graph.py create mode 100644 venv/Lib/site-packages/pygments/lexers/graphics.py create mode 100644 venv/Lib/site-packages/pygments/lexers/graphql.py create mode 100644 venv/Lib/site-packages/pygments/lexers/graphviz.py create mode 100644 venv/Lib/site-packages/pygments/lexers/gsql.py create mode 100644 venv/Lib/site-packages/pygments/lexers/hare.py create mode 100644 venv/Lib/site-packages/pygments/lexers/haskell.py create mode 100644 venv/Lib/site-packages/pygments/lexers/haxe.py create mode 100644 venv/Lib/site-packages/pygments/lexers/hdl.py create mode 100644 venv/Lib/site-packages/pygments/lexers/hexdump.py create mode 100644 venv/Lib/site-packages/pygments/lexers/html.py create mode 100644 venv/Lib/site-packages/pygments/lexers/idl.py create mode 100644 venv/Lib/site-packages/pygments/lexers/igor.py create mode 100644 venv/Lib/site-packages/pygments/lexers/inferno.py create mode 100644 venv/Lib/site-packages/pygments/lexers/installers.py create mode 100644 venv/Lib/site-packages/pygments/lexers/int_fiction.py create mode 100644 venv/Lib/site-packages/pygments/lexers/iolang.py create mode 100644 venv/Lib/site-packages/pygments/lexers/j.py create mode 100644 venv/Lib/site-packages/pygments/lexers/javascript.py create mode 100644 venv/Lib/site-packages/pygments/lexers/jmespath.py create mode 100644 venv/Lib/site-packages/pygments/lexers/jslt.py create mode 100644 venv/Lib/site-packages/pygments/lexers/json5.py create mode 100644 venv/Lib/site-packages/pygments/lexers/jsonnet.py create mode 100644 venv/Lib/site-packages/pygments/lexers/jsx.py create mode 100644 venv/Lib/site-packages/pygments/lexers/julia.py create mode 100644 venv/Lib/site-packages/pygments/lexers/jvm.py create mode 100644 venv/Lib/site-packages/pygments/lexers/kuin.py create mode 100644 venv/Lib/site-packages/pygments/lexers/kusto.py create mode 100644 venv/Lib/site-packages/pygments/lexers/ldap.py create mode 100644 venv/Lib/site-packages/pygments/lexers/lean.py create mode 100644 venv/Lib/site-packages/pygments/lexers/lilypond.py create mode 100644 venv/Lib/site-packages/pygments/lexers/lisp.py create mode 100644 venv/Lib/site-packages/pygments/lexers/macaulay2.py create mode 100644 venv/Lib/site-packages/pygments/lexers/make.py create mode 100644 venv/Lib/site-packages/pygments/lexers/maple.py create mode 100644 venv/Lib/site-packages/pygments/lexers/markup.py create mode 100644 venv/Lib/site-packages/pygments/lexers/math.py create mode 100644 venv/Lib/site-packages/pygments/lexers/matlab.py create mode 100644 venv/Lib/site-packages/pygments/lexers/maxima.py create mode 100644 venv/Lib/site-packages/pygments/lexers/meson.py create mode 100644 venv/Lib/site-packages/pygments/lexers/mime.py create mode 100644 venv/Lib/site-packages/pygments/lexers/minecraft.py create mode 100644 venv/Lib/site-packages/pygments/lexers/mips.py create mode 100644 venv/Lib/site-packages/pygments/lexers/ml.py create mode 100644 venv/Lib/site-packages/pygments/lexers/modeling.py create mode 100644 venv/Lib/site-packages/pygments/lexers/modula2.py create mode 100644 venv/Lib/site-packages/pygments/lexers/mojo.py create mode 100644 venv/Lib/site-packages/pygments/lexers/monte.py create mode 100644 venv/Lib/site-packages/pygments/lexers/mosel.py create mode 100644 venv/Lib/site-packages/pygments/lexers/ncl.py create mode 100644 venv/Lib/site-packages/pygments/lexers/nimrod.py create mode 100644 venv/Lib/site-packages/pygments/lexers/nit.py create mode 100644 venv/Lib/site-packages/pygments/lexers/nix.py create mode 100644 venv/Lib/site-packages/pygments/lexers/numbair.py create mode 100644 venv/Lib/site-packages/pygments/lexers/oberon.py create mode 100644 venv/Lib/site-packages/pygments/lexers/objective.py create mode 100644 venv/Lib/site-packages/pygments/lexers/ooc.py create mode 100644 venv/Lib/site-packages/pygments/lexers/openscad.py create mode 100644 venv/Lib/site-packages/pygments/lexers/other.py create mode 100644 venv/Lib/site-packages/pygments/lexers/parasail.py create mode 100644 venv/Lib/site-packages/pygments/lexers/parsers.py create mode 100644 venv/Lib/site-packages/pygments/lexers/pascal.py create mode 100644 venv/Lib/site-packages/pygments/lexers/pawn.py create mode 100644 venv/Lib/site-packages/pygments/lexers/pddl.py create mode 100644 venv/Lib/site-packages/pygments/lexers/perl.py create mode 100644 venv/Lib/site-packages/pygments/lexers/phix.py create mode 100644 venv/Lib/site-packages/pygments/lexers/php.py create mode 100644 venv/Lib/site-packages/pygments/lexers/pointless.py create mode 100644 venv/Lib/site-packages/pygments/lexers/pony.py create mode 100644 venv/Lib/site-packages/pygments/lexers/praat.py create mode 100644 venv/Lib/site-packages/pygments/lexers/procfile.py create mode 100644 venv/Lib/site-packages/pygments/lexers/prolog.py create mode 100644 venv/Lib/site-packages/pygments/lexers/promql.py create mode 100644 venv/Lib/site-packages/pygments/lexers/prql.py create mode 100644 venv/Lib/site-packages/pygments/lexers/ptx.py create mode 100644 venv/Lib/site-packages/pygments/lexers/python.py create mode 100644 venv/Lib/site-packages/pygments/lexers/q.py create mode 100644 venv/Lib/site-packages/pygments/lexers/qlik.py create mode 100644 venv/Lib/site-packages/pygments/lexers/qvt.py create mode 100644 venv/Lib/site-packages/pygments/lexers/r.py create mode 100644 venv/Lib/site-packages/pygments/lexers/rdf.py create mode 100644 venv/Lib/site-packages/pygments/lexers/rebol.py create mode 100644 venv/Lib/site-packages/pygments/lexers/rego.py create mode 100644 venv/Lib/site-packages/pygments/lexers/resource.py create mode 100644 venv/Lib/site-packages/pygments/lexers/ride.py create mode 100644 venv/Lib/site-packages/pygments/lexers/rita.py create mode 100644 venv/Lib/site-packages/pygments/lexers/rnc.py create mode 100644 venv/Lib/site-packages/pygments/lexers/roboconf.py create mode 100644 venv/Lib/site-packages/pygments/lexers/robotframework.py create mode 100644 venv/Lib/site-packages/pygments/lexers/ruby.py create mode 100644 venv/Lib/site-packages/pygments/lexers/rust.py create mode 100644 venv/Lib/site-packages/pygments/lexers/sas.py create mode 100644 venv/Lib/site-packages/pygments/lexers/savi.py create mode 100644 venv/Lib/site-packages/pygments/lexers/scdoc.py create mode 100644 venv/Lib/site-packages/pygments/lexers/scripting.py create mode 100644 venv/Lib/site-packages/pygments/lexers/sgf.py create mode 100644 venv/Lib/site-packages/pygments/lexers/shell.py create mode 100644 venv/Lib/site-packages/pygments/lexers/sieve.py create mode 100644 venv/Lib/site-packages/pygments/lexers/slash.py create mode 100644 venv/Lib/site-packages/pygments/lexers/smalltalk.py create mode 100644 venv/Lib/site-packages/pygments/lexers/smithy.py create mode 100644 venv/Lib/site-packages/pygments/lexers/smv.py create mode 100644 venv/Lib/site-packages/pygments/lexers/snobol.py create mode 100644 venv/Lib/site-packages/pygments/lexers/solidity.py create mode 100644 venv/Lib/site-packages/pygments/lexers/soong.py create mode 100644 venv/Lib/site-packages/pygments/lexers/sophia.py create mode 100644 venv/Lib/site-packages/pygments/lexers/special.py create mode 100644 venv/Lib/site-packages/pygments/lexers/spice.py create mode 100644 venv/Lib/site-packages/pygments/lexers/sql.py create mode 100644 venv/Lib/site-packages/pygments/lexers/srcinfo.py create mode 100644 venv/Lib/site-packages/pygments/lexers/stata.py create mode 100644 venv/Lib/site-packages/pygments/lexers/supercollider.py create mode 100644 venv/Lib/site-packages/pygments/lexers/tablegen.py create mode 100644 venv/Lib/site-packages/pygments/lexers/tact.py create mode 100644 venv/Lib/site-packages/pygments/lexers/tal.py create mode 100644 venv/Lib/site-packages/pygments/lexers/tcl.py create mode 100644 venv/Lib/site-packages/pygments/lexers/teal.py create mode 100644 venv/Lib/site-packages/pygments/lexers/templates.py create mode 100644 venv/Lib/site-packages/pygments/lexers/teraterm.py create mode 100644 venv/Lib/site-packages/pygments/lexers/testing.py create mode 100644 venv/Lib/site-packages/pygments/lexers/text.py create mode 100644 venv/Lib/site-packages/pygments/lexers/textedit.py create mode 100644 venv/Lib/site-packages/pygments/lexers/textfmts.py create mode 100644 venv/Lib/site-packages/pygments/lexers/theorem.py create mode 100644 venv/Lib/site-packages/pygments/lexers/thingsdb.py create mode 100644 venv/Lib/site-packages/pygments/lexers/tlb.py create mode 100644 venv/Lib/site-packages/pygments/lexers/tls.py create mode 100644 venv/Lib/site-packages/pygments/lexers/tnt.py create mode 100644 venv/Lib/site-packages/pygments/lexers/trafficscript.py create mode 100644 venv/Lib/site-packages/pygments/lexers/typoscript.py create mode 100644 venv/Lib/site-packages/pygments/lexers/typst.py create mode 100644 venv/Lib/site-packages/pygments/lexers/ul4.py create mode 100644 venv/Lib/site-packages/pygments/lexers/unicon.py create mode 100644 venv/Lib/site-packages/pygments/lexers/urbi.py create mode 100644 venv/Lib/site-packages/pygments/lexers/usd.py create mode 100644 venv/Lib/site-packages/pygments/lexers/varnish.py create mode 100644 venv/Lib/site-packages/pygments/lexers/verification.py create mode 100644 venv/Lib/site-packages/pygments/lexers/verifpal.py create mode 100644 venv/Lib/site-packages/pygments/lexers/vip.py create mode 100644 venv/Lib/site-packages/pygments/lexers/vyper.py create mode 100644 venv/Lib/site-packages/pygments/lexers/web.py create mode 100644 venv/Lib/site-packages/pygments/lexers/webassembly.py create mode 100644 venv/Lib/site-packages/pygments/lexers/webidl.py create mode 100644 venv/Lib/site-packages/pygments/lexers/webmisc.py create mode 100644 venv/Lib/site-packages/pygments/lexers/wgsl.py create mode 100644 venv/Lib/site-packages/pygments/lexers/whiley.py create mode 100644 venv/Lib/site-packages/pygments/lexers/wowtoc.py create mode 100644 venv/Lib/site-packages/pygments/lexers/wren.py create mode 100644 venv/Lib/site-packages/pygments/lexers/x10.py create mode 100644 venv/Lib/site-packages/pygments/lexers/xorg.py create mode 100644 venv/Lib/site-packages/pygments/lexers/yang.py create mode 100644 venv/Lib/site-packages/pygments/lexers/yara.py create mode 100644 venv/Lib/site-packages/pygments/lexers/zig.py create mode 100644 venv/Lib/site-packages/pygments/modeline.py create mode 100644 venv/Lib/site-packages/pygments/plugin.py create mode 100644 venv/Lib/site-packages/pygments/regexopt.py create mode 100644 venv/Lib/site-packages/pygments/scanner.py create mode 100644 venv/Lib/site-packages/pygments/sphinxext.py create mode 100644 venv/Lib/site-packages/pygments/style.py create mode 100644 venv/Lib/site-packages/pygments/styles/__init__.py create mode 100644 venv/Lib/site-packages/pygments/styles/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/styles/__pycache__/_mapping.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/styles/__pycache__/abap.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/styles/__pycache__/algol.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/styles/__pycache__/algol_nu.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/styles/__pycache__/arduino.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/styles/__pycache__/autumn.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/styles/__pycache__/borland.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/styles/__pycache__/bw.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/styles/__pycache__/coffee.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/styles/__pycache__/colorful.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/styles/__pycache__/default.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/styles/__pycache__/dracula.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/styles/__pycache__/emacs.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/styles/__pycache__/friendly.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/styles/__pycache__/friendly_grayscale.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/styles/__pycache__/fruity.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/styles/__pycache__/gh_dark.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/styles/__pycache__/gruvbox.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/styles/__pycache__/igor.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/styles/__pycache__/inkpot.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/styles/__pycache__/lightbulb.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/styles/__pycache__/lilypond.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/styles/__pycache__/lovelace.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/styles/__pycache__/manni.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/styles/__pycache__/material.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/styles/__pycache__/monokai.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/styles/__pycache__/murphy.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/styles/__pycache__/native.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/styles/__pycache__/nord.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/styles/__pycache__/onedark.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/styles/__pycache__/paraiso_dark.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/styles/__pycache__/paraiso_light.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/styles/__pycache__/pastie.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/styles/__pycache__/perldoc.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/styles/__pycache__/rainbow_dash.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/styles/__pycache__/rrt.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/styles/__pycache__/sas.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/styles/__pycache__/solarized.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/styles/__pycache__/staroffice.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/styles/__pycache__/stata_dark.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/styles/__pycache__/stata_light.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/styles/__pycache__/tango.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/styles/__pycache__/trac.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/styles/__pycache__/vim.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/styles/__pycache__/vs.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/styles/__pycache__/xcode.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/styles/__pycache__/zenburn.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pygments/styles/_mapping.py create mode 100644 venv/Lib/site-packages/pygments/styles/abap.py create mode 100644 venv/Lib/site-packages/pygments/styles/algol.py create mode 100644 venv/Lib/site-packages/pygments/styles/algol_nu.py create mode 100644 venv/Lib/site-packages/pygments/styles/arduino.py create mode 100644 venv/Lib/site-packages/pygments/styles/autumn.py create mode 100644 venv/Lib/site-packages/pygments/styles/borland.py create mode 100644 venv/Lib/site-packages/pygments/styles/bw.py create mode 100644 venv/Lib/site-packages/pygments/styles/coffee.py create mode 100644 venv/Lib/site-packages/pygments/styles/colorful.py create mode 100644 venv/Lib/site-packages/pygments/styles/default.py create mode 100644 venv/Lib/site-packages/pygments/styles/dracula.py create mode 100644 venv/Lib/site-packages/pygments/styles/emacs.py create mode 100644 venv/Lib/site-packages/pygments/styles/friendly.py create mode 100644 venv/Lib/site-packages/pygments/styles/friendly_grayscale.py create mode 100644 venv/Lib/site-packages/pygments/styles/fruity.py create mode 100644 venv/Lib/site-packages/pygments/styles/gh_dark.py create mode 100644 venv/Lib/site-packages/pygments/styles/gruvbox.py create mode 100644 venv/Lib/site-packages/pygments/styles/igor.py create mode 100644 venv/Lib/site-packages/pygments/styles/inkpot.py create mode 100644 venv/Lib/site-packages/pygments/styles/lightbulb.py create mode 100644 venv/Lib/site-packages/pygments/styles/lilypond.py create mode 100644 venv/Lib/site-packages/pygments/styles/lovelace.py create mode 100644 venv/Lib/site-packages/pygments/styles/manni.py create mode 100644 venv/Lib/site-packages/pygments/styles/material.py create mode 100644 venv/Lib/site-packages/pygments/styles/monokai.py create mode 100644 venv/Lib/site-packages/pygments/styles/murphy.py create mode 100644 venv/Lib/site-packages/pygments/styles/native.py create mode 100644 venv/Lib/site-packages/pygments/styles/nord.py create mode 100644 venv/Lib/site-packages/pygments/styles/onedark.py create mode 100644 venv/Lib/site-packages/pygments/styles/paraiso_dark.py create mode 100644 venv/Lib/site-packages/pygments/styles/paraiso_light.py create mode 100644 venv/Lib/site-packages/pygments/styles/pastie.py create mode 100644 venv/Lib/site-packages/pygments/styles/perldoc.py create mode 100644 venv/Lib/site-packages/pygments/styles/rainbow_dash.py create mode 100644 venv/Lib/site-packages/pygments/styles/rrt.py create mode 100644 venv/Lib/site-packages/pygments/styles/sas.py create mode 100644 venv/Lib/site-packages/pygments/styles/solarized.py create mode 100644 venv/Lib/site-packages/pygments/styles/staroffice.py create mode 100644 venv/Lib/site-packages/pygments/styles/stata_dark.py create mode 100644 venv/Lib/site-packages/pygments/styles/stata_light.py create mode 100644 venv/Lib/site-packages/pygments/styles/tango.py create mode 100644 venv/Lib/site-packages/pygments/styles/trac.py create mode 100644 venv/Lib/site-packages/pygments/styles/vim.py create mode 100644 venv/Lib/site-packages/pygments/styles/vs.py create mode 100644 venv/Lib/site-packages/pygments/styles/xcode.py create mode 100644 venv/Lib/site-packages/pygments/styles/zenburn.py create mode 100644 venv/Lib/site-packages/pygments/token.py create mode 100644 venv/Lib/site-packages/pygments/unistring.py create mode 100644 venv/Lib/site-packages/pygments/util.py create mode 100644 venv/Lib/site-packages/pytest-9.0.1.dist-info/INSTALLER create mode 100644 venv/Lib/site-packages/pytest-9.0.1.dist-info/METADATA create mode 100644 venv/Lib/site-packages/pytest-9.0.1.dist-info/RECORD create mode 100644 venv/Lib/site-packages/pytest-9.0.1.dist-info/REQUESTED create mode 100644 venv/Lib/site-packages/pytest-9.0.1.dist-info/WHEEL create mode 100644 venv/Lib/site-packages/pytest-9.0.1.dist-info/entry_points.txt create mode 100644 venv/Lib/site-packages/pytest-9.0.1.dist-info/licenses/LICENSE create mode 100644 venv/Lib/site-packages/pytest-9.0.1.dist-info/top_level.txt create mode 100644 venv/Lib/site-packages/pytest/__init__.py create mode 100644 venv/Lib/site-packages/pytest/__main__.py create mode 100644 venv/Lib/site-packages/pytest/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pytest/__pycache__/__main__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pytest/py.typed create mode 100644 venv/Lib/site-packages/pytest_cov-7.0.0.dist-info/INSTALLER create mode 100644 venv/Lib/site-packages/pytest_cov-7.0.0.dist-info/METADATA create mode 100644 venv/Lib/site-packages/pytest_cov-7.0.0.dist-info/RECORD create mode 100644 venv/Lib/site-packages/pytest_cov-7.0.0.dist-info/REQUESTED create mode 100644 venv/Lib/site-packages/pytest_cov-7.0.0.dist-info/WHEEL create mode 100644 venv/Lib/site-packages/pytest_cov-7.0.0.dist-info/entry_points.txt create mode 100644 venv/Lib/site-packages/pytest_cov-7.0.0.dist-info/licenses/AUTHORS.rst create mode 100644 venv/Lib/site-packages/pytest_cov-7.0.0.dist-info/licenses/LICENSE create mode 100644 venv/Lib/site-packages/pytest_cov/__init__.py create mode 100644 venv/Lib/site-packages/pytest_cov/__pycache__/__init__.cpython-312-pytest-9.0.1.pyc create mode 100644 venv/Lib/site-packages/pytest_cov/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pytest_cov/__pycache__/engine.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pytest_cov/__pycache__/plugin.cpython-312-pytest-9.0.1.pyc create mode 100644 venv/Lib/site-packages/pytest_cov/__pycache__/plugin.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pytest_cov/engine.py create mode 100644 venv/Lib/site-packages/pytest_cov/plugin.py create mode 100644 venv/Lib/site-packages/pytest_html-4.1.1.dist-info/INSTALLER create mode 100644 venv/Lib/site-packages/pytest_html-4.1.1.dist-info/METADATA create mode 100644 venv/Lib/site-packages/pytest_html-4.1.1.dist-info/RECORD create mode 100644 venv/Lib/site-packages/pytest_html-4.1.1.dist-info/REQUESTED create mode 100644 venv/Lib/site-packages/pytest_html-4.1.1.dist-info/WHEEL create mode 100644 venv/Lib/site-packages/pytest_html-4.1.1.dist-info/entry_points.txt create mode 100644 venv/Lib/site-packages/pytest_html-4.1.1.dist-info/licenses/LICENSE create mode 100644 venv/Lib/site-packages/pytest_html/__init__.py create mode 100644 venv/Lib/site-packages/pytest_html/__pycache__/__init__.cpython-312-pytest-9.0.1.pyc create mode 100644 venv/Lib/site-packages/pytest_html/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pytest_html/__pycache__/__version.cpython-312-pytest-9.0.1.pyc create mode 100644 venv/Lib/site-packages/pytest_html/__pycache__/__version.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pytest_html/__pycache__/basereport.cpython-312-pytest-9.0.1.pyc create mode 100644 venv/Lib/site-packages/pytest_html/__pycache__/basereport.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pytest_html/__pycache__/extras.cpython-312-pytest-9.0.1.pyc create mode 100644 venv/Lib/site-packages/pytest_html/__pycache__/extras.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pytest_html/__pycache__/fixtures.cpython-312-pytest-9.0.1.pyc create mode 100644 venv/Lib/site-packages/pytest_html/__pycache__/fixtures.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pytest_html/__pycache__/hooks.cpython-312-pytest-9.0.1.pyc create mode 100644 venv/Lib/site-packages/pytest_html/__pycache__/hooks.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pytest_html/__pycache__/plugin.cpython-312-pytest-9.0.1.pyc create mode 100644 venv/Lib/site-packages/pytest_html/__pycache__/plugin.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pytest_html/__pycache__/report.cpython-312-pytest-9.0.1.pyc create mode 100644 venv/Lib/site-packages/pytest_html/__pycache__/report.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pytest_html/__pycache__/report_data.cpython-312-pytest-9.0.1.pyc create mode 100644 venv/Lib/site-packages/pytest_html/__pycache__/report_data.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pytest_html/__pycache__/selfcontained_report.cpython-312-pytest-9.0.1.pyc create mode 100644 venv/Lib/site-packages/pytest_html/__pycache__/selfcontained_report.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pytest_html/__pycache__/util.cpython-312-pytest-9.0.1.pyc create mode 100644 venv/Lib/site-packages/pytest_html/__pycache__/util.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pytest_html/__version.py create mode 100644 venv/Lib/site-packages/pytest_html/basereport.py create mode 100644 venv/Lib/site-packages/pytest_html/extras.py create mode 100644 venv/Lib/site-packages/pytest_html/fixtures.py create mode 100644 venv/Lib/site-packages/pytest_html/hooks.py create mode 100644 venv/Lib/site-packages/pytest_html/plugin.py create mode 100644 venv/Lib/site-packages/pytest_html/report.py create mode 100644 venv/Lib/site-packages/pytest_html/report_data.py create mode 100644 venv/Lib/site-packages/pytest_html/resources/app.js create mode 100644 venv/Lib/site-packages/pytest_html/resources/index.jinja2 create mode 100644 venv/Lib/site-packages/pytest_html/resources/style.css create mode 100644 venv/Lib/site-packages/pytest_html/selfcontained_report.py create mode 100644 venv/Lib/site-packages/pytest_html/util.py create mode 100644 venv/Lib/site-packages/pytest_metadata-3.1.1.dist-info/INSTALLER create mode 100644 venv/Lib/site-packages/pytest_metadata-3.1.1.dist-info/METADATA create mode 100644 venv/Lib/site-packages/pytest_metadata-3.1.1.dist-info/RECORD create mode 100644 venv/Lib/site-packages/pytest_metadata-3.1.1.dist-info/WHEEL create mode 100644 venv/Lib/site-packages/pytest_metadata-3.1.1.dist-info/entry_points.txt create mode 100644 venv/Lib/site-packages/pytest_metadata-3.1.1.dist-info/licenses/AUTHORS create mode 100644 venv/Lib/site-packages/pytest_metadata-3.1.1.dist-info/licenses/LICENSE create mode 100644 venv/Lib/site-packages/pytest_metadata/__init__.py create mode 100644 venv/Lib/site-packages/pytest_metadata/__pycache__/__init__.cpython-312-pytest-9.0.1.pyc create mode 100644 venv/Lib/site-packages/pytest_metadata/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pytest_metadata/__pycache__/__version.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pytest_metadata/__pycache__/hooks.cpython-312-pytest-9.0.1.pyc create mode 100644 venv/Lib/site-packages/pytest_metadata/__pycache__/hooks.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pytest_metadata/__pycache__/plugin.cpython-312-pytest-9.0.1.pyc create mode 100644 venv/Lib/site-packages/pytest_metadata/__pycache__/plugin.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pytest_metadata/__version.py create mode 100644 venv/Lib/site-packages/pytest_metadata/ci/__init__.py create mode 100644 venv/Lib/site-packages/pytest_metadata/ci/__pycache__/__init__.cpython-312-pytest-9.0.1.pyc create mode 100644 venv/Lib/site-packages/pytest_metadata/ci/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pytest_metadata/ci/__pycache__/appveyor.cpython-312-pytest-9.0.1.pyc create mode 100644 venv/Lib/site-packages/pytest_metadata/ci/__pycache__/appveyor.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pytest_metadata/ci/__pycache__/bitbucket.cpython-312-pytest-9.0.1.pyc create mode 100644 venv/Lib/site-packages/pytest_metadata/ci/__pycache__/bitbucket.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pytest_metadata/ci/__pycache__/circleci.cpython-312-pytest-9.0.1.pyc create mode 100644 venv/Lib/site-packages/pytest_metadata/ci/__pycache__/circleci.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pytest_metadata/ci/__pycache__/codebuild.cpython-312-pytest-9.0.1.pyc create mode 100644 venv/Lib/site-packages/pytest_metadata/ci/__pycache__/codebuild.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pytest_metadata/ci/__pycache__/gitlab_ci.cpython-312-pytest-9.0.1.pyc create mode 100644 venv/Lib/site-packages/pytest_metadata/ci/__pycache__/gitlab_ci.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pytest_metadata/ci/__pycache__/jenkins.cpython-312-pytest-9.0.1.pyc create mode 100644 venv/Lib/site-packages/pytest_metadata/ci/__pycache__/jenkins.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pytest_metadata/ci/__pycache__/taskcluster.cpython-312-pytest-9.0.1.pyc create mode 100644 venv/Lib/site-packages/pytest_metadata/ci/__pycache__/taskcluster.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pytest_metadata/ci/__pycache__/travis_ci.cpython-312-pytest-9.0.1.pyc create mode 100644 venv/Lib/site-packages/pytest_metadata/ci/__pycache__/travis_ci.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pytest_metadata/ci/appveyor.py create mode 100644 venv/Lib/site-packages/pytest_metadata/ci/bitbucket.py create mode 100644 venv/Lib/site-packages/pytest_metadata/ci/circleci.py create mode 100644 venv/Lib/site-packages/pytest_metadata/ci/codebuild.py create mode 100644 venv/Lib/site-packages/pytest_metadata/ci/gitlab_ci.py create mode 100644 venv/Lib/site-packages/pytest_metadata/ci/jenkins.py create mode 100644 venv/Lib/site-packages/pytest_metadata/ci/taskcluster.py create mode 100644 venv/Lib/site-packages/pytest_metadata/ci/travis_ci.py create mode 100644 venv/Lib/site-packages/pytest_metadata/hooks.py create mode 100644 venv/Lib/site-packages/pytest_metadata/plugin.py create mode 100644 venv/Scripts/Activate.ps1 create mode 100644 venv/Scripts/activate create mode 100644 venv/Scripts/activate.bat create mode 100644 venv/Scripts/coverage-3.12.exe create mode 100644 venv/Scripts/coverage.exe create mode 100644 venv/Scripts/coverage3.exe create mode 100644 venv/Scripts/deactivate.bat create mode 100644 venv/Scripts/pip.exe create mode 100644 venv/Scripts/pip3.12.exe create mode 100644 venv/Scripts/pip3.exe create mode 100644 venv/Scripts/py.test.exe create mode 100644 venv/Scripts/pygmentize.exe create mode 100644 venv/Scripts/pytest.exe create mode 100644 venv/Scripts/python.exe create mode 100644 venv/Scripts/pythonw.exe create mode 100644 venv/pyvenv.cfg diff --git a/src/app/main.py b/src/app/main.py new file mode 100644 index 0000000..f5dc8c4 --- /dev/null +++ b/src/app/main.py @@ -0,0 +1 @@ +print (1 + 1) \ No newline at end of file diff --git a/venv/Lib/site-packages/__pycache__/py.cpython-312.pyc b/venv/Lib/site-packages/__pycache__/py.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..17b4967afff1eda03b4625184076b04c6857b8ff GIT binary patch literal 490 zcmZ`$ze~eF82v73nwnCn4vG}1#i>I}ag-tmMW~3l8Un}JoHaH{!(CcPr*_iOMf@B5 zTU^>rvM6*Aw?c90qIdm)E`Gy(?|tum5AN zQU8>yMHHur@3%t|I&Ghy_fkPrri`^&PZua4;Z3Z~d7O}>Xv7A_*PXD#D8@`)DOP#J z;|&}{A?0CN^hH3d3dj=Dk^L*m7^caw?zQcXOL?qgOk5Y^XPAmP%xbkmt5T_4Tv{Fp zX^Aw5O})qpqEbkBtJLf`Hgz4JT3zaQtuyD!;*RhIq|qWx%7w8UM6*^)MpxKfGXCQMXTwiugAouOu|>};gT(@4(oe^o#IPd%dRNJ^TkiArY5vvdvX zb#hUid2ZQc>~n5%Eg93+zi!5O6rs7hS)^$-RUNzbYaPdFqRo-kiN1p+a#(#l+z$qW z!EhAB+{ivZ=R(FskQRNzvq^uPheAf7lEGA}X>b~T3bGLUQhq++W0?gkEsV^}9%G>j z4P$T9V&xvhiO3`QUO&SX)p6NAriDcad4k=A1MTf^dk@VwmvmuM(tEN8KkV1PAL;X7 jV3QucZ|z*3Jih2&eONRQtg<$aU$?j6d!uXJlCJd^G=G8c literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/_pytest/__pycache__/_argcomplete.cpython-312.pyc b/venv/Lib/site-packages/_pytest/__pycache__/_argcomplete.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ffb86329fe25391d3307f5f44c91e5dc601f7f3a GIT binary patch literal 4828 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/_pytest/__pycache__/cacheprovider.cpython-312.pyc b/venv/Lib/site-packages/_pytest/__pycache__/cacheprovider.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cfa3bc646a5462168d4f26a19ccbca9ba7303a6e GIT binary patch literal 31689 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/_pytest/__pycache__/capture.cpython-312.pyc b/venv/Lib/site-packages/_pytest/__pycache__/capture.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..050abab95b3c3b6306f089e1010dc1aa857d7001 GIT binary patch literal 55728 zcmeIb3wRvYbtYKV51`-Ocs~e$g%?4P07!~&ij+ixq(nVHii9K!k_*Q#x={eBhbPHyibU1z`>I>#o^$TG=bU@*pZonD0oPam?vCiY=LO+E&=2i$ z^ME+_O@|;{6l9@CkVV-R69;WQHj%>in7zl&o{k;|o{pGvu%M@ar8yBU>?vg70)$;X zF13ui$IZ%kdOUa*#yo@G9+!KPe~%weS1d3X>=i_F!eu>WEbK?Pyr-On0|;02RIqRm;mV#$77ig?)l~-RI_j~!pnM= zv2Y2(H9a*fT#9gQPpv3KYLx=CuPjzKxV&dMODm5_gY`Z2EL?$bLr((>SH>C#n|hj9 zxGL5>xT0qT3s=Wl23Pj1WZ`89xAwHMa7}F0;Od^$EL@9lTTdGc*CE{A)6T-nV;zHQ zde)#F)v{EDZt7VpKPuNh!6rl+T| zo^iaU_I1xjn{clnuY5+3TO*!hF*Vxsc+Jj#^lWB1S0U%>Tjbora<(C7dt{4Vek+UZ zK-N^g#A zkKD!bY3;q6rENjlR;2Mdw6uFz+BT%!8M!C2GpF1xmbM*fJCK%JXD3U$3u$*RsBxpnT1_>cWI|2Feo-hKQ@K}gtuab=N8R(=o4?~N?Whlt*n z_YF0$eD_j5^t;C7g^a_~F`@MEGzlqK6TGRm_$?GL#q@9*!%?h`fK8vZFZi z_`4&6hz%>Fd!ww7zgr3SM-GSkk9Us_Q!VzLL!)>U;BhF)Ua>1Wg#7#VBi7L$P9*Vk z?nPXT0t0HGkhLn>kGR4Iqtqx$_H=~@BXal1Fnd=)cjW0LtH82Q`?PjGj}W3fm-hm%A7TnFGah^&4!@nD2tbk&iy4aix0Vl+%pJV6vl*O=2VtNz?h@)uT$cfcud7dHIY4Jn{1aIvh#=_ z7km?=%j>S!3V6CGpoG($mxpDy?2)~)Pxikacuh?gg;Ak3*p)3j!jK`Ba<{j0B#LC6 z34C)zX+Y_0!C)kLG%hRk_>o$#W1kt{D1Dn0ABsrv0ck)U!*`Dj#m7_-v^ui|;eNuN zY@r+(2#>^)*+NtrJ`q;3h5bk4(f&vx>o^*T4X14FiL95sytg07npB#o359CAQdNhJ z0@nQ_NogRiNP2sB48>a=$|}n0WW}-$89k_UAg0xxbsdQ$k0|kx;jB9>%kg2>6i=@n zZnb3#h8cQfoh)C17=|Q$`0(y+Pp)0N_Q2y$4u+GF_Hch#jtq`IIXv2)3@47a9~p_t zkyvyn^5lug(1|A>j2?b60laM=1}TJ(L=sQ-aui#mHos$dG#lbk#>wMje{43q!uq6 zQ<1e7HxCKJVnEjM6Y9F;X3Q0nAb~Oo#T@)Ti{7K*}a55PNy(Xk2#Zi7m0YMV)i^e??F0eW| zh79o`>9OdL96y=p@UR5E>X8JMM`5s(LE<%(m%mI(Mh7Du(xJE%9l&b|(}{!1abAMe zGZ0n4Y}Ne0Z@u1=N5TK3eQWlsyf;E0L5O}L(q_sPjm3C9;b=^X4oSfJXnZ6goeYnn zsq_h1=gH{M`a2YYd{?VeX~Z+@8jgjN!1BSYCy@**$;8QM@@Uqjy*=yQ$H28mQHYBx zccV;#xQV1}q2i^W9}e2ylX2yE80bGFD?5?2n{sZ(?X)l-5?tOdj-46H6xO8+>n@#~ zE^L{#+1*7qe9LB?f@|dm^94d^+53X%E_%lonziHkVZw*9FFRU1|vg!&}@?qgXKhrSZ@)UpnGYI8R3Is zqD?w53LZElF;t614@YD)F$(^L&yZFkVKCgk|4^F*TxFbVScx1SiN=zw!oxH;MuBmO zk^ZAnI6<|@(&15QLbdECK4kN zYBD1;&=eKvkB`J;iH)|@M8r^gLCGBJRdlyyUD^wkdw?@nMWqvuTwK<2m~nXzavh?a zHMpG?X3OnP{|#5g2RFRsKv<{$9hVOX>-2MkJ?khGzUVHoe_1TC=ZwiZBb){HeesjB z!cejhtznnJQe|BNRm4%dkz-tt9hyv;$V)ky*tVjM+8>u0u_xkS z^=zmCg{VZS(){|4O+-ALT>!rqD`l>_Lt;FMh$+E&z$ha-RKDh<5={~rsw18fKFR1* zZ$Je60qLw*U*G2sf-Kt}Lz&2_PLMOQJ=WK!Grow<@4z`#F%w7GKA5QiuU#G-QCIN91#>I#>QCFNep*L z>x{({k^9)tj8u*FXk)d;;8Y0WOX;IAKx>0M`wjw9eB=n@(r7V0Ad#fni2<{gos2{k zc_@;=sLNiT*HaNG5s3^k(SdQx!$_xQ$&!Nn@j)<9Du zZ4$)J!SE=m0)vNGnLxM2hao`A`hdbJOJLHXjzSplV_=R9bO3&0B!T?ki`sn6)K@0f z3_*dx6SF0hrO)Os;*t-4Sy+;z0|Q{vLy(8jfTPj=qwMYB7?}28Bs>JjnDTJ&6Ps2d z!?9>TW^{yS_~=;tNVH!Y;2I8$_Qs+Ek^a&CSfrzWTXZOzWH_;=AAC3&>196})OK%! z+XIN05OkmvrmKW#iV=gEYhNE9pN>Fo;v+1Tr?H2| zLRNbOk{ctR5j2G+ZPf1J5oH*?sfl}sA^>Mr4{B1C6A`89605}q-iQ_Cm3V-SW&10&myuM{8}%eGUtA)D+tWlQR_wqxpCPOFuk zNu6gVb?$YHTYm3H7vp|#k$CU6e^bJ`D5mwd~Ph{<2 zQP~3GNJH_gD>4MG3=GH?0DHidvaX@XNfH(l*#JFydDkIFt0OB8WF3QG;#s#In=RnT znxLT{sCJr5%>G_ZVH~|v>w6)T({zG_ByiPe+$4N-Y>&mkA`*9X+7DWqQ=YJSGG?1HqQ9`=kFiCpC0#(-)H>F_-fL=nmKPl$TKSxxIM}? z21Mm>)^_r6tB*|~lpTmu?xNd6bR#&kp2&Kl7(p5BF%1K%FJI{3yT z(G3{WrCz@BgW{Y~t7}4Vi{b>4r_y-py2p!{_w8S0fbF zz1aU!^o8h^(y36#Go8=udj~Ozmr^gJu56epT8o(b^J9MVeg@s{tI!mky+b z(5R6(LJV~NfFN?s4kGg}rq40+5;cP?GHy_1@PMSw1FWPxjgCdZ;079vBTkptA8&Av9PNVc$L2Lc*+M!CZcX1V6KvggehKHbcQd*0% zrN@WjCx?2&%8^9x5M+@AbW#yzE#Ab)kMoDa^m8ZOXfBoY564E1M2FN$vH?}Pdx%#` z)3hU)EP^q6p*ib?Y!Ds-TP}mPZn*bIEPgl~>(zytY&la}SjBVsCpRUM_3wh9v*+pl z2-nV)(2OrXkCRD>NgVodHtH zwEfsubkkRy@wKLXt=E04KRCBn5C7L2-pcm{hchr!Qk5xbPnWb$m8=8JB<7jncnLkRtNzu9n>!#2>77bPB=`)`U9Cgdf~3BIYfj zxU<;)RAY0> z(y|wpWvW`!Rjrw-b?K^g*BY-WQ&rohf_F~3?qpEyx(dC#5`krANckcvY%R>XdKowC z?ag|6d-1lBSOj5TZ|~p|#!o5Q)C#l1%%UK5q?$De+o z|LGFvAmuZql1>I(eIh>NgHwTnh@r=EPIy}Eagv~%b*nsg-~JqwA`XTs#km6rusH{w z{2CX>gfC(4V4#iF{6QPTNEs{JUw6DlG7Bzd19EnK=zOGmZ~GSIw-D705w9E1Y@sSu zXZ?)r_9l-)hkWEHWNIhuf*1zS8N@w%d$|l*xQpy*u+7<&vs51a&>ssY&=1_4!o_Um zBGOXD`ge9{mHY+b6B2G8Kz4Td#|y!(UMzm8`i1JR*IX<7e(~g?$0l8s*Il1kNOANr z3_O<+xHZKk)sKf=Lm!M$P`xw?Nib?$g!1(1m{!UVLM$mzx0RrmZC%R$fMOuIUOc!| zPC~eH2{&e|VhV$7fgB+wxPa_vY%&87S_oF)FA<+0-9ODesxCFXwEBhBuPCpkUQSI1 z@0fJmF&VsLCRlR*^W&e-1RK-A#;IU)CfJq^wq1E_D!6gdwUGh#s!e&8+TrMeIZqi! z5Dk3}59N8feTi<=g$gwz>sR}@KN5@e_Cj_00%94DxQCP11i?RB_HHGN$nk;uB4{X+%Blp9oL8Ds@DZ(n0BR(NwT; z($&a57j|5l7AmE5BTQ8OC)^PGDurnn;P`2e4kZ`COxEO=DCcF|%-Hx%ilo~#C6I=L zVWZ8t?3XT^v*|qnH;$2%sULs(MW2gHoV$U$fsYZpTrP_y0mdO$$d%yh&K_sP*;9a2 zh&Mfjh^>+xh;_-;vJ)|Gc^Q=G4!DQa$c1=%3|%gJ(dllb6e0lnf%q zhnNsb`tdB1CAnS>BBfYvkVA+mksIYAJWG+Y7|$}fNiM;&oTgCbe{!>2iuek&euZ3S zYJKHsptU7iv^zeOB)#%pB|f-sXm})sR0(~aK?m?A}{su#4oL(>)gJESKXa62vhG+2E8)cC2%;L1tYO5AVo z-isDp7%Xd@)=#&WcxUU8(!|%wik#t6|bmNdo{N)7TD?b`~ZCOiBPw z(q&_sLOv&Qy(E#+1)c;7SL{oD?TNk)Qa0>Aw1*U2V996kVu-8JR77=6;4>LWvtc<^ zzVNdt{--E2M>vl0AW$~r`^=o(7KFCQrfQ3F`qYMetWOC>i+QGK>`?6+WjFHX_XHEs zpvLR%wdxLtZ!GOUp(Gm(!1gdEp*x=Y!lbM2V}f4!7u1k%Y@v^mr?NAm>+hI8J9 zFD16a`eiB9gGiBD15}EVYXmG2Omjy#Hk^oz$Z^t}w!s*YfT@F-b%x?ATNQe3e&;EN z5Sgl62p_I`{TrmJy>=teFxl8W6?kOQ^~fh^b;s?tIyRCxx=5qpKt`#l3X5h_!r$h# z`p{IMd(zdtu+_-NVeLTRqA*G%y_e4y@_V-+N~-HoD^T&2U!jUQjK-{IOK<%yKImG% z%*m`)R^P`G{?7C&m+#iE5|uYm!unOr6i>c@R>VsbL}~Dg7L>QB4n`w{qS|%a=(_ec zJ02Fi<11;XXZWhgAO-P(fkXsKX)>zyN0{d0S7Cxo$6ASi!?nmPFCGt8Iy+f1Anvy8LImUSfI$DDQYX}CM7c{gkJLRF0n^+)7s zQh!+Cs+NXz6heZJ@UuR_?O(`fKGOFyZGqLO!;PEG^fuB>z#R&Xd!?#G!Kd+}@*q<1 zjlYM7a)oYeenoRC#J+*YhaRw6X-r~se@aZbV^H}&0bSrzKsgrU-v{uM>$o|VSN9E+ z0KHJQtmAMz4!>(=^u;{w&k)Bmks`qKcGeCywN@MUQ%a{QSSB>=uP987%!!>rM2fcs zLlSEXPiNCb-Ux;4(ycCqg#T1Eb?Os2R)(jvPrXSQN^{b*q@S>)91nLbi8a#vIx@Vq zR^@9;&LfsjT7I8Gopk$O==KM6`(NosVwdtKbo&9_en__;(T$X1%D<)CB;DSo8}S{5 zCf3T2>Gm$&{yp99qg$SO^LHqYZvQ*}64U^wH%YT;bFNS|n+VOj>`o8Vo1xOv_suy( z=koaiM9g_aXX#vl=zLV9`y=dLMnON_%I0x<%!uq3DPlV%88?=_bheAx9RIqSre zFtcg4kU}n@bmgp@LLR{rfHfPT5L2?+@McoHzK4c#@riv!KR!UeK*e;4b6A7~c}-Ih zJpz|gOl)o2k%o049+oNR22c1Lo5Lcxe3-BstA`2~&4uZ_HZ$sREt(6HeLa_Z(Oggk z=(#+L9y-wdc<-)9_U_$tFlX?j zA&c=zA|msoSF7`^$W&sOsFrlalki5?JmAceR;2-@&Epf-hLxHcqASDJp&RGC$d5UC zpnfW#>x6QqVYk~Z@Bn4$1+ZkJNbzMS`UU3DyR+iP0I1>=@Ie7HXw2p`7a3_m+{kq%SM&r-GT(`}w^e?zwq==LGq z@S@Ayf^-aW%D`zPL243L)hnMmmwT;PF z>~oYbeb`~T(Wrr`BX#;O@R7nVD3mjlPy{Yb@9C?TUY7EUoM9UnA$oOohbfM(ar z^tgq1k{;KE#Z{!ob<2!B_jpF#t%0n&E3Q%@v#sSYSb8C2uq2&A-Cz<#mH!J-3%O>o zUYpJ{y9ww16>={WED^bA{b#6t0tJ_46~dTQ(5RlV#K%6Jx(N|e0i$l>em$Q~)r8)u zBBN@R%RTk!)J}Ah3K_L?cGh?Mm6HOA^}D6Ag;Ap!FvLS>s7q*iqaxm^DsrMV{8|iY z^7$j9`IHH1G{!iP^9fzbe?`5FFR_UN_|{)hgduV>8P}V~kd$9grbYP;V>OKF*2_({zveOTe;K>9_t6MS2mVEZ4P~jZvB?7`F&dInN)C;#UADO#f6wS5| z#QH(Og`n&JqhN;|WEAX_LyUq8GH)go9P!&}hOmz;^A0 z9Aw9W$A0tK$0EvEK3jFWo$m;=Am4@G&~ z>2+h+jez+LdKY>D!mGG`%%1Qp^aUI$O$N1Qz_@K?0H>DYI%OM!NwL?(f5Bywe$nKT zU06E`@3Sz%S+D9t1?z;|>dE?s!^!@m@c5Ra{aEM;^EC7FZKSM@-gr#r&KPWF!py_p zq?E$s;4o>KVP6IFNk*^??kwW@5Q^On1O;0p2&^ch52*VzMGWC!sv@^PbOw1Gr4o-L z2i#FG@u<193l6vE95-qjU+IJoXiW!fuI`!AO7`#;pSv6Oh>4TWe&I&->Z$7X3xz*v zU;AR~i>tpI`~HT1z4gtlQ=1-|D4eONLw4UxdE+_9msF*_nMes_tlMd(jZGy)t7X1q zd$ux|8IBxd0J(+=Pm2nGU@RR%S-oTM67|v?1)mlZAQIV*To%(G#FB`ScNq@bR~=oc zmD@jmO2U8YuO0McowDQe^~xzp?J4%-E``)cslx4#bRF8$eHUDG3TVw@ILSNtA-qI6 zO*f`(DyJ@_&eZf*9_}H+JQN9nT9;ChR7qZ!lIzweb>}{$lEq|eCU{Rec+ZTle8v|# zzi)is4S(rG_oYo!{)V^xn_fv?+5PQ(SNDDE3vc^&zp<6FJTU$MJsuo?Fwb*3XNIA7 zUl#9KwEHYO&&rYsa9dEB;n8o=%`^#O*CJg7Akby%W>SadT__vMi^5N%n-%O2BDA^{ zYz-nXlRjtg{|uS9GuUYg<@Vcx`4p63)GbhgEhIpmaF%+E6dXA{7CPTG-j(q+rhSc{ zxbJixiI?boN6drSG4ag5#%w2q#Jc;5FwJGvY>Y7Ti{Q6xvswm?l@w}kXe%m=`SGZ5 z)!vm_tt!AlMOf6s=tv?u+9pMw9_9u+NJO**^Yf=twl?XiU14-W{gQPY!(uDM(C1(& z1Xe+?3KF>{4}UNGtl;OysWerSM>sjD&!O769jK1k9LXK%(m*=cG%GmVo*CcPjPIVb z?;d!-aVN*irLR`KTy}CBU&#dqnhZd|q)t(=FAy9%RGB>@vl%P2wbVhJ06>hO@12dog(C z;p{?2!yOl0CS?>gCZ#U6(~KERy8df4(4>!YD9ny#OaxhI9GR{bC=K-4ETWOB3npoY z9PZ`y4jn{(dBbzy%rWa!0F@aM(vFv=sRo?W`PnqdPGIin+6nI9sxpu>> z-REqY*>nGU>{(pKe@pHBdDkxXyy^DOIgxL!rV!SRVs>*P6tFv`IZvUpcCOg&Y?${r zof6k0(R=#w3jI~JrP^lfGy$*n8%+J3mFQtAyenlE)Sy%$4v)n!Z;?* z72{>M$1tps-by-w<3DPpi#dZ@3>a3j=`nql$IH@PlWMSr&2r%Rm~Yo5-Gy&Li$yHk z0GTme0wXi}0N@@QA{a^i1tPfNiy7D6rgYU^&JIe-(Vk7WY&=|FuZup%8;F%?*O&Tf%Y8#ZdwuG(qW@|j@q zIlfjiM-`~!02v@YZ5+^g?vrAG;UfHzYSN`Le42V0En*Tf*G*IZV$#pLmW}kx9(o^& zaEqt4cB?9%z&bP~*BhuWu^+|FUAk?uT}fm=jp~A=YK(=MKxm^s5ISv zmzrLLyrdZ`C(T%avt&NxaMp9SM0q|DTk;rC<9caSs9!oVf@j<&G8z&Lm;;s1y9^0b z6aMwp6&AT~$$qTLSTK@2=4VxkEcSVqft8K?OHq^Cl*TOg=Qw^ zeE>6)Pc<|7a?MPmu2z56uW!LZ_ST#sg@Db(if1uqqO)^Wy0}eKZ!3B=cczT_pP_~} zDBW^{2z~7d%II@SYr^be@vB(N zgE?@m7EUhLLb8bEZ|FR_U>$HmUZV{Z)nUr~Hf)L{v23`Vz%u0GEGv(-hUI~^EKMC z^;S<8v%`Mq97zteZ-IkYH(rBB4<5=ny8RYz-QWY1ppbyCe1&dbr5ouDT1&DnXmem! z7?72R5eo-!Qi^3mS}c|&!gnt!E7z#Z0m_1nCr=EG#A4Zk{k;cwKXwosL}J$?T?YHi2PsC6PEb-oyT#e3=GcCrWa$LUKB@K`|wTs>xRNAvTBmGpj^-4uR@Mi8RZM z8<X-5U>-gep^EnfzkB0+rLb=O_X@vL_(!%s@%(}3Pj~(6 zeQ)lYTHD3H0bh3S_`MUmGo`KR($*_mGQJJ7%TVe=whC*!tg}vdt8S;X$7cVbDB_-@ zkSF?Qyo?Nyq>u;JA(Vev>BGJb8n>27kV5tm3+zP-jBQp5$FGB5AVueFMhlQq@C`3k zcNTViC~p5ylv0HfOeX{Kjt^a$@g(KeO(!3UcR^e50?Km{k+4VEPdBDKu+$dNG&!{Z zp}3O$L!_xHuA712gyUl1LLd`rNrzgn!0C#3rRm%4SKF^Ff1~jGj?6s=)At;ly!+6s zAa;x8Gv(D6k6$=G(R+E{75Uq-tFg)U9dA60jr08Xh_iOPwpK`I2Wav?Fde&6M{vUi z<$_XAV4$Yzwn3Usq?YATp;=juP3Q!L{AQP!nP*Ys?a50fX6ZnoyvrKhmJ)ZZkdlNH+5Z@u4VU}FB~rf#``Y# zGNHzFsBtRPd^!2*sh3Yd2~QFpCV3f`Hve8s0Z+ZfSWGz=mloNX*=)T4?A1XIW%Wx} zz}pQ&t`(C(X}u5h!VtBrn2XWhraCS9-MUUq*kQAFi$VX)V<=}4Z{3#To6&#EZ`lqD z7HPEOmZdFPW0Xd_LI%XNkk#%)#_R%RO0}HE^O!C;8gWLMJoAOU)sgBwguQMIGHJs) zPqtSzta?}+HHdv+`F0i9;x3Avbg9SSRzSTO()}&oE z)2=%DeOs0norKBN{r1>3(87v@b?X7rE!g<6B`30mVc&|BQKL2CTA{q!Hr2d|uI3k} z%H*q=V=7}fFpqR55p(uy;7UG9P{veQE=s6&xH`fL!Z-8$SvBTphKJQ}dz)c+(0gim z+O>SzRZqCUcyS(%%q(W_(R++kV*j{$Y$?kp`q+X&8B9FilBBGo-c(0VM(?aQnQ_UW z{Jg<5ze|}dsWi7sxhZJ{_2_5vdeqvKr1vQ4K&M@j-lL0*uBOqn3o|yxm#*MVh7Odg zQ|`bAax;)8UnR=SKp)8hw3jNQ+a&%HEAYB!ggILa%sK}}2dp_hX3~KTXD$$cNk=Mx zNhegGnRKXNKg#HrMr3nMh%)JA$o1D|m~-Sp?)E$CY;|RW2Ar7&L9?);8dG$+ zaiQ6Ec{;FsDp3CkOesXcY`RE&e0x3laa04?l+^-~hF!~$DQFt6kb8>36qNh$la3Ry z;0)M0neDKsSaS$twxIKdM+nqw8K@WVhM(q60xoyI`ozmmT&tLB-g@1&O=S|}D9X($ zebmZ4vkH-kLN}ZqwtxYcEueg7K$f-D_6u}@@C(uo<}d?_#U;+Iz}*DY79 zPHRJ1$Uz~D=><5>iEDZfV$oVG`QiRQ3;R4hrZncMjKs%?Fk&|$N5ENu(Z-pAMn z3}g3pzU@zkTEkv`nv^NeU40#Fxux}{dk^k%s3n-82pNU<^hLB9EEO& z2{`1v|C_>3Tu95|o^UE&-~eGNEpDD*gJm|=yK0+(= zZKN&Qs^iounn{Cay9D=V#haz&<8IEV_3zDTnBGA{mJQ2Yt-)4t&>mV&wr+z~Uh#Ds zh3dKu7uIbQVdaJsD>MRZ<%S1OTDjqsi`mKzpIpLLZuqfsqcrP#2z?q=`>JbiKXGO4 z!l!V$?mz%^ontj)%OO;t(zeD>qfvf#sjIiH+Oyc0tk>vgfcPvjD4!z`>(I#ENkH8o z&fF~BQt&hZm#kixr2B-m>tQQ!&8)dYe|*Ws0G%_;5J3RM;@qQ;`X?Z> zdLe~W<7U7ZDWn=F_Lq05s&NaQmPs|9D&B7XO40 z4BeqTf>=*ZJY}lLLuy=C(z8@8lviw1_vK-CQ zF9I*?j1Lgtok4B{{IUkWQf(b?HR_wBU(0S`by;-RE$g!ANM#w?Wf8x0U88i&9M-{+ zju>6Y=C_O*NZ)Jf(Y%h!2AS^H*ikxFlhcWLspqIKA3}|g1ZYn7aD_DLY4S;2Jgsp=oO3XdHtfj+_L+>Y7bD zLWOO>Me0nNt7yv54(t@`F~&*W)kFns094mB7zmJKx5*X{V^eWH!1gBNy;N^%c@6?B z%y5BlAi{9qW=X}xyD!{5Rno*a!G6b!ov|w}Zn>~!vZQ6w*Mhr>7#Uw}+E;t2f7;h@ zTbba&H_>_S>2pEOcZs~UODAmYiK#3ly+0 z<>(B?BJDQNLl-PG1xJoT!KdzKiRSBw`{Z|vwU^l-#%wpPt>9kkvnjx&VhRLMzfd#RmWs77|Fn3nZCN+)x%?P~Zq z#EfVLOUg&M099Z@w`Do#mYet;Lbk)`69|{;8Y5b-aih3qvi6Rt;`Nih_11#QL*oxU zzdsKL7m54Q{zaq|odro~z|wd|8IHO65pHrQWSnjldhJ9e3cNIiC%xW z#78l7MSA{awa`-=w1z-bO#fv;GK-RuPMpS@a_Y00)@svnTht}E;8E-bEn`~9#y~QB zF>#1b#Ggh|jvS<%L`)v^43X+hg5C~PfibSW`PIsAR-Tj3A00n>esFy7`S=wD3NGvD zp9X6#9lDGi;8#q#Rxn7zm`FnTht=x5nEXkf;OBSiyQHxe{}yub7PHW^^a13S#SJ~* zKnNaj6q_pJD}s0>ZIW8qRdMfC2j}O1O~s4~eukf_TYU;!AF|z%4>ARe){xdfzRrY6 z6KsSq>_{W5g>)OE+i%eAEZv@=8v!(5l`%wdbR+LbCN#kD-d}_RTS}`=KQ!xbIoIFx zx6C^6#NId9#;lM+E(*CR#5XTPXs(nQpx8T^nd&y;jJl1u#p*6(bq9=g%Q(VG7V=u)V!4{kxeV73*)F?Z_h@uuig%df%{3Yn zCws?)uiL(18a7*f{HP@6e5(8%fT_^(24$FTjr6mLZq2xLD*;5Nsz-*{8A>KrsOkVE zP%lFnrnMwbQRNJ7aAIvmakGR{)C(`w-a*ngafTXEo3E4!zw*L^qj z{rVr3PH*m-x7+COIBW-wjWN7slNIZ(t+-Y_S+;%3vt!b+gEL?>QomG|C(d2NH{|IP zm#dp3Nqq4eHl}w#0at*H>9IQ=w!d#wz1JI|sH3e^8dXJI6SksvLv`oHR`tzL);X~O zej``bVQYHY?mi&5uwD{5t+X+|o~zO(M5^Sn*UL5eOH)nCtKncW078M zO0QMZmuc<5x7L`_;k3cpX-=;-r7s_?Yi(0v07m!^fP19cR`F5dXMU-+Xnxd}dQdC` z0tp8(;shNfO8fR|B{&t(_TrHt+V@pnBV*5N3P|#Zf?e-DWVy!a<1uV*K(0-sA&B4< zcbvbDgVcwy5qU&f3Bhe-7{{dJoYgiYgcTgMIMmu9?Zhcu3eO%NU^U@jZ4?;BJ_t!T zSW&CgmOvEIchip_PKI&xBMzp<%dp=J9U9LZtdKviJ!`NLF>495fH-g7IDVB59K~Pq zWL#nfdv+|g`mSyKO!-w3Kj;j5H}G$#!&J2c#Oc-j;UNi!!{UG(9Py1k8Q3}A)aE|g z)xVE5MUvv|kTdlVU;r{4qr>2}I`iSVC@j_57(~_2SOqVGr|DdQZT!I5FbDbo_FiUv z0pU&EYyxMxYI*Sm>_}RE0hAT z+V`WT%>8}o`}-#M^+V|{i_(nLl#w>3rHzxzx4Z!*yXe2uVNU!dfm)p-kY{Fd+S@$s zZJ8-qhB+o2HEG|Avf4{>rfz+@ZhfZi&UD?KnYw$^b@xuz-ZxpYZ_>AKCTO7u*P$%| zeoIU-;Vw*5%q}KhK}0%lg)Co1WU6W5S1be)^{_f&q=Q4o#>Xxtah8>KdQgryP4Wsu zh;vIc@eDtohVrt@TBGusib{F97p;)#c?(?z-{cVj%qexv@5Ya70|{X5&I>cMijPc2{PoN;xe zT^-k5YZo#hy@S7kS{Li!Gur_#q~~8tO}CkLD8B%H-$HSSDT|@^BE=!!*TT*rN&ABF zSzbO6j?l9c6 ze}>C_#?_d1HD2z#?pnD7I`2l&kNZ)^N9&)Yd;`GA22?E6J}VEG*`_KJuL>#oJjf1 zBBW@pNri%yP(&7QE78g_uYHlc7WM_$KJ08kr;QeGe#3MwcCMoi*a5b%^kObXn(GnF z9^qX=v$IqY=7wzkk$_YsC8L8848|YpWL{2a_?i4q0d0?;}1ZOS-*CX z>4#1b#SBN-e{0!>}tW6B8=w%wOnUGIXOnur{pYg3s`&M4} zt)dZ&Zzz)^HS4{<6g&Jx0N=eE+Ms0g1SyrcY6Rr9xTb;JFPWZ!I;KsE4uF(LJ2b#4 zKg5?VfLgJvze0yweIDEvX4y)RYhaLr&29nQTGGCj>%P{%2e|1}ry`3>^Nq{@aOGui zuvA^%m*xoycNw2)%mEGbSe5pzy6$WH2k@DNaMYs{Bo=(-x9BTt^S&}yY}mE*N4BPY zt=E047x_pBaG;R{))(=j2Vf{zY^21t%-AAtkBN-=V3d04#` z9$fl6Ez+#q#kKlqKX3L;^OSs+iMu98By^`1v4Si9D<3e3gICRmS8f1M~-W(ChJC+PqlKax!Gmv z{0p*(mbX_^o~Ksj;qwxC?<3@`t%Qomq1rz2?Bw znuF&&?|5*dwcIU=UpZMk$ z$F2t9&Fw1?{y{@ar`!IP+m3L*nRZALWYqWs3fvulP~qPj(LIdCq3`b{+D1x5X%Yvvtn7MVokbY%~Zv@@%zqoPL!}Ll>PGVD=V*cPW#BkkXOKCuKTup zFzXfkMW1%9ze?2No6gpEHi@q|b~%JAJ8Ka9fn#TFXTANcdOLn}D+9=qV`tF^!vp9J zJ{TGnd+d=#0wcRLTfOr1tx!`VKMYC2(h5_g-wtWnm0A!E%_Z_Kt1TXA8aZGlU&lu( zZ5wmsl!mS!%tX9Mj@}3}!n9x+2J^`w0c;Q-R2e@zxEUtR+z3oWvHu{cJ{97doIDJo zaH*7#$QHm@8yS){l5ndQ*tn9#PN>6+lSMUOz0^AhOI)v7NF}pWYu=c&c-AP93^@WZ zl9_i2zVeCaE2Y!k6%4yqqtH1bZccqUY0 ztU8};-t>ljvT6HtXvY#s7zjfp=REJ*g_6p1oouhw4`z#mqB8!b>!J2vF>Tk``durC z0*XKZtqA_0sCZYS{RfS9gmX}zB&C6Y+0Uc-sEBa8l&bOcxHQBf~g!nSX-UT;*HYD*qe8W;JHERP6}W zlnr)>La9dU-pa`|M*Y-|_H)(-CgZP5`|B>9yyBShx4$Qd?q%81s$bgi{3ZQ(pKiZm zOUTZatP#KJXtclJStEYS*;aU^aLwO4xE$m_p!^8%QT|OnkV^7^L|GT;*(~y$Sp-BU zE+?n_ZHs|O`E$Jg?;CqunkU5~AgRdvTrGQQ#nPyw<@>EgK$E(%VamUDNzk;4*tSm@ z9f&H4q_x6uQ`G8n(wbY+^*^$hZvjD0#uv-V=^Af|v^jHGii zoktUtzoI(Hq)vMQkdinX=+(X#zP)kTtu>rx4{;eoQZRC~r|LNi4jj?YT9yNuAuq=pd5_^L|q6tZJ3=JxIg!62p1) z68$7|G&8<(6eFty7em>S6}B{i1XAU9DB*Rw8RF{8^poa=Sx-Non8u=qvjH`%&QX;= zq+G<|l}f7UhZOoVy8T6d;mv+{@Zwv5KgO|@zXjU?%c%574a6%+f5PGi;NB2(1)6QkH zXjsWiRl}?u&ztT|?>q6Nj5RFfy(TRqok+Y)O+W}MiPaiDT0(N#SwqKFHd9r#bX;Zg zdpNE#Fp--;XGJ!1n37jqKmPO!)I^s!$K3|BV}<#$X?UeNUbJa$fW{MRlbz2LV7r(A zb98j#Nsf*MSfh-?df@=cS0uaeM; z)Y$&R$1n?0w;VG(&9MUO6eLiaBBT6T21gku$VxglrGF`whxy=HpZ;4pIe34 z8D`9Yg#AubkhrvYCfoT0iMj=%bXT)W z01-P814rG31b4;6C^qGq+1yDkA64p^b??--TsU#%AoD}pxD^jSTo*FUo6^mjGR>Xo zW|p~nLELp0-_|;3;HHTgUMbGqL|`!u>FL8JS9JJj0rGm^p_qEfHtJ(5XR24k-xe1w zWnZckTm)2{K_6-_qPOT_Be2vYlVo&eQDn~IaAEp8l*0qaOADI+n)-^lgR}no8U3{M z=J^*$(e2hM5qIzvGFPbQ`cN(NJUPE(e8;3$x>2(dn!1_NX4*BkIZgk!T-osL?N_%W zut@ZLp`(`e^nD0x68uHyv2G~i(^TDlOLGr_h;`zBMqsf{JQH06Y$||JRKGN?r7wQQ zF?Cj5J|?E1TJE=*$enWPB&t^krl983-?m6=me3T?l+261%)m?@bs2NL1 z*$g=^g7+Pn>ZX0GW@@B!yT9}hltMl}YUR)&(G%-J+UsDkE(FLdINmASVhf+nw4j6q zr7oqe(KVe=uuzwwZYWDwh!j08qf52WA{FpU<2AI5(l%aaVw;|Y zV!O?6U~(Xq)6=R%iCjBnwn|Dm8BHFAtr>$uA0L4G`Yoc_- z>DrD=?Vah`JK6T~E@%D*H?X#Q(DB)GBn zFP8*_$&hWqHFZE703a9RnrCP$7NqB2OIerT`b~DpjkjJ?tC{|2Ni7>h2D8^&XDJ`;T`hQG6zChGj_O#}SitV>usRFvVu@=&Q37 z?Lp1z%sxj;>(V-S0VQh*S?VxCK+TZ5V%4ipzWijSc}u!^OQv~mx_NJ=`9Qk)z>Kd3 z=dZ5toby1*9jMIs*QfpKr~Dg9d*hG#m|fXUCk!4%G-WDrGsUkpgEiQ-Hi@Lx`&us8KnR z8W|)TX~yJ0%10(KDxG(k$;nv$n$%?`I$Ih?=>RZvF~3YN51a4C8OF^1ytK!MjZ9rE z-;!P*AnqdbJ9t=|9Dsx!%;Mn;hro#Uu*Gz6+khK~L8b8fVu!h5Jz^ZP5mXr+i6rg1 zr;A8x6qA&Ub$kotIgCStVNiPbL^v9wm5S1dXc#r(RaAo^#H?&Z)qF`96XD2WfHj9; z7g)Wh3XD{6B^uF2+m=y&LyF_#n1vJv@h%plb%A|-^o=MKXSy2*aO^z?VZKp@AQBD{ zQ%Z)}LO5(X$TSPv`c&Ff89ouZq$jeDLB58^ydb=hJ^2i6eEk#@%2wr!K3 ztT`GV;yFor#0o~XPLPxn!z0O+95Ai9ax9jC4kcGM9F4?c@rKqM5tsKFR8Ttz_W3Z) z$;!{((7UJuZ!$7w72nbidN|hK$7n6$PjF zLxysCKOP56Fb=rOy~~@+0sl!hQn{WNyAXUh1iAvMW8?T-GQPXt!{!bt)DSwOi=158Z#3L5+h#G@OVH6Z74g*Ya#EX&Fk z%A-zt8d{c`OyxoNb~wXFW(-7Qx5*fs?7I}wsKbHyqfDM2zgGDD4b$cK{S!kV8a5_2 zW5xt&>50_sDpcAHhOng2@c#{tK%WRc_DS?9zgAgc0Z*`$7d{?m_~eoUV<%kOvZUzn zO6rIm=UL_$qdCCn2-)j+_(o3P_6< z;Mn<8>menKCS*Omy#ph(!52<3Aib9I|KOJOs($-e5|hB|KW4!?2cE=3ZGLEn$HBqb8JP)E4R+_+6>58-g}8!F@~Fq6+do%?7np zuF^;-5*1_}zm-Be>9&V%5xS-5wx4cebh|*eZ_@2|aLd|v?&?&2PCx0OHtyzXqmZ9& zchQZ><4lXWmwqxDqoFwK!ztthB={~5#F^zj>oK&<+}Y?kDnh=CTp3E*Og6V?!a5tD z8S&*AOFndMAH+FxvB+IY2}l!#gv1vSz`k0d_!D8>&xD5mAb5T(6u&DpzbiE1|Br>D z9}Dhxg+|@V}PDOYpgL6us+M@iU-2&Li5`L;`WJ#OmRcHxM5zvkNJiIanpn?6Oz&)XEOoG zzWIW3(LdpZQvy|p@cbS3h@v#{#O3mI*~)o=e$4N+1;zS_-?$t}SG3Iw2+ubK#OjH< z%l33p6E&=QzTrtK^}ywVbZN`HKtJYx!&V}$o*2BmCtbd3UO;%htyH{o!hfkBhpWyD z2+wX39EBNsN!ngAaq?|@-OKGaot}A{!&!RCbNSvYdtcr^C*TK}oV@Q*G@Wr+G2^N` z=jB^FUHZbcvNxR90#mNLXI#OFg7M(I6FKIK9~Q-yiOrWbUBE|Ec} z^Lql_=TA5v62;<)$1Xj3p?6-O@a%vMJB3mwkdv;>O`Wg|ZKSy6dK^NtyU}HfWV9+= Nr51kAh7dEG{l7X@^27iD literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/_pytest/__pycache__/compat.cpython-312.pyc b/venv/Lib/site-packages/_pytest/__pycache__/compat.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..dd85f5ffe6b8c552197dda9b1ef97ac1a8cdc91c GIT binary patch literal 13008 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/_pytest/__pycache__/debugging.cpython-312.pyc b/venv/Lib/site-packages/_pytest/__pycache__/debugging.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fd79e6e24f687d68db84f935c757a3d4c1ea1e21 GIT binary patch literal 18286 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/_pytest/__pycache__/freeze_support.cpython-312.pyc b/venv/Lib/site-packages/_pytest/__pycache__/freeze_support.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..68a7994914077ed974c86b674bbc7f5587b5b840 GIT binary patch literal 1831 zcmZWq-)|H}9G|)Ub$7k9w6!fD$&D##dr5l|OO&XoN(gEMLWCGCM>gBtxns-T?m07C zxXWH7hKHK)01pZeKEP8oNWvq30>-DNB-F(R6EVTJLN!rd^gFwI7UEno^PTVayWgLm z@7#~&auLD$>YrEL7d3=_m6!ffQUY%s2I4Am&;oL>lk;#Zw~)h;&PUC;~2$rn)n=M)_MqPAdk)M~MNP0>WrJV1ihlM_gY} z=35?V1y1OJW+PxEq|#Nv!42tN~q9&l-qz;b=t3!PNUK5r%|(HhHvzhhM;Hq7+XSe5qU zA=8CqOsI>Qm7|(tI_SnaqdfEkmmd=H^pitx)c5V%cl^_O%Mx_fvMq@Lj`tG_p$mj+aH=^fx1&+y(?Q73mxIftuS_fZOQN`hs|0A)0TAHY$LP=9g~ z_}P{Z*n=qweFxB&Xg6JeK?27N05dRwcE~vJy-ux|h$LF)=jVyrN@g=d$a5lz(dqv| zbUIV5R{v8zHI?ydrp20e&}w^B(5V@+&9?)G*5n4`lt+yF%xaX_I zI2Z8PXrFC{!u8^!3vUA#mT%Ko;ey3^(`;MfjA=?rjwMi~WQ?-9FbSPll}fR?6u3T^ zG_pc4*%mJFtHnBGghTp=@~f50L8e)CE#Su(#7O8@sw;58GIGCN9Zq@ znK7ij@IW1YXbfGR`eyE)QP~(A`%>M^qn(xe<5O$nQ!Ay7@rmp2Uiti6g7-Wc0+?XxRO>pRF}1sIz;8rgMi|CQPGQswag z*!!Ey(fi`y&V##gH76e%`Ra8Gvf-zo$fuznsH!tLYuzzmkcfy*oATPx8OxXOnQ1&o`fkZBxM*Kh9xX{8LAG;y1MS0V-|jIv(B}LnEV`+HOoX X$`hLkoWGO?lRH#HANj)oPGbLGpl9Kx literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/_pytest/__pycache__/helpconfig.cpython-312.pyc b/venv/Lib/site-packages/_pytest/__pycache__/helpconfig.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d635bc2b7ad8622089f9d8cba5867514d05920b9 GIT binary patch literal 13151 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 D;-`b) literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/_pytest/__pycache__/legacypath.cpython-312.pyc b/venv/Lib/site-packages/_pytest/__pycache__/legacypath.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..17022f8d6380e9d0d380b1c8d9d35abef8513c53 GIT binary patch literal 24163 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 RuZVb0^rZ2wi?U2b{eSald8_~c literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/_pytest/__pycache__/logging.cpython-312.pyc b/venv/Lib/site-packages/_pytest/__pycache__/logging.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7a0296a9f85294350546199bdeaad6513fa12980 GIT binary patch literal 45968 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 H7y17Ii2N>l literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/_pytest/__pycache__/main.cpython-312.pyc b/venv/Lib/site-packages/_pytest/__pycache__/main.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c1ee5793ba2e01cb972190dc472550eef918cf3a GIT binary patch literal 47339 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

}ILm|@vLX}e#k`~0RQI!Eei~O~ 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/_pytest/__pycache__/monkeypatch.cpython-312.pyc b/venv/Lib/site-packages/_pytest/__pycache__/monkeypatch.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..92c3ae81bfebbe5d87d9a363d07c6eaefe5aa170 GIT binary patch literal 17212 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ʜqYO{_rKhyM8T(K7H@{w78bgT4q8^QJch^Us~&Ba(UaXa@3ut zdtZ_-Vt?qRX^Z2UaBKBWm-}$*{Pxh+2g52)_gK)?q za-uv|GWBnk5U0XWIgwc=g9@GKWHLh(5O91BPY@-$ku8}f57{VYN4P?;%Xtq`FoGWn zD&YYdz>YBMPOo*uHGy|&oNbXILITl&!2b>gEaZw;b<+aos&_Cpt?+EY5NE?3)!}E5 zGFj=g^HMqu2c_CL&z&kELtQ9=eQ>JUd{#8GJQ66I&n>LSXu|>%=gtbb)~7x! zEihW0uPS)Nx)mDCk!#T?!Zu6_8QoY+?B|x??*(VID4IjyX<4J0F}}t8WQtZm?3`oc@Rau-h?ku6wQ+bLcWI$ zbC^VK*!jtGw8%^G$Q==Xp0EOc8*fp5*lLsq@tUXYDZzMMEQrY&N~e$-BKpBpj3UM3 zTvZ*$MjLSm@v~F-^11!r**=`PrV?aC*2J4Nk^#sK<}Kw3M+#DcZ^NXq4(@lK?@x+% zfb$$>RW(i5Mn{zssfX*`ks{QW9hQJwB>Ip;sI5tOXMBQ#3gkVJls{VyVHuE5T5Qd7 zsUX$`56Yr%Q~|u9uzgV$@}Uea9vg&EB1=hPGQ@?O>#?`qBmqL80h9UqX`_n)5}n1b zj1}EyCsRiuu&@lSDDkWsjs)45i0#EFi z1~NtkZpPI-hv81xVPLuDO`Kqh(WdlS^=gjv7}{VVge+#ACoUgb1b8R$-736`CJF7V zZO>VwBO}-<3!jLPDyIx{W<)QJAjJc=M%;4^N=~I_?LvM)kU63)_IlWv=&;M;u&K`= zm5b>)bX)@-NYP)>CIW`xy9OLI@;qj>X^R8Eqwp)jg)D}JjZ+p|@mZOrY@t#X6GJf2 z-z*`K1>0z0t6QtGvkF!K3pX$2S1k*|bO1Y+>8;$V<|i(+3km3Xq;p zf=81+QL47@U|CE8i`BqoBUh{js5g8iw17bcx2VkM$d2K=p;qxV4i+Kh$i4Xunthzp zl2ffl>mE~}fX4P!)N zf{$p$(H9AE{o=^J+AO9lHqkUd_3!wzegH_k%_#|yc1|zId}7=)von` zQfpyyOC{WWr@eng-q<^ETkgBN_do?@|NiB38&uqlw0;s%Zby{0$o_k*CwNA};XT^q zJ-6jOtF!C!U@g!VIV0U^?z%a@-n{qI1kv1kgvp5wx#i8+jo8hZTl-dG>+;ag!tsrG z=Su8=rMbzm(%g5l}Go?pttcbnQ) zp8C6)AI_}xr+(D^(XO?lr~j+}^qscejkey^!PVWB1IIs{`S8^rCo1PB|Kr=0mcQB< z7~Bl{+v7FnkHx`mb7J}V&uU{5Yw7tlYXP%&6P+vH-q<~K>+83^`l0#Jz(<{x-luMc z?sV;0o&Km}z3a)9bN`(f_~7{ut@X!WTpv1rx1(#LtABNVHCGv&taQA((Y0&!*y`ZT z@r{nJ)qGOt^HMFqI>&sqgxuS9Gq~Btb|3rjk$*Y)qmv)Kymsu=+OE?#!@vEkwvWbs z@M$YwMZT5~EXt<*bKB_=|G&vir^~| z8@+pK;esS3@as_OUK3R}Vl6d4RqnRzss;JO5W^QYE?r`<#_8}qPAIY&BLW^w|34i# B^*#Uq literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/_pytest/__pycache__/pastebin.cpython-312.pyc b/venv/Lib/site-packages/_pytest/__pycache__/pastebin.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4b28d6ed1573d29857c2990ffe4af6e6aee265c7 GIT binary patch literal 5886 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/_pytest/__pycache__/pathlib.cpython-312.pyc b/venv/Lib/site-packages/_pytest/__pycache__/pathlib.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f225a2ef98ea310e1e3fb85ae459395ae217d063 GIT binary patch literal 41806 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 Qhg~eVpKld6GcM`>1+rvvV*mgE literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/_pytest/__pycache__/pytester.cpython-312.pyc b/venv/Lib/site-packages/_pytest/__pycache__/pytester.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..addda369a226f4f93be81ef167dda4c67bf1de89 GIT binary patch literal 85010 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�+ZjgxaG7wB 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/_pytest/__pycache__/pytester_assertions.cpython-312.pyc b/venv/Lib/site-packages/_pytest/__pycache__/pytester_assertions.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..02812429d4a3af6c63909e3ff6d4d325e17d3e94 GIT binary patch literal 2487 zcmb7F-EZ4e6u*uW+i{YnNm^LH7FT0*-rBD0aa5BANK6%~5L7~BnIhM|ZM`~nxW3-n z;YtM|!DAfa0Y&k90+Fs1v52@v+a0dNOdsD>=TN;yI^RZ9sBi;mb#*U}uPolH}z zNgQVY%Qaa*S^l%K$@ieNR7?6SQ&SGw7`AdpEzj4jyrn?@?5eC6ZcSgoP74#QCa-J$k{39Z=6Y0{$C~3A7Pd6c)oki(-c5J%FSblwh;oJtJ_fZt*9TR3 zAAb~J*TjGeZ+eb{P5K^gd4z&NorBloAex;+_hP$d*YfB3SlY~InIVkm|Z@DkT1`m5IyLG{!~~+x;Q(hi;>ha zd>>np>@VA`7656%upMl{0z?P9x*927KuxcSeFBG%NR|mkh^W+PP-2*P-Y}P!YzsFU zkxH=PbRy;$?ndPeNn{8(PYv6Ju@t=0f-S?A&jM=u&(mvfU93+{O}+bJy=hQ9VVH0r z&DDBqb%GlH^2A~Q!#NP5egnHV>hIX|b>9ZR7RU)@oEp z)Xu_49}HfEzdr|J9mRcUU}P)Z|LE+6?ey6C%tr2R?ay-m#`Q1H?^a&g_;{ysYBy1z z$mw2b{YtE&q0!9`ew4P0Z?9iz=PDa3zvQZ0Y5o3*owUB4tL{NuG;6Z0>pl!~7F>rj z<}ApDh~cA;m9oSKsl@sMkMyw~nPWYqV?E?!J(Oo2R`#B>rY1}b(_to*xFr|LpA5cW z6}e}aFt8fu~ewlMYqe}(_fi)jE zXfN0-<2y{(3`hloXg6)T#9atxPSn1ERYp=bXwGBeYiFjkCN^9j?g`DZEq9D+o@oX| z*WiX>p($PgQ$Sk*)fR}?)C@5BR3m{qIhW)UJK6(DC1FB5;z#mIH!)W_2`L@PKk_o` zd4h=n5VMhT(;%+xF8YybVIQ;0D9PhRO!Yj;Gta{*d?W-JWG+KY6qqP6Q3m0&zWdn; zfK;+I6>^AixrFTmnYFEd;4ZjXIXC4YW)r(&! z8-v@*Fc`Z?E^Wyp?NR;v;qQhworjlqMlV6r@V0WYJ$kN-_OrSQ2~GN8fx1Ze;q41u z<(qtKkp?XXU+or$tIrC-wQ+6VdZ=D#q4vMMuJO~1MBAkaflk7t_|;4piX?(*K-?%j z>$zB0j!gE5Iy7sO9*&s zO(hZ&>@Md#`~mSp;!J(2OgB9%aPTEE2JP&Y^?wBsi-I8hhTixmkAzc?(JPOU{yTa# z21fO@;VoC_ O%9f=5-48at@c#hobyHga literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/_pytest/__pycache__/python.cpython-312.pyc b/venv/Lib/site-packages/_pytest/__pycache__/python.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8a8d2c466ea3cc1a444375fc29af2b51038ce4d4 GIT binary patch literal 75209 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 YH~SxJ{Pj5MDRx!O*8E7rLss1X2Mx#v4gdfE literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/_pytest/__pycache__/python_api.cpython-312.pyc b/venv/Lib/site-packages/_pytest/__pycache__/python_api.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e1d881a331a84702b839ef400d579d229ba252e4 GIT binary patch literal 35389 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/_pytest/__pycache__/raises.cpython-312.pyc b/venv/Lib/site-packages/_pytest/__pycache__/raises.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..89a9292f04d614f11ddf952d962332289dd354a0 GIT binary patch literal 60036 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/_pytest/__pycache__/recwarn.cpython-312.pyc b/venv/Lib/site-packages/_pytest/__pycache__/recwarn.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4cc5f89e692a17b09ffb9ad2670ad66b8f6e8ca1 GIT binary patch literal 16099 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/_pytest/__pycache__/reports.cpython-312.pyc b/venv/Lib/site-packages/_pytest/__pycache__/reports.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9ee32d52a2824c4e694303d92529d552fb663a93 GIT binary patch literal 27004 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 CUER9? literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/_pytest/__pycache__/scope.cpython-312.pyc b/venv/Lib/site-packages/_pytest/__pycache__/scope.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d7f05682752e42bc0203fd4b3ec077dc238e4af7 GIT binary patch literal 3569 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 Q>cOrZ1IN?{Jt~a<1%Sq{EdT%j literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/_pytest/__pycache__/setuponly.cpython-312.pyc b/venv/Lib/site-packages/_pytest/__pycache__/setuponly.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..28e952902297d115b5d79c8ec3567586fe49eb5c GIT binary patch literal 5358 zcmeHLO>7&-6`tjC$>l#Kky4x=X>CcCsb9xw;-AWDB3q7=SaKRWb;HPESKJjvdij&t zWhAjwAqNI518J=mX{^8vssaU?z$ucVhx!;4$f+06VoGLf11g#T&5ey(ATs6PQ{b$vlWWQkuP_*S9Sy&1{ zpP}tMLQAbu5cuJ*8LP<}OKOvvxAosT?~Xk11sN^E=pHeX$!emO%w|-mx}KIZG8VNg zh6=BW(=wKGgmxUuW~P$~!X2ASYR9sYOt{aBSe1#~eIhxh<*|HRo+h<3`KcG=SM#!} zL5({T&*tQbxVdz?P6y<0WXxs(h-;{bQro3e1;G(oOITovOv*fBQ%DQRCb5!T;@)@M zup4{^-AB_Y@u2D2t~rk)?g`x^$y516oUX6n85mGyEuYI~l=*_!tPGHv?p9%2~y_jVRj~|$Y!_6OrqW~pOCc#&gOHvOO&K+j*LRa%h6NOIVw|aurY#g_ssDl zll%AYfBvP(w5Z7gVqBEu^!#LQen1n|3j>LKQj(QqMxK04&b&5xDmgW&CP6PJ#xIBo zS)GjK=E;JF>f1Azo7aN|GgcoSx58)v_^GELTSQfF^VQQ=POprwdwUnhs&?1q(-%)y z`EZqQtp*;d`VQ4xhztE4ah#*(MVz}q{7owU2N7>Vf9NcjlXDh}xtxsSWMNiv0?|qA z>5ssD4l!U;a7oZxwrj+4EUj&2YI|#wpE7M|n;ODD(n#Ml6&bKdvmH)>&GMFHtHjS~ z9;>#qhGo4y zx!u;W>i(7g7yfeFP*vz9)Vs4EEWEd{E*yQE+psv5q0tij3&()>aOcm*-yUBctM(2p zyUW3z+g@Sy!FSJn@XC9y+&uS*cdWJxdHcRvI`}^v{a@-Muqm%IV-e;OJNg8=JURsA z@1GdmKem_scrOc;dr*!8ZP;yQ^Ftu0*Mzb$U3?a9?hfciY;h-qOA%k(kZ_MyM<$8{ zJM;%wSVw~GmzHLqy+Vc=r2 zC%BztB#6SqZVYu6cN2Oq$%y8?miI`5<>j)f24O^Q4M}2&A4`#}Az5o!aO^xn(I(liLxfm#Tt|?j6v4lSbRE6JZJU8ja$ML4 zCN{1SOQcD1ULrFiM%j>#BWng$4-3ws6A$e?8;MWtZs-82k}E~7LqjS$tsWaoI4^P$ zXeuJfUB7R=_Ziir5db#zQ?RziM4u++t%ZH^zr$K%!im^ZeU18XVkQgZEN%k(9QZmN zf&h`ggzk@vIr22nO?|CPX+w>SgynY~zxUPuKLwrPD z1QNP%5H2+HhO5ZD#ax4@-Wz^9nm4?qjpD?_dk7+8+u(kk6>-9hg{h~y>l#kLV`nah zM+!YV`MkklB@13t9T}`Eej6sCl3Pw)*sbvRO8n-rj}EU7pL#Q13XlI&C4toI?hx}s zXNW0;gnicAK*+1IbVT_6LZo2p7bu$DWnTWG7t~2an-=F(0I9po`AD1Y%PILpGGlI$ z?tⓈOMPtUd_oFDV9*OQ=$?xH&br`m6#^uGy%KVEQYup>vj^%lU&tw9?P%E@KTeo z2S(I6!|yv{DtVac9BrXlO>%u8LIA>&SH2&==M}LnK9s* zfD>Jn&P^+Mbw&>pKu^mJGmY>Df)nu8*|eH~07lHgN_Cqyt9$7LXi$>TAe14Kqf@X; za!#dt3t@iL-FI~H#HUVQmG@Wp z?h@Z!=6g0=fr_iMdLMa&+4PA(MtEhQuo2Nu0w0S!)5!C4X1l4@%j&L2f|=JM0%*yeW)BBUk{8g z*+2CJs$T!qlUGizh5IVu1EuhR55Mud!@oIv^XzZGwH`iJ_8$Mt>0c_WjFg?dRo;D- zzrrs+`sR06QkC|{OYM)}#O3hlTIkqEkFGV1mH87J#^f(O`)6}2x=MW4C;VP;W4xa_ zFR(>k!}HTgtZMgMLvlx{BYOde`vo351s%9aVgNXyRbz^oaDxam*e1?KSnBxA5SF~S zX$%ly3|CJ+511ili|Od&X^Gf)EG>c=o(1_U@KXs!FQN^n@AAUMg^IKNmb1OOXYZ2Z zs_%-g%y(2h0b{9lOx`+}+*sX#;mwcXg+7tZ$PouM(!0$k>Yp@T&Q7J|xOUzMJ=~-n zi6ROe_0J_Qla~F0p=Xun&93k{(t?I9^|tssz_acHmDAXV#A5h4$aGfKFdfCvv>UMz zmD91q$&4lx1?y`JkaY5Gx;qw|&J&Q0#V~_-WDqccd~AS^62?XbEx84Jn1qp-tXlV3GJ3HMByt!> zNxvHC{7g1`A(_r8^tnI-Hsgwuv&H*KMzGgFZ+?y0eq z7&vGU8(i0y9N}!T$kt(K)p~iRpQ@6QXhONcxi&c{YpoA4Jyb}_{sT3Zl!u3DNtK{p Y8{QhdtITwKX=9ln8A13Pq3AOI1MYT&TmS$7 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/_pytest/__pycache__/setupplan.cpython-312.pyc b/venv/Lib/site-packages/_pytest/__pycache__/setupplan.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ed796d4b3ee84b983666700600cfb772596c8b29 GIT binary patch literal 1903 zcma)6&2Jk;6rWwMch`0tn>0;`2+ih0F>dQLLP$smxU>NwK@yQ(Fh;A5XKb&t-Zitk zO&ldvB2`Lys)U5vBgdlj58!{`lC+UxEs9hLao}beap?u#%&r|LfW+84^Lz8&n|brT zeom*82*&q+y=(p;A@nOFx+4|>;p`B=V}ubFEYuK+g21q7iH%q>#&Ha=RFpWDtaw8% z${fcnrJ)v8j%C1!VuEAEN*0qijnxNIF(o3BtiRd4^nAsMXUPL0q!Z};hX--$fpicF z7SsPnA7}7DpUz-Q$!C9OC**}7W!Sdk8J_9bE|BtV$F7)FKD_iPVY|x)bqNKO z{KVYwd`j++3Zqv1)i24nK5@Opa%3#GOoka8D4Z<=c#O7Cy`RfP5ID5^z-?hms7DTu zn0&z^j$sMMpUDRh9ySEjLV0B|NMW+-SIgYk%~YY_63=foEyLa%h=>BCf~4y?l;|Gy z$>wJ(HD_I0uNj_J318uAhK)53%y+eQ$G5PyN;I-T%DzVc`<{j!JLh$`K)U7COuL#F z1KB7up9NBlSWTeSD)Fk+@tZ-yz}RUrCKGFN&joSLb47T9roCRd^Fe8Tet!9@Qp4~_ z!6+M;G+L!*tKb>#TA}Kjm{_JwO81C;uk^XOT5?U0YZ~P>qe|S8-fS_WZ}nb1*K7sZ za8vJHPp`mjDfrx5FzulBz|f++UnJ|I?~R()e(ik$xZ^r zhyO%!Ts{L8beH9UM_Uz>x?Y(X-yJMiJiy-pfB*~iE~js;_iIG^^(c^iVumikYv6_z5xVm=*#(^H-248|50&*GMZ!bBJ|H zT;KBYu^<^`2PTz3p<#Ll1C5s6({b7JHBF;Kum;1C<0Cbl-$EyVH3FZz0MicYB$1lg zyT5z?L>WI)#!r;l=gMq*bn;~M#?k1Fr=PwUeQ$rDJ@d}fn?Fg1(;w|8k5#Rm89T}3 zjxxDJHP=pM=rEjgq0E?nc@#VVyv$&|JpL^TbP5aLz00CN>%Ex$rv!UEAK+(k@orwC ztbfrlm?9TDw$+ND3pJM)=qNB<)&nA+U5R{FZeW(^dc!bnIt~0OCU_mD9rRL3?``jH zpD5R!E7#gnv-|R+^uzSAI&lF2P1$(zv3;&895hLjG0L_JG;#j9s}*kks{x&-$XSQ|6vvFe=fx zFa^Ui;*2wPkuzt|D(ex@RYKddU4FMmQC1n;Na!ZK?s;JcYRz%h%tq7Vsm7i6BjP#2 z)AF_YVZ*_`MLwnlIDq-y^Nduc zF*_ky#pcif+J}ucIm{|jSS6?9v?pEmbh=$_ZdWU!!}*$?GaJ*!x`G*&;fx@R>jm&F=}#2yjK%~JWB zLeCm$tyBTeMyW*#z_ZChWZS?2V}kNesq%xW_c{IDLiQ7>`b{F$ybtq+*ZcZ|^sJRy zrCPuP6Z)8V>HyEGFX3sG+N65G(*}4%xmB_z?S#}6_7sGg5}<0eL6sDg8r~F4O4jA- zMYDdLw5Fi`iIw$ElK&??AvG4jwoC0&6Hv2$%o<#uTz_Rbyx3og3=c=+ z{asMD?McM@qWw^EoJk}GqeH_n{Q7c)Dl)xUM$60^<(X&vJ&ke_-y-_tbCbs2z z+AelRVu~CT+aj@OMA@!W9@bF@HQ{t35z{=30OKRC3F_FeOi^kyJ8K49z-x|vS?#BZ zkzvglk)*^h8#xX^Hq83PtdCgBeoq`$+kL=ImT@+6 z9^>pSz|n76-r^Z|BiM>-AgAsMKhr~?=1I(Qk)PxTY=t#?`Jx4Q+xg}16P5|S&mvjg zbQlr?N|}F?jB~1qWq_Hk=K1`?ST7Y`!*b$2iot7fIl8reQ>Sa1}gvy~Eu|zLO$}TAAFi%4Z#Sgd7 z)8yT!Za#VIsSo$eH$9gov=u%sqZr;v15zwNVT!=GxUL@qahedJ{fcHA9K~l&Bp4|g zA5RP?>mN-8RBs1l3Qh(xw4e3L2CLa~;-9>VQqe}`h?0o!>`KJt;C5zLn6}bY0t%05 zV8qd=dIpS)s0(i-1}Y8PhQxj6B496My}5z0F`f`dBV(v`dPgXg&JTwN})6AP^?LZUXP2l zUZ0YP0L;#G?N?6@Ow>qxCyowE%m}m9ZO>VRorPutCK7uvmmt5`V3%O2!6wMPsw|0U z?t#&9*pEB2hnbEC`#vTrfF;^DCPs2=$c)r!Sw*u%nP-4Zf&L4U&TJ0$G?o|?f1H5!SlxN!10xpxHD5Y~1Xhla$1eXty6htXbu?UflFZAy@>mNBAshP@ZY7>fdv z1Fj)48qZBwJEH|C$aewb2TKji51%M9w$d&}Rgj5TOgxR&9sL`aD4aJD{QzUi{#QAH zEq~CV*>oACl_21>9dD?}z}u6gaBobG#L>>~GsaFK0wJ1TH?}ANau}^9IvBbNqni-L z6L>>p3Th%zeC|12+ZYzpRCjb6)MFFnD%}1E8pdAo*7)n=i}w24_WIvC0(ZR?mt&K$ z2|nZSE;?%G9JLE|&n-B1-xD}z=>r?7S$*Ai%{RecDp_*)uGpsSGvy18wRZ*AqEI&{ z)MXr{S&PN#e_*wOf%SlaE%?qmPjU~eXnDQMD{Rg|?)O%w)uyXh9!N7T(}}zXBP%os zDT`_vSHrZ>B^AfduDnrH9mY2>azzZJ#Jw$y^Lf(gX+Wa#m7=3W$(q@6E=)AEj zMI^z%OxHf5H5sTi;PcOcRr#_}%Jy-4nJ;H*BBKkOUf$Q4YNIN zQ=7~hTZ%WR8@I&#J)+o7i?jqmyA0CMq)ORA8{OpLCSE4{5?wu|wKQwpRk zNx(Ah`nEmg>a!drlH(C0vq+Wi@KP0ncB?_7>y%sae^6@l$`J!k6H%KDm}Ww|#~ojR z*$J3kIwcUgxKoY+^lDdJ=QsuP3!WtS1xSTd!tUOFVceOgNAeAzCRlN$oW|-cr}j9J z%Ele%9hZKhKSwzgRS3I)>JyJkb-zX3V6+?PFiQESy4fhFOxG1kIqCfo_w`3`g}W3m z#xP3G@yE$12WwC`npeJZH0AH`{oMb9QJh;jiePml1fJDC!}*Db3fS8i#E8t-$|Vv^ zH!0+DiEx1}MQ1N2tyk1B1cq=Eth5OxMir5{iOdkf3p!VA!B(kVHy5EXkwT~!AS!|j%VqBIbaBUOd86aWc6i5)Pw~PG{-$RUXdf*|qBk9i1 z<}OXZ`Ae4WqZAT6SO!NGCp_uOLnQ@VnTMRn0-uN6tI4{fV{tWdjh8 z*tK6zBa9$Ni8*vEt6^eNLcw~?6$JqBdBM|#DOD+b1}2j3z}rr|;k-iT1pGWm14x=R zidiVF=8-c@F|^TK-$ZAIja{>i3`@W?n*B^f!BCFwLI`&_+#gGvhS)3|)&yK2&EA_B z8ji+f`Wyo&m_Kl<`uZReq;9Mc1`^RYMZ=*v+5DY`w1O7USu$)omSN;5ik6I`$)jMS ziN-^=Bk0it$VLol0y2o^G$#adAvuyX7cvC|IT1RhIr@MoMxoaP4fiG_8CM5+TH~Qp z%`v3(qxYuJeXJ*VRXk8a#xO43Z!60&kqlUJ!4y3WkjLT2;>k35P(d7JSAuUJU9hkD zw5(#OymF~p%v9D+4PG7m%xSG~O*kHSNL>?zke3fl9$NHt%y~LK6n^IZse9JbG4I)X zpSRSx?h(7QZQ>9(b``6q2R>+BDBrYH323@LPJSGptvCfhRb^RXDJ{#|NJ)TYR}TH! z)0*``y9a*aDZjF1I9&-SP$Z+DxEv=J;G71mI=FQ+DOaiCwco4WxO> zn`idEcksr+nVq*Rv;NJQvSXj|T!5swF=c( z!pp~%&Vx_#A2;mw9NftN(?%=2bc0T__0dEU;%2~RUOA7SyD!40DI${|NRjnez5(gs z9$vEa2of(_B>|NVWYle%a1_j6vRSteC}5bYFl7d9V;Ch@X$yvuWsqezga|SpSV{K- zA|`}gfXoqQ>L0X>D;yq5NFy;B%ARoeTO*MeN;SOM!(qq-gu}Yz`oiIYM zti_US+b*ks&JPoAB9QBCXh=Pgh%Zy z*p|%{_Q^q@k*BQa%tV2fVsrT}_vf6^=a>WBOH|$L0%y4%f*Uips+yV9l+75sWJibH zd~;l|NRDyadD|#wIIKZua_>IfchKECmdVilJeKM~4+s($@(_AR=$WA1MnPb{5RLZt zLq19hX*Rvhk2!i6x}<0`bhafFNlvzi>jB>wq(0C?7y>6kHGDJ_nvmZ7*)B*{c%VDV zLUo-FTxJP`}<_RI=uAG*x4_+Iba|bgu z_19~z)l43`Tf6%D6W5-Y8Jw%#G+VP};?QR{;;+pFYNk$HJuwx&8lE|Jlg{1m zS54c#yW_T}0c>4u^F6{ln$;#IA7m6*)`F5rBm|-ma6KeJHMM+@Hc&C z2dHcf^q*}crGXq>XBl0gdql7v=O%=#g?Aq35Pt;{HCw)EMtX1X#^7A}rrVxPpz{F= zk|iGh=MU^meRBw2R`>Rv8SaOjvuj_PTlLa>S=Vg$v02ZtFS0yVKUAt=Mt;%0dXLZY zkOCFSk2avV_2(Z2K#(g?0C96ndrS;Fdgz`%ouv^U+L%of1=` zIs&Qc6GiG+q8TBU?_hoz1Ln%UFQq1Z?8 z8{U2iKLr#DQ|ue=`nRSJ-u3w2cp>8~dE+3sTyMOX6?mub0S{>~UD;!+Wy&72Qm``I z{bQi+9#7d{;Z9Yc5-YBtif)L)Hjk;4Eq_8gtD&ku4fE+b$aI;TOh9+;Dz;4p`JSA* z|C3u5_E}T~7agG^zeP1JNY3J8%o)NW*}rk?BMqT5X9%reh}w(eElLq&am+a{FbgRg z*cnD<)-NnEH_2z>}KNGMeFbbic#LU&^WvX9W;SoF_6-21Pcyr5K_Tj zv2Hq;2%*&oI`dY7VQ0v7wBJGm`|!rB7g`Gi&9)IGiMp|_|@YFNI zKy81dfm${F&6%S&TW+N3@Y49Zj! z=AM=%-AxY$l@MK{=1dDA#e7TG7aAk{yNPsXQTx&G?4SAQ-nc=ydZ_I=v6&vZY)Us0!CyZYKf75`(EHSb2BO5q}Zm?fEgX+;gn{Oh$34{O0`;QKuKuai*V|4 zZi7u2@&n5h%8Aji;Q1%bNyFEflcNyNHDUBs(7RFmzoTN57%|je@}nG{4M_tLK^J`; z9=aX6fF+c#N6Hct!JtLANpAw=vSSL?hO|84SZYp#&^lsfvX})apggxUfn|OAqWAD$q=Q|F~dJawO#k}A5UYqkY-RK9o z^VEF~_y1AD|4>92;f3ezdjs4rT>Ks%`9;ZY3%q>f+s*GS-9#=3a?}QWtq#?LsB1+E}cwt2$28x)L{9bM`V`8Wv z`f~9;i(wq7WV5*JmbM%MgZcGvq%2`mF@dVFg!A4c&WWYi-mmgSil=`p-Yri6i3qv!my z__&I#i4oV(+7&a$547d+8KuIRMt+mPU0E^P+`u{mI{%dTLBK|L!@RcVzZxZ4zZ?%7 z;w#!anhWU9bYd6<)@2kFZMTwYrciBN^bNoVnOg|_!OIX(`z8G+j=jA5=+QPkm=B&3 z@g%4MTR_3;m5LXmfV}^r*f?pk@5rsK3nmsY1!oH8p zXwbt0WH`;r(g76hDV~f|d(RjfD*_&mWzmG@h$Z6vaKeWMt-5CaGX&@H3+i}=PO(CX zMHafdWNcj-?$S^M&SkShLi7g!S%Dfq2{)$k?>UIupWcJP#FrJn`>oere{IoTGiR?^ zv^U~`Yur4MDd5tsM!s*9`MUN_VI-pLy$7uP;DxAv*O5B#j*rwt3W2Q$9% z%c;rKqHoQdZ_T1_?VNAztZ(D2V_YV}*oFu+0L=M?iYGJGtFL>nd1uu3Qa4hIt=r~Ww=Gm}2Se1*c7^|* zJJZl|{rt7_Q1oOg^zKVZLn~In9u6C?54UoTPadCcxm_Y=e3h5aUp${#-J-YNcx(UP zcKvnNT*LDWg+p&*&7mm3o^gBAhyJLb{=cxlZht+u%xg5`mY1P^dE5f$%yWu@)`9^k zG*tkL7v;v#G$D=^7u4`@wo~Y$m!SrnZe$&eI|r$-&fN!Jgd@`Sb2{s5+%fowVgXPu z0hG?Pm#`V^eCS-H0Pm2TYhG>m6^y^a=$?cBuZ}46F97HnWHqp&Bu$p=-ifnUk_*5? z?$XPdugjE_Up{s56qejR-JKRFf>U%-u{+EXYXUnVD(IdM&K$jtw;8xW!2SxB zF)zZ>wG^Xl7Iedo>gfSELCAoUem(bwyTt4?;;Eoe&=T<_-7FQI5_7%f@v>UaF;pIq zqAOah6Z0jMKJ#xLPGGZBkhIIAMM+M2_vHQxK?l%@ZC&21UF?QwJ;G+Vu#97P2Z!s! z8>6yLA10_YHcEY{2GJosMF^WjeIqcjuq{Nvw;`IZHxY}$OhJsNgd(SVDYB5h%k74J z?MQD7)uN*Bo3kx*3@B(fw)N)DxrZQ24u8Z!+45kJqSY*~3BgHXJZKL&e|>eUjCwt1 z59bzpktl_&pjKkk#$gv)Ve6~;jc+-Hs<6#+JqSfNAWuv#W6M;l&uEonUVXOE2`Yde zi9=qMVi1$>nZ>abLm75VpPf8po7kGuoE3c!0RvE$Rn=WYrg!n!ou1|>ncVrEGyhGaW zkhVKy^&NbL|BZJ@@D6EV|Dm?=zO9V2-Cx%Xw=S-e<7%@kPb|%l=7*A}hVy5i{xWC@XUh_}3lbS4+KC8fr<^`_mp2f?(z+p{wmK8s1G^(vjG^Ub%jd-_A6kY~7l`=3|+kpag3N)MeYqXnb=0Kzu&m}dm6W|R$siRht86LRf}AO5AZNVnN~)YqF`n^qj|^?OoKw$z-RHg(yTrWw9p+`Z#dBYW z+9Na3&8x9~jAb6BlD_`tK(X)-dMFyLekTm0*oplplz22RhE3ekt|MdIvzK_th3}F$ z->ma%j%ZnPu_LRkoVs1%&6Y9WGG<(DT(^m*M*Aq9w;5+;!(-)?XOvAZRZcg}Vp{ZJ zX#JmyiybqIi?%OqC$yub_qZF|4aq&*L63clH|;orFzp78l+*f^#Zo?ReDk@XiCKc&@od=--B>6MD9*z_Q&aTn>ErsLVUS zugV3vnb;_m>{Tu#Aay*Cd(+eQ`O=hf_92@sTCH5A5;`?rskHKyN-gpl0m%cE%I$^| zBqP~M#fw~Y_f;w~h+wdU`JUg+%-3fwdO;U=8VxFy&Po$7UttD=Id>MFow&Zn30MnppFN>(A-d;m` zx$VdH8KP3xmm(3?6`YusWd0`-8N{p2r~z^W0a;%0k%T?OUaR_U)vhXPcWs@smm^{G z_nlfj;A+wi*+0~&D!l>H`75BW*s~HoOcGfNBah3{EZW6sy)HmP7mt%mLFC>71!1Nd zg?fzEjY|Ert(KpC!IlvSsh3_!4Qb8G%a~15lxD66jTJw%*PJj`z{xlQGM*DVHoD^~ z0NE1~5YzC;eG#cCukhyLf>crau%$9{#);L^Oy&#tDi4ZZF`NneqJBA!>XYit&f3=2 zlm{wq3RK3Tf+-Pq$C(@Gx_PF!Uu`Lhf`fY0M~X^wJ_3#efgd7&Sc_QmU)kYnssgFX zRBOl>tc^hoq8MB&?Rr9M^oGh$;`UAWJ+3G`ewbFrYf_sR4c>KC@L2c&Rn76gcsvi^ zK#tgVbwNcb?f6A&0q*T_JHeT_)f++A?+N>PMkx%kO7{b1)zCh>-I#T-Us8b$lwZmj z>Y$tKQ>ZeGQ9UymGuk5%wwcd1ScUr4)x=ggD_(^B5Sydq!Lp=xZ9y9x)pI}e<4R@y zh@w-e^Za9sO4{f)*=OH7e>eS`g07>Or+bmC<{gKy%{EdS?7i;buCQUej)$gEOsyZC zb3i)Gx^LL{i&2d@o7olp|3IUu`UzJ!Q~ z__{84V$!*!Mf}j429sT&?R=oYe{SSUW_sOy1r-NsAa%X#! zOW`Y!rXXyyK9EX>KgT9mj6QvxW&%gQjg*_Z&gR$}d*8gy)(ikMdnhpHs1#!{H$+JL zmeUC0!_nxjvIG^7H`hV%p{9z$p_NU{>?ezf;Me;!Ng!T>v+|p0HrbPm*?0W2(PIzH z`#;zj_{P8gJb3I2W>DAIuVJS@x^`7tD{17fhFk*txpEOrz&Zfyomv z1RL?}5Qu8;Y9My$gQ`TN06&}nyR+9MVgNH}h&Av?@uaKX#VC0V$AJ!}bW+$4b4i?r z7vg0!`=QEXisZHRA&uB>&(BD`goc1i_X3wf*Vj(oo!J__cUdE7p=%F>^pw=X1ThtZ%k4vuVj+qL_(oxzEn+=TYFTmPU!Ojo%%2Vrk*gx7m>h*7#8CGRTV zi;3^mXbmTV5IhEa5=TQHM+9}qQhP9t?Zs5f40x!jnxLAxcTd9i1I7?ap%aRF6r`r3 z_#2{lZTrIg3!e_2+R2?#QOv?-MpM3(o{d6Y%%}qKtY29{?YJ?2QxM~HL4B2^@1ns6 z9TimZ8a1ky3U2bY?tO)Z`a9$6*1Wx8&-7}jLcMFD$L^uCwbFC|D5liZEz=LbSG_Tpo9>@gd|?@5LYjBdTTlRvqc{XE00fxB<~@~utdPw9RwJk8{d(YtF~ Wx9_b#Wf1>0_EWOlvq&w_p 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/_pytest/__pycache__/subtests.cpython-312.pyc b/venv/Lib/site-packages/_pytest/__pycache__/subtests.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..040883721e364f11b4546eafeb6f5688b79d10cf GIT binary patch literal 17728 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 UDQ?;Ig%uDr-tdTUm}dKb0mH1OP5=M^ literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/_pytest/__pycache__/terminal.cpython-312.pyc b/venv/Lib/site-packages/_pytest/__pycache__/terminal.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9b7e383d0747e15c020eb204b2cc040451e04212 GIT binary patch literal 84500 zcmd443v?XUc_!Fz^b_4cH{P$ti$D`-fF!^d_yh^?B@(1ah>{@5EfCcp0pX?U21&F4 zi?Wp%(8v+!I3c)_mrz!F1g^dC3^^0vofB6qO-CxrW`N$Cy z`o}S&PIp7c>H2h>o->T-M-6=jJ*AB!#y%sirV-PqxzEh<%p;c3oW2~Ewjgcov$Av! z(zZSuOIwk)_t{z6IO6DYu)3UmPF!sxuF>4ST&&}?6`aHPWNAgDV`|_oG-aaq8 z=j-#aYe8QDu8xty(W1U0R>p~RabGb@yO1vFD`Dweq)YosS=xgD`)9E zq$~O=SlT#J*;mQlQPo$4YyOCTw7RdF-SZ+{(^tdNKBQ~=YFWBqq;9mnub!m~M>dT% z^fj<_(MaQHpfAAE#Uq6aqucwovvk$Sj?tZcJ6YO~^sc^LEL}aad$gmkgQaVb-qW{- zrE5p_j_&K*$I^93@9*2s()CCm=sTd-g)4Xq+P7)s;Am%GC+;=$9pW1M4s(INE`#o< zj@$gEj%x}Vhu@MXkb3)jJohwr2-jzEJ&fyf+*z&**XLQwy3w*Da?ASqri_7eu}E*29~~O& zAMx|yiE%zUG@#BBL|Kj*K=Z}m} z@&hP5&ihBk$6ul6`=jjn%l-V&_+%ub;!7xiDt<~M7tp62{bOU}(f;Vr_*kR|zjF?c zPmV>yJd)PK{Uam&7e>NJ+m43E!hC;poNnZt>YtdvOQ_7b@T-&Iu>mTRGtrN>_m3cH zI@2G$gsZW0Y>G-99_f!n&iB*9=0WNy5;-S_@E(@5_wk@jI-eh zdUvio(9iPpZsb_63`DLBajdZ}esXLKRfWdKLIXqeeCW)`YyPV8l9j{S!}Ss(g4v z`e{GZ9|@zG`24u(Iy^oy5*~;u8TMY3R(|E2!!$rYFN)Lu;emi7Z5iwz8VPf0%LIlR z*PO^JLlYB7*fCCFs*EzPN~LnHO2w>K`g!VfByA4!{5b!_9htm96E5NxOJrn-&&E7T z3HcRHCc0!s1Ms3rtOO=2_h!(9oN%K9t}r z>K!@7<#5I~fsST%SM`0CbGm>v?LIZmho7Dt3P(E!==;Y$?DUV0b3=neVa`7=J~1UP zr+)q-CTS#$Ow4kBKP&COa%pJbl9(0obK_UW{QYB`e{zEB$3WQqIA|%3gfI3FO!;4> z*I;=?`JoH=n({)6Umr(Tx#=C`; z8nB2X#m-io0Z8?aw;=Ib`Ww2aa&yiyt^1}~r<>MKV+^N_)23D+O)2tt@%?8BNc0iswi=zJud!-WU?Cr6@bE02{s#A8yJE@2i13~6&@aw5#9 zt)bA+*ibYS3go0sm^p*|cGS!7pwlio?WR))PH9^xG(amm6p9cvLH+jouRS^2)$!t% zEnCh!|Kcb{FxWrP&xJ>)UYwW;M*AbL1TRhwabb)>_{GcNvCA)>9J=r#h9(@G=pT5c z|6(}uVrXKDW?(CpC zV>r$b1D!QvjwFr{-Y zh5@^yJo`FW^3t!vC={^p`=~770YRj+Z45Isf-w%KEtmU8Cc}}mYt2MKiSU&WTNn|V zSnOl^`2$o5=EMMyK`5m43B)@?C>J@16TU#MuAqG3_+s?KsrRRrs}sKVezH&2ydIv?o?IN8*Txnv~s z0;cp!Vqc9&vgw$%j9(ZAbYs&pZMrZ%KEfvF6FW-yr~N(SW9Ts-j!yDp1dPYRxAi>% zJAVXki51BJp{0|*I5`>~i;D1@KT3D=mAizW*x2#M=thNDwbCrH1usc&nk_HFn!B-x4H6L}k_pS`L3)L=4OANh1;RjHE7R9V#{m)mTASX}?ejO!zd z&Yk}#hf-Eue(@t4rR+Ly(IW??oH}<2EB`20XV3jxH{~_!>I07)cJsc6o`y$eTv3N_ z)1w?pS#^0Wk8G5(>xyewDb!)l{d*Vk*wzmtW)vBGgwq$ISgSFXQUO+ zQYMx%BhNyqsT@H3e8Pw4E&*Gf9KR^y#mS>sqcYfyfKmZG2G}M@;*VgJFe)el(B~fj zRJu6MPc{1^RKrL(3P{6Xz6hto{ww1=Cqn2WpsB)F`+?oV{+C~_;lm&jYMT8uXK`w* zIoo~qnI5EGtC_;*8NX7~;qT=q!!v=GUzYGd#}G&`<&7N->eI^VMbjAUB-F%*sa?&C zfKy-g<7@AUDEPB$S((#qhS2f!0!|!=n?xHJGIg)3BF{4H5ggwi#>? zfZo_B;0NKqNG8#F5$%hFBiZe%X{iyLRTGgD-7N9T8bPD0HsL(OjpCRSP5T8c@DGhi zs5DIYJ-n_(XFAWF>+X_UbMC3*XU>Q}m2t;6>^^(;^x2IYN?(URiHVlB$`d7R5l1Ih zCocRbQK|%tn*9ow@UL#`x%lUE){IJcGMG$MxQMMqW#x>)i7FVUL_ruhY6{ zuKa5_Urwo4?);r+)w)3WJ=YVdNo#Oi*j>Xv|s52Fhi3=Hd0K`;n; z$v`;B&|gq)rI%%fSfe$tb?c#eSX}0hT0eE)Fm; zdn4h?;gJ@6jaOqi!5|~rWBFa->|v+TRUV3!o&$fW{A}@ei40qZe+zYmK1tdXodT!E zm@(EDLy!meMjs{Z;F%0z;1Z~Qe0RF^L*l7B2fPkKCd&6q6p{Ea zPMmN-S%`sJ8hRQ)ZjJdbg#D8e4-WDLL&|vgp-2?fOSN?P8wR@@0xiJQYiCG0XB3eM|8MO?Q0Lf$#+|&k^)Y9FWYh3dG#vI#g>oats_eM1@p!b`4{g%Mfd1 zzFq!K(M$aJm^G-Om7Z*654O@Xt>PoCW8(o- zK0#|d`!fz)8XtnlRQ!zgV33KwsEJmV3Pmr(wzAR87()O6Kf8;!MleVuOpRbXq8I$t z`v7$Uv0QO*f&={%;3~1^(55TW56q>j)J;ny$^kmCD+u(oab)~r+6W$+e*?E;ry)s< z^5YnOJc{|sDgs<#54GZgXVBk^DnUFx<`YKfhD_EH!N7aSBy5MZAK zz{k>7$^tAjj*pE@`G*GajaW07V0=4d|Lt^_N zIm7&o{Vu){?N5*$e6g-(zfuFQc!T zPj!S8OiEJCwC{ZP*+Zw#9q)ZU)Z2ZoH*~o3T=zMQ7fFNCX7=s~iTc0-GQpQe1nz2U z_;+xZRA7-mMY(Iblr#UG({rcenC;>d-7vMRmAp*o#hPmB?sr-UD~#FKe!=@PW(23PCEgrnzcG z=(MZ!fu}CzDSBiur(D}Kn^a1T$tzqi&AY_ciT$G~ zf=T4~#;G?>rL2{Y42IkQMGJCddf!Bmm5=033ICatxCwf2!o@4BG> zI~xX&$r09w5QrfJgQmOULGYlk*2-<&Fo`F! zjyV%V-%_m*7wCo+Hd!q=>{QcoDpI&XS%tllO9GiR&6=22$qG@2PKzzmV#S6F)EeXz zXPnMx?VM@2NWP<9Wpy;ak26J!<^17NIi+4N>t+nohDc+yLe85reIyBNW{h8c45y8p zSslYE=!3vGT6)qR8S{!<0q~FV`S3#eKwkRLBS?_KiPLLIi=v9OA*y_Gbr6*18s?0{ zl3YOU3|%>NFovsKPEuAq(feb$FEA7+%QzajNIEQ1n1=ZXH4JjQYm&NRbc#L5$7&?_ExrT9TFYLM?A_lYmubX! zN1E_FbVsTDf*Xz38{cbtFS6M6?*2sn=Gnt3r!VQO5}Z|W|CZ%8!N23ab0=FgF_&0* zhgf;qbG*0v?5PkC%DLmGdqT&%)Zte!vpenSm53pce+N5lj7&xNuc85I*96lvkM@s& zMt~*`d?gqbq%5MQCT-@z7bY*J9U0@6HnSgT6F)v4O*_Hq;ANnBBOuEx;0B`6skC7n zia+SuhIpXULb(BFByvJhZ<|TGPG5q5m)cSx5?hSt4xtT96Bm*@0<{urD*r#AY{ZNc zM&B=LB+@d{R0f^@7zJQAqltEZ&N*k&S|V6W zlGaUvb<^VOcde_|&WAbfZ@m7->#I4%P)fL}KB)O{^ZT3Q&gMsEoo&;nR-GsKprC2# zSfXJ2>=B^wq|+}r{Yhs)a0Zq(#kY0ccXp$wCkSwCE%^cvc@~{wQdaaU`_R%E&ge<@ zddV;Y`70KRDo*%1e4qdNE;y3$HUyN&05g&O!m zR)dbupEhbC07#dOGVl`X4%mCr`Xg!mxwI~=?+3GZ^||iBw7xT)b7n$HqresLTEBz# z@MAco%}~F9YJHW`Ie-fzV2V-Tj#9uu5~Ti-A<(+ur$xv?Y;4-Z#2xY+mIyy5NKvH> z|Hr5WHdVUF|A7R+L{Y_!nd>vjqAfzvmTONv^cF4{=Qm$_DwXHGb|U2|`%GsySIwGI z4)^>X!BLs=7TxH$-tn7zXS*n??X5kj693$Zlq+xk+*>cCN@_CFWh}i=y5g!$ISUu+ z1ZU;kiAM#f4^$Iq&3$wGl8(TNBd|1Zw^V33oGK_@=nx9(XGxdmER3599_8rlo_8wd zDw4K_63j#vty z6Sg!={m9egIY+aIv^Ljt4_%&XM;M6v+Dt0XH)lx!oK?P6iS(@Hp||A5{_Fd%9i;-V zz42PSaEFkyO`qkE#!`bo6!G5_oCoI~mQ>H*}+V!j`t2@g^rGFe9 zJV(}0VAmlb9v}$`Nf0iPs8*4Gk=Rk@krn9&NG5-h zF`xA7%e+mbl_0ZZnKLX8vF&DN!nP1cWUP|oS_9B4xqe7{)@<)G0YeT zO3{f>RD{f4pd2?@nVm~+-q%m2N-C}$nLUvJs~ZSjI}!J6N|=HVH*JoaN)~n{Oq)MP zS)W)Rs|6J(hlH5)j~`)rt00nlhC2^iM6+nM$UO_oLsOqAZ04ThOc{pC7UVq7nKN>7 zSdJwl$I5bYGIDGz$C{C258H`1sWRWS`SISrJWzP4er7s`l3f|Lm{ahh_d8J<$ zxFY=Wp{`=oRlp5!CAb!H99N2K5!Jzk<(kwhH^`Nt4aM9=?h;pyR0%i4Rp5zIZkVgY zwTyd(tHQNheFilFb0DvR8|A8Tt>nhI8eFT^lo{u0k>}?oxH?>`+1S=+j7SZP&1<<$ zcN%1Hz`e>fX56picrJh^>(QspxNgF=3D*WTw#^wO8*wLyI{_}j%-T0|QEn23?dHBF z=^a~?cU-c`gU^raNfnuaQjhyH=a9y zYX`1m?!E_CGI!rAKPz9kuQK!ZecbEJ{Cz)njhVk6;NIZ6QT`yFJA!K`u19e_1cvC? zdwTh4^~!yXJB~Sa80Ai&To?Cs?kQZm@%tpMM{qrb>rw7at_Rm++^=z`aXpUwGq|4M zew}+7*QdB|aA$EnNi`$y9MY$xbT869Q~JOx|36}}^$bXa5?go#5g4lbOQ0bleL39- zu|6PULx4Zx0VJXeDX5DBQ=}SgvR?|pJ4E-0$6yD$r^v_HFGX|$gAI>dPQ7Z$r%v4p*+v!&6ExSpV|m zGSPZD1tF3&k|+&9lz5>3M1yA?!<(}*@DoodJ>H;YMAv5>cuJAkMYqZ~hquWo^$IqF zOrNt3na#*(d#PL`x>HWcrSLXo#5C_Jl0Wo-aDUd3QBRe8E7~cih7ZXp^_uYn(Hrbw zU!Zo)uR8hHlF26YRyUA#l*-evo#Moy3(vSjI*# zfw2OY6G0xSi(p4gdO>ICl`-&GA+lG9@IOLje3ELkp)4Pz$M{>6$)sO}WETNxpCW7$ zWq4_0D0(GrBdsO#)If70C>b6LNk0cBc^+ytu^Eh2XA%KXHb7#Iw1ezKWV;gErTz#E zQeK&y;K}?Z?K~{Gxf~+X;U&s4f=5i3D#EUiNJ>Y*j4*K{mo5&8UMK7CRD_@bW74uP z8yvL~A|caWjZEH0@60(qhCzX5tCYr$SXJqDA>ztIY%J1c5MW@CxTz2n{Sa|Xg*nB_ zAYBxCB|LRu9K<{5e279OC!(Rz@aQ;*F`8H+QE4KgGMyuRW?613l98g3eMxVYfKsut z{~FOdR zee1cK&m}8bg^E`3g*!Uf^+8#~k|j~bGGX#xeQV#%eaW)zLK$kOHQl81$=YtA zwmVUKlrsDeHgEa3<#tPAGkbeOYUduxZcOd#oHgCI)-n20TZRJ1Mj#R_b9{{KL(}>a zl0q2>a|R{_soLOa1+l30LZ*iI(E*7ez!I;YGH>v7CfXApf#Z#;Q22r;zwo_~)#$k# zNOAO2R-|mK2l^=+(sn6rpE3s=v9l8I1u+ZL^+NkkcufaYuq*GKr{Vd&*lZ@qI~ez2L2nZ`zyi?wd8H9J%rQ1A^l~%3G0gdFGG4^%X2gHTBS2 zynceyG-H$!DbY8a;0l$5Leilr4#cE6r{m9}h&0L~9Oo!Q8eE&A(_b^hMl>W=x^9S- z!r!OiBy(COb$lyc%8YZ5^hazA9JCwReCwPU6-Y60Zp-hdBpcsk%iTV+K z;`YvW-FWKyQ%T<@!M7>t+amb3B-~r$)~)~l>EtD?PEuxU{pV8+T>sS<^m41<+nR81 zi(9vSK`-CJ8#bD1*O~#xwU(0xG*<1~wWMV7CzdC#Hhz#NwVD4h4TeY(l2A!mS{2pl1uI*)h0J?Y&h zc(*~(9k;fvpQI)fWs~$68x+wu=RJd#B?cz2Vthi>l{Or>3^W9P(X{?5|7WNYGrI{F zrqiPCV&yXaNSg8dAK_*u9!*=oAc3lh6|L*5c;g{;a5J8QBDAb>sZDUVCfzuMGbLxL)_XR_L`P}d=RX1knPD2QQL;gA#*WHPj2APqKFbW1G!Db zP&c&YnwrHo+|Y_^-eGj|jP|`Z%8_rCo3CD>v)2vdZP~|1QU*+#@HSN}6on36Jn)<% zSdsG})G*03{|wRV9`%l#8dgLlat^F(q$D$-(t1Y2lxf<`F@3(g%=9_~aVJ_&6Nj30 zOdDWOW#Y_3(4%~y{|!B}>#_7aF?4h=M-%=x@c^Pdj7&x@^#&Xw+OU$5iX<(V7#WK4 z{~3k&$5b*Pvbf4pgSn=nA`F3Pg$m?iNc6!a@WcN{I{giurs#1qV~{1MXQoX^!#X{Z zwur1g!Xt=M3L7*iVsf36vzJv767PcfCZEh$-MU4fQR*kOB7P6ez>3H#xKVb!ESXm? z1K z?nWzfTF=(XUd$JbHTp$1EaVZGiH0|*K@o?d5z?WG3sHxGuV=GW>+OnQReqAZ=?C=? z!AdigiEWje%vjFQ^V9$Ki(mW#vJy5Wm5Ie8L=d2<$kkaU`0(J+RfNhQN1C+d6=+H? zM%D^Z($X}7qYyOTn9lUXhSO+GssBpT2wVhBBYBc67OprN7Wf$rT|M3;58aBRLLMM#)asQn zsAOYsvTEVGAYeS^BAE zRXd#nux^<)0IZuOux<|I#G0Pv`zI8W3g-GCjuvwwP{=NV*)0t6_a1?zj9HY>x;cJ) zB7`U_pfgzjihK}FWS^Ch*TcR+AcukUbOC!XG(H%TaswO_n=XqgT)z*yZ5N z|0iUMtJEs4QkeQc{t5RQ`l@`8PKdXqF6|Hsw?f&Ts%`w-W_IO1Hs*NkvpKNn_Ljy=TjQl$;`v))=CcNP zUlFW37Q=Dtj-|^9>kc-LA!VSBDw7pyokCq5!jBswUPmhX(3-B(dQ1SQf7al2C58%z%mf4&;j?P2;;37P8M0Qw&beO4hn9Ya3QTCObZ?E37rz$|X^1(L#|x=*M2J%7 zoP>Lm)>6S*y0CMpK5i|&Z{6|*aG1Q4ewBWy@K%Pe(syDDAPcmzZ>8}~7@>yfX}X9f zv)M#4DEut$Du*(~7Lc_@>ZDhF1Y}Y+;!=&sA^z1Pc9B#UGYh1;vJSOR40>sxbSnmIgQ~Slp-_dl7+etjH~WC=r`u0i^o^JEl_b-VX<5$Sj$!cDXaw&Y37=9 z&2xgaeqk_StrsVU=5YTQHDhvU@`uDbDgz5EUA}1pq)kgG>rl~=N@p?j!jQ}i?2chS zj1M(q9)nFLVjU^29BTO){f0D|2KoY;5s{mjP~4gihY&CQ1h~(JuMCZG@IVCbAC=Tn zBv$#@q_H876|W=Eq&u*?(M3k^A|T3qU0%@*`*r)m&c*J8XY;I?Xx2>q+?|2tx{rp0 zlAU*}g_1q9CsMGiE)~4Bb6pR;1+!fb-1+nE|7;2ZhBXKyy1-q3ZLwD<3JBKrrSl1E zySRQbXSo8dE}$t4xT1sEM;6dQ%d-zHl?9=dY2%;~fX5^Q9+}KlKnv-VCuH-!<21NX zB_Z?>&%Opjozz73Ek#O1!bNy&Lqvmn?KHeic#`ReY_1H*;*%f?JS67NEz~cB(D2Tc$E1*vx&9lB6Ub(WCQlQvnV(7m06G^&5 zO1TKxst`)e>Ki$JY)e=-eF5AQiG>YGVU2H{jZDOEDxua)OmP#9;!mM-YcT~Ci509{ z7X%GDsV^l`UotVkfME46oK0B$>%S4rW=8hBhXiIYN&k?_J}&EFq@;OAOSn9(zo?Tj zh8EYZTF-*#Hu8C>Sz%k$H>>MLW${e>U!wrz|6w?@iy;RsAlzY_jSxu4%rFy?=Lbo~ z(`F{wP3NvHk~LeHdobLzVQ`{>i$mo1%WfiatuAsH)nd++R4@5HF1=kEhpsP|h%)<} zJ!!2FtQ8BFmJA7NQ_{Lkux?x4zG~h5iK7JEqv#cM-&q5rP-m(3++tsZzQbA)NM^&d z#vJaBm@9O^`v(S0%mac}VAiqTbMEb8lMWGM5eu*xHN@P- zI@xIOM5su#44l8N!enNGX8?Z9EKU0)gL`H(0zY;}CdKO4O6CDIi~uY*r;b7U5r@Pn5!4#1;cU7#nAn&HQ;}g9&Cf z1aA2ql3~ACBYRMJOFCCBBNVZp+I6-) z4-4v&1wo-8xb%9mwOeTIju&*#+CRx}SaK)wcg^Pfq-0a7sCsGV-5sAB4W;(kT$mx) zT<^H&+za-_uf*%x@5?9%6)Ok6yZG&SBsS!JGM|th)tVQ45m;C~%=_VJrwy_)0h=TM zjCs$TeCFu!9t!8f+%Cgh2b@wfG~OTJm6_{`|6R%;@V|CV$u2;mk0j=ZTwg4((V(v@ z`V|`Wc2o_}C0lTi)L8WXO8H>)w0Je1zh(9?9KT>2f;F~q_`cH*-*4yU^{~o>n%L56 zL}DZQhXzf?1~PlR8RcQmz$T_>f5G=+lCIGhK+3)+1U-22VHSlj z4oV?hy$nS}RfttC&HL&8OE{%1+&EckiYS4Qy2M9_)TazK5blz+!+70AX0y35*vKw9 zM4xvY^@1|B=xR0#*2-DSyk)_HU%;&n|BAy8^}c9;v^cmlxIB1wFyZZHC?n~F=VRSs z@6xtaXA7Z_6LTlNc@iFz?}Xk8G1ScNpIfEB-hk!e`V*T7#W={*cNoZw_{y{%u z$Pi9w`Y38487&V)7WG$}S?IWOsN#;_VBWLZ!dy*ptWirxIAfVH&zPc$N%gcPnlHah zo>vhg2hs1CdD_DKs4Nf&>$CzIkmqqGnwvI9jMJ8geo8l;bM1VzNUj%iR`t@gLjIs( zV$SDGTO(#@1@%LEynE-yrFeK6N~P*hs#Jc9dPNDWK~|3!CCb%1aw?+^@E@#yBu>ma zhO4tO@Dugbtk0E+1rQtBv!H(cGto_Qo|e(NTqBqB3VO&NXWRz?)U0Ee>>;v_X)}|E z&Je(Cydi{NyJcVrdC&)?Di za|>p+En~JAIIBDpXYAAV$bVpOBFSdfF>RZ+F#b{QsphwiI5XTwZ7k8xee-dgObFCW*sDDi+0MHoL!zZO#0@a^NcfkSh??lp6I%D_+b+VdU7&UwQ=wSM^)q2$x+-y`>p z%Ogo8#Mk-q+}7*H%D#N5s7JcoZ!QP8ey682Os4EjTg4sEOPqev0Lydy?aSVe6JH z^)E4zWX$Yud~GHWGx!^y=mSsmFFet|#Ke@4&YK3N`p5V>&?SK;1pi`A^C2Et&ns^> z(KZ{*ho0}F%ophNBApHrIb&vi&Wuh?=fF^jg~cagS9I{FJ~JyX1mWX2k(ILJxeW6- zrU4*nsA!Pl3Jpa>-wTpRq#M7;K4oYSuDqa091 zIzA?f{YV+fB(V^Qh{i4msyZ}^y-;}U=A+{&aHpu9Ag_AFd4=uTm43u?8RF8_tXf&~ zy2e^Y<`@4n^_l7Iaz$kpeMb6F6MQAroHdLjgoFmHKN4aeShTBAHbuD#TYQ8w6q}!R zBRb{~+Z;yn!tsdhhm$|!8=oO+Z^`P&;9F@ZV zHZ_vI_b=`tk4L3@^ zRr|Wom(kC1vv#rYmX?tPM$PlVELHI{fj8+b347 zI~A6&PH@zHa6VaoP^dqcaCF{#dc}c&HkoE1I|OI*;`tTCzRA_Wn#kRda5pTQl3NZX zwj5&SMf(KLz8`rGJoMI+sUo=pEDn4){QhvInhh0xNaRWyKey|e_oUFS^Gl(}MniL{P}~S+xMj<-W$Ehjt4sUi z#f^VeR1vS)7O&Z#DlWg}zUhuP?piJ0&G?R{6>n3@SDf@65`2g5`??;ItyQhysa^Hd zlT(HDy7flx_1wk0#kLQ3y}v72+a}buC2MyGwL9W9JC|RLSM6SfuLZF%QdKvOT|aiC z=Xwv`l`P&b6z@+IAH3Hl6d(CvFB)7>`?oqnK_OaLP$HLHY+Lm;q&$`LCi!&>7|WJL zjActk8x|sqy*Fd4p3SNHhI!Mfr$&-re$y#(J6O5OL4X%4L;sjVVk1GyHNy$r*GPg) zrLzv?X64XNEiub0fUmN9fRXM&$#0=_wTYqE2ur^vEgfDH6U5epo#9(R(2FREcgkAQ*j7eSDxogZj;T`kOjaT%d zpGl4=oXSFEWKYRH1VbQ_oiBlIvS?f*xXG7-ssO=Q%Sy(BRf#N{@NXAD!0o zWZy_{&pHr^$e`-KGh&S>0%w`x0)=V>U2cRh8PP^KGt$tbYsHbLxJEKnc@F+}B);Tw z*xC9WGmD*~)mG+JH?ODGf@im*z3!~W;wR^j+q1!ar+lBN#SO|Ryu%li$^8=bxW7ah z&zC5p@=0hj)WUP7&t3 z=IY^!B*R7k5w7@M6Ia5b@RSZ1^!P3_mMgkISr0#eHpUFEb;JzZ%oBtEg_x=JwV9V< zM*j=`Cq{p(A8f=+{C`6sCKnaMc99rGB=?&r#b|zFa(@cT-d`~0WDQ&)bN~s7{_!Cs zeoHSRglSPH2T8crm*jLbljfK=E+G!Rj_Ks|1d;)0(IXvpQDoFV>>mv+ThjWWv_~;w zl;ZwK+Jb9L)cVA>mjX;(o#6M!y4S(pUrObx0WFnk9uah|LsNdn!1HH1LrrdK$If{@ zv%T1Q-4(Cidf&74QzP(L`WVi)3rc#PV!B zdeWHWV@yQ+ewO;LXN>A?0~-eZCXE5rEPJN?6Ar->8Ug|rY|5;(_ui{se)@aQ{lRlW zd#~8XuN4-r?IWp8SRc#Ifrr2iOhUk{VgV-N#Q8I%7AL0&nat7_oiaoMWBA11$Pzs0(*NIn4LIAoP zWPp!<0|OJ=_N#-uAJNy@gcE2_@B;75yfqWIme0UN*1ep^|kKdULR~dAlK%06UpcF2|VH0m^!9rp<&* z+OJP4c_op*^HYa8Z`UKIn#m{fw|(le+*D!FQuW7;w;S))B|DxKI-XtG{Oq^%5S5hY&Ypav!v#?@s%joHOtR;| zAMgAAzT}=BVNVY<{584AuF9P~EfpcySyKPG8P~@;6Wu^tD}&2!q2j=-=&)P2;;y@m zc3vYt1bY%Jv89u%MduT~^Wg4_^U;pHe2kE<@DqnG zRZz5GN*1>W#VyNTp}767O(^cXR}(MlzIQh6Jr;K!gFFAvKMSDg5t@B}R#R~_X!_rE zCY;w00i^i^w$nfUUChRfSPRA~V8Ue|601PUW)0$8VMsLYX|ok7Q$fZ*#5}OG={shb zxSBDJZNl5M+9C!@b!OCI$#}|U0#@fY=uP2;Te!+$mEu znwd&ng9(wv%91|{aAEeLNs1%vFtv;cz|EX}NH3dMLMiSL$wYo0pYDmil_#{rXu_D) zzkBy3|MuOZ3=)eLv1BvVi(MuPMUmw??~=!OO$NkfaQZ(X55if3&uUmB2DmajAw<6u z{`*wchEPeO_&V)YgtPE4zK94(Hbq99wyU+J^&!!OgyaMS9qAAOV72f!pT+YN%VVOF03s{j_s!G7Pj_Qjq4`F$iJbKP_$i`%iFW{;EH z?GS@yS$^J4%hE zyOCe9mY@eV^H@-vo@w#SOy{Gqzz~>CV_9LZV0?c%z*{1MGCXpi`BWDG*#REHT_)o1 zh1J+HP5(FO^f8_OhVK28PNdXGoB83%NL2Qa{Tk)}0TsJRDVn?d5&X=y@Ma>USjVq4 z2iJJ(dTA04qk2q25|uUx-iCxXFlU`L&%T<<%}?gG3%Tuy+#QHMlQBtw*00W8O}fb+ zcCnLWeR{ziAj|K3m^H(6bH44y?(4f3dVlkPU_H15f7XM`+wayb2NTwV>t&+om;H067HAJMHWcm_@Iq&tAgz4Y{o zn+g5lArUWYMl^-!y#2nX3j~~}^`WfSsuEmPNmqm5YFOI3;%b&`pW;Q0%ci^SvnTF5 z52AdoG)*$e1clJ~ia1R)nRwo(4HL;SFw;tCtV)}KgY|(XOa(c!=6O7=Ay!Z|$QwMdqYO z7Y7%gyAS(2m-n5oAQG6AeVi;$$?UGYhT_iu$kRFh>Ycv3`af*HA3XT2ytt=x){GNV zU6!s`OWzwsUI@n`Ix-kQk*+8!n4=o@k&k8LCPLy~JcF1+B zvrkJhE!VXsLWnZc&?YSjt|Ez*-vtT-GHrQX$W#N?&p)Onqee}}e^gB-jhaX%tYJAQ z(n*lw>#S-B%6i@@ABG zXO!2F#>;bp51`kI{9PIdT+kX1_SnvoZNSJm0)5d9SF2^@6qhfLk%EeA<$LONT45BK z&5u_^o+=3AH=}IKE`be%TZ%bk>;}9FyB@NT5oyuiM@bfTh`$an6|=z85pK6J2Q~(b zgej_Nm;-Yr#bS-w{BkBCX4-FzC&xqP-V(^qM#+DRy5Y()(m#Q~kIZ8X8}dfd<>F3& zp~I(7p6ou{d%Wi;_69t6`s6dc*oKJ(SLHvaRxuF!*OV%tcRZxjNjfp~f0XhV(r1n{ zq7Md7WI2C;ZZTy7jYHZhw^`eCn*ZNWTq9t`TClza04zcxOob*8gTf>o^#bC;=&{2K z_9gl&w+{l3yn>{sRq(VXJv#-@&V*-o($gt;Iv?aKQ6BS(9<;Q7Ja&65(XyXn#5DaR z&o|$Dqwjj(jjvq)%HmPB#XHLe!)C==TAyKkBQP44KXH+{`g)G?-#cJ02Nmbq4^b(@G0>D3Ty2%QJB2Olq zwTl-8XK*>1+<9Esi3n{gJA1yHAMbrua6SiBgUvo``V^Mm`9LG{0}GoLiWdu)F5k`j zUtYUyyl03v9FF_C1ZUT5*C&nwvB>6y^2Hb8zO90DE9FWLHY}82dnjKMf}6lnTnUq6 zU2!yuk2NloF7A%|f@ljXK*29TZvlCooIAPTTkKjk&7NF!ZvVuMUH5!UCDMN1Ll!02 z%+DgbQXpPRH6yk8d-&#SkdPG__MHf_p4nsxj+e2H2X; z17d)%J0^Act0ASdjK$@5GIm!(>N2m7Jja<;19B}A3P1}0lYk<&;{a(U9_lk(4yTjb z!z4vpIqR?nnu?P4j2+QZIfX5&beyeJ1ASpvJ39l<0KS}!9z{Ex-!U^_3-@-;6)l%* z(TYJn?3eGVR|4138u|Bdot#py3g{;{%1vs%oZ{RB&Lf?;+bHL$S55QaX7#?Did3uP z_~mN?;8(_(ufhoAsVGwY8d|2}G8#}2AoXsm+#2<&lE}=QR;!2G)Y5WFD^9w0uaK)@ zK&~3pGKZesa=q$R6^4k2Ea2^VqTd(ul0E`RgvBGrhC_%d9>H!_43)%e2r?5T{RZ$A zHV5{fnTlRQxM1SqCZ;HQB?U1TEhqmJZ~7^gGceW*bT{}!A7tXhn623_GIT+f#IAQ2 zr-F>R#D=@D`3zqe8jG2zdSI|Sr~xjo)C?GeMlS^_M1dkvaSRC|z`E2}a!|!4QpB_K zPvL~xq}HZ$@j^6IZlM@x^%hluiKk;xaQNS%6vJLWM+!07rS7D26>5&mQb=e^3`@(RuB9s_Q&{O1Nk1Z?L4JHRL?*fs zNyJ15S_~6UO=2?2o7Ct(r`pZzp=@DO+AHzS(vzac(fSZ<8l`&i@gkF`8s=if*n915 z*MrKsTVprJ5|u3!pc~ucdf$2dt=EB6$##wfLW`Ggy=$7yUA69ekYAe24+!~zM1B)O z?x5&b|JhelzKZt-ZjIa=S@_D*p5-gcL-G8Bvxgt}8}1n6!Grhe68@ucXT>LOf2y?N zR^!da586J6#GCisy>R!{cyni>_E4hq@O%!U|11>J4mnHu_~t!#JB7{r z9zrko{PXW!NmjKARjs0SCSJMguIOZgLqwpw{DJXq&Q=7ck&HJ73vfA#iPfg1}tpyesZo$Y2QGU;p;oUO~|`_3JY zYIPnlpw=2BsR*SO@XfV-qv8F$mvQ;D?$`k?-z2AHZiZ2|B)iBuRBsR1VAM|=P#d@% zCbQKlt#JM*U0~9^y`NOfqDQ}pHrk|B1G8(!m9u5Vs6Z)lK85s5yJ*UW#(5TsDk9Q; z)LKtStr|i;wu$Kmt7V)f?lIJZaG@4m<)+2(yP?l?X4~f3Q)Gx7yyIPU2Z>N| zUw1FM?t5AumIvTkows?ui$2@w>!%mH?)w6yQQZ8g4bO=J!c55zNm1|?5G=@L{%@MH zA99zyNK$c4(+&OtcKCx!wH8^!S3&G&(uxeAU0y~;K*nSd2PKylpUqh^E$c{xoPB7w zRGvAEC(Md)i6@-dwZQv^Y}MJJ#iMJfrI>}_LDZ%YLvjt^#Z~nheCsrOrb!%aFoMd4 z!1AccSK|A$cxkN-T@)e^c5?i#4O(T>BH6TCrEXZPB4)W=*;lmF06Dt7CuXDYpV;fH z!_SPiV)g#UXFJdKfMe_M4_$;0Jc(&V;4TI7N2nYB6GJnsy{ZE-rx@HH>V3( z2e0X?z&^U;H^<`@E%E%;c-|IlH3_NT{fe-h+zi+uLQM-@An6fGtT9c() zgwid`zVDTMx8&~b#MbUa>5*0E(T4?P$%24T5J(p67Yg<#3JxOZ0P%`aODd9{I>7^< z;SZmG|M}I{!}m@kT2CkH&m=rgi!HI}JcYBa_25xKRlmW&BPdi1Rra93c1v_RxD!h#lks#~szYVd!wp-jp`ll|4B`Jo`_26GXs1@+ zHTX&`u!*fmy0BQtWsXSVtV0w0C`wS)6Ji3h4(*o8K!`o1@S5tQnr5(y7z`-M$kVoz zTCLO&$$|e>++FcTBZv_Q_L!-6wfasurCqPuVlpo#y%{VVY;eC5Hyh#q4Q3^i{ikK& z+xXbX6#p&CW>U8X)c-_Z9rMbvIq*7z@X|sU+DIUeiKRqH!|3};iHMdUh5F1OAZ6r| z7+;xG4Lp%G{4Y?sx)X<}>6}6y0XBebY?RCPEDCcZ3XHJWKcbk8#e1?x$D>mCHaP~? zkEm86tVHd|*O1EI)l&8pQ`-h$O^w^wQ*2$)cL??oiv-vw$K6}#TD4XaG0nD3@e3Y* z(sMxYVAB(}6(*6sU%mcSY_dy1JYjE;cTf*Yrab$s8J0{GVvD(Ab+1~tt=(ItV%5=_ zau)6{86@4xuOnq(nvh;qmxoO>LAk8AHiu}* zA-_u7B0{EO$RL@u>?JHEX2$eo z>(lfikXs3kBS1A@gwFsXCt@$8F5SHTC5$igKhU5c<$fXpf(`+EZPPh?J;M3gIGySt zFs)FTr8-89>c_m(Il~$NQmzevDTmjCSIYF$S8Aa*VLbl;t+JyYMf0MLj7&R+YvmH^RURjd!WN8z>&y3mvkhY&W*^g5NiXG+ITE#h zZ*Kz+`aKio!P^#^8=#Iw8+ZceYYdx+I%y;3tq%#s${Nkz$PQo2qzMvM#kok_AVIRU zuE)!bWP^X(#1|7VK7_Zhmh?cJMYdyn8M2Z7HZExsgYE=NWD_-Jg1&1#&#QqHG&~h< zV-ZA1#wyxb`=7qozUcd~?ENy4Qe1Aoo0q7D(BytKA}c>F*`WuT^w?!UWFc64;U3~o z8^^7^r_vT? zeH}@ghznpYF8t?I+6+n8z$9LApKvIzjf)?!03Y)<(1RMv=hi_!;L zUnG4{57BJ!zo3`~YoLXw|7ZFD-$i+#$Ew;}uibntS+z^3+VzKp$=$ue?%qVzGcc`A zIjR<`SFjt8yLR!yio5Yai65Wihuc^EPbW&Ap0_-x4J>W?xcPSTM=gok-Gr8QTz_i5 zYoR_9-SsXWS*lrjX6b0Wrv1K;qQK?t06ukjfvPHM=%H3oq#@(Sx(}P)Z@TYmp^~lS zXI)-7Yfj~Pv4w)K(N8OgWalg-_K3>w=Czu8ru(R?21qFi?G32ygaxx@-K|b z-M?>-m+x8e?!i+jPr(h_b=$WcpXKPh#o$ef19!Yj&wNz6R30zd@fnbNj~=2ilwz26 z)l>bTv|=Ir?&if;Z?-IT2_;+Lk}ct4*LBxo;i7fPyJU`6wyb(uQ(lh!KnyPDH;<&nY^t+@-zt9D<$Xmy59wUPa z3?)c`yOw*t@4N0r6Pr)oFFysGj$)tXQ3F49QP)L22A@wk$b12CLI3y;7N`g*B=6Q= zAs}QXpYz4&KI5(i=aO+(gM-Pqt0j)1rOFO16f%g=M{t%jVPnDl0MQzpM`RoFm4HZc zT_6RtsCfmEspY02u+XkS<-CAQHH9yH1D@1Co}4iQjcGn9BL~iO$1L-AcuR&zRErm6 zsJ|HR)Zj{FgrU1>cX^6!Wi~m&5lh81 zHcb7JE|HvL)^EPb|2^vrHrc{c5o{Ehc1cxq^oMhRYkgujq4#ckYe$#0tsmFiCxR~PSaV-Pd$H!eViT!WvgW=+&6iW^eQbfQ z`)$Jwiu|A#|Cn$TXL!|G#bgkb2!*(6^|N$lzOp9(%9jh)@>Of)BZJvim&PWpG~H_j zXYH!9o~3CQ*!#}fpMiLSH-^@&e_)$ye66$kP*DGaI^!X$?gtH&{u67>q0PoW*=$65 zjmi!k5Y4=3fLRO_uTL&k{21s;{z2T&o4x_@)1YP>T0VUJ{nziA z7GF;^U{hz!t(mC>ctro0qOxry2w}n0Xl$|$b+M47PMp97)>A&0fO)pX${x`3vy*|3J@fa5rKev9WX?Lc+?_vwDfj-IVP!wARDzR#E$ya zWFmzONbnqe{H)_2QAjK}3rHWLZqj*ANqe^+S0?_!M(is=TXg#S zrCld@>_u?_j3RA%y{^1}G7^Rv3hhr3#O`DR;Q*0Lqo_P!7fBK9eK)}mV2_APVub3a za8D$Beg^}Qw$Y>PN&dgm9fiyST}CUHe~xl*BDE&kF`Mlgt8_!U=yRITAEO#fXxRmp zj4le}Y=dK*kxsh(g4-W=H^;5bIHz(8lDSnvZdEe3LC9@@uaIoSv2d6`siSfcp1$>2 zUKLD{DX zU3?!VCTh$+RLu-$A_*T9xS8#cio=ReF=5wQR`S$ihR*dcBhFB;qg7g*n6kK3a|^i^ z#gYt$+M@R;!qy-tiuwyet#zU-*l6e<{{tHQTZS9DIg^%FUuyuSl^BLI`Z**25{5}* z0Ok~FE`JifayoTS8}*KZB&w)GQ!lT6CX!(rXsET~H`7X%hk?rNrB8yG&RR5;I&jQv z5zrR8a0HU4UBN|>-=E@XCVYbn-yNjW-hnF=MGxj+*uo}6ZvBY&XN6)qLL0A%!YPu< zv8|1m<{GR7lhrX{)-kH@65Xd&EAtN5t})?I+C2ISs*VNMVbwJerBzuSE1m;X61jrf zeugzGTOQG-w#z#b-VRvjx(gQSZUt`!mn^HLtx%vW^t=~Y-0|*f3nTHOrg>AUr0Q1r z&GN;8M9F4IP|9l-x86LSEDs8h(F`oNC(3utTjpQAZo?8>Fy6A>v@RAViYc}n9`zQ@ zKE;F``~>xhNht}2KGDAjySu$Xf6V@Z9Nt=rCutH#<0K8l89GraU=^+3)RDtnggEV7 z_Zg<}7d2iatdIimi*x@{IO+UvbwgUjA_U{psbdM8j_EhSRY3p5@PT ze?U(2-wxjxx;_M#_)dK;V!JdF2L*HQ%O5zFx<2l?-SdZC_X_{z$%Oy7P;^}I9G|CH zhcE&=H#ddt?LjJdtLR1fQU)8^HFO}lAwsn}Bp4ltYxY4(9v*)P_JhDTJuxSd(y}2Y z$Uqwz3^G$G8GDe1k8i>7t-&5?FF99B)IB{zqolE2rbekU8cP!2U`|2>29=uVVC*eN zWP_AjBM>-&3oB(I+|DQ>d0d!`hP%n} zLL$0(re390BNN;%8c;?HumRTy#+P}&nuhv0JPwakgT+=#+nGgfOkbZ)76pZ(V8YuH zceecOfxF@}oyk`E$Y6ApJ}4?(XuGxd=H5h6BV?qA%n)4h1b_CxOKMA3X}YKca$Q&H zlSest;z^_#m4B_WTKB%Kv(WfaQK#AP1A`vPADC^O`Q{(wvUHxF()lLjubHv56xnwr zoU{?4tBF=J0`X@!A3|ja=P3oj5Qvw-Jrrmj=5**a;p@#{{;zfW`wCi+wuL6R3q-k% zVeYjcwGMLHZyqs2Ov=tm;Ub(;L?6>qQ?sIYLxu4_0BG%l>+hzK$Y1i5?7(_U*A#B9? z8yG|o1j^#TJ?T)z!-zTeI?B+T$~v?#GX_wqjra|6Zosn&v7%PM=_bhn(SSgR`i)8e ze;dbOr3ihoA`4N?*5@+z+MYJjn-Ciw(f6R$3*xhy5q(cXLNaaEB7ao>HBHVS!!+gX zRujC#K={E!AA}!yml-${bdKhk9P|SdD*J##MGl#b+_A}fQ9#&h(&8IrMiV_)lC<6b zZ+l-J9oKcPL^g2jBzBXN0}7Ip=%lCpefPd)06b9g z(w_X$N1ls2_uY5jeRuipw|}J7Rl~)cCS9=+I+$~T2Y_#>*c;cGXI}UKgDxF z#ZRI{y=Cv`o)fUL=1Xm9X){!JBbNQ^`fDWo2h$Bx*iNto_`AmDx@y+iPVxtmP7;LI z722q;x&iP}%S{=muS$DJN*U!rO{G*-`9d{fRb5iby*R8>4O*8}kQ4_)bf?D zb;XQdVlIc{ontvkcw5;5fhnx*n!S>+yuNWhPn(WPHf9T*w#hzGf|_t2Ms8TNSU}q_ z5jYM3*^~fU8BW_w%$=V(?0NPwDc=U9Z+`YN*DLrAP2)F^kdMSTjjQb7?`&HC%)FD3 zyZ8IQHJS%$G%*bFXc{ZL-!CYM>0|A{k8|s$VLj+?9_ty;nP{JQ`gd2n+jZ;2w14+~ zBkD=xTGya&unQm32i>HE=?O5kf!FBg>y)r1gEw)tJ%j;6w9NBhJBOfZAbW!3@PoX? zr@dS`p!>unrjyK61Q-QJzyyRwARv)&g-b4+@U~yX^6R3}c@r-sYOka$YEKmgJ^~wL zd2~yvJn-go*Pj~;zH|1**<|_J=&p2rd8(jd8q``r{aC?NLB}k7mMw>CZhwCC(w8>I zx8C;E5{k0s`kJ>7L)hMztXZ8bhJogqAJ#P8XX|)v7Zh}xy)ZDT^@^TKkShfx*Dj4- zy061{&jdQA1FI8()ycrxkIg8U(;3MZ7`QTwbO{&88!I5~v_L%sVmMu958G%kaqz$4a5E8YnhA$$J(37Xk25cY@GU&9ZM z+-0ELd_{Nu6ceOu{wHWc=xpO^GWA==d&d0Z`rj@{)Nhg2qdP|*-U%bLZ2(GZ180VY z&WF~vwn9}T)WX^2ErY`8*4}|u$W>wJ)e7Xad1$cpWY75_Fru5~k!m?JbgmyRCpM$w zVIL{PJpKsRNPn0u-QyYbv|Grd?^N;IpP+}FJ8_6^i1d@~i@o&o2qg+nQTP}7NqYs3 zsmb}^`GzbmA8?TiLb*T806VQj!jtz;k(B8^EesALkUQ-eDe6Jma6u5d(0yT#+=;YX zx+j5G$cN7LTsqOKfL*4CnznK68-#@?(b$u0$4Ln%@bhUX1HaNO>BkJu47t ze*DDyPrdupt;(cl`?P0Y!n5y#ho?LTiRwC}|HR_S^4_o3Enl7Rw}=}KM7Aq3)3|vo zIFU;t*G;LW9n($w5>5M(P5VF6IkKQCTOYN?%I{RHo!FGD*c;8h#ChNB(%eO>#rOIl_1R+`0I=XYFtVXiZi;|CUrsWFF zn%L<{Pi@BU*WI(Zc>io3*=O&%x{DhhJoX7QF~Ih9m2f@=MqRlSYQGUhj4I5$D^=sx z)T+J(V&O2{^;1n3Ez}$!!Ptph^|gsAC*7lQQ;tMjfI`2xbNY>lAXTDd)=qi z>&M3J6U(P;n|QBLL%9^H{FgEAOH$={^TJC@tOV3!6*~S^h+C-L1(%Bb3a_AsLKdpk zR4kaZ@*iMb4>U)n^@$(@#{MKD8#M_;7G_Z4k!QS3Aq|-tu~WSx=iu4V5=2mS2!y0D z!`n0oMb*HH)$qwJsDDAcY7jm$+6#K%1gV%Zdjg1?F5*FU5Hj~~g(T z%aKj9E}ZE*c}9W8cEqvnX3?T(k=|YCkRB zucHm-R1FFua0@Zd<%XZMO1M|9*jfty($x0Ofg1-V9!fUtiCXXEm!xs}0V)ni2&~V}~aT+5tSL{T&H^2O_PkeRu7YeL>LHSN&Mtl&cNOsP^^;EKCY6_4C4eTDE}sv>?I41J6T)S%{GZ<HJNRtXNjfDW-8Bm5ZwVP?!l6p17XF%O#N(M&A%^C?fMqKJ5% zreZr<=O@sz(Ph)#>V&s?+S{D)Hb<;?T2>&=SLO`e_B6t2XT}^|8eet`78jT!F7pPT z>~XyPB=v_bvf&m?ivJ zk$|ZcNpUL!I>Os4gNy4Zw9fGrIOi z>8)CjSGDLRgEX!0F~Lj=!H92K=HcO=-9Ig?-|}F!vsaEPc(@Gzi6O;JkiLoBksAi4 z4$^dl7c-#PpV!a4crdb#6 zU2GKgjJ(Dz(QiATmm}!7>72Lwau#$QKj-1e5^3wO()+XVLLDC845sm2wuE74J@3+$_G1NJtNrhz(^RzyZiyYfJk0}Q8cs%Wxu?{ zDb1i#u_f5H;&`|pHy^+Z;!5rlw2j`!b2v6QyKpgGTHo}J`-XeMnylX(*?GqYLNrfh z*x4ps4P>+)9u3Fq68Vil#IhP;#zvTYOCqmjECeTy_>I_{t4CMIF5ULkr;3`#jftY= zqg^v4z_-43=B+dFV`D?(UE?dnqBS6B6+*Xb?8t;Enmy^+fHKL&#^EVX1CX(-M$(IX z5Q$1GSnLo@O>(RVjLyPKCR^DmromirX`u>Qc=#gvL1RZ?FC2a_{h-}PEj2%)G&0Ub zaCR8gz;_F$8cY(^pz+n3>IIop!xtFUAaieCz@RK77;G6zyXrbtNe6{$5PHyog|q>! zXblG70{+gQ(n`ICmt&>coXm;Z(6x)B7fAy*ujup92o^ic7?z?Dz?LEAE;;J-fdve{ zwmUB>L_?90*bt}*ilh!CN{zKkDkfOJ2c-3D zy=-H_CA6M)Eq>{UmBmxBQ7jd>7cdJ&&s$WvzN%J|QKmQwAR~Xt-La-DUws-_+99aL z7`2pIRE!kv2B1#cr-!16Im0KW{;9mh{g$BvISaT9`Tj0&KCzkxq+x@AO7;k%UFLXs{xG2GW5l=;Nm{*h^i za+U@?E0j}hCPHUI!`D!j&X>cJaaWQ`cr)P-sB!~sTXsMbLX5W(B@J`11(nn0^W+y) zc#nGGAbm$T=Oi(_Qwp>OF7b*F`dXjdMvNfXq2xo`SGY)jx$%6JsC10V& z{BpQ8gcMRLr@g!ig;?u`);6^sB62}Nw|R!62p)pGi`6e>q?^oOLz4t|Z{UR-L)dtA zV{9vDy&yRGba6|fxMjL{WukaxvUoLgd>IX5FS+*fzD~{ww9SD~dam)Zl!|O95 zspK_8oOhgg#Gi(;^>jgFqM#8TxJsa#X|gYq!krg2K&KmiE#GXt-a1yDC|WaKizKr5 zjvMGl;TwnJ#mUlU7}#(7hCNZbCgE8FMFCgdw5v4XDvh0;ay8D{(HIbPFxC66_Qf2@ z?5c=yCLb};#k_iOka7zri`vDGtSzMSI1r(FQ`K z;H`qMmA_S=J^EP3%p&cFnkoBimTSgf4i+j3Y`>>*AA$uy%G5 z5w{MuA`S{<43#w$FT9BQ4tkg}Az6{=gE=g3K;9tbNy!l2e_6TdCD+RiDQ`yJN%@Ea zA!&_43+j=zyixT_4MPR%O~qT)h-uy(yLx9{xkD`vI^VEdHsd^7!jc82QW>C=6;de; zdRwL8;RweD&Md(#TPE%QO27owMxg~`qs51F1+#+L!R(vv42r>mclz*7JD$;?j^>?6 zjK>akkyo`%!G!4O;6xMpxsQ<{g}tj*t*Oywov3=Egm%*O;c4vIg%`*mI<$A9t0q-- zT)WL7eF}w zDQRT5+}bu)$(?Tb;4CDM?mRyFI0PX1P;ED7RYi?=3+ko|mL&?7jcuFk*qtoc6E)w> zUj^APO!Z#>oqIx*6&%=e_T8L6xIoDx5T#4FHxgxAJNru_BEn%ERHkynOjME)DAcw`dz$$_7ZT6`OzidR5ZWrBb}2rPpY+Jy7ge9^&lw? z!Q=GXt>cY}$_*1kV#PM>m^LrMrAA$`0c;zf0KqrUTt7paLSy}jvWF%HkVKt#3){!5 z->-kSeq!6V_kL^d2fk$CLog{Ts)?1xdnhuInBPVQXC-8C7PZ{LKK2*Hw!hsqX8Dc1 ziIUcYA8v+bBo9AqQ|6;t#xQg%Tzz|NOgxsgL|@yMjMj@OgHH<6qORko{h*7Fja}iw@UvY(lps5* zoImSPCFH3VR#%-`2a{@jFo0uDMy-Iq8Vb=3EldyXXdF_iJwwn)PZGC5GS zn~`}3987#TFD3|H46usJ;Kf|FgR9}tJM@NRCT%+A42?T63y z!9{iu-uEcxZrE@bP#j2cniuSyZV}blbq+W(MFVZpk z%)?KLVI{RtOS|2QPS6>+to5C>H`czh@y5o9hFcFOm+b=2e$$TEj>84P#;cI7zcvX1fg8*8VR9ZoDeEQ#>n7#w>lQMP8HQ!L$#bFnEKsyC5+u@+KgYRsU@ zn-WF4Ct8q1_NH+e5A9$M_j_QENYA%jUA02P5BC^ebotHb^Pe0uPIep#gD@H zLt<%1bO-K&#)WIVYx_p`#rMWHj2p(6kJpR~6SWfsw{mW+yjA}}wgPLn9?Rl$wu<+Ht+((3eykye8}i8Y9thIxpVtlE ze2}Jkcg5Si@|)DZ+I?Dt9+)$5c8&25S_l{`n7dUC4mF~Uxof!9%f#H_OVz~ymrVj( z9}rAX5qR!DfDi_F>Qo?=&_PgXGgZAv31dJm1*wDxRo;hgGs6ZM=U`y+=fOF0=fR|5 z`1>c=8Q%J{LnXP5)dZIaA};}#X7bDCblD6GAsyDy5i!42^t2+q3r1bl*P7mHde1zW zzf$z*Y(K{R)EfJ<#H%`0B+_il|P8;A}?bbtCvdnz2#@*xz1-N~gTjIZOR2#N3%B z+$oe^_kH)**VCG%{~c z7t~U-FhDf`<|Xs_z0@pY^t-sK%fF!qUZI2{Hwf2}q`f>CWY?}O4|i`tFvtV@5A$*V zeQKwd5=H~4GeB6vlM0%!c{69r7ojl?_gssyf`0~N3yEms(~O~pQsgHlhrQ~Pf-EFu zE+i!$IBor;I>%o1kF_RyGu$P5%ThU|cXJA2#bc`{b2{h!;Ki#ZbC%!FvhLP12a8@E zbAU@4;}oX}`ZD%1IKYLuV(zj;F8EQ&T&U`%-MM!>dC}q5vtWp4FT3x6VveFJAO_al zGJViV#ytm91z>6EvuZ&sL)cHzg-lbP8tC?B)xz@QZzHpmF!cp; zMoKjoTrap#u5k(oO(@r3^vb6#Qv_Jh7X1tv_UK+D;j6~GlzSS_X+Lqwa9EB8CHxRi z34cw=v$Wz4Q({I!9(#0>g7rA^TJmyuXn@G5Z-8@2dubb(e zy48ug)neV|Tid3$9lgEnC{wT|2b+^F3hg!RtGey05?xiIuZsPwzU`|PUDcwmns4WX zyFBjaPO?RJhiL2Ivz0j(lm|4^&pn6CQnLkt0xWx>A`O#m=8?@5q<7n<$}K^t zQ{yj`Uj-AQ^74D&!l8_2H1myk4_#=>Ce@SbBa8)S^D;#p^5V*lWlpP;r5uMmWQPLq z!H#AcdP&75oijXec7TjYhyx92TJHd_L-M>uCIrFWAr@((H)9^OObE2<_0xQj>JR1% zdjfITp_}RSMTZm4lDKtjMIz9aDlCia!RGZ8iMGPoEM0a%Y(*j~5Z^tX3vZ=~wR=;A z6{z5lD%f?|d9mC?R#`kaUAy{r?P{3P;x-)eu;-1sqRPk~s79l)#rqAt;WL;BA9!Sc_mRgAZa>U$th9-&Q2Bm*3*RN| z$2-^_l{I&>GM7gv?Km|!452HnmH8Isw2`&;Y-rw|y+~`|6yAX~P*oGjN)?s8*?1i~ zRYv=Y$ey&jFy$+K`|;bpw((=PF5g~pB;~J|_SYx;^x6-@eM{QROYK`NnUU(71>RFM zm0e|+dYBX^lo`Nlq)WZuVCeZZ47Ni)7q^o5J}`8qH^3z4y#p_V3s?N^;Gv{L z^x8*5xLG;x4QPx!j}9TEXMiaigaSS22f79|KWem+^}G^!yyaZa+1|kL`2gfv>?Vo* z`S;)^^}Y$el>W0mUF;!=Q=sR>;4{5}zM;Sca_by8(Yto7`k^#jk1LPKgI8avd`4wc z;Mq#JYw8`STpKtd4EJ7cXws-Je=Dml^akb1^Qs@8l5ss@|THXV{m} zF!uy1xqu)*_c289XN|VmES|We7=AuNfeJAogUWlDurB~Bzw^UG^M<=Oz>Mrkxe`Mt z4KWQIdtHBM5COj>K#W1Ugh9@jpr!z<1m)?6g&@LsD{n-Q@NI|Pjsza ze2&}D5`zaF6(DGOcu%79g@Oab=E79ojl zP@t{5PTjuEXwMbm9wd5maAn)x_j%Qm?v)2D>FqpI@W7S&6n0CKg75 zjiZg%T1H#qzW9lxw*gK_iz~n9J{awJ`}A0k*uLw7>Z#fTDaH3?7(%iU8#rUq zw{q4o+!Q&38Sf)~gi{n*6u9FEa?GXA5k??>YOVVxz#v;vp@7gc0QCSuui4CU zb_`<|6B9$YLKpyXhtmk|JS_C|_l0|*xXp-arrzaLF~yKDOttBA`RX}|K@nc0gk-T= z77#a3H{8C6>+)F;<4sys@1S93TU=E)UAZz*xiVR~I$NlCUDzY=}&bwPs9NYP27nBJU{erH^_Fc(>-BEL@2*`f?nX%ysN1}DB zSlC5DNO*{n(>LwoH50yM{f0!@hKXmy(j8NtowLw|-KD?pK|uRdUiD<%##<(_cDtCn zL-g;AY@c!aqfd$Ot!oP`Mm`jM*+0Pu%?sPBxj0pnLtX;&TrX)qqbwKY8M2KvEm*Pm zDcKe|pi6BILVJ-fBFI}z1A~wPam%AB_?!j7k-_ZMZp$t&ds?)lZ3|0IrOknz+CAVK z1M(eB!6kbSqgWq&dO9?J;iNtNkJp_S27x(;LUm1nI_4^iGDCgv?SssYp8inpac~a~Qp;D7 z$a$g{fk|Xx{=&`^gMytK^hQH>9pHj;Bi?18rWnkaDcSX3A05g%;(m#_OV1D7NxRub>|QEH!T4fxVw zQ*RNzNKfUS({%qy_yAdBXvjdil#>fp^ zxR_Bo#4RTpR30}V(8NAhUU$PJe3#x-GyiqcrYYaqi@nq{G$=eyyQ(RG;CO)0@?T}2 z&;Vj@u$HRlx1`@en}*8fuLZpLdo*IT^Q&_nez;WmPQIive;&{1KH9!X4KO&sK8m>i zSZ8Y66cH($%|~GS#lE2~_(5mi|EYWa*C}5!K~0#1GJ3FN`^A&J=ZVza4XMQATM>v) zDaq`IBV+gpb?0k}`TPDR+Tl*WX_)xXvjH;*|68t!HOmZcSq)}&%W`D1&0A6?3>`~Q zGfG=w4|A_}1jpFJ+_&1IDzbQli53^kv_td7A>|dU3v3H!c7e52rF;%sqSBhl%~*G= zXSCpsqfc@A7qvaB}_h0N)7yb`uPGS zWE3ovQ1T8XzemaMQ}SOaxkJemC6koQQt}U!cxjL+;*d~INd+Y=jLa#z%S=z1`S&Wi z0iAg`!l#5BcM0SONg#JI+#0-_a=Da{;{t)Kx&<;s<)-6gZz7O%OCqu;$3z^&<=`|I zQ9%M>Ow6C5u$ew^6D41jlPl;{=o3(@FX=uum6*5cXV>SO3uo*r=FIpz+o7{$e`GM2 z3+If;%~B4r7O6~cu9C`-0~?%|btq*WO4)LyY`IisGzX+Inu}3eJn$4Y3A?30S=K49C-Q4kUp4D7E=QWyZXT7?7;9L1Hjk%xYH?Nw> z@z0v^7ybA6XYG`8z}x<;lX5PdFK?D=&1UO-`Lk}yd328Kxg5$h={nbZqSGU1(XHM< zx%N4W9)n^rnQP}<9`lOXMlgyI!E)AaGq0S<^30m?7qvY_vsTL4bpA4S5AQ_#PAYRz z`&r0+?AM#?sKE+quxid_H2Y^AS!k%RZPtvxXb2s(QqD#X*^!$ov!K35Z*HEoxXgvA zJa}QJzcriL-x+(>oEg98O7`f@xwCLCz$bEtJd_6W4*h+a%4l%XT>O#2Y0jhZESY7w zqGfpWm_FOQVJ=`cZ_#5|`138&pc~9Bb2hp&mu)jwe(bL_SKi;HSDPy^uacTWZno2A zZcOEN%o_2R^$dUK{C>Q$$!~6+Ybr4#80LEU=~+X8IS;DFvqt*cw3hvyu@`(~#&4z7 zy!)N<10NY&m~`HI%9ZHxadQ>}nzm(|y|aN_X@KZ&Rqd>i{`#WQI&6)B0 zqZX67>?4=eTr^jpHy=SWo#yPhe!bqjk)~)f)?c=HQ>rvDYox!;ZT#=XE$lD*IF!!? zEM~`?$AE=mb7FFf3}!51o6)>a56_2{wfD0M%w77K{GwTW6Py%S;AC~y%CZnR`DUFg z>(b?x&$5~rf6Tj^O6NMWFnDE7ER>=ubKBe=*kofBK58_Zm(OK8sNYueUj3Xm-@If4|tGE_JL zpjx#I7gRpO7xkAZ)VEn^1m&&J!t{E6Aqa~W`%7Vz1r6}w1JFhn=z3F7A4YD(LMg-| zCNQVq)u#aP?TA1;5YVjk1PR&MT}-f;A+WEZdV@7>7`njm7m&mFcGrr@v;YemJzd|Z{*Vd1&kDHRd znk)ORY>TY8V|AtS3&}+;Oil{lDjZvptZ0Xo{-tF8hKLIi;%E>q&YnoP8$?^fjN2D+ zvMJM^Ey917nk{wQiFpTgHuAxUXhvl14(n8X&;&y!g=vWyp*67J3Myl-%rfRLgO8!u zxwDxtFc;JMOGeaDTUG+|WYpOVL8r0;R5ocCIURad`ht5dimxq}^B*ds?dZ?Ee?My($+Y1Fw_DGFzX$kY2&TM%1 zhm9>QEmHK5vVy?IuYX%5-oYpv_u6~(vuOmV1A*t=Oi}#-QC`8=C%oY6{ zJweGmTG4-qyI_dAbaob8H0uAA>?>PRR$FB2ix*PaInl~!d*n0>Lf!r=yYBjmV>?*z z<4TB45l`gGzLeGX+R0e`m(C@uRq?^w))iRSGblR~3uTnq^50k8H<|c94ixm8GCoBor4&{3Y=l=en2f)I%+~c@af3r+RO~5 zEE(*5@I|qjq+I}RB21v5t^uAR_>Y+!PeonwvK+tSWQiwe@h-TauwrDI@CNk0 z1mj(_paOo5*co@$sYYamvWsKHsf;vq_g^r>D%HXEIZVq~jGi*NCB*t+F?y(6iOpjU zZz92%EaqmSP?9e9NV2h}z>si>_Bud7PcTTO+&(fOIj3%6R%WV_YS;{WGeooZ2uvH5 z9XssEU|V)GZ8fI4%BU%d{lgt@7owNKuTv7F3^lpYY$C(}31P!ie*VVm|ZD9Le z+51l)7wcR_(62LR!2#cF0mQ&p_RbdL=cgg4m+Jn&zQx+rsr#c&`!>DtJ32j5?TAJj zF^1PDgbhwKsQKK(*+w`Lnpk*AO7H5KsaZ>nye{v4rk=h;oAZ?3)w5Q!W%+dHUOPW; ztJ>f41ipgsyvvphXr{|hyH{`q`73~jD9ZDMsr2cT-E74>vKNk)B3;VH-nSwYsRN2` zR(K61oDlfnDL~i($cP}9VR=B3oW*U7*i`ddO=}+|AR$Dfe`%s9Fm|-0VS*|{N6c}R zSN)FN8zoLY=n|SbVsI&n|S|3cC z2`2$ZneH=A8Z*iHQvwMzUBWJ^XJM5>At!!hzz>cZk5V;CihyE-mZ2dAKDOw)^gtnU z3O>^L%uUeEbNeY=p2+ZAq<7(CwC2s0>n%5HCf#kKZTI+!iJEV>e5*x-rM7Li=x)1< zxOXeAZ5-X0M1Z}X+wMBiwt1{(Joo*wcgw_v%}Lv4(Os9SsQp^mTNI@^Vu@N}mLKMn z#2&k88aw)1u4K-Nh~+LEmad35j-5#Q+9Nj5=2_lX_gvi*tGn&0fYEpifeYc8za+j3 z)~qvaD<@17r^MFnfFZuZ*vj}y(c1`^VY9#Ly6TD^j#VaYrIH8uFHG5%&DP=5pf|E| zH8B7Df@>E>FMR0|0g`1`oe^W?C_pELX@4ygTluAD#k_|2VR-YHvNgi5zj+yOmn#o( z@@#-c{~RSut1@GK z;`AWSLC&u`qSZ0qXrt)t6iuDuCjOTbOX!JmDI-D$r>u#It%|dkA$M@_qz%Xz!lvnM zn;~&?N4$%yp z#uFf73yQFI3*a2xW)56+VH264Y%}exN_eZpoa*?Nc<^ieZ}rE&D7rf%CY+bAZo0ZD zwq_FXdwux`!efZ8X2)psncL1%sM1rghgkKLt9&*G4T3}G@=B|Go_e~1mlInU%NDru zS^6Y+Bn=wA48`WJU;z%HtNha9LuBCIiXjKw%u3IYuP@dTC}2#ihIhk9hOCsj@xf86R=f)~(M z$&Pwo97%Z#V^z`8SdYlGLVlQ)JDpXY$SS{S9NYO@uF0$w5rbUiWV{lpD2D(fMcad1 zF;}7s8B^eZb~)5J!kD6<2~%PI3N*}7Vqp`MdDoh8*S6@cJ>#w;_bhwpHcQVILz`HC63^YpbjN)12z zMK#o?a;aWJ-Y;bFkS0VxmX5cazJKNik#GLf4>vM;qHpt7H&#x|@>6NUaOiY%SIOV~ z)ykqx!W6oH`d@C`zx~jOgPU-_{nY7a{a@et{qReh`hEo9pEiJD@oU%V`qm~6ktb;d zeFqDt&jWM{&$OKi8_os7hBJY*{v4MTd=GUp?IhmDIuIy1L#wU=8FrA{+|m5o)`C=y zH@YhJ2-&6{7QGE)ULNl|g=Lv{+nWD?+qo}3|A4#x7oX#IkBn}NSBc)*_z8|9P|Lm- zFE4#F=f!82zDZk!^>}%-Irg;Zt`Kb%)S~jb6QdjAKG9nfe|XF~UU|bcepp2C-W<`r zPPDB{Wo1V$#Hz189}9s$5$}j!827*R+(c!fcKyU*v1ZGy9MRb&nz|lv`jFIWzETz# zbQp2Egk})QQl6La6hKQeR(8f2lRctWQA)Ku$XI-IilAj%W}7_Et!y%oa_tb}3hFKQ zRI$z>(s9AX5PyVKU9#5-Fs2Bh8NF7@GkOiYgY0h+ug*g4F<|{P(_RI{1wal#aTUiJ z5#>-|?gnXWT*%R{|_ z#D6=BSqt0v`_=~b3sKBc+Iko-gC#xZ#@U2Rs3cHLehf3roA7CFnac2TLdk(PeV0lZ zM*U3`gs@k20ojA|Wom$u=V`nBCT>Ax$)U5kBhN&`lh(>qPX4v>(ej8T<;7j)PW!4iVi4&bKIT@DI z9Z0p$Q8+sXCOLrEZK2i+s62Qf)Qb4cQp5WPhXPQEQ|bnK&kZAb{Gbp5Kk7U!U#wEn zzwhm#Xm1K9`IH(d4m$XFsO2==7!IFk=^La_aa#sLecM9_5!Xw)7a>WdN#aaTs9SbE4qAW|1tEE1$PAV~rx*!iQavpnlvKqIvbkyL?(Q4t8|v%=mDP)dZWgH^(f&HL|iavG;m%G`OO9%X#+RcIIs*g z?ChB=s_}vqx~A&glAsfH3Q@l=JMxtJ$fwu@FR)AW~CE!zdsh_fXNh$t^xX|2)XT?;7La}MAW>wGO+ zGgL~KJYD=A!;O0ZarsTwwA!d*m`|1oMGLp4ZQOjwx(Hm6zv|pe#D|{8>g8+uT=U@~R zb`&4S-e1?YuK0SxLNDIG&ep28r2^P_MrY~jNCY{~gi`ng#hc;Xz zLppg%kfurzG<3`{UMm)_p_>IMU$KZFFcnQ>kB;pbKP8rL7~M5f-Z8rC zUQU2D@F5yls?Lm!Nt&i@MG0F`Z1q?tX#Xi&8$VtY7=wP(@xnHBCo&7+6SfT9UvR0S zQ05lbuwVEQTEw^$kP`l$=V{!T3fnx=ial`$2q^|;`F5)SoyRC7g9*6}O$sW+-Y^dBRm5w~bXyxthqVT^hX1#h?sc3wlJ0 zL2K{?n95`xTc}0PEx3YQLQWB@G{f5~Q-IG{cs-?oVv~a#oG&yDqs{ro-$%Cuwj>y|B1OHl5$FxyKTBeSCm~JEC`V8TJjlP=J+e^Cp2AkP7=Ql!C(W8T!p0(QCKI7g$ zYsO!!4qquN!TCnobyT*GblE;K7@YLW|rS&pfTT$P&^Nc9*t0XM0DbxwIcqxJ9LJz@6(bz%N- z)F#wX{)V0>xW}YR;rzq09)A}%^L?1BPP1piwHb=Y(6wQovsj7#IGR04Ewax3H~Lie z0kp)zZayON(~55S8?@F;j1QYOpXwj%8OrG9B3*pe`NsuXifKo8_o-nbySlpt+SO@W zk8m2OaHu!!RL{4NK*veiol(-#bK;}`&lLIswKd@Wi_XJw{B;QPX=asdv8+mlYsodxrWQ#2A z%P48NaFVMS5S^CxD0krSmJlRhtkSCxff?T>Fnx!=L|txq!IoMH{iKbbE>vp|oa#H> zA`Lx#H0>j`7yfv|X8I;lEQM1GG#7gy~r^6$>UPB?CHc z8O!8hOeDkD{fzs{ctQ-i&4+lO+SZ>N3=a4AZWJ6?m*}A`ltv0cqF#UBWYn8Jap?3_ zKhU}F>B{fvs_*G)?&+$K`B3NmkP1sb)MbCDv)|Jd;OB=r2P@3Grz=LuJzWth+|w1_ z(*;oe16|f%Qwe?|t-Ggdx~HqXr>p-^myeYFuV9U%ecUQz|51*bsMdh=zC~xsxo4^d z*bP*Px{_Iw%Wb&goGsB6l!!Y2$CgU{hUl4VgQJ5V>yZ6;k3L7=8Y}u(hd*=mM-6(* zoM7_nq2Sw+r_a0Jp))}AH>uBu60p7r;MPzuOS#!Hogr&lpO?_*{iC5!@1NBnq1(_c zr=P2J2J1f>4&>-7W_6U(E$b{RmFI^Ojh-*8GrGemZmu?al8@_zu>8}EYv literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/_pytest/__pycache__/threadexception.cpython-312.pyc b/venv/Lib/site-packages/_pytest/__pycache__/threadexception.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5d63531a52e729fa6675ad6078ae03fd26de535d GIT binary patch literal 5751 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/_pytest/__pycache__/tmpdir.cpython-312.pyc b/venv/Lib/site-packages/_pytest/__pycache__/tmpdir.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..23b6114aa9d16c878dfe18fbbf54b5ddfb8f8672 GIT binary patch literal 12715 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/_pytest/__pycache__/tracemalloc.cpython-312.pyc b/venv/Lib/site-packages/_pytest/__pycache__/tracemalloc.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f2c85c962bc5f3393f82fe94998c3ed094305a2c GIT binary patch literal 984 zcmZuwL2DC16n?X_*=Dybv9TI!K^84S#3a>|6j7=c6hwvMp(U_vcBje4-JLKqF{Y*x zL7_)INDrl;c(79NKX~?{^k7{P1W(?A?Lm6c*>1H3eTO&iz4_kz-n^N4H#lg3lxM%r zdIkpg7CV1LNer}kL>f!rf{T0z&@4jWs;=@#omJX6pZXrd@+j)J!;lJFcr@e=N^*C! zDz&VQHs45N2|Cb4ZKQ*%xZuMA>Y#dRA9~m9z}jw&1*M}r9CLBK?>{J|uKycd+YSA# zfL*nNmtjSj1~;{!x>`qF2DfhoPs0fiyXOQL1BMaIKpVB-9-e^~%BOEcz+%8QGUg8( zH!F4G2-Eg`>ezy~rY$Cn`CBz18hoNua;d|MjkX|M6e+8gNLb>+uK1)>qpgCVCC6@v zCL@KG%|b7%@?uQ{{s~4nZ8B#sJb!g6jMGfPY=;Cpbf%zYm53x%v7I|+t45fF)QCxw zwpupdYprNZ6JpM1%sI-;fJ&qn&e38%74`3{8uecb8kC7C#wd%1C4rR`vvxo+Po&nV z7e<<_6WBuLYe}~(zK=)|m1rQ@lJl^nhnvjj@r|0XY~Q{zQ63*3zco{q*`#1Qwo8I` zxzR2No8Ku^o1RO2FC^u=B)nU`?p4a%lemUtwyT7ftz@31eND>|M+f)$RzNtHU^XOK z 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 N=-qeq1Tf_Ee*ukw&Kv*$ literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/_pytest/__pycache__/unraisableexception.cpython-312.pyc b/venv/Lib/site-packages/_pytest/__pycache__/unraisableexception.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..56fb3a516601fac3d8f7a3c97e0d9afa962ca8ee GIT binary patch literal 6252 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.5.6 for python 3.2/3.3 (older versions fail +to find the magic string, so _ARGCOMPLETE env. var is never set, and +this does not need special code). + +Function try_argcomplete(parser) should be called directly before +the call to ArgumentParser.parse_args(). + +The filescompleter is what you normally would use on the positional +arguments specification, in order to get "dirname/" after "dirn" +instead of the default "dirname ": + + optparser.add_argument(Config._file_or_dir, nargs='*').completer=filescompleter + +Other, application specific, completers should go in the file +doing the add_argument calls as they need to be specified as .completer +attributes as well. (If argcomplete is not installed, the function the +attribute points to will not be used). + +SPEEDUP +======= + +The generic argcomplete script for bash-completion +(/etc/bash_completion.d/python-argcomplete.sh) +uses a python program to determine startup script generated by pip. +You can speed up completion somewhat by changing this script to include + # PYTHON_ARGCOMPLETE_OK +so the python-argcomplete-check-easy-install-script does not +need to be called to find the entry point of the code and see if that is +marked with PYTHON_ARGCOMPLETE_OK. + +INSTALL/DEBUGGING +================= + +To include this support in another application that has setup.py generated +scripts: + +- Add the line: + # PYTHON_ARGCOMPLETE_OK + near the top of the main python entry point. + +- Include in the file calling parse_args(): + from _argcomplete import try_argcomplete, filescompleter + Call try_argcomplete just before parse_args(), and optionally add + filescompleter to the positional arguments' add_argument(). + +If things do not work right away: + +- Switch on argcomplete debugging with (also helpful when doing custom + completers): + export _ARC_DEBUG=1 + +- Run: + python-argcomplete-check-easy-install-script $(which appname) + echo $? + will echo 0 if the magic line has been found, 1 if not. + +- Sometimes it helps to find early on errors using: + _ARGCOMPLETE=1 _ARC_DEBUG=1 appname + which should throw a KeyError: 'COMPLINE' (which is properly set by the + global argcomplete script). +""" + +from __future__ import annotations + +import argparse +from glob import glob +import os +import sys +from typing import Any + + +class FastFilesCompleter: + """Fast file completer class.""" + + def __init__(self, directories: bool = True) -> None: + self.directories = directories + + def __call__(self, prefix: str, **kwargs: Any) -> list[str]: + # Only called on non option completions. + if os.sep in prefix[1:]: + prefix_dir = len(os.path.dirname(prefix) + os.sep) + else: + prefix_dir = 0 + completion = [] + globbed = [] + if "*" not in prefix and "?" not in prefix: + # We are on unix, otherwise no bash. + if not prefix or prefix[-1] == os.sep: + globbed.extend(glob(prefix + ".*")) + prefix += "*" + globbed.extend(glob(prefix)) + for x in sorted(globbed): + if os.path.isdir(x): + x += "/" + # Append stripping the prefix (like bash, not like compgen). + completion.append(x[prefix_dir:]) + return completion + + +if os.environ.get("_ARGCOMPLETE"): + try: + import argcomplete.completers + except ImportError: + sys.exit(-1) + filescompleter: FastFilesCompleter | None = FastFilesCompleter() + + def try_argcomplete(parser: argparse.ArgumentParser) -> None: + argcomplete.autocomplete(parser, always_complete_options=False) + +else: + + def try_argcomplete(parser: argparse.ArgumentParser) -> None: + pass + + filescompleter = None diff --git a/venv/Lib/site-packages/_pytest/_code/__init__.py b/venv/Lib/site-packages/_pytest/_code/__init__.py new file mode 100644 index 0000000..7f67a2e --- /dev/null +++ b/venv/Lib/site-packages/_pytest/_code/__init__.py @@ -0,0 +1,26 @@ +"""Python inspection/code generation API.""" + +from __future__ import annotations + +from .code import Code +from .code import ExceptionInfo +from .code import filter_traceback +from .code import Frame +from .code import getfslineno +from .code import Traceback +from .code import TracebackEntry +from .source import getrawcode +from .source import Source + + +__all__ = [ + "Code", + "ExceptionInfo", + "Frame", + "Source", + "Traceback", + "TracebackEntry", + "filter_traceback", + "getfslineno", + "getrawcode", +] diff --git a/venv/Lib/site-packages/_pytest/_code/__pycache__/__init__.cpython-312.pyc b/venv/Lib/site-packages/_pytest/_code/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4ab0b71bc74eca5fc91775f226bdbcf6133c1e98 GIT binary patch literal 684 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 b{LX;hnGLt)-MnwuFZX3{c*Tp~vdaG-nw69# literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/_pytest/_code/__pycache__/source.cpython-312.pyc b/venv/Lib/site-packages/_pytest/_code/__pycache__/source.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bfdb07f14952eaaa149c3e633b116d4ecceab869 GIT binary patch literal 11944 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/_pytest/_code/code.py b/venv/Lib/site-packages/_pytest/_code/code.py new file mode 100644 index 0000000..add2a49 --- /dev/null +++ b/venv/Lib/site-packages/_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/venv/Lib/site-packages/_pytest/_code/source.py b/venv/Lib/site-packages/_pytest/_code/source.py new file mode 100644 index 0000000..99c242d --- /dev/null +++ b/venv/Lib/site-packages/_pytest/_code/source.py @@ -0,0 +1,225 @@ +# mypy: allow-untyped-defs +from __future__ import annotations + +import ast +from bisect import bisect_right +from collections.abc import Iterable +from collections.abc import Iterator +import inspect +import textwrap +import tokenize +import types +from typing import overload +import warnings + + +class Source: + """An immutable object holding a source code fragment. + + When using Source(...), the source lines are deindented. + """ + + def __init__(self, obj: object = None) -> None: + if not obj: + self.lines: list[str] = [] + self.raw_lines: list[str] = [] + elif isinstance(obj, Source): + self.lines = obj.lines + self.raw_lines = obj.raw_lines + elif isinstance(obj, tuple | list): + self.lines = deindent(x.rstrip("\n") for x in obj) + self.raw_lines = list(x.rstrip("\n") for x in obj) + elif isinstance(obj, str): + self.lines = deindent(obj.split("\n")) + self.raw_lines = obj.split("\n") + else: + try: + rawcode = getrawcode(obj) + src = inspect.getsource(rawcode) + except TypeError: + src = inspect.getsource(obj) # type: ignore[arg-type] + self.lines = deindent(src.split("\n")) + self.raw_lines = src.split("\n") + + def __eq__(self, other: object) -> bool: + if not isinstance(other, Source): + return NotImplemented + return self.lines == other.lines + + # Ignore type because of https://github.com/python/mypy/issues/4266. + __hash__ = None # type: ignore + + @overload + def __getitem__(self, key: int) -> str: ... + + @overload + def __getitem__(self, key: slice) -> Source: ... + + def __getitem__(self, key: int | slice) -> str | Source: + if isinstance(key, int): + return self.lines[key] + else: + if key.step not in (None, 1): + raise IndexError("cannot slice a Source with a step") + newsource = Source() + newsource.lines = self.lines[key.start : key.stop] + newsource.raw_lines = self.raw_lines[key.start : key.stop] + return newsource + + def __iter__(self) -> Iterator[str]: + return iter(self.lines) + + def __len__(self) -> int: + return len(self.lines) + + def strip(self) -> Source: + """Return new Source object with trailing and leading blank lines removed.""" + start, end = 0, len(self) + while start < end and not self.lines[start].strip(): + start += 1 + while end > start and not self.lines[end - 1].strip(): + end -= 1 + source = Source() + source.raw_lines = self.raw_lines + source.lines[:] = self.lines[start:end] + return source + + def indent(self, indent: str = " " * 4) -> Source: + """Return a copy of the source object with all lines indented by the + given indent-string.""" + newsource = Source() + newsource.raw_lines = self.raw_lines + newsource.lines = [(indent + line) for line in self.lines] + return newsource + + def getstatement(self, lineno: int) -> Source: + """Return Source statement which contains the given linenumber + (counted from 0).""" + start, end = self.getstatementrange(lineno) + return self[start:end] + + def getstatementrange(self, lineno: int) -> tuple[int, int]: + """Return (start, end) tuple which spans the minimal statement region + which containing the given lineno.""" + if not (0 <= lineno < len(self)): + raise IndexError("lineno out of range") + _ast, start, end = getstatementrange_ast(lineno, self) + return start, end + + def deindent(self) -> Source: + """Return a new Source object deindented.""" + newsource = Source() + newsource.lines[:] = deindent(self.lines) + newsource.raw_lines = self.raw_lines + return newsource + + def __str__(self) -> str: + return "\n".join(self.lines) + + +# +# helper functions +# + + +def findsource(obj) -> tuple[Source | None, int]: + try: + sourcelines, lineno = inspect.findsource(obj) + except Exception: + return None, -1 + source = Source() + source.lines = [line.rstrip() for line in sourcelines] + source.raw_lines = sourcelines + return source, lineno + + +def getrawcode(obj: object, trycall: bool = True) -> types.CodeType: + """Return code object for given function.""" + try: + return obj.__code__ # type: ignore[attr-defined,no-any-return] + except AttributeError: + pass + if trycall: + call = getattr(obj, "__call__", None) + if call and not isinstance(obj, type): + return getrawcode(call, trycall=False) + raise TypeError(f"could not get code object for {obj!r}") + + +def deindent(lines: Iterable[str]) -> list[str]: + return textwrap.dedent("\n".join(lines)).splitlines() + + +def get_statement_startend2(lineno: int, node: ast.AST) -> tuple[int, int | None]: + # Flatten all statements and except handlers into one lineno-list. + # AST's line numbers start indexing at 1. + values: list[int] = [] + for x in ast.walk(node): + if isinstance(x, ast.stmt | ast.ExceptHandler): + # The lineno points to the class/def, so need to include the decorators. + if isinstance(x, ast.ClassDef | ast.FunctionDef | ast.AsyncFunctionDef): + for d in x.decorator_list: + values.append(d.lineno - 1) + values.append(x.lineno - 1) + for name in ("finalbody", "orelse"): + val: list[ast.stmt] | None = getattr(x, name, None) + if val: + # Treat the finally/orelse part as its own statement. + values.append(val[0].lineno - 1 - 1) + values.sort() + insert_index = bisect_right(values, lineno) + start = values[insert_index - 1] + if insert_index >= len(values): + end = None + else: + end = values[insert_index] + return start, end + + +def getstatementrange_ast( + lineno: int, + source: Source, + assertion: bool = False, + astnode: ast.AST | None = None, +) -> tuple[ast.AST, int, int]: + if astnode is None: + content = str(source) + # See #4260: + # Don't produce duplicate warnings when compiling source to find AST. + with warnings.catch_warnings(): + warnings.simplefilter("ignore") + astnode = ast.parse(content, "source", "exec") + + start, end = get_statement_startend2(lineno, astnode) + # We need to correct the end: + # - ast-parsing strips comments + # - there might be empty lines + # - we might have lesser indented code blocks at the end + if end is None: + end = len(source.lines) + + if end > start + 1: + # Make sure we don't span differently indented code blocks + # by using the BlockFinder helper used which inspect.getsource() uses itself. + block_finder = inspect.BlockFinder() + # If we start with an indented line, put blockfinder to "started" mode. + block_finder.started = ( + bool(source.lines[start]) and source.lines[start][0].isspace() + ) + it = ((x + "\n") for x in source.lines[start:end]) + try: + for tok in tokenize.generate_tokens(lambda: next(it)): + block_finder.tokeneater(*tok) + except (inspect.EndOfBlock, IndentationError): + end = block_finder.last + start + except Exception: + pass + + # The end might still point to a comment or empty line, correct it. + while end: + line = source.lines[end - 1].lstrip() + if line.startswith("#") or not line: + end -= 1 + else: + break + return astnode, start, end diff --git a/venv/Lib/site-packages/_pytest/_io/__init__.py b/venv/Lib/site-packages/_pytest/_io/__init__.py new file mode 100644 index 0000000..b0155b1 --- /dev/null +++ b/venv/Lib/site-packages/_pytest/_io/__init__.py @@ -0,0 +1,10 @@ +from __future__ import annotations + +from .terminalwriter import get_terminal_width +from .terminalwriter import TerminalWriter + + +__all__ = [ + "TerminalWriter", + "get_terminal_width", +] diff --git a/venv/Lib/site-packages/_pytest/_io/__pycache__/__init__.cpython-312.pyc b/venv/Lib/site-packages/_pytest/_io/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8910539b81c7fd2ae9ceaf226f7a7cdbad578a48 GIT binary patch literal 375 zcmXw!ze)o^5XN`!PZAQ6LJ(|h>>^xpSc+I#idcx^aWJge^<1*IckJy&uJj?qXYeI_ z0bf84Qi(;nK)N(KmzXJLe*4Y$G3;l*-z8YDzbEno=Xdkim9qu93K8B2C!8vh(uh*R z9quY8bt8Af968{;NYw*i<^0Pkdl8d=|7oW@}o zUf;(lvm#&<#zk7ic@=1wd%R5;5ZqIpXn;ts;0Gsh> AkpKVy literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/_pytest/_io/__pycache__/pprint.cpython-312.pyc b/venv/Lib/site-packages/_pytest/_io/__pycache__/pprint.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bc5c7b9ad18a681187c7f47a621507aadacf3cdc GIT binary patch literal 24430 zcmdUXdvH|OndiOzeyO|leu7$W1mbDH#uz&`PXU(gfJrPM?MC+sB=qv_7FeQ{I4Mu9 zWXRB3oS_M1X-`}eQTC4InpB0Iot-7A)U2mwr%H|FsOc!RWX6AttG2deedqC=?|k2P&bfb=m*)}ieDB{jbp6?WLHI}dp*>y>hcZAWL%s&Tr2b1&gp-kX&yHlKVXiuR(i(=qhS4o!lL|#%}cEY8>?aQTrl=r5sy;=%Ne!N3Nmgf9VDPyyk^+x*o`eTt; zSASo$4ZmI6BRxHlV?8p#tua}N#QGJ255$zNzT>Sg0Jc8cH-wj^^@R>jj)y93@lGv% zUle4aU6h1&izK4IEXT!mn`A|6?2-+kW60j@$hyPPNT(b=DGxsKUH5S0z}AM2JoV$n4oPYBP!6LRRZ5*Zkf zrPVwobh@jjC)A0y9gB3F48{6Gk60j?FLZ2(A)-{&BGe^Co7aRwJNkhNeI7d9ubd1i za>t+&?K&m*47H$TA*rh)wx%m4_eNJVhk6G==0xO_%<$-)6++8;x@1u44;|}|onZ8- zqg2YFQ<0uQS?!Cg6|_54j$#8vuvHMmI|Xc<;$mC~vjhr;gli)BK%q&{Y{}Z9a!)7w z5v5|G(A|SOo;gu8yZYA-41lL%YX*k0u5h@kuPYV~4;SdoT%*M` zgL2f3;0+;DSTbt+uImnd2n!nJLnJb2b0N)$^lA%q8#CH4-Hq) zI9wFdg4E~$f;WVDi^J{D6qa6mV*H7ue6{ORSGuri%GdNSbAhtY1)JMHS5$h%mOOa% z>zBTM**{&>^nSs_jt}-++w)%4RN>~iVBy<4lEqicFO^?-VLDhp7c59@y0~S0%Z0po zyY)%`XM(lJ|G8kz^Uqs>>uxj%qTea26fQrz?m55pXL%yRwq_^s7R}3WIO_?Ad;6uq z9)f-0@N0vS9yP@o4om$Vh<1m=(VqTTG#pk4pHhb4nxNzpObu6P-C)5o0MQBrZwjAT zoOaiIflyGIaK6#{xlOc}-f=kXeqM}9bg29RP!Nc#7_UAF;Gz)I)8bfa$`PlJurbAA zOzE^75Sp#SjbOgvnu_#4^_;7mpFtY4QE#f!h zf0UZ|hVY5EDDhI_wef>zpSjYSicE;_NS9xj@~)b)tzr%AKra13yXX<`9CH{KftN8O z>7TTmzqL@RmMm}Du|R~dIM~{4TD;j?s=?A>Yqy)FX>rJ7M;=F)(C(BRvP*J4h1zJT zW^a^mp@ds@%TC$TUBarrAb1E2ZeZ}qK48e}u3Au-{dh#I9$*c~0bmV2A_gxoU zro~BhQW5ghN%c}O-t}6%*&Cx=f;T4Rw3*^GGeJkxGHJNp50TUtEmy5&Ne` zDg)MLxtU=-_@!Yj2i8^cDu(szUmDg5U|lV*W>}AWX;>?Pb*;RXVSV*W!&(Kb>*e(f ztMt2Jl^UdF!1maX*xZ=Sdr^^Nv7r}9m6VmX4x0fttwwC6JqO^T5EmtpbRor?TXS

>P-mN$ov&R1A-dL`AF`h)Ss)VJRw` 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/colorama/tests/__pycache__/initialise_test.cpython-312.pyc b/venv/Lib/site-packages/colorama/tests/__pycache__/initialise_test.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1ef7616c2230a506261ac77d63a3882ef8f37e45 GIT binary patch literal 11736 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 Th~4evtM+jB9{#J{9KinrFj)R+ literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/colorama/tests/__pycache__/isatty_test.cpython-312.pyc b/venv/Lib/site-packages/colorama/tests/__pycache__/isatty_test.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b37521c10cdee764cdaa3cc2c77adb1e0efd50dd GIT binary patch literal 4892 zcmd^D-ESMm5#Qt8kw@}q(URdx)=`n;4~14F(Nb);iqU|wq{K~X2eE)etN`MoyE?Ra zq`G&qOTh{H-~tXDAT0vJ0jviFG0+M!lKufj0SkRe>1-w@z0S+h(Npb&(X|}0)%{n1GkAaW$Ob_ZWE1YT$ZG; z_hmW5m+~2Wisy*NYeH5q#FS|GQ+^nWnm-#bq?BX?Q$Zt?3K`*4*pO2)%muEKRK!PK zC7N`dXhGe##yh*D_UIj2=<*AMP{~Jheyz4SM+#hli-#ZK_*~p)OEbD@ol#BQ77R6$ zd*lNm-QV#Pa5@!4a+DYeBnI`#fjl zNcYn&kW~)8i2A{7`!l9$S%o-f`%U&GCi?5&o|_z>OC%C+&dwRCr4Om|s-_!-xrM@z zrJ9$AE-q#?J)6ntb651-mAN-E@6MSSSht|gUsf;b=G=Tfo2ROwj#zMP=7_UP8VAD* z1zXCPX*CV|(k|F72!Ccbh+mM0;qFiOuk0&@k5eQN{i#j`aiYLG7vjtPn{?z6H4-@03zn^yEatYx|L;Nju z)emaeHSU2W<{1!x+Me&-ynKUyAjuzheAuxpt#sXql%=B&Bl~X7J|Nsp-&wg>iVc_L5kx0}=p+!G#5RqAxAu_y9cJ1k$g>2tg=xmxCCSz5y_+^} z|MWy@@W(Efx-ZmRKW-0~!oQhBeufW4qGE8_yjd`jHZ`46*5?4|&PA})+lpMnB!X{Fg8=xP{P6`xz5Ka*;Ww$zQY9r(mXEhJRz+A|B_JN7*fufo zZzlI9kMQ@8h{@sR)IvgHME569+Tr*5$v*q$e`cb3ns@s3S*vVAzycy{#BQf}OttxWYBJbaWTE8rD6BPS1hx2+_;3 z!R64(*qx52aA5->GIaw9a(n9_xV$ANd-%_KlM(Uv5@`1$Vsd}*erEvn9x?g+{}rsr zo(-_#xjX#X4_XtfeXX!M^^}Kfd>3S!mmWsfZ-MYw)dM#H;GQaVv#V@(I&I)orXs*V zuY=&WiC!^XSXzDnHAK6##8j&%CX{`(T7%iW8l>3{b;ho#SzqmXW0bq=GZx<4thwai zKI`_(2Ljg8>Xcw%g~Hm-K3XZ^Y2!AOaiBeAeAL(8-dsOP4dI5LOzSbnf|1<`$h33- zL?ihtZ6Z+j;%{L0`12OV(d#V`-lZS}Pd(h*`{`t9@4?4}kDMwWI{7=Rbm%l2Jc#bA zMEi@;{=413J^XoJIhts&+)vVZ+HB<b$_fnTiSQuzctODOzY+}4L4M1$XiYUuopEv zDJN@L!Xlm1qbQD`XwBMKYt}aYm#;0ouuIxDtxSM(^9v9Sc{|0qh1=`*f@|TD@=`^4 zxv0EcQ6`GYL`j(}%Tx8D{3rL6wZ2m9`<2)$#n>yA*mN;AU5dR{me1C`@Lz^jLnURb zqKp@n@rsfxD#?;^rYuiB%bIFX2u^WT$n+`hF_X2T@yv9mp87PF|!>GaPQ)vPl^6+F!6QNRx>gdRgNgkl86aS*G7VyLjnnC?dn1yZMp za9k&wf*=lV%7U2KlzDMyL9&+^@R7CD?Dg!x2HgvGiBI R=Wl}a%jpfC2nU$=e*q$Ti4p(+ literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/colorama/tests/__pycache__/utils.cpython-312.pyc b/venv/Lib/site-packages/colorama/tests/__pycache__/utils.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..27ddd50a64c2567aecf6a2f7293bceef5b1b32b2 GIT binary patch literal 2476 zcmds2-A^1<6u)<7XLoj4wv=VXAkrnY;-V~2qnM_eY+I#L-8Pn(u-RlBc1E0bb_eGU zrPDxyWQl1sDSc4<7YLg8=8G>p`_iU9kO31DYoc#UmINUtdd|%32b7rl>YdEF=l<@w zALn=Px#!2$)(C;}<=w)xTzh6cBH8tfp$w%yT#^H z(LZo>iaN1^QL@zAR#7g=)2a!Gc-=C!()2r5vju+zx1xTd+78MpQAv&}B*!VVOme)< zrFbU{168>=F)>-F8P_I2hpNC7476#A%TBD3HR=SIs(-}LHhJ*DrE~e=;o)nO`J!y8 zgK|Mu)S{iAu?H=g-5i{r)f83NN^1U=TDp~gSDVT+4eVy*!cEvW%NGpYFy*2=WU0(z zL$j8qv-FJZ1T`jGmVKwKzPYrAgkf2RhqXvHPsFRBknCE(5hbZ47gb4eB9c@zlvy41 zs3d(dE9+j583kKYMA3?(1x2tX5N9C}Wm4s+(Bau&_kW}_>g_mdl%9ssx4jLyUb&2V z+rq8hi8s&WyN^4syYFo>YsLUP>R22x6gX9rV+qeO>^XGY8~}arS3d-Cg-|$h3pB6X zz}_QefzCHsNcF3ukgw<}M9n;HjBoVXj&dDaNej)~ac) z@u*?=IxR?^T_}M53kJa|X>cIVh#Vgx2W%Fcb z`C@t8uaM=F-5vnd^7wugLu4t_p6U31(QS5vU-+1KjCv0X=s>6>nWvDoL%haoyy7uv zC0R#;Uj(yWG(ALc`hMCY*i|+Y&jFuVze0i&Ne_T(IlFJ3h6xJr*9*QW_FAbjioU463MxX;J$ z=M7?m6C;GtIgXM^Sdo%1`fl@+WR5<8_j@=#9VtHR&=DtgZSvyf%=_<2m#bCX_VZT&%NL789;grm5h=E8O6(6-s=*Yu?ru|X3Uj9URoU64#!u<9)-Ji2o+*K^^APi|ETAj+t?CY_jmyJu;;ND zxfi(`c@Xb=D4yOE0I48+;C8y|U+gTbw3N!cM%Sa60sea-(Op0&t^8$n=Ld z`a*~d2$@$x8$%($Q&A`4XMSCqGVumFoMxE#Jh?a4rNPT;ZU(@Iv%!Pw=#fluAfET~ z<(uXR7~>USHi!yODgBL{`I&TXk;oPa?*=*Au_EpgsCGq;CRScYJy8(}*S5n2XuQgU iRza;2b+HhA-p4?zwEI|Z9qaWmfu3=(9S$@%LHz@)NY_*V literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/colorama/tests/__pycache__/winterm_test.cpython-312.pyc b/venv/Lib/site-packages/colorama/tests/__pycache__/winterm_test.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..33d613b0c7ef03dfa7c41a3134845e9ad3c654c5 GIT binary patch literal 6600 zcmeHLO>7&-6`uVexg?h~YwO>xEG0H<2GNh@)OPEjhAb#W2%U!Oh zl@vH?5ulyJo7wq!^LF0%erEm@3Izz1pMBAjx)mnmGyJfMr_$JX4;uH0Ok^fQ;#jj8 zCdG|0=E; zigLOTQ&g=lp(;9`O{8*qK)sQgypqc(sv2c={!%`9L-$N3wB+?w7M9?OtEpUHK9i?L zYeXw#6x|1XF@+2i2nuh|QZ(%vhu2YdM({DOPHPjK)P_kf?@!7o&E76HdtxMnQq z7~5A6qai(5Q81#V*pNi^C$fpo=~PYwwPSsL#`VZm}{m z!%BAl3_HW5aaB!`D!3;^^IJof5^r%cBptMRtMv?*7OP)YOJ*&QFl5_xF3dK3H3jn~VZX#RMIE!y(FMPvsa+L}Nchz`Sl1!m-`@4n8>eF!8Z)d;@kNItKz4BUE{NDn%8w z4P=t@(`wQNCd@$%8DRbi0KpZ^41k%bg4y`8&B6GxVGPi-fs6u}1;FnDWDDrvFKq~F zHniRRq*IC)*=c9)u2Q%_AEdDVe($Al$mp`S%oZW?6yaDM((bp;IbCP9sx?Uam9*oK z$Hjd(Mt77}+fl~N{{!xX8=`o`tH?7NG#mkKgf?wL(u|}5i0;3pXi@^qT%`y*-8*dzYQfueq{iW8ECGq4l z59|NdGl4W7zMsFB-z3GZYkzb7xA|Y^OJe`$z+`}-?eK^0(e8KPHe4OVor72 zfP8xJ@Gnly^2gpVKmzzTc~t~l$wCbxCKeN7v&SUmwK!u*bP6Xs8Pi&-5Z@{Fj=@a&z zhxr> zXFO)=g2oe=3(WW22|JAkuN33ULUeaWXy?VSmwm5j1y|+j2>%Od1BCSu$Wl`*w`(|0 zypz=wxUaYb=d_)}R;R;Ex15_xW42mTtX(JA6q#^b*C9Bwrt{n>4twDyCqtk$ys44= zHjdaA>6r44k-ZvMY~7DxSW3`qs_q?$T^PO+qenp&x3C;GP-F8cwrqipJ(dI(Dh1*Q z&28J?U7s|xygN||!e;1Q7Fujnt%&tyv7;n*l*Q8}@$^c#u^etKgB|K-cuBC26CqK0!L#$LsjwnvsV2f?LI19?$|J$!9ZXo8IGtH5k18qozw z$|bUjBS zTHjB+k$LXEcuy=xj+G+EKJY9>Iv2aoFM0ab*#n*d2IpR2){OT0E5sYL_(VKsDtw@| zeq<*;f;ko0Ky$tBKR4HwPV@vS(m`wekV9^3WSxbU;TVj=LY?6oza&K?6q5{Hn1W|G z1S7?Cn__lhsw!j+bmAkSdGkSS>cPS6WFDRxDTVic9tU+FcwKxzG@ciX*90@MHI(!w zvV03kI}&_LcrLtd;^n*aDtvnjbj+*GZOfObd^Zg8qYE9pYYLt3WVt?krU6n zlwkt%$Da|b*U}u}#o6MYx#qv{!jIorgU^> 4) & 7 + self._style = value & (WinStyle.BRIGHT | WinStyle.BRIGHT_BACKGROUND) + + def reset_all(self, on_stderr=None): + self.set_attrs(self._default) + self.set_console(attrs=self._default) + self._light = 0 + + def fore(self, fore=None, light=False, on_stderr=False): + if fore is None: + fore = self._default_fore + self._fore = fore + # Emulate LIGHT_EX with BRIGHT Style + if light: + self._light |= WinStyle.BRIGHT + else: + self._light &= ~WinStyle.BRIGHT + self.set_console(on_stderr=on_stderr) + + def back(self, back=None, light=False, on_stderr=False): + if back is None: + back = self._default_back + self._back = back + # Emulate LIGHT_EX with BRIGHT_BACKGROUND Style + if light: + self._light |= WinStyle.BRIGHT_BACKGROUND + else: + self._light &= ~WinStyle.BRIGHT_BACKGROUND + self.set_console(on_stderr=on_stderr) + + def style(self, style=None, on_stderr=False): + if style is None: + style = self._default_style + self._style = style + self.set_console(on_stderr=on_stderr) + + def set_console(self, attrs=None, on_stderr=False): + if attrs is None: + attrs = self.get_attrs() + handle = win32.STDOUT + if on_stderr: + handle = win32.STDERR + win32.SetConsoleTextAttribute(handle, attrs) + + def get_position(self, handle): + position = win32.GetConsoleScreenBufferInfo(handle).dwCursorPosition + # Because Windows coordinates are 0-based, + # and win32.SetConsoleCursorPosition expects 1-based. + position.X += 1 + position.Y += 1 + return position + + def set_cursor_position(self, position=None, on_stderr=False): + if position is None: + # I'm not currently tracking the position, so there is no default. + # position = self.get_position() + return + handle = win32.STDOUT + if on_stderr: + handle = win32.STDERR + win32.SetConsoleCursorPosition(handle, position) + + def cursor_adjust(self, x, y, on_stderr=False): + handle = win32.STDOUT + if on_stderr: + handle = win32.STDERR + position = self.get_position(handle) + adjusted_position = (position.Y + y, position.X + x) + win32.SetConsoleCursorPosition(handle, adjusted_position, adjust=False) + + def erase_screen(self, mode=0, on_stderr=False): + # 0 should clear from the cursor to the end of the screen. + # 1 should clear from the cursor to the beginning of the screen. + # 2 should clear the entire screen, and move cursor to (1,1) + handle = win32.STDOUT + if on_stderr: + handle = win32.STDERR + csbi = win32.GetConsoleScreenBufferInfo(handle) + # get the number of character cells in the current buffer + cells_in_screen = csbi.dwSize.X * csbi.dwSize.Y + # get number of character cells before current cursor position + cells_before_cursor = csbi.dwSize.X * csbi.dwCursorPosition.Y + csbi.dwCursorPosition.X + if mode == 0: + from_coord = csbi.dwCursorPosition + cells_to_erase = cells_in_screen - cells_before_cursor + elif mode == 1: + from_coord = win32.COORD(0, 0) + cells_to_erase = cells_before_cursor + elif mode == 2: + from_coord = win32.COORD(0, 0) + cells_to_erase = cells_in_screen + else: + # invalid mode + return + # fill the entire screen with blanks + win32.FillConsoleOutputCharacter(handle, ' ', cells_to_erase, from_coord) + # now set the buffer's attributes accordingly + win32.FillConsoleOutputAttribute(handle, self.get_attrs(), cells_to_erase, from_coord) + if mode == 2: + # put the cursor where needed + win32.SetConsoleCursorPosition(handle, (1, 1)) + + def erase_line(self, mode=0, on_stderr=False): + # 0 should clear from the cursor to the end of the line. + # 1 should clear from the cursor to the beginning of the line. + # 2 should clear the entire line. + handle = win32.STDOUT + if on_stderr: + handle = win32.STDERR + csbi = win32.GetConsoleScreenBufferInfo(handle) + if mode == 0: + from_coord = csbi.dwCursorPosition + cells_to_erase = csbi.dwSize.X - csbi.dwCursorPosition.X + elif mode == 1: + from_coord = win32.COORD(0, csbi.dwCursorPosition.Y) + cells_to_erase = csbi.dwCursorPosition.X + elif mode == 2: + from_coord = win32.COORD(0, csbi.dwCursorPosition.Y) + cells_to_erase = csbi.dwSize.X + else: + # invalid mode + return + # fill the entire screen with blanks + win32.FillConsoleOutputCharacter(handle, ' ', cells_to_erase, from_coord) + # now set the buffer's attributes accordingly + win32.FillConsoleOutputAttribute(handle, self.get_attrs(), cells_to_erase, from_coord) + + def set_title(self, title): + win32.SetConsoleTitle(title) + + +def enable_vt_processing(fd): + if win32.windll is None or not win32.winapi_test(): + return False + + try: + handle = get_osfhandle(fd) + mode = win32.GetConsoleMode(handle) + win32.SetConsoleMode( + handle, + mode | win32.ENABLE_VIRTUAL_TERMINAL_PROCESSING, + ) + + mode = win32.GetConsoleMode(handle) + if mode & win32.ENABLE_VIRTUAL_TERMINAL_PROCESSING: + return True + # Can get TypeError in testsuite where 'fd' is a Mock() + except (OSError, TypeError): + return False diff --git a/venv/Lib/site-packages/coverage-7.12.0.dist-info/INSTALLER b/venv/Lib/site-packages/coverage-7.12.0.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/venv/Lib/site-packages/coverage-7.12.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/Lib/site-packages/coverage-7.12.0.dist-info/METADATA b/venv/Lib/site-packages/coverage-7.12.0.dist-info/METADATA new file mode 100644 index 0000000..8c3b3a2 --- /dev/null +++ b/venv/Lib/site-packages/coverage-7.12.0.dist-info/METADATA @@ -0,0 +1,221 @@ +Metadata-Version: 2.4 +Name: coverage +Version: 7.12.0 +Summary: Code coverage measurement for Python +Home-page: https://github.com/coveragepy/coveragepy +Author: Ned Batchelder and 251 others +Author-email: ned@nedbatchelder.com +License: Apache-2.0 +Project-URL: Documentation, https://coverage.readthedocs.io/en/7.12.0 +Project-URL: Funding, https://tidelift.com/subscription/pkg/pypi-coverage?utm_source=pypi-coverage&utm_medium=referral&utm_campaign=pypi +Project-URL: Issues, https://github.com/coveragepy/coveragepy/issues +Project-URL: Mastodon, https://hachyderm.io/@coveragepy +Project-URL: Mastodon (nedbat), https://hachyderm.io/@nedbat +Keywords: code coverage testing +Classifier: Environment :: Console +Classifier: Intended Audience :: Developers +Classifier: Operating System :: OS Independent +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 +Classifier: Programming Language :: Python :: 3.15 +Classifier: Programming Language :: Python :: Free Threading :: 3 - Stable +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Topic :: Software Development :: Quality Assurance +Classifier: Topic :: Software Development :: Testing +Classifier: Development Status :: 5 - Production/Stable +Requires-Python: >=3.10 +Description-Content-Type: text/x-rst +License-File: LICENSE.txt +Provides-Extra: toml +Requires-Dist: tomli; python_full_version <= "3.11.0a6" and extra == "toml" +Dynamic: author +Dynamic: author-email +Dynamic: classifier +Dynamic: description +Dynamic: description-content-type +Dynamic: home-page +Dynamic: keywords +Dynamic: license +Dynamic: license-file +Dynamic: project-url +Dynamic: provides-extra +Dynamic: requires-python +Dynamic: summary + +.. Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 +.. For details: https://github.com/coveragepy/coveragepy/blob/main/NOTICE.txt + +=========== +Coverage.py +=========== + +Code coverage measurement for Python. + +.. image:: https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/banner2-direct.svg + :target: https://vshymanskyy.github.io/StandWithUkraine + :alt: Stand with Ukraine + +------------- + +| |kit| |license| |versions| +| |test-status| |quality-status| |docs| |metacov| +| |tidelift| |sponsor| |stars| |mastodon-coveragepy| |mastodon-nedbat| + |bluesky-nedbat| + +Coverage.py measures code coverage, typically during test execution. It uses +the code analysis tools and tracing hooks provided in the Python standard +library to determine which lines are executable, and which have been executed. + +Coverage.py runs on these versions of Python: + +.. PYVERSIONS + +* Python 3.10 through 3.15 alpha, including free-threading. +* PyPy3 versions 3.10 and 3.11. + +Documentation is on `Read the Docs`_. Code repository and issue tracker are on +`GitHub`_. + +.. _Read the Docs: https://coverage.readthedocs.io/en/7.12.0/ +.. _GitHub: https://github.com/coveragepy/coveragepy + +**New in 7.x:** +``[run] patch`` setting; +``--save-signal`` option; +``[run] core`` setting; +``[run] source_dirs`` setting; +``Coverage.branch_stats()``; +multi-line exclusion patterns; +function/class reporting; +experimental support for sys.monitoring; +dropped support for Python up to 3.9; +added ``Coverage.collect()`` context manager; +improved data combining; +``[run] exclude_also`` setting; +``report --format=``; +type annotations. + +**New in 6.x:** +dropped support for Python 2.7, 3.5, and 3.6; +write data on SIGTERM; +added support for 3.10 match/case statements. + + +For Enterprise +-------------- + +.. |tideliftlogo| image:: https://nedbatchelder.com/pix/Tidelift_Logo_small.png + :alt: Tidelift + :target: https://tidelift.com/subscription/pkg/pypi-coverage?utm_source=pypi-coverage&utm_medium=referral&utm_campaign=readme + +.. list-table:: + :widths: 10 100 + + * - |tideliftlogo| + - `Available as part of the Tidelift Subscription. `_ + Coverage and thousands of other packages are working with + Tidelift to deliver one enterprise subscription that covers all of the open + source you use. If you want the flexibility of open source and the confidence + of commercial-grade software, this is for you. + `Learn more. `_ + + +Getting Started +--------------- + +Looking to run ``coverage`` on your test suite? See the `Quick Start section`_ +of the docs. + +.. _Quick Start section: https://coverage.readthedocs.io/en/7.12.0/#quick-start + + +Change history +-------------- + +The complete history of changes is on the `change history page`_. + +.. _change history page: https://coverage.readthedocs.io/en/7.12.0/changes.html + + +Code of Conduct +--------------- + +Everyone participating in the coverage.py project is expected to treat other +people with respect and to follow the guidelines articulated in the `Python +Community Code of Conduct`_. + +.. _Python Community Code of Conduct: https://www.python.org/psf/codeofconduct/ + + +Contributing +------------ + +Found a bug? Want to help improve the code or documentation? See the +`Contributing section`_ of the docs. + +.. _Contributing section: https://coverage.readthedocs.io/en/7.12.0/contributing.html + + +Security +-------- + +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 + + +License +------- + +Licensed under the `Apache 2.0 License`_. For details, see `NOTICE.txt`_. + +.. _Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0 +.. _NOTICE.txt: https://github.com/coveragepy/coveragepy/blob/main/NOTICE.txt + + +.. |test-status| image:: https://github.com/coveragepy/coveragepy/actions/workflows/testsuite.yml/badge.svg?branch=main&event=push + :target: https://github.com/coveragepy/coveragepy/actions/workflows/testsuite.yml + :alt: Test suite status +.. |quality-status| image:: https://github.com/coveragepy/coveragepy/actions/workflows/quality.yml/badge.svg?branch=main&event=push + :target: https://github.com/coveragepy/coveragepy/actions/workflows/quality.yml + :alt: Quality check status +.. |docs| image:: https://readthedocs.org/projects/coverage/badge/?version=latest&style=flat + :target: https://coverage.readthedocs.io/en/7.12.0/ + :alt: Documentation +.. |kit| image:: https://img.shields.io/pypi/v/coverage + :target: https://pypi.org/project/coverage/ + :alt: PyPI status +.. |versions| image:: https://img.shields.io/pypi/pyversions/coverage.svg?logo=python&logoColor=FBE072 + :target: https://pypi.org/project/coverage/ + :alt: Python versions supported +.. |license| image:: https://img.shields.io/pypi/l/coverage.svg + :target: https://github.com/coveragepy/coveragepy/blob/main/LICENSE.txt + :alt: License +.. |metacov| image:: https://img.shields.io/endpoint?url=https://gist.githubusercontent.com/nedbat/8c6980f77988a327348f9b02bbaf67f5/raw/metacov.json + :target: https://coveragepy.github.io/metacov-reports/latest.html + :alt: Coverage reports +.. |tidelift| image:: https://tidelift.com/badges/package/pypi/coverage + :target: https://tidelift.com/subscription/pkg/pypi-coverage?utm_source=pypi-coverage&utm_medium=referral&utm_campaign=readme + :alt: Tidelift +.. |stars| image:: https://img.shields.io/github/stars/coveragepy/coveragepy.svg?logo=github&style=flat + :target: https://github.com/coveragepy/coveragepy/stargazers + :alt: GitHub stars +.. |mastodon-nedbat| image:: https://img.shields.io/badge/dynamic/json?style=flat&labelColor=450657&logo=mastodon&logoColor=ffffff&label=@nedbat&query=followers_count&url=https%3A%2F%2Fhachyderm.io%2Fapi%2Fv1%2Faccounts%2Flookup%3Facct=nedbat + :target: https://hachyderm.io/@nedbat + :alt: nedbat on Mastodon +.. |mastodon-coveragepy| image:: https://img.shields.io/badge/dynamic/json?style=flat&labelColor=450657&logo=mastodon&logoColor=ffffff&label=@coveragepy&query=followers_count&url=https%3A%2F%2Fhachyderm.io%2Fapi%2Fv1%2Faccounts%2Flookup%3Facct=coveragepy + :target: https://hachyderm.io/@coveragepy + :alt: coveragepy on Mastodon +.. |bluesky-nedbat| image:: https://img.shields.io/badge/dynamic/json?style=flat&color=96a3b0&labelColor=3686f7&logo=icloud&logoColor=white&label=@nedbat&url=https%3A%2F%2Fpublic.api.bsky.app%2Fxrpc%2Fapp.bsky.actor.getProfile%3Factor=nedbat.com&query=followersCount + :target: https://bsky.app/profile/nedbat.com + :alt: nedbat on Bluesky +.. |sponsor| image:: https://img.shields.io/badge/%E2%9D%A4-Sponsor%20me-brightgreen?style=flat&logo=GitHub + :target: https://github.com/sponsors/nedbat + :alt: Sponsor me on GitHub diff --git a/venv/Lib/site-packages/coverage-7.12.0.dist-info/RECORD b/venv/Lib/site-packages/coverage-7.12.0.dist-info/RECORD new file mode 100644 index 0000000..8271c91 --- /dev/null +++ b/venv/Lib/site-packages/coverage-7.12.0.dist-info/RECORD @@ -0,0 +1,106 @@ +../../Scripts/coverage-3.12.exe,sha256=fOxvgRGMkUqldMDvAG5LyTGfwS5O6au9eMOV3RKbUHQ,108416 +../../Scripts/coverage.exe,sha256=fOxvgRGMkUqldMDvAG5LyTGfwS5O6au9eMOV3RKbUHQ,108416 +../../Scripts/coverage3.exe,sha256=fOxvgRGMkUqldMDvAG5LyTGfwS5O6au9eMOV3RKbUHQ,108416 +coverage-7.12.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +coverage-7.12.0.dist-info/METADATA,sha256=dlyOoFTmDua6Wyxd29dMFdgbjyRChQthmUlDTDU7Lmo,9295 +coverage-7.12.0.dist-info/RECORD,, +coverage-7.12.0.dist-info/WHEEL,sha256=8UP9x9puWI0P1V_d7K2oMTBqfeLNm21CTzZ_Ptr0NXU,101 +coverage-7.12.0.dist-info/entry_points.txt,sha256=s7x_4Bg6sI_AjEov0yLrWDOVR__vCWpFoIGw-MZk2qA,123 +coverage-7.12.0.dist-info/licenses/LICENSE.txt,sha256=6z17VIVGasvYHytJb1latjfSeS4mggayfZnnk722dUk,10351 +coverage-7.12.0.dist-info/top_level.txt,sha256=BjhyiIvusb5OJkqCXjRncTF3soKF-mDOby-hxkWwwv0,9 +coverage/__init__.py,sha256=CFeApdLXmFTDa5ClnnMpjMx9rGF9dhpYz1_gxldJGuo,1105 +coverage/__main__.py,sha256=AyEX844EOW971nMZlh86LORYXxo4bGnRJ6kY6AdMANQ,309 +coverage/__pycache__/__init__.cpython-312.pyc,, +coverage/__pycache__/__main__.cpython-312.pyc,, +coverage/__pycache__/annotate.cpython-312.pyc,, +coverage/__pycache__/bytecode.cpython-312.pyc,, +coverage/__pycache__/cmdline.cpython-312.pyc,, +coverage/__pycache__/collector.cpython-312.pyc,, +coverage/__pycache__/config.cpython-312.pyc,, +coverage/__pycache__/context.cpython-312.pyc,, +coverage/__pycache__/control.cpython-312.pyc,, +coverage/__pycache__/core.cpython-312.pyc,, +coverage/__pycache__/data.cpython-312.pyc,, +coverage/__pycache__/debug.cpython-312.pyc,, +coverage/__pycache__/disposition.cpython-312.pyc,, +coverage/__pycache__/env.cpython-312.pyc,, +coverage/__pycache__/exceptions.cpython-312.pyc,, +coverage/__pycache__/execfile.cpython-312.pyc,, +coverage/__pycache__/files.cpython-312.pyc,, +coverage/__pycache__/html.cpython-312.pyc,, +coverage/__pycache__/inorout.cpython-312.pyc,, +coverage/__pycache__/jsonreport.cpython-312.pyc,, +coverage/__pycache__/lcovreport.cpython-312.pyc,, +coverage/__pycache__/misc.cpython-312.pyc,, +coverage/__pycache__/multiproc.cpython-312.pyc,, +coverage/__pycache__/numbits.cpython-312.pyc,, +coverage/__pycache__/parser.cpython-312.pyc,, +coverage/__pycache__/patch.cpython-312.pyc,, +coverage/__pycache__/phystokens.cpython-312.pyc,, +coverage/__pycache__/plugin.cpython-312.pyc,, +coverage/__pycache__/plugin_support.cpython-312.pyc,, +coverage/__pycache__/python.cpython-312.pyc,, +coverage/__pycache__/pytracer.cpython-312.pyc,, +coverage/__pycache__/regions.cpython-312.pyc,, +coverage/__pycache__/report.cpython-312.pyc,, +coverage/__pycache__/report_core.cpython-312.pyc,, +coverage/__pycache__/results.cpython-312.pyc,, +coverage/__pycache__/sqldata.cpython-312.pyc,, +coverage/__pycache__/sqlitedb.cpython-312.pyc,, +coverage/__pycache__/sysmon.cpython-312.pyc,, +coverage/__pycache__/templite.cpython-312.pyc,, +coverage/__pycache__/tomlconfig.cpython-312.pyc,, +coverage/__pycache__/types.cpython-312.pyc,, +coverage/__pycache__/version.cpython-312.pyc,, +coverage/__pycache__/xmlreport.cpython-312.pyc,, +coverage/annotate.py,sha256=vyvuxxsKr671tYodlxb2htM4pPrpA6KzGNGm6LfjB2Y,3865 +coverage/bytecode.py,sha256=1XrItJaRMp2TgRL67F3odsBc35N0rVEvv7L7pyL7RtQ,6862 +coverage/cmdline.py,sha256=XARQjbycSxqHDMc66x3uAFmWUuTYP-VAAj_IPozzh3g,38003 +coverage/collector.py,sha256=rEsKk7bzVxTMnoXqxS8_TSIYulasvhvM3poII1f4F6E,19027 +coverage/config.py,sha256=qZGjxCPgv5C3TqIL2dSkt05whymBVaoKCWm-sYfN4VM,26695 +coverage/context.py,sha256=3pPdrOvn47vx6lg5mubrSfpnqhOiGDMXvh5O5JtG5hs,2508 +coverage/control.py,sha256=R9_7E9hMJ4dfcLBU74pwx9q-3Qc8zrZxK84PXh3TYaw,56299 +coverage/core.py,sha256=hyMTdPv85397ROxcj8U4zmGjLCtIf-vykzNyLvCqwD0,5543 +coverage/data.py,sha256=QcHNqAqa-AeCK75FBXgwMlCv0vMykXO8s5fIz-ikTvo,8354 +coverage/debug.py,sha256=jg3OLpuXxCCo3GSBjkNNBj0XTnUjDI3A5w8TetRVkxQ,22422 +coverage/disposition.py,sha256=WgxvFmAtYeHdlzjxW_YwNeh5kbGG_tRUpcHgMxOahbQ,1956 +coverage/env.py,sha256=6_e5O1ZS3KU3uU6XcU1-bckJT3K9KPQGId5jcFa0dLA,5107 +coverage/exceptions.py,sha256=jw64NBBY9RCalx5jvFbokCeLkQTFTOltnYaS92IMWE4,1725 +coverage/execfile.py,sha256=JiDERquClYHUCxIEX9IJEj9-Lrub4EgAuTBI4EZUJF4,12322 +coverage/files.py,sha256=KTKHAW5toUVaBguEHUYOB6h7OXMrolBFYMZbyLikQOo,19910 +coverage/html.py,sha256=Ps8Uw90uQZ_aXLp_s94RMR18NvNioRuZZF2_6INcUDI,32384 +coverage/htmlfiles/coverage_html.js,sha256=-JxRQSpcC7xtwn5-8bYQYfGfbjC0thjZEBsiAB0yewI,26185 +coverage/htmlfiles/favicon_32.png,sha256=vIEA-odDwRvSQ-syWfSwEnWGUWEv2b-Tv4tzTRfwJWE,1732 +coverage/htmlfiles/index.html,sha256=jhElZE5CbKkRC6I_thafeelvqjd5KbmZcdtNtsf8gn4,8901 +coverage/htmlfiles/keybd_closed.png,sha256=fZv4rmY3DkNJtPQjrFJ5UBOE5DdNof3mdeCZWC7TOoo,9004 +coverage/htmlfiles/pyfile.html,sha256=18WiSJ_ElmMVZlklN323JcSei3d7VvaBxl6Me7rOY-g,6657 +coverage/htmlfiles/style.css,sha256=znA60E9XBKF1BdEriaOY-cICGPskXDLd-fhjlMudmHg,16326 +coverage/htmlfiles/style.scss,sha256=_JSuVSP4vDyah0tzSPSFUwKEwGKdhIFRKCyTyUqHUts,22133 +coverage/inorout.py,sha256=VLr4ICCOJ4SMeaPcXhIuLA46JYQ6P88km-mftYsMZq4,24959 +coverage/jsonreport.py,sha256=Ig6Vx5MB7748u4_9HYqsIT_SsoR0TAxhnjkQMcu7of8,7525 +coverage/lcovreport.py,sha256=bcLLuiljvvVyZ9RaDksAufBlFvzIbtOL0S-iMdYNR54,8093 +coverage/misc.py,sha256=-8v3x0aq5CnIsJgKn1sKnl0HrB-8tf96Qo0ySdVN2tA,11664 +coverage/multiproc.py,sha256=IOzx8IKdnDvYgC_v2toQ3bGOTJsqCDzpz4xT2yI-TpE,4295 +coverage/numbits.py,sha256=OiFNEq70Re9SZ69PnhI-d_LkirSkhTKZ5BxItwYTxmg,4819 +coverage/parser.py,sha256=SIgQeDa_uuGu3ZXWPiRbqEVaF1XrveInjPpQ1r0h5Ws,47528 +coverage/patch.py,sha256=BqlahYYwCMoR_0_4nZF9MaO6Jtsc-UnYQgPEvRQjlSk,5733 +coverage/phystokens.py,sha256=jPapmh9dz75ZRTml7BkS5x_uAMObERgb1dLOwVUQmGU,7647 +coverage/plugin.py,sha256=uG_lpUw0kmWGnh5ax4W3HIGjF888Mhye9xlC5umpYSc,22126 +coverage/plugin_support.py,sha256=HHLqs6_FLyiVbJRDtBCRDl-Dc5OQNh8eXaGzOIHXdKc,10743 +coverage/py.typed,sha256=QhKiyYY18iX2PTjtgwSYebGpYvH_m0bYEyU_kMST7EU,73 +coverage/python.py,sha256=V1deEDpOZvz4n1XPgMWMnS1MuFlaCWzM8Y65ozwKTVg,9025 +coverage/pytracer.py,sha256=xZyDHBOdZDQurAL1pVBq6ZPKYQpbsK1-rmJhpw-hU2U,15691 +coverage/regions.py,sha256=3uy6bnR1AUvj7d46eiY2Jv74I1j-tTG69x4BFvRQPUw,4627 +coverage/report.py,sha256=vS2wM3t7ljF0Tc-Xji6JQJSogiENVN9TfRxO4mH9nCM,11116 +coverage/report_core.py,sha256=AgHLC0uIdEyoyPPNLdn7ttEKkb9w2eqJDVv_51h0DdU,4161 +coverage/results.py,sha256=LFXfg4BLzbsxWAhDUV6eVvHlVE63L0zNyGXxgW0cCEE,17744 +coverage/sqldata.py,sha256=tJP2lGVX7BiubRKvEKQAL1CwEtI7Jis8TkFV9Ay1wI4,46681 +coverage/sqlitedb.py,sha256=g4eVB76hTtCM_alroqybS6TH8lXhBZmQbWqPKNKDMh8,10263 +coverage/sysmon.py,sha256=8ND4TrcFnXCTzwkGOO2rn18C0I8DL6CHUlqCFa8dHh4,20242 +coverage/templite.py,sha256=mSrciuRPSwmF2HpqNfoRPr6mfR9EfBIfHG8wEeBVQlo,11625 +coverage/tomlconfig.py,sha256=yHTifwf3Ty_E45nsC2HMMtSSP-gsG3Ei-sOlDxtbTe4,7768 +coverage/tracer.cp312-win_amd64.pyd,sha256=BHzLquLPa7GWhyKniqUgV2YJ4ffsMvTBD50aZP0JOrk,22528 +coverage/tracer.pyi,sha256=juBZem-dX4wFTKDBDcdHJSVpVAqLKdiUaYYDkIuaAXs,1250 +coverage/types.py,sha256=Tqp7G-cfBPGCJ0Vl5s9hCtF1_k22394flJ53mmxf0VA,5806 +coverage/version.py,sha256=RYH-FW18Bi-zIP-V8GFGP2gbm71asuK-j12et0UoopM,1129 +coverage/xmlreport.py,sha256=TvdAL8gjywLQCrNmPOSzo9kYnM1xFtcUVYAYiIDihCg,10135 diff --git a/venv/Lib/site-packages/coverage-7.12.0.dist-info/WHEEL b/venv/Lib/site-packages/coverage-7.12.0.dist-info/WHEEL new file mode 100644 index 0000000..10ac2c2 --- /dev/null +++ b/venv/Lib/site-packages/coverage-7.12.0.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: setuptools (80.9.0) +Root-Is-Purelib: false +Tag: cp312-cp312-win_amd64 + diff --git a/venv/Lib/site-packages/coverage-7.12.0.dist-info/entry_points.txt b/venv/Lib/site-packages/coverage-7.12.0.dist-info/entry_points.txt new file mode 100644 index 0000000..242b477 --- /dev/null +++ b/venv/Lib/site-packages/coverage-7.12.0.dist-info/entry_points.txt @@ -0,0 +1,4 @@ +[console_scripts] +coverage = coverage.cmdline:main +coverage-3.12 = coverage.cmdline:main +coverage3 = coverage.cmdline:main diff --git a/venv/Lib/site-packages/coverage-7.12.0.dist-info/licenses/LICENSE.txt b/venv/Lib/site-packages/coverage-7.12.0.dist-info/licenses/LICENSE.txt new file mode 100644 index 0000000..f433b1a --- /dev/null +++ b/venv/Lib/site-packages/coverage-7.12.0.dist-info/licenses/LICENSE.txt @@ -0,0 +1,177 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS diff --git a/venv/Lib/site-packages/coverage-7.12.0.dist-info/top_level.txt b/venv/Lib/site-packages/coverage-7.12.0.dist-info/top_level.txt new file mode 100644 index 0000000..4ebc8ae --- /dev/null +++ b/venv/Lib/site-packages/coverage-7.12.0.dist-info/top_level.txt @@ -0,0 +1 @@ +coverage diff --git a/venv/Lib/site-packages/coverage/__init__.py b/venv/Lib/site-packages/coverage/__init__.py new file mode 100644 index 0000000..1f7086a --- /dev/null +++ b/venv/Lib/site-packages/coverage/__init__.py @@ -0,0 +1,40 @@ +# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 +# For details: https://github.com/coveragepy/coveragepy/blob/main/NOTICE.txt + +""" +Code coverage measurement for Python. + +Ned Batchelder +https://coverage.readthedocs.io + +""" + +from __future__ import annotations + +# isort: skip_file + +# mypy's convention is that "import as" names are public from the module. +# We import names as themselves to indicate that. Pylint sees it as pointless, +# so disable its warning. +# pylint: disable=useless-import-alias + +from coverage.version import ( + __version__ as __version__, + version_info as version_info, +) + +from coverage.control import ( + Coverage as Coverage, + process_startup as process_startup, +) +from coverage.data import CoverageData as CoverageData +from coverage.exceptions import CoverageException as CoverageException +from coverage.plugin import ( + CodeRegion as CodeRegion, + CoveragePlugin as CoveragePlugin, + FileReporter as FileReporter, + FileTracer as FileTracer, +) + +# Backward compatibility. +coverage = Coverage diff --git a/venv/Lib/site-packages/coverage/__main__.py b/venv/Lib/site-packages/coverage/__main__.py new file mode 100644 index 0000000..d9d42b9 --- /dev/null +++ b/venv/Lib/site-packages/coverage/__main__.py @@ -0,0 +1,12 @@ +# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 +# For details: https://github.com/coveragepy/coveragepy/blob/main/NOTICE.txt + +"""Coverage.py's main entry point.""" + +from __future__ import annotations + +import sys + +from coverage.cmdline import main + +sys.exit(main()) diff --git a/venv/Lib/site-packages/coverage/__pycache__/__init__.cpython-312.pyc b/venv/Lib/site-packages/coverage/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2b1ca83c77f63888f962e28bc8d7d99fc01a1b1d GIT binary patch literal 758 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 E4=4PsmH+?% literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/coverage/__pycache__/bytecode.cpython-312.pyc b/venv/Lib/site-packages/coverage/__pycache__/bytecode.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ac2911ddef3217e008f09a84039c1e7791a931a0 GIT binary patch literal 7791 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 R&sMNy!uzH}F!HZS{NI~WZCU^T literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/coverage/__pycache__/collector.cpython-312.pyc b/venv/Lib/site-packages/coverage/__pycache__/collector.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a438d5b4f305777a1b044a60a4666d88e49cdcf1 GIT binary patch literal 19340 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 Z(>F8x-N!HRU%IAUeEsLpE_i*3{}1@9I(Ps8 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/coverage/__pycache__/config.cpython-312.pyc b/venv/Lib/site-packages/coverage/__pycache__/config.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4ba7509e87535ae645a87f6b27732b01edf5244e GIT binary patch literal 27221 zcmd^odvIIVdFQ=&zeoTCpWq98NFphcdfJjHTQWt8dRQ+@v~1IIAP5(vK!E^r0h%HL zCW_bDfLW!cnzo|XZcR6y3B7jL>dx*~X}gWH$#m0X+64@`gl^bY-6S(jyVIpbdy}}c zv-|tbeE_6jJ8ftG*x5_+-1ENYo_o%BzVG*a=kNz*Wi}4i@BQzc;lr0W?ne|*FO%%y z*F>Jfi|`Y=5gku{eMCQ^XLrMhf!&QGMs_!inAqJsVrF;Ch=tv)%H1|% zL)Z|pPdG*#tS)1uY@&Rme8M^6oN$e}Cfp-#7H^7FOn62-%x{iVPE?IlO;nFmPt=Um zusBPkcA{>iZo)g_WnpWiexhNdVWM%QaiVFYX`*?gdBQj1n`jwn;W-@_st#G+#QV@S zV$pGjIl=YXe-ER+k*G2O@( zR+9@gxkF8%mNyMbxelR5sCdCJ(uunV_b#DUsKkA%P$yL3-fiX9{|H{88u2|d`p)`f z{Xi@_8XohFh9e>0XiW44W6y=ez*wkva^|P>gic62Sq8Jl9^-{&9sfj?;ACHNNkl+s| zLKAVRJRFZj0*R1+A|_0slFInh#dso|m_pQZ0WnN9$2)bBYdAI$k*hl$5aS_Ha#GA- zC>Rq$@&ioBBxO@~R@qP>5@+S7L%x6*@`bJhCc;q+ZeIYi;!=pFOkoOjBw|;3k=*TzhZ0khy}{A3 zPOU1MUz4$TJd6QN=?T;l@70oNZk2_jm|T-IA2528R&_e|NgQUIF+MekVQ=yK@rZ=q zFS+9KF!xW2q0#V_P+T%$=7pjHrkWuhii}DYwJ#*A-ye(w;&Hz}PM^}}yZX`NgMH_> zZ{L3U>GKol*PcKyAcQ7n&QH$tBm(ivJ!4a0AruKmL+76hMV~u=Jbdwd98J!>Loin1xtQ?opzo{%^vxbx*&R_GU^g%4kXE_6h`KU{_91bPeWDEoo zQ-O$&bs=a-0E3%R-`%VS0;|$jJ^vQgE=hES9{jT)!m5Q3{ zTe20K79+WeUFiYIS&^@4d)L*rYDLCX8|SK&ySwDTJC53w?p8Vt5!#jFwXW2s^j~xp zb+$q6?3C_JZE#Zeo*Y8w(c8(LXQR%GP11PmG_t-EyO1cE55NV%9^-0NaLO zvf=&?D%3YXrF!9@d-65)%QYR@nvSKKT+NQOBX6(C*xOb-wb%D#J?NU7LpjgBjALKk zsq}_6Y*3z_jNakb9)t%Agw_)ngvGsvRJsbiqV~>N?hXDVmU_d*SS-R`2~&(_6?@k> zJ?xr}&q3zp14?Pv1G|I2luCOrH;Ar0~(r^2d#fs1XZ5 zp(~0$_j+>j0Ch{<{oSHeM8h;r)8Ugp_4Nab59d7FGmh;m<#h|kmui;m8Sm~~`2!it z1NV2D_Sr9>;u~C;=n6NkeSAbJSK)(${?riF`M7}r+M^6CP$>PwD1WWafKCP7=9Al0 zx`SmW5sD__#JB|1F6<^M9`FfaMuGz3OfSX|b&I4Q3nhv>QQ`$jkB(6(vOKaRJF=;S zr_=~5NX<9p$Nu5@&z`Mjs*#>3YiUNq-C9rrli+MOQASJmBk>iSa)V~g=z)z)-> zI{1~LRXvjKI$deY{S<}ho0I0ypTqNs1iTI%XiOOu0&XfAgBE2BnhG=pG=>lH+x+jC!d%MmI{z#DtT|yv6WNtACmgDO zyj=Aa?&`;!HK)w9^adI+I_N6~e9sZn-R%=YlaWA>*eb9ZVKEk+z=uVm!v6cfg~f_k zykW3_0dZ`Ka^Y)(FC(TfuB_n~1{g~wO9{RBUKhH37pD@waFk^mjZH-bjqOqzaN$Dx zlc&xMUbx@`mxjRSC^U;8RW7$KB=|1Quo_qy8g+Q8R#B;jqb9l5qPCv_XDCMFa#O&_ z#V?^^R%217qzyztp0vJX8MRy?YM>C7!kQ`)b!XH;+(SP43X(1sm&{6QI;}-SV>_i9 z&~M-qFHh55+)Aa0;7I&)@&Zew2FokElqH0!PFCGdRM4|eBPsqxI3Osim0WY1y0{vx zr}%ZNtFv-$I_qp%?7nGw&$;K0t7_Fuc|Ndlo~A|H(t}G~H&48?<(>LW(}`Tg$&BOV zkKNVRYp&NXoL_n{;~uy<_^x~4)=<_xkmgq$zD4)z2fuObwPQKQHcY$HQ%U26h<-3m zs3c7)OcHQG+R!g>KaA$o4~4JzrY139nW#J&NL<1fVJtk+7wRFYVp5C+L*TF}3q)58 z>|$~w%H2;zH&7fiI+DtiBHT(+l=`_54k(S&^Wv4cD+}hu>YTG9ZCF94w&$E%GL|j( zlM{_L|Jg6y2V)%gJ$?3JQl@cg$%gAqB~0lcP}M0z zDM>J8QrQEZyVhaguIr;CEK_Ndk_d%1TQ4Yc2$MQ0_%6uP ziwTJdaGO9RZ%ECv(kP3awFr=h7%EW0bVzyUs)m~+3doEYio3`cMp0p|N zZD00wWxZWXyK~;{#AJ7-kLByymg_pRb)8FXxw_taoW87c-k7g#eAWJv{pGTGqvWa0 zS9))pxPD@B(|eU2@0*cfRga>?c2}p%?jP5*Ok%F`Yv3#F4YUmu&4Hn$)lo`3D^C9s4JulL*q{QxbmaPxg(nwBbCo@5>x$F2ShKYG zX6N$Gq3q6~+|DC8=h2MiC>s)JQ8UVkyWmJBku9u};bbft5=nN3kO}I;CCgOA@)=B< zXT>du`^W~bFXdx-hbaQ@5>K!UsFjIUdSnqPO*{t&l~Jtt6gfo7WgknY=PRW?HTKDp zdBJ3Howkxs*iX+Q>M4>4EBn|}sU@G(s!vO2EpZW?M3Ot;B%5EsVBvT8{hx(UucVm$ zejyh0`x(B&Fbi1%VeCgIHH$cGkt7S55g8=kNpjA@k;)YCDHw`G{Qgc}^dUA*%kiVK zI9Cm?NKPvy_~HEg1@4Za=R6wrpKBn~V?L_4rnexUZTxZ##=wwQx4ihgQUW z=rS8CKddkqtJWO6ar=iRgYm&NhtcT%u-s#GuXX8-9cwn1vHEB2Hsdj#jUV&&*x=t`7 zY(=^S={CU{vI{oiy%1wZj6+dulmQF(7O-&DC)5jNFBnJ4g$AJ<^*Dt_!HK&|XcAnw zyU`9e?iE5aQ+Rj;A5(Z#hN^@XQhBJ{#7Ol_RcEVIKA@;FWMLrr6*UZ)ay`DMkZ&U0 zfdtl7hzcPlQ_7Grfu@>>K@v%93ah&U--3$|B&fQC7>j7)KnyUg2C1tkDW0o5DOk*h zQs1+c>;>LhB%F4i))8e%ys)`dK3 zh~=zQLo8?2x)2c$cCnl_T8P?GtA=0-zfTFsU!_G0Q4bPOUUkZkskMrPW=ZBtiHQi(l1`Nt5wZYXXwspU=)E!#`DqZDlVw`ecpR#rLXc|c zd{nBI8^wgpxF2l_O-w>Ak=*OzA=e0!y(mg@`IQiE3_Sw_5RL{TQvxIde{2HMg-Kpn zB(w^WP*ebi2eE-_2~LS3lo~UVnU%hhkPN|?$kL!U_D{}4!WShg=}TCmW@)WfGD{9M7vtfz5Nov+%EDkc4n>0GRGz4Gd|YzGFU6+)6JaPRqhknN z4o~8%;L~DY*;t51*$kA6Xt-p>#}WhnU_35aXlVP76BbU)cs}b zky;CEm;nCRRALf(dN=u%?(z$v$;2hePLn}PX92uUA~6FMD0HV%1&dl=rJZJsTE@cy zOl}EY3I#96r(l9W!|W19mpsfAHB0;ro=Mh>O_fDtS-n{^u#U6nRdt*H9@93yhSeH! zjD_2|n-rOCce{J(-gI}%R=U^bdv`JgCpg%;#)E)q9l}nQZyi&A8b=Fma9DZ}evx*sIm$w{%jCb$Ks6&Z%U| zH==2`E#7<=Ys{AXcGe;F`JNpVU%S%w&^KE$Z4Xnxx6-h+M16Ji(DM9_2PjWNzM+Ng zb=uwA%pNCq4Em3CLV3Ox+3?bGKR4yOx67ry=zla$X%({9qt%FLVcpS$hz8{gwcsW3 z#qzD__}iA|qLeoFNqmT*ugX5!BL~uk_bl~HoY+r%lsHMw6gkh4Gegd0a?<2nA?M3* zfMRjn8#?iGxDDeT!+i+%&nov>y07CDiGu_GCEJ<+^WaPASl&b?x`4c4MLmyoXI2M2 zi~cHq%?i|m8>f|M!}S~|dJ-D9n9?USUQq?^Mbt3H10U6R!xXeJz(^JT6Uf|NRV&QU z63l0^E0U2W->4Xy@LiB0=L^sZ8zrM``{OwA#Mxp0!9)JzN6!vRHc+K#JOQaD#KdR` z+F=+TB}5f@pjBGpFN=@jz5#Kt(lh*oX(wwp%ug-Fhq)O)hOz)r0XH4Le*9}EnHqR* z5Q>@40!E&j&3h^r49m5hS^RoB-x|N={=uW)$vQDt1?%L{c1 znu#GBBW1JYDMzY|<<>a$Lhe+#`b^BzZ9vZ*;~LLi$W=r+h84 zn>0cfa1L){8rLL(LMg$f<`nmoZc!lz+R%nJlp~)eKNQ!5{!^$XzL+Lc6mq1>DUywZ zO=u4{uE`dKJb1=#Jfos?Yf}|!Ytffg$kSgasrm|e@PvbEY~h}&&|2{zTA>&9XptwX zOIc?#yv!BU*(uBOI>9h&`MhPC zpXRRUpXR1{JVk5CKc|)*fH0?F5%o03fsuD{a}_!5?yqJ>Zu9oa${@b!v8rbjGN z99&S%#+W{;*eDG$CP{I{c~zsn|2GUAZHku3rz*UdNhZ|n45I9Yb}R%TT2vI`fG~rx zsR(rKq+baLu;+lXE}ZaPg0WsaHc?QxGr>f*RW9Ih(3FQ0MSYbt_9kE@ob(P*rO_=3 zR;dC2%F5i2bbY>)L&HDCC*wZi`;yzAh(bb&jYY#rQr|I~E8mu9J~#WU?{l+{Y$0X5 z!rm}l@aJa5*HBW@i5ELJi8Ker0Sa4~=7dBfT5uWEEC$I(tDIyGT!c87xWvG8lC+6m zg49NK%|>R;EYgZ5>-GgIVM0|f?UeKrfl0|TaO&jH(Zl|sqsI^VPn{k zm5V1fAR7p+Nn-D4`H+Vb!0Siv*JZes9R+3UWFj(S$hgAWVnO?4<@#dz{ z?|Ea-+Xvn_aBFXF>)>t6p;d$4`XryPtADlTrJBXsTwPcC*!w2V<;{C)S>dLvrD@r+ zDQnsEdho5*x3|2pjVU-h5N*^~0omZG&lUP3v-XPqw<}tzfQtXWE{xZ(gq7 z1w+YuoX*;lek|YAxzzGfbh&9~wrS_hUAd+`bEndW=g;LGH4Emf!?)~c&pO%{&ymrt z5hWlIw{$Q2c4U1!az0dIvsPyv4Jh$szP|C*11}v|+LI$IOvzcl(%ibZ^BaAy^(~F% z+8)d`@42-l+uVPTGrOzj58tV*&DS1Vu6;aP`*^;gBj3=LZ)jgL8|u6t>P=PE@9U9k z)r{I#y_}=`#r<>p7q;BCx8}VKuXerEwfJPt+k-bKYo0gdH*NXG$ZI1v&9|y^n;xAv z+^}7@Ej*ZYA#c-lGa6mi4E1wO{f)@=2)fSNoF2+M-7kKA?(++y?>RT+d$+$GeIt77 zT&{O;-mvUy%evYYhqA7%a&a>XyzhdwGHk-;2Jd>j>A}CYJ6D=pUY&kv`qeMI^o6A> zx#qp;!P}086?fJArA0%|y=j%>%XTce^BtXUo8Bj z{DWzpb2l!#TG7_mPi36D$<*vgex;%@hC#K`ud)>!sx$ijIAG34c*yfn4{R2C{}&`I`~}eoqtI=T9k4h zkEpBJze3rYl{fgFS^6Db@(6jkrO{ zOgqqbe-jock!cG0n5F=adWoVc% zO#~|$q=iMjEbw3<;Mb@-0Q%}nfxZR9Oa=!TVNl7JGR%avxJebd#r7h|7n>AQ28hkt z9u!!2o7E-|)C+jVTwGd(V|Z>xKV!h=yu?{D=Z+RED16vBMO)Bl?;WJK&=oQtR86o4 ziVd_2wvOurM3jSASX3c6f0FZxggf!yp^ePoXm|)1F^MhAB3dF-Mv{ZXw-5z`=_X3~ zV>mE>Mzmyv%n7xn5jHp>e?fYXOcM}AV?vyIU6l-&P$2#RVv<`oa0#CzZ!Ht|a0;cM z4XWkpeXnkPY3pJz=k1zvrp@!!d3)K5kIX%?P_wxCw!L$us$t>G;;zN1#j(Z4T-CPp z(24^)Ru{?%6(Z&gF+XXN^HFMEj+555sx)svhIEs=KFP@IRW;z0$p!doe9~0s1DW)?3p6E_E z)pD51v?)aCCqk`ebifMvQn+1V2kv>~pS8?d36mCkQ?Sn%*HAjOW{!gv$;UKIC2+g( znnFI+m$DQ_Uy9j@&FG5IJZx;)uvN+wIEYWF&>01mQMPnoAt)GV6cGr;#+zy16<`g> zM=aW-+1e^L;~-2J^)M~K4ip;XilsPVPa1rsJd#Wzdwsqk09dpccrt?RD^%pd1)0Ub zo{{y9yKvzdFp$q)xS-Xf*1k?;k-?~G75h>e1O#e2jLilYT8RmjpU`@t_!XIsO}1i3 zD_~5l*r3yPV``V&n4%I4t~&WsnwX4$e&nJJXj3p=m$PDvNg2x{(hwJE5X(3aO%KLi zioZ`jqOFpZu|u>iQ3isD2*6Yzlu$q=dFz^827y#0hq_QaL3R8NIggU_eR6(8&X3_p zc7;r^tly;={pHY1+zy{gm~ofM3O!DtC&{`E@_)tjm5UGcRq zKK^yH0v+u5)iMGNI#6Yq2Tp@e1xyUq8UQBVO^X*7KfADP?y>aX{3cNH1$)lfCTkRh z#Y4AUU3Xm7wAr*~wVZ42k~h-a9LP28&06Zy_W8#b^mhO#?OSaBM)zyoSx;|%;7EFK z+0mSJG%q{4vW~8$eYbQu$G%$wS;wP!M+MaMPH(26d+C{_QyK5Wx$;LcmPcelN#9<* zh}&RgWq5U)k|34;ADoaP*z$lf9(|inB68{#TOXlnT4nFX1uTcaeIRA}&|D7C?*9-iH zOk)%-@9!yTJuwmg2gMN)5m({d&)rEj;yUCO-A6$RNghm_`0t{IVhTcgKKrisWgLy` z=Dgmz_2cJ!P18bRaX44g^{%UH6-eRMRmU$k<2^OYp3Pa$=4DS;)+5h%&%R~Pfvo4i zJNlewFyk2fgjtUPs$IlNuvvceo`wsb^1Y`q%aeOf1TNzUB04|F z7ZAp$;t6?gFSMUn!r4(NKKLgn7ih_ZZ;Dxw_JY@#x~M>_!%1DYFWLBntoI?RtJ6S?kQ-K{sDF&1=6imEE!LO1-fFi>i z#cz@CGC2|)$uJ%ZL-!;fb;Kx?RCfI6;UmNT;Ui}b^$(tv^f)I(l}!tLP=*S_+E;CR zI20HElvPL=gH#p?k6lXmWwbe7f|NOM#0-Qi8EmElp5z0a=!L^$y~T;!h@4?pILO)E zBgg-%?N4mWN1nB%_I58l{+@T+yLJ1r z)%$K&JwqmNOcz!6uBC2ac)77Ri(gCcdoa=?{nq`_ckML`yI<{lsV{5alyC5X@j3x| zRoAdExZKd4ZRpO`_N2@570rw7%dLG`{8sd(&3Eh-L}VYj{?IGu77xGd&v|;%gG}4; z$SvX4xlGewuHsO}aVTHAY00qM(U-+TX5cXyM!PKlv$ zQ0MTSij^1w|H8*`OQj6I3Y1m#sq+fLxh~nqW=KHWc-ld%akspvcKRj*zeKOEi8|_Z$2T9)>v0 zWcv3z`e~eI!B$y1$q4)FV5l4?*#(CY>3X*pSbgREA(?sq$olYDC>pvlDeg~pZy2ny z)i`^Dha)kZ(iGp{tK}Y|(Ypn&@He#g{M{Dejp3!oZ}OQlXP3`>CVS>H8UMxi&IA{C zECycRoNW<)!KTXdb({ECtebd%ezYvm-Zug}u(6HJ@~E)_ zP?YpUEqK<=;op1qcb{E;;7Im?Be@5T-EI+bP9b9vWZqk$vdGVI#P70I*o8X&>8o&1 z&(>h?x^JkhW9&)HDd2?aH^j;d<29_iv~F{yR$B1&F|A&3^P;H$Yeef3+Uiin41MQhj9rLC}9T{wvk5`(re zo#Yg1$HS=h+GlYphPLLGGE6M2uccPff+^;>wwkJ~Ex<%-TgTf9X{t}1?`P$8k#Rv- z6?Mt)gKUF-COdK^&y~D>~kcQHIV6QIGL2&o+=API8|;bOzfSj*cAi_a8iax_|hHRDm|Y>_2hI zA0G9~xN*U(M*I)xG#Qv!A>TL2c?u48tpwts2X{->ix2K*2XBS21_QdsCX}g(NgR2h zM4AM4VxV#YRe>x?2GU$gwh8ba@?&wD8d@?MjS~YZ_kavIShZfh&q72?IYLz7Jrsnn z;NV&}f1~lW#^sjX*_Pe8mIs%er(ddEa4+=V3VbW_&B*t5y|@1qAq>|I8RzM=A?rMy zZ){m^+?8#Fsf5{DJ9h#G%E>!hA9$~7%OCFi)}C+f`PPAN9)LlDt8)JF>*jR-inDH^ z<(1*v&eoO6+E+Xa=N2D-xhYrKah+dr*Dv(H62I+kyHn|1*zvWa^L)O#1`ve%D~DH| zs2jE(uIgoHbJp4Xy5SqP*KF9i@7uoY+mrR}$@w12Is2BK{aI&!&N+~=4E$ov!?_;k z<23Jot8P$dx@Cm3?hG;wd3_goikHJuI{kuRP>T2f60o9@4VOAyv?*oZkO9Gsd1gt4 zp~aF#d`W_(A*F>H3q%7petH=vA0{?bn~+S-zo312oHj!yYm8M5;6%lB3>X+b8;yG- zRH8y$w2+@htUM@1997DRW#rI2kxWyQ7?kX!&0qwlIDjn#7w4yq>E$e4j@V!e4}&hP zF=Y6*t^q}9Pty=Rfj5N_6WN!%n4C+#vROI4p>KKX!`ZD5=eF+8c@HdmhqB(GoR>D= zrww=9m0zpEQ4f~#7oBs?Web@iEhKJR+VjpT<*?fL!Rl+JXh@`EM(^;$wLC^>WC$RF=QOTC#vq6tWae{ynaaOjbI~px| zK|@h#sxw(#TpA~fDhCiKZ7>mP*q)6X}In;#sAHn$&_rA_(w5(MO^Tq@BB06iO z>FV)&UUQu(W2joIFd5s|TxWS><=VJTXKcG?F?A!MW}$hFGZ{_yD!8gWS5MqC9OdgN zdi8>itFB$OyNy+M9d)Zl+yTHiD^|_qvv4lYs+D{=LBzvyfePEo*UAvLwpnM~wPtBE z?q55`d*SZ&7$4);x^%`6zh-e72l>@j&g8y_##XQC;iG0(%dbXnwT_+EBLaW?+N1FN zllFUPj5VVzJ7qS1y$D=<>=4#iMp~d$Rb*grv&6^9W~Vmr(jY%r=S6H_8$f&!M=Ktr zeB@lDxldXdFs)EU?wQ-OY;Vlk8yAF}y)9#CyPsFpUi5jC`scl<*qlHe@>Ob4D1`$T z8-Jy62qg}J>xspCiBITNY-#0FzG6`v=Bg-q-li53jq;!ZNt z2q$NZI;jioL?@Z8U!MNr^p`*X#m~Nb6;3Ud+K_kiiTd?~9YC1r%My>Zs~d1JxY zzSER^WPo;BGG&yqAq;A}09Zzxo+FF1v|e6@fgZ~fg?{d0BoMs}69RCMN$b%liiHK= zGfCaEy-BlzBa1iD^phabM{$Ssb%-`4PfUXAl#Fcmt!z6@&{(HQGB8Ebb_B%7$T>z1 zk#vaVA_FO%dch`U)Gj2*7^RB8J_Urbqm8FfVI=%HN97IhKE;2;=Iu*W{U+VF zO`iS-jo+@T?cZVi_6{Tb>u8e!Eyqyg*M1Ei5-$}UI8-#V6w*!cfLr-^ec`CiQqxh; zAX^ffL_-RtQ3I94IoGpXfrm?R2@R}OMK48%&IZn?9{WCwf@uuUALFIW$*WH)Cuo4N zVJc7_dgtv%|ujvLIDS1>t(!86Hz)y@Q|^QFpf-B&Vt4^0~n4| z60;)@kA_2nd^B^?uI<+sgTs=O9q-bA_>lk109n`Lco?wz%3;n!IFvPMRt|Y)f%3^2 zU~S`A{nbNe(73`4cfe-g*N^ z=TR8H;w<8JP^eiNphQd|WmIq$&-}!@=C*vb7Y5r^z9mDZs{886JNCLCyFEARuGeMi zyL0ZIwCRrBo%i~npJ;4JQ}W&FwzPf4UNwLD^b z!uKLEs+5jH9V3dE&|A3HW4stDLKOvgkVI;gf>-X~qEJ>4>H1Cx$*EmE`<@`9)t*U7J~ zTEMR(n;ZM45T+ND3(}$a8^u;%>>`mkdGC~_*2Y=$VhralKtf&DtaS}wG+nVifvDDu z(U-*8OvRhAl5X|jMUrk=0jW$Q!$gE}niCzmMO1n4(7`7TuUq9nE@VKP*gFl-t^^Um z3iV0sl>%Q^1HR@6zLpP-;~7H%sf#Bza4+hN6Nys~o(7@el3U2uUp^TF8_<-ER=xLw z)c=LZGl7vZNL}B*)Uwe3>fx6Tzj`!V-~S6n<)8O9@Zw?QDSGFDQ)doIRoXibojNmc z$S-gC8Wx+-0$AuU`HI#6S*RnTCNsF*6bH;!If~#G`Hqk?K@JlGNsvPov>q|hTb9pj zso*v^l5v!=OZ2hC9*R(pkYRfq%CN5a3ugWR)L_+*Ge9Vs9S38>Kn*Hqw3ML^jIF{Y zLujz~zP|^>NARpysL$Hq0V>hWl{e(d8m|sv>yyQ^QUjHDu4Yf#ad+FkdmJ4fn{{kW zADZ90@ENRU3UKkj&GvU{Gv2P8_gMO|J5Dc-nw6Z5E7dKFCvQG*voo{rc&7D4uKHxg zb#m3tj-YjMRW&z`UO&1J!_nYZPuwwA=IcAsCLB@$Sft$b;?r|aXKJ?O$~vwN-7%LD z?BSS$daE9GIrxf8&%K>`BXw&ax9!o}_5HUk{U1R1tax!|Zf2o5=j^lQq=bHq_mc`!ZBmnC8Q;s&8T0$3mXmxa zPXpm28xcrA66&#s$%c)11Z9Ab1sAY{MIJu;=^@{t3i0o8lrstoKo1Sl&R&K`(D8z! zQ#kt=M-7TJXBqe<{wMPNM{*b}LEso0#*$f)?!--qlAJ|579sBiMdEF->xLboCm)JP z%!f!C6=t#~foGCwdN1}g;P4ojsL2PZlXg`}!AUnkrU??kwJ66Y7s}DbQJI#>TR3Sg zm&&!mWIP-f2{9C1NLVfjDh>=%z96~A2kCvdREi|60BaD3}exYi$WWk2L9e#m)# z$l2Mi^@m*f4>{{!aP5D^ZO?Jrf5L73EAC8|JM&ju|Ic)0UiUK&&ifUdsdD~c&RBC* z_kn4G=a2CVj^)}N+1ec+a4fibLC?8cGwr*xuH9G5?pX{T-Bl;#8sj6YdY$TT${W0^ zM!i>;;i^AyOquxV`DYhTe87=!ZL&*-q1VPIFe;plWQ|X_^%2jILPIwCe_Ar~-8JT+lRmvT0MK zdq=sGAv?lQpcXK|>kmad1jDqSnj*jCxBU?t200#Jjp%>?OMd8Y?z{m*e%aogz9e;& zouK2pK+87$)-M;}Y?00&?;iwTk3= zd5sTFT+c5DCC|x+77gESZFF6yGK7;es5R=MM*wv|_J8jvCt-g#9 zRcpwRPl0wGIqEhlNRIMp|E5%t@+cR#V&JZ~DX~I=EzDeh$?F~mvrpX6UM3V5Xi&t( zk{7yb@VYb2jJ`e$ZJ~X9v-@HJy9~DTTo&S>fD0wx7H7xdvK8XcTII$H{NUIEMPF=V z(WO$6_#uoPOi5UxK8F-i0ENQFiq6hjrM(&gG_tP-nAzfMx6y3< z(ygiOA2;>UJ^ftsWcJ=*_E3X&hY2(|as>M5D}|=uK~?CYcq@JzeUKcuA|ejOL-vTD zi_tV!g9stUqq%^o&X=5eCSrO5-t$H50(1cE+ZRy(>83vXKe-87AN_9Z?W&q6%7p=kd1EuF_A=^P0564|uFO z(w%liV`>E|xu|NXQ0u5YW?>$c<(#td@{brKAS6wz#PF=+HN?VD07CPXZgKcQr0|(Y zqb2`32(v)HcmaaykVvxw(}w~Tfs-NhoQ=!T@W&;~zT5o_1>Y++Vfv`iFOu_g=d4 zwGM3A*K>_Tu9?!Gc0YHZaQ|PxY0q-MaCNT`#2jf;hA{A?I@aj-VLpCr5{6EUUoMU; z!jgk;VwGX92o-J#;~b`1D)3ur2;7Ka2i1aG;(zccI6a3)_Uz)iFL@bsaDh~ zT#XfVg{Q-@b_+RR=N9^%oJUXCmL2s8+kA&tZYV25#}>nIGhT^5bKm&N$kQvj7S%lZ zKbX{&=X$N>ICJrhOYai6ATj5mTX5lS;cOIzwRPh-1Ou6UPIuSG3xc5w#;mC9k-~^q zcqAE7!ZbMrO*7X|dGIJFZZofU>ON*2r| zFIa?n7xkHDF>p#AG0iAxnzm;#1|nDz`C>->rrFINGpC89iDGsD1)pm~@gfPA1BagB zP!tX?l5LMMp5{>Ac@w3Kj&Z=ey9|H!06IvVWO`?;Hdg&%vp>D_e(nA0JI#Si^;%O) zezx_it=;VJjo%u#Gkc@wZ?E4Qz0}l4cD8C;2m1J)K3+d}`^9~IYFqg_^Zf3}ZmI6< zXU=cO?`s3qEv|T@cH=BIPG2{%t(K9u2kUmZVE;d%6yq)LPT8XS>c6Z`%TnY1^0 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/coverage/__pycache__/control.cpython-312.pyc b/venv/Lib/site-packages/coverage/__pycache__/control.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..904f158c8e78adbbe87154e11bd20a617004a5a5 GIT binary patch literal 62597 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 A5dZ)H literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/coverage/__pycache__/data.cpython-312.pyc b/venv/Lib/site-packages/coverage/__pycache__/data.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..059ada89f2ff9fdf84f143edbfcbb9eb574ab3e8 GIT binary patch literal 9906 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 TYwsR6SI~ib9cH?b4Ca3U@dIY! literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/coverage/__pycache__/debug.cpython-312.pyc b/venv/Lib/site-packages/coverage/__pycache__/debug.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6430c36839ce24e7e913a6d76ec7c666d29442be GIT binary patch literal 32402 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 J{6@yb{tpsS>{tK* literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/coverage/__pycache__/disposition.cpython-312.pyc b/venv/Lib/site-packages/coverage/__pycache__/disposition.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..89d62f4f79b7d812b08f65fdd9479122e5533d96 GIT binary patch literal 2410 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 Sxh_{YmG!^y2~>mE1O5w{Jsw#A literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/coverage/__pycache__/exceptions.cpython-312.pyc b/venv/Lib/site-packages/coverage/__pycache__/exceptions.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c41c643e9d6bccb2c3ea64d199fe849587adbd5a GIT binary patch literal 2958 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 z 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 Dgwp|V literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/coverage/__pycache__/html.cpython-312.pyc b/venv/Lib/site-packages/coverage/__pycache__/html.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b859d91ce442f9f572dfe56160a9ba762bd7e5ac GIT binary patch literal 40485 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/coverage/__pycache__/inorout.cpython-312.pyc b/venv/Lib/site-packages/coverage/__pycache__/inorout.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b3ee1087173db20d860e87f282e3b27484298fb2 GIT binary patch literal 25173 zcmbt-dvqJudFKos#FGR_fCS%!NJ=Ecr$|YBSk{x0OzY)Fv}MC~AczA=ph$w50VNRz z6UE0(s8n53X;RTmQqyg^rjOl*>GqtcyFFXAO;dOK45Uzze!@enRKh@f6pO>-`4yH2RI~S>!KbPm|xop61-s;n%_IQ$L<+c@r=DD{_4l=;ipd(pUS!tHlYl>5skD*P2J%{1N5tSnXk=z8l(n9S2-D*%?$c+B zM_zjFaNy80hYuYaK6x~&KQK9yHH-$OC;;d~lb2D}aReGZiC}aTJBVs6|yp)XwCnJ;L(cpN124oVw$d;ZDPYQvlndrsHBukaD#ZwrSz*I1bE>B8m zbV*o>j0dBkzy#}Uwp5yta&ajj1uusL>X{Uo7Dq#Y^AVB3*^n?15zqS!S!=-l*$ zL(%c9b7b>K7)MSZSJpIgCMZrGnZ%ftC|#Gw11ToC=Tgi$9+sjnYoq^awn}cC6u1%! zTntW4g(d^h2x6B*qd5ejspX9L({miZzJSOrE}BD8j*B8y{>3>3QI+ScF0LD+lAC@4 zujG()ab9eU7AYCV%t}mqj@h)h5{s*gM9@_sk!m{4`Sh_*J{O8ki<4fUuUDNmFDB)p zH*%h(%Kat8rOEW3r7=F+X|;Os8{Uv54Ae)_(M!Ohb}BpR6=mq*sN@yGVrVoP5obE7 zSUBpX>3D7eeK@qNvtW!dsZ!fmP(F@)A$yi#L^3oUSwdf0z&x}h9*bHf&N&A zYSCP?W3~WW1A##2)Qm{e<|`6ic!)GMS#xMokgkNI7qhyElr_*`WR23)I9oIZ>VMXv zg3MYdfzbG(L{p1VdGxuV!B@JwyPtdMl?kA8M{qPKgeGQQnVRW{2Bk|K7pB8PXgoX_ zdgU@!>nq2@=U%}o4Rz#(PI_fDw-md=lMyj89Yrg%CF<<3y2S0M>mdH5{RpmcpE$YF zrtj`s-kECZPdNv!4QEWww5cj-s#@q+F}1!oyy~vFb@b-ZTPJRwSoEdb?MuIwba%~J zGB$U@;9ds|ZUg=rkihU4`wH+k&VSh+=f^19rr#tVX4o{Q;qoh^zNeOVVTLi^B0pE39c7@nh{C|fB+=r*kyP$_SaEgbeSI4O9~6IIV4 z;t_lvWkh%yR6h(1n`x1}Qt%RR`%1_g93PKd@dg4D!SG}t;LTAUZ#07K#S7E|B^JEr zW*GKkxrIeb(C&97EXmClLlPQ6%?wY1wu8(^ zX%!-m428%D8xG{SkQ+O>Z|FR0HYkfilCY+u7lZNu=SUG{$*uUwmMD#dklTwxLc8~5 zWHLk)LMA;SAvXpmv*tj6N(KTHlZTXI(o|?P5b&9@mh*uN*x`cXQdW;5f7Y?8Aj@@EySO_-h(LLzHM)<95D24l+L z7U$rG1bUT-^8_uSpXft8>!4(|?a5HZy-0lyf6_M*T;tX%IbUa@bzuJaTY;|!Zj0AW zX6oD1_1($(?&a#+BdPj>*N%N;DxGipwzOhuTCy$2?zSFy;HXZRs~^~^(zd#!t!`m# zsX1lqx;Av*WXrfbw+3zw%$ZkhMMr z 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 Z(#H3$*B0@+eo+=F;@ux}6tdO({{S#X3RnOD literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/coverage/__pycache__/jsonreport.cpython-312.pyc b/venv/Lib/site-packages/coverage/__pycache__/jsonreport.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..176176e128787d46f54461b73c17ebe34532de38 GIT binary patch literal 9553 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/coverage/__pycache__/lcovreport.cpython-312.pyc b/venv/Lib/site-packages/coverage/__pycache__/lcovreport.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9241949bdf90b5f6c17f15fb9981472b37aa8f4e GIT binary patch literal 9588 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/coverage/__pycache__/misc.cpython-312.pyc b/venv/Lib/site-packages/coverage/__pycache__/misc.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7ea10e0efab680f8e9d01331db9e7285ea3a73e0 GIT binary patch literal 16753 zcmbVz4Nx3co?rJ&&ldv>Gki+uqXkG}Bp3^Q8_AMbNw#ILuy3W+ow2l|VY-1q^MSj2 z1T%(RtW#p}+9wp-EB0*H;_Ou{#rB==s_x>XD#`98RjFiCDKN6dwb`svw^x;|N+sB+ z%3kHFlHdQm?wJAM_4!)j^_%zo{_p?(-~Ee6I1Pi@VE5Ceq1~wF7I#% z!VTfOf+F+?il{h}V#?9u5Gn6WI(wY#E%iw3?dox{x4Xy9-ku&0dwYAl?CrDOWj$r= zx4*~F-sRT2qNjrWuI#B~??6ugZz)-o3ibq3p`K8xx~Do-(^JFdcO?U<+MZgLcPHyo z^*!~ehMtDhx}J5c%#&RIK3}Ofxjxm{)5uDF$qlKEJsU+KA@pog%GAw@|1dVIY*Wgw zNzo!i+nWh_ViKZLDrR z>KY4m?aD*S2Gm8AUCKthA5uD$-O8q`uAW`0d$6~3g*>n9Q8t&XbGNcrk(KDMros$+eS)&jC)od#{Yo=N-=`c<4k|6k?N=UFwqk?>IcK;t-*P&k$JJyq zmR2)aUDl0kU!UBU(d2mMvZ}@U)yPmT@*7%8STy~ybUI_ij6^1_cZVg@d&)qY-lS^f zjErV_PRE9Z66t=^dq#aOtES^BI+mTzDA}ZXHaDb(ou;!Zoin}NneV7ZR!f^6Dn1v} z&`#WeBzWAK`s7Gl9iqulD4p%n;`t3qB5pie9IQ<)<}a94r_Ma}?XI&YPee~Yb>h3< zK6xf?;RS=F8>v$e#N{fK-VjCwgB4S9S=25YsN>I|Wao0VWkCc_kcuw_U1VhygSx2rfI6QiYbhYoYhq}Uc!)-tcLNa1sk|cR)L#2V}J^W6Un69 zuNtxuOZO+yH9I6H`e+~wKwT0VTVR9@b~;8Y#RB8B(@Y;5)7tK^wSYuwD5DuN=4E4B z+b+Ej3<7LCV<3ZB;u-8C5$6|!PZaiPbTFpN0CMY^m-60dG#yK+(WvQE61XW?b<8j{ zvn-(}(mK{3SItUmx+q_i>Ct&3+%_kQOix-JraDuip-k81STd{Xgs}ws`%j(dJilw# zuBV?lpTbeK$Ko+XP36uH<=TyyeyP1bn^4qbBCVdktfnuY|8}DHypA(!ABx2<;jZiF z?OXp)3S+`frDt<~&V{q53<>gqmjdymR>N!}FnC6P^Wm z`)zN-boHEf<6=$ywDZ-+KNlq5j)@bCHI389XKLnawoRN|46K{p`X{w_wsg#G>9`%( zz1XmACOhBIKIK|0ubz5r`s~cUkITbLK6JcSCX{cOK0fD{XC-+VD-hz8*pGEj#AO*J zHw0Da5yAZ&iU@A!QXHUk65dX{T{$W2HY?BM^wSK%&%}U6`7V0t3=!g)w4tVfotu~0<61?s4)$v&NikhKj+3Dpwmgb|JA>sM|!VpkB2(yNh- z3yY1L-qe5c{EwfXZ`?T{ee7+}Hllp(U5%sWhVadIwHEozL!cqj39^WVfGqw_k~ivj zM0mMY5^`eLk>CFvb{ZfYxx!)adkYGH+Z3m(pTI%(4ytibA|s}Oaz(neCagc~)LQW} z{nlLqXVOhEr4!(Bi8us3pl;x2YQAn20Il+)Xsqu;GA{hOZrz*BKiT=?o%40uZr0oh zKRk#3bq`PY7lIo;7ewFYiLS+}>Kp#+{^_pkm7h6L!XVsz%VD}T&IG0IOj-@QwGb_= zENY!CNJV)x`dl`avU;#}FqX>3O5r@?L zwQYul5$7|)u=u`nn2z*V7w9PN--scnVrft#jko~6b!(rq#TH_6ZxS3R(j9hi01#Lp zGPrcQ-=()Ah&yE8nkdyO+5xnE6o2{-B>z+Ra$LCQ7AhL&%QuXlTyi*F4WD`|?s_Y( zRZLdA68Lh$#WR1e*P!_o|3UGOq-Not1rLfp4efLOrSqV;;?@*Ij=w;K(o*x=;APh z?`NTEtbT+?klY6r`wnYDcAwxoxdm$|lM@xbs zTVlakx{k3VWQurFjiqrl(StZTCuwr552Tb8@O#`=5I=05BoAlO+YQEcA!4dAh93hs zMY(YE6GlXq*-S|lWSj{Mv}lzx7R^8CToosOSd!-ZsoaM{02I4|)$uMWJ=fZ;d<~f4_9%nKH8^8{7E~E)aDiCE# zfE3SUlL~__JCg!%A#+iyLVsO;6rfhyO8as`1M-M-pfjQlpgD3bla+^KX`@qC2);~g z0vx$8ceeY{u!~(1)5jN`O!S%+JZoJg(|?Ms=A@=&G_3__hs#Htil9?^{X$kZhDT9$ zB-$fr{xtsd7!q7QStx6q_igx0kX)N^|9qPkt2f-K-Z59bOCKvovnWO3l}O` zi!X6ms~U;MqFvpzYpQdueB&2ByL!ouiu+%9Xe=7=zP=T+{7~$waK2L4?sz}gRp$D4 z^y}w75!s)YIVoS^>Z*O$OcH0P-J25usy3d-IP z-f}{ycI5vW0w<(rIh_DU0^ZP(YU&Vp2ylzZizI8rfS-heeR4XR>IKcRpl=?6>e9cdk9VBnkCTDP}+HeARUQ$VeGb|oX7z3D0iRlACB4b#E#ElNAwo;5E zywD&^tJWg!i|yK8>>G-kL?eN&5;rxSYFb&zGJUj5oNGeUj-x|A{&b?nkQgh4P~8hp zjGvs?v*0P8a$Xr-2-Q#3&wAJ0rT4Tr-8@}88`wPSUBO%IEyd7sDvf5eXd3Ob$1q=(Ho?WDyB_a& zZXr

p=BX*AIs$DjyV+h>to6QPOOwYb|oDgxnXGu80hcT%&^G;2N3Y9JH)D7^?6n z(p3Q9M1j-3Yq17w-IYIaJd^5$aE~L_RV@Ko6&R(d{pyH?dOFS-^oedgQ|zE4#0Suf zq$p0sQjy=#h#E;^_r=i_=Rlj=Lz^Svi3@r17rztF9>%Q-BznWG@hO{I3jf0s)I?7fZISjrII(8&dsQyEA z(i@O4R{c@^-kIlsI-hHsr3bbErrs;7t*T#hmb@azF|ZzX|wEn&JS0t^QX|XH56W?x#+6pA9>Rgct*+sFohTSx)ms zd(~(^UAqfd_CLj+-iZXXLy}yfMQ_jOD<#|Kx%el4`MPRE?UFaosJ^D zSFqXOgq*OWN%?b((Cb;)Nr-?#zlYwD(Q+I>B?SFqq3R-WVj@T~*+No7My?HDCEiSi z4Q@s_3!L>b7)LXgNWeXZWt!d;xC+M6QR^#=w*sftG_56yO05@NuHjE#k7QiyhAsOO-Ec*M=vD zul->12h+xUpygv}D~AN?3y*b6Iq6$vV{ltG1`oF}C|=NLsmGmjg?*;`aj;t07`Cyu zE6tp7LV4G^d_UO6|qG0v2mTGAf zn4wWXuTjxPCi^{DFuE4UR32yr^;H}Z$DN`uC8pmW5sL&?aZU>(;tLO*M|Y;=7KbV1 z)><7%@;`89%n15|Il(viW6ddl^EvHZvwfVRQ88}$s&Sn(ZaT>3tX#7Wa1e#`eoFr_ z=TSU`e<%#n%WS1pYp#Z`l*}OPv7p0u!B+~$5S^fHHCo#g0kgWrWFd{fYLx!h24QLG zn2!@=aB@TmRz0?U4nsOgW9)$u-5L=y<8F*eUoo>}w`1@Gwy%DmHApmUvE9pItZ;zY z0vhesMYv+VKVb7ogJhUn^(ck;H1XwHgRDR7p;>H#Fy=D0*yV#wcCPpyb=hmd7zh5* z`znkAnD5>Y|t6PUp0QMqM1IsDF7ScH|Y_NOS)U%)=bg}dHz5`FG?8|J(X zZ&tr~c4o^PzyHRDJL}r!*0tUI{rPqKZhQAHR0hmo?cY?@EClObtNu~#LRHNT-*w-e zDtWF-o(aCw@OHz^?ekl9e^Rvv%sy0qH`wr6?T_kLHT$HhnQGVF4a!rsuT|fuovweg z_9ykPZeG#uPpW5r@27R|Y~zp zl?R)rx)y307*`Nq?}p_w*!=5Y?dx?5^$j;JU%&k7$dt6WZu5-v2JEDc;I@+1uXPtX z&TM&G`nYE6XD+l@atWb^r7|H@y|`|}^zXe<_qpI!UL#Zr6tw8sx=>%`@Um z#~T$>U5oXb?$o!=)wkXZ-mc$?y27|6Q@q~26u>xeZ1^gQz@D!8*t_XLv50Oo<6(!9 z`6fKfrYgj&*OSxDnQwu%3QY}~DZ_GTYDxlr7{gM>7~V&^e35t>NSet?%o z98G(Ol3hq{Ie1<}KI~z3YORARcT)nrP~bLcLS~H?!qh!BEXVSGDx>5r{OPqw$Tq!; zY}3cZW%owc=6gpRt{uDwwTqVu4y4mCpszNiLSq(fGN%O;2aGanYE@&-bXak+S_xyV zdO*yjwX`ajbEswwjfzY>U=MzKATs(Wu2JV34#myPK%TeUHc`DIhQ0X%kE_Wc%cvbA z3r%;%fZ;5ul9^Fen7#!4oK$pG>x;$7UX<tuP>X9^SND2MbSv1FkP2u7@g!ZB59UVT^%LrtVfMZ6jndJ5bag8`A_)MFCl^O z?WvqFW^3DSd)gPO8YWz@t=tKO=K|qL>26g$euIcrUav&P_A<@}n`b&^zWdJi-u~Wf z%ih_*z6t5qWg8d#RTJB%f>V2^T4&csZsu-Y`Y<%>J-qC6_}xp;6y5v|W6r{3p_aHT zqiB^tZ#xa$1*f6MEI*<4#j;7`9JyQb|Dp>9782Rjr0rn>z{$EyT1z&a)nTNQ72c(t zxoG*hSSGdOh@UXDEF4poMb5Gea&J31v|&IeAzhAfBQ}`~+t}brLLW-T!2Z(m0Pv}d zAWnMvnIR~RBV<$`%G$=;6&Hwh%q|ceG^mycB>_49Z}G zsJqRt$oG`5Erm!y9r*94(ur`43k&O8paTRr?1>}W59QfP2z|baexBhg(r%#5YC1>W zO@au#o2ysBi~k2Y=_Cka6`*)f?Pe`PP8uZbN7E=N5I60g;HTw%Ta&8&F*V;y2}xj_ zpuJ8xNtTPT8bY9cT5U)c*je^B=Hqj?7yMY8Bh-+VPp}5aq(iy^XIz0 zGj{Q!d^x5iC}f9hf$~Liw_W5|2nGZjG><2+@DxEg#kRztn0GD?4~1cwwVAn^m=)CA zWU`&aAXq*SB9hTkdS@qhA`p(YV0wnE><3%S%AZHasHX7Hjflf1fw^Ep!mWb5haq4x zA*&Ov7A_xLOLlvYn`u=r-Cb{New2$Is^TkcO= z(79LuC9~w93090~r5LO}fQVhY5~SQZZQp7gBn1oRaAmZB!ylnD_SxC~v)DOY!PcWA zVXUlK{w`<@jt(Yx0NXP0=HO8nDL~}>8e5@FHOpP4duc0lfR(nm>5^PCi6-enGpcDl z`t)HAk0UbX3Cp9reY3m*PH;zXzk@v*^&8G;N(Z_P?_AjrR1FA8wGVVV|GYiG$~R5- zeEYCQ-auu=0;!!n*=~C>t8+7-WR3P~@1MFg%PM4jb>(iX@z_KhT`|+q(p{BMchLG& zOuO_IL$ou9s(m>9Bl5G~8@sRr!?1aQ`G6_sBpj0-Sa|rTy^@vhX3UuDg&U`|c&zX% zT+BI5H$P6(#g2!+cE<~p2V{zr7AQaBGLgd9ouvjTMZ79FNZTVOvFe$0{?Qul3Nm6V z+;b-8;6fg2tH4l@3|-6-WJ+}S2s6|B%?fLUteFl0kUIGaJF!*R@$qrS3(d-vJIc3xll{AC zvt$VTl0{!s)nJ>`&rfG5BC{SW%SsZ=UTSQqJ5P4PdhFDY0P$4Ga|+u zak7y}sd;i(keJ0>*o=8WFdz#{Bp=-RO#9YU`&LEX`grHo)AH6lJU(SHNFlr*%+Q9` zN55c&b|^-Pb=j;Xks)S8*-TLykJ@Q}h1E9@evq&T1=e42?UBhxet7hbzj@BzJlk?$ z-hXgbI>_NtN`EA@2W-UU5PFlmJ5J73F^}T0l3?AnXx3Ve*HMgMtC>(D9pjECgezY+ zjt7&idWuwQ%sJ{T#_0?e?7_tjJdk+QF<7wa7VG%@Yu611itX%N(RvB1B#%w_Up#CB z{Dq$``A10Mu;YI%G3Tiv_)$>Es==a34uly_12HXebCF64i=7qqaS@@T2r6JlbldWh@HouW!T#Juvxgxnp_*E;jc9C~tNnx+{ zFaTmou+SyVdNSEXDVUkLh8X6N{AO11weTux#DZG1t<=>GE1{xlre7UFXWkHWgiJv! z=7m;?a7^mdfU-hlE++^KW@JhnD4{)2_ky6KbGADZwi`fIKo znLKjG-!bR!_+a~o!1v>f7R>q^XQf7@i)Gc0wQi3B1B`uBP6*c!ebx`{hs6mvASEC==3T4*bqB=>#F@#a&v6Dhk^LHm z2`c}8&f?nupdB$cii4uS1z;*X`b^#6rKI`t&=)yAkWF8*{j5#--Fsq9a{Cdv_1UI; zQ=7bdPn+BnYr24xRfVnaCg4>@OTeZAechf3$PR&$N!IK(z`S8_(TmI~+^I;nfMFtx z!<^S9@Ua6bGui%uLgZT0NJp-Tspm~2yLM5YbSLQcP_jwx&nBQYSz62rVT*_t6SlaC zu{2_!=t*UPe+80<*Z>zf94OfDj8O3Ei~0Sh`s9ncrJ~qzLj2~Db_Kv9o*uzstPw0c z0~GtCL?GQ<#wyIRb7b;h)+rEeaYf{W+t708pI;vx$RcJl3Sbmv8D4jYP`d>Kkt5&f zt3{aB)N`-ypE&&`BETtdt9GHf=B3A%5ENT`pXuyZ$|{6c{Ef~(78{*;SL=CYXBZdk zKVXQW(w_I>u%LBda;)=DX`L?EMG^)=ds)l>NJV0NWqeXuahV#Kp*m=7Wc(TzmTO$D z+IyA4mgaZ7Z+mYxe9$p3@B5^D|8&RXxv8#Kp82SJ|A*qNzjIdV{4MhATE7+OkRCWS zC$SvbZ>fNcZ%qp4^j?9DFm^k0hQ2i*`qmG>295&~1*zvnBiyU&C21X+;fqO8B!gDOJFH5OPYPF$jW{r(Qcj8@`@h|B90#++Yjjs@(%P$S6k}AhH@>q$1hGx1W?Iha#*O{<-+R zn_$^B;Qhc-ci1wV#Jo!>U)F&WHTEV z-s}88`LN^9`{#EanceZk&uV@a`&sYL8s>I9G0v@OK(YUfh7_YkaoZHqK_~|i(!~bX zKEkh3Rm%)LxB~4q8l9m@$-@A=t0TPII=AtO*|X2y_Me}X&T~FzAq^S{P+l!Q*Mtno z&{jAeG*iTMIz~jDNGmi}k{%hF^}zV9#gd6U1>2F@WStmjE$*-`caXD^zHS3r;9w!o zJO%t%6`6?&d|SfeT#(&~`$(EPI{;3_$lwT-O-yUoku%+FEPW+WbHgWqm1qlS5u<&R z0~o{>Q9izjt}B-p0yPumcLTK_1-4CZooW8@&ROra2c_kLMREeYOVOrCxYqg%t;F@R z{dczFhMzxF883YdXxXI4vc`81A?tu8StiEo)M zF-2cD6xhCtrvTLqsy{?hCN_YmrijP4F(bXI$74gFht~IrdZc(bD&Wr(iWHZ1*NaM^`o+2KD;IwBc`0Rouy_PW7dPeJ3d}c{; zvDESy4NGApC0eqiJxT>+_2(1#sWrGW4Uax&P;tM4O$t)KTE~`H+*WaR=4?c-7!9}5 zN)H45wZcqOE9*&pLOns6_j(Vxe9-zBl_tdp1uM0CN&8FI#@00>cBZ&DX6(VRB!=-} z|1EWa|DU7}+8=Mr!25uZ(#YXRJcAa%ztEt+qJ$lb&u(`iEIx`k=V_+8 z0@zAfV(L)`vS3K;|t#@foUOPD2Cuplg?CM5cdLay1CeBczFGr26p4MvwKK zD3j^fUD8N?FavRz-y!foJ<}tx-nb@F7ou7kDJdFJ7ygl@8xbC$rW_p!D>Oe6v)cBd z;tN#kt74s*zs$-)4gXY=VK-^eW*{0Z`QRCwt)trOy47yh;f*D{8HF_w`0$&P&FHh%nN&#-Bse2&$bEfnyGKiyXwasU%1P} z&7TQKmevc7@;l=CIdT2x4!78_>=Yc0ODq?v5jQPI{GzYg;BsCQDN&LS{e}Z-UFSplX7Bb6 z2>DZ~>YjM>_RY-OH*aQs^BewDRpmzT{Qln$#{S?(=rj6aKYUTa>wg1f0Vybg6s#}_ zoMa*lraGHoBdk?&rIL^E*1MxrIwMZ&U5E%!atT+`9dTQI`GhCwjd($KM5P(pdgN3}Gh`#CrSw5~XM0jvD7;72(adtk;9G zfK(L0kaR}DkX&XKMK}nUGlNeRgL*nV`}Vy)ueu>BH9NogjS(;q@8FAxFHV@H*|tJ7$=Z=tZ1@fHSDaT%aS|r)m}c`VsmZhPsvTMi zc1w(0$k=1I$fI$b$G^qDbHq@dc?-Xczr}}Vvk!>)KHE#B+%lr3irv;W)w3k<4Y5{N zzH@0Qm%^t#8k#~W;|Qhui&dnuxCZyCS0 z;?veDqka)hq4VrBXbNu|)xFbB$b9e{GUClU->tIO7Dg}9HR$}Aa|&0y{lDfdwf}z` zImG=v*1=RcZy{gq;L}7-r&S`#M9ZWUQ5w}WLpQ)kk;>Z2m7P3D=q3c*5o}}_4l$-j zkqueWGh<`1^QKGA7@9;a1E#Bt5xJ&5Igy|^S2mS!ul%`OXaD)Anx+URDjD*vZN*qB zW^_Ru+*mB3=9&lKU7t|Dj;a;!5B?Y4qX|_`WzrO8)$`HZ?lbb_4!-FaLp7#gV=i$) zZp;R0N+u~OP1IPD0=lY5x|ShP)#R;Ynhr^!m~A@r2`!UQOn!>Q4Apd%<1_g*jNUL= zJ*z`2Ls1DaT`(h2bySX?GhJ4-1d*BiSR$iOm;sH%#$zctQJFlG(+w>R5<(9NP^yVB zlat9fxI70DLdjck6r*Ng5QJ`iLeW8|@F|v_yTeDjMmjq?pL%8_DI03L9F-L{nH@=I z+YMPi*FK(!DJo2@Ix?xICP$u#jgIIrAUN=%=im&}N9_D`l&2ofHPc;*HMe(%BPU#` zr^mGZGC;-*`lK~9$FDJdSKwoB=+nUN%f@@ga-e&zuW+Dqu5a1fR`B{>IWd3Y!l^=C z!$Ra@WNx4k+_e~NUG%nsEcohPnVz4%^z5=PRA|`yDu2^gTkzK{crJPt8y{cxA6fHO zxw=+^t(TR>VD~%2e+_oOcXlb*{pqG=N0$9PYmB3+zTgio2p5H4yUWULH}cn&{XKHS ze|U`r%bkt;k$=~hh;s#2y)}!0qklg1m##l{-SD1W_13L;?_2WT_tu%WBX34-cso}i z{*&`3SG^C5+SrY1_k4-is`{J3hC=PGg;N(#EsDd7k>_4LwOso= zeD$j}C)9p*+p{@{Z;bXe_np;X^+erXWcvIjKq#tQ0A!DVYS-y^{3PjGQRdI3-EZqLM^fA(AU-9V8+xR8W3k z33`C&l!MNqo7})U*Ti>`19s&Wc2uGlc)bm(x3MD!@FOn5DNcojl2-(UgVK@ZLoQP| zV`x+Il$^~y(UTIj(YP8lD0HS{(Q^s)pr~u&)P$N6V}=;h#Z0PbR}NA`R8EP2zKJXl zCsj;tVv6BtneIS1R0q0**Xb5rKzW=;5@^LI3xGU$1)B~MrDzyp2;~x_6NFAXilUVh zno$ayl9a2lLk`=fe%hGg*9p5g!raP1(EUi6TOP$h+y2Dx8FYmiw7yR| zpf-f9fOLB1mqLMQ99Q9#0X%_l>NbT_F&~HKI^`x1lE2|n%Wn?6eqhnJZ&BD+3~h6M zIA~_&M+9X786|oFR1qt97uYH!D2vD;FaWmKSsYm^qf+reDy7dtYeU_VK!{r3IqHh} z6!_LDhR&c>Vb3M?^6}TT8$#F$dGHEnWd)GkATV93ks&FQ8`M&C(jJ9QcdM0H)OZ%E zVci(yxe!Nw1ZMJMDrf`J4I<4L33c6ZJh>9Oj@emb{Q1Jj2)>3bwTDvWJLD4C@ zqdVg4I*0i^>kiEC-vH53>_FRYnREa3nFbHm3tfBa*r}uwJiw6%Y?OJn=jAsroLq|TWO~-G=Xs8VBxZelkRB@*99D> z7ou&?FImMo$IhWKM&W0@(@dVpW65JTs*wJjMz1*R3nE5YN>0lZ+1PD+7-N8bvlu;t zF6_67XGq9>viZ7LM1^e^#Ud7l-EMk54^IH5s400gp;j(jVp5g$3@KjKv@x-C%Y+sd z0|6>Cn%&OB!WDP1)TEY}gx2!-IH8+jaw{|eGb?@lg(Bj-K!J?{7;LBs3Vl>BR*9o3 zC2|>ERZ4vZO<&*8+5Xc#$NHtyeFG<+=(jl~uczgy)RuJzYYqh|?$Kra8nXaEIM3wE zb3KhKJAIzD_c-12be8h~L?I@3MM~lhiZT#iIRQu=OX(_H z!hx_su8Oc8HU(P+B%@mcF3B7{flREJdu*>1&?xJ(JzEsG2*Pw-i{i{o5r&(0YA8*? zZCkRxlk3?Ln!?=)Eec^i7XEG*Y51ltg19q-KJ~Z1qkQB)G}p7**mC*oM~w&PdgkKu zPZol;bH}Y6KMX+A2aQP=6%=T4n~(_p)$m-%XF4ZjT{Zx@eA^(?tbVFzxbJxXQR&H^ z)2I4R7f)Y$CY!#Y5;H9^Jd@FMlcVdzbjqVJ;Zy2 zR`);@vC)j77T=u!zcery1U>XDKV*Pv*0Dy@|AHtUDKdi}Qacqx@Tbp#_}2{Ds6~SB z!q4BTUUqgAgzC?m9$IO7e5vX2*`Z}_?}mU{_rHDY&0}v5y*c#Wz(*~Q&i1}^@)su; zec}?=wC1nokK>Pp&?jx}D{Z|?ZM};^>p$F_t8UGU_8rG_{IbyeIp?`B^v3h=4By}$ zytAlx@j^hs zo}8dH&XOO~(<|$0X$r|nT2E(}G*V$vE$ei#QoHroxhr%R@#--WfyVS))+a!~h%p9Y ziTm*vsQEKge+%usgFb2rUIdD}jfX0uRsl p3Uw_jbsbA}9g8Ti;b_5KmpV2O6#pE=ZrpJjf!Np`!iTKY{U6!fQ2aFvBP02%*Jd`&QQz_x4{r)pEyKAs% zl=h(`?abM8{_~&PcfSAfm*(a;!}I%p4H`=wjD13Xf+yq^q!kB+OH5-UOp~-wPRfTy zLK2n3x$sC>eB}`tUpW`aM@OQfEs|@V_2rGIF^!EO`}lEsgAA`)a+?pQ!<9B z@{+>!0@rQbbW~5)8duC>KBIHR)*U+)kDssc`%5YFt3>yuY3k_1RY{N08 z^tfR<`h?DDeyb$Kq@aoVWO7!9X5vihil#bhMzwWlK7qgbjGA?nypB!5?4X!rG9^d1 zq1pR`&RE=}Rm@?lbS&Gn4Nd1XO;a)Qg`A!bHcioVM>TTjr$VVQG3hATN!7$28D^X` zj~AUHr?FqPb<&Rac>Uo_Tl};#Z8(!?w9x495-q@{+0pE=(%Xcrq~s=4)3p5aI)?v^ z&pSAhOirh^vQbFqEDUZtS)ic$G@uk_Oj`L33YS=nIn~w*yXu!UsSNmPp)zF7N=_u` zU6BHbv!Mz*$WCrVd$;B_3Ds4D*R1 z9f&<7{!$f54?&RYMVod<0UpIR(Ut}P>Jz8E3cS!RV1ykfHLvJ6JhM*kaQ*qDgv{+m zEbBIH;tH)VDZAmkI^)J)Q*%ZA5a$+mV+1dibG76~1kxZR(-hsPDgL`s#tl=23=K5! zy;lwnjSdV9y!7U19{09i&8nK7FO3#T{f=s%?w=?EkTmnrDczhJeZk0#;&$l$biZ*y z?a|<>?ts<|U8_RLZKV;?j+OT7_y&wdM`xeLYnI*VkRuyzwRg^)xVULvz0kF=WwE{I zT=Z@ui*?L>YpHS5?C{Oj4WBb9vh`f}W@GGJ&s_UEz3(RG&deWJ*mC)~3zfyjp7V)2 zVF-MhXq#()_uI2CesQ;%H9h({3q>~ky|v>`1mAmhJI4EQ`*4(f5RDCYggdXjRZcq8qI;;2NsP0-DfcOXr&^(y1!Q zgMN%vLVz{uA&bhZ{+JRwzg1@Ecgw6I9cRg~x)lupHmi6H;F&XQL0PoV9#PSmt_@Q< z971Z{z4vhGQ?;jqNN{~j2IB$P#ELZ7>yx?(^iNB#XHkM-+j*3!U^Ys`bwz2|gAl3_QdV>P&k zj_yV{k)#_{3kBWOk`cEd0A&kg-6&m)wBgG1#icQvv|-a}(b^OayUPTeY4?0Y+x#)q z_Ty)d;srQwW=$=#2XAfMvhc*x#%-UoaAe?}rgPCb`PL(w=XYLw1E7ozoI3m~?7S&Vq+v?@d)Do<~O;Hi-R$eKE^!v<+Tsq&&(dVm1x7r3+3gu!KK9Db$Re12+^S7zeNb_Z+I>8(&T`! zfT2kF2lW^ttH*6!xP@OIN~^Sd6*%!OtoLic2>g0KjQAF);@$M3V_0=y{0Y>Qp9oH^ zewu4w=s0z+VW?~Wm8ahue2>rbg=c>`z1X$?OWq5K6+!;Va1BLnloRz8k}}^00luAH zbQ|1``VrH@s8@}-M^~L|&5h$eu@~^Ocj2`Lur_sJI@d;)yY??_+J9Ys^k(Amj}luK z6I&NvT}o`fE^mJb(*9Rmq&8zCo<}G#2~z^%D2Y3YC_y;A#Na9yet*V-8|yD~pJ#^- z>Oftd;)Cf)ofIgJPhozPp-fVM0TXJO{d&IOl!SX005t`d9hS$ig%uiC$(qryCY*Aj zM{?nbAnSL3ePYa5R<2Ju!Hei1{C+HlLX6sq5CMoq{NEvadw?EHHoa=K4>F`mc&eeh zW#IDD^MmvJ9G`pkC)0~910K44c67tg$dlBio|4^->kY_vL-uu&_qnj+elBdm7z-oI zEdxu5f$Q?XLl8O+HLJN0jvE;vJb_bJsk4~1SmZQpJeNwL`Z%TH#UbFvz|E^;L%BS4 z&_c`_pal`6uz#?G0>FS!)gF%d;9bl8Uj-q834#*xt@64EoDp6G*a?g(c|eB)7bvP3 zQ+Z2s!#UlAcLQzU5^{$*9he&t@c@DYGRtJgU?P2Yrv+o-%HnRZ-!&F zHT!@c%H~wt9vbr^h9^^qhWt%Y_7qA;W|%_{HtqEWvofc!f0XJOCqmD19T_N}%4-Al zA#-Gd{2fYSi3lVJOHd*9CJ0-^t@ih>c4l6}$5cvI5osn<9Kr)abSx0yLf@I9&JEoj z@{ffsAJRP*@(=z}gcMbeb1PrXhou6ozHl(D2cju&FR4GRQh6p&oAMXk+l}lL<(E=g zraM_3Y+Zf5)UPI&g+{7LWsBUlcyFzxM>%T1;RibjON*Qz07p7gf5KbE! zr4)s3l)Wt_rD?(+`3o@5*x5R$_U`MW8fs5=dyy$=rKfk-?!MZ3y^?U-Z<9R{ewMIY zoOn}&ih`0?d8WjC$cacRBvWsfPWd(o)zzoY@>CQM63r9GNz);zemC`%1j8sWYqKADJJyX zcTplKukS<7s4D zC`X)5b66q9$!&KV{QfvU3kh;BnV(WKSx#oujZr?+f%jm0!3j|7gPJ1M`;J!}l@~Wr z;zEQY03FRjpV iklI$ZHAqjcbT&$TE04ydohzGTQtIx;ZYeE^Bl7Ft)Y(Ny&awcTxbyJkz(UA--L^>(?YyQeY*GB{g%Rz2-KJ9TEy9&9u{ zWzX!+et*P$Wpa^SHN9u*O57V65jP@k#2 zXm{Xg=}Yg=XwT?(wmVrl*1pXCtoAGxw)MIC-R*7`PV39=&uP!;&u!1`_q2QZz3pBW zZ|}?N&u`CXVMkvZ|InZm;fN*}l?fFd2eE(Dt^Ge|@{nWY}*AxV~-(xZg%U(A8eU;<6E! zqr}zXy{tfPAom@Q9LI8bkt3?vN;Sg)Rj}|M_6!;BezF=sSGIM23H0 zAQbUOdO`!?R-Y+hKNvxtbA7>t{Y3EPq2NGQ5V;%yf5hL_=MRVR!%hXa4G#u=7KHtM zeLVZIK`O-Gm#{x8h9aS^P+!8*jhB-KJ2@hu%UhD{+>kW2AsGyASg^(EO2&3qW zRCR0UL}*Ct3ML$_p~$ntkqe;#L}&GcLw){8u%kZ|7(%-o13g{AjzNEqh$39)g9B(U zQX`>@!GSRHTiTk%u0&efk)DBIYp9F9Q@*6ZGcNvn#N03h4edru6;r_2Zgv>xU%z0dEp8?xDX)-tl3Ig`8TNE&I1PTKVyqg{< z3Z&zi5m*t(z|)B`op@#jiUXN=X5n`ho-U-j@N_RzQ%S(BDLp$-8puZOoIqJ12hUtQ zbMf>Hn|wmT$vg8|z(P=r-egZe1HdB^61sZ^09jXpT|*HXNTCn?7H&XHSKm+|7*Kvk zx*F5d1zc?xdcs1_03fQL!Hs~QfCAy1KaA|50RaOV>FE;u0EXdkPk4(k5aPA*S^y<@ zg%#B^a4FPxDHxDSZtb$DeQ6-1h7mI^KEMROVHh!n9V4c2+99AbxmUkt95G!nh6%-) zMhqRw%Ol2D?IT9SNT|VJ7$^-`0@glLzj@SR#7LVABbE_EukwZ(kjGE^_J|q3Y;v9e z{gP0w!4S!o%f`!w zE9MssmyJG0^sgBZ5KQrjFLe0_gmXYzV(3y2!Ja=XbVh<#BApFFD^+eUn%bLg`Tbp>MNIf2t0#=81x4k1z~RzXbXN3C0_`I>F$pR z0P3y_N=cp4JU|V8ynjA;1>h|Lqe>u4jgQu8=a(=L3#`d6UspxusFtHkx=l zT18r+fWflQ2O}MW4B2&r8TyNQTSEd5N@zO}+#=LO%{5zueZJg;T`ntOp$*%!}X(C zXXM^nO-p4^#NwE+cXad&^h7#3qF(LDHpi2+LJ89sx@!#9ka~ZS3I#9>Kos# zrf?y`N<4}vE~nT6l)7;fg^Cc@u!f4A-?ob)D(83Y9xi{iL0@Vp)o3ZW+s3yZij- z!=D?|yA%*ae_8R@L=?s;1ioWDY%mg?VK^&b5RKPNdelL$8Cg>wnL9!)P>I zudo<~4S!_5Y`kM^y<-vyVI<7J^geUK)VL;L>=Ek`6{f-j;T3Teo}W9m5ixsZP}~u1 zOhP-XXMs(zF44lXtuNH&?+fo}RPtYe9qnQi5ZEXg=voc)=Ho87t|lWU~7>>6cA3BM9}_36aq_%3t5jHLH|I)+7%iah$L)7gJ2AT39INII3M(-vyZ|rS|$@|tii!>A{~4Kqw+

?9CfXpD$W5=6dAKzj1ov^ptnn@~-2K03oNi{320c4%zxc+-4g?ezM0H{aR(?#??q@2$UI^ufuQ`V;ZO6Jv+w^H)xL z-YvXS_-@&qvU{F;(GQwte8=MX$HorKJ9A%ez1F%&!}Dn4)_aFO$o^652M1!$zwomo zGaFxgLL>9UV9Lw_zAh?5QKhxljxA)P0Sh?>cm7z`BARbVf?(xNX?qOg#Y`Ih1l~-axzY#*dTU5K^+^Cwrq8Elg*j z=SCn1Vxc4GIi2OSw{mG2k!m>({FD=|#ZS3^!U zCYCdd`C~;(>8;_Bbd+uu)3D;0E&_f;HoMTflsfk7^)BB01CRvP@S(_Bxg97;XO1Fv zot#5`zLP1B_oy2=)~hLUs2AUodX2byH>bt{a-=BD*c*DcF3qR^rSe=mBa!7sZzbV(ju+J|56WNg#`JIvM$PPK&yGIVGPfhARHC7I3QV+;+>Qj?? zNR5?4-4=Na#Gj62_a2es)u;Gp`tboHp|8;#%~#}T>S|R>kV8sM-`2j<`>dRLPxh*ZU@5ztr5T6LdX&g82NMZZ}q7%2}sxJgLhnVnZP-}-g=;0u6|Fk zcK_ir_2}Ra%?Mg_M=Sf+87<(#{|l76#pjM*=2BAPjwD%(EGwm`o&gZY!El7QrEWjO z*@FLEXov}?I5VZ;L7IgNU|70``iO4=iz4=%8;XE^A-NMLpk?_d(YB0Z+1J(@HSbuv z=5wPEHSO4tFs)l761x1{xFu??J$*H5_MIK|8JTD#i5?`1jd3)Dh82DWvc6I9D81_7 zDyc94jOiBNPHlz&A2 zFi}{HKY37oVEW~$^4qn4P#Z5lz4nRSt?H0bfKEi9Qog&@F>iOnsFu zb5#SOgt@ou35jSVR5rPsle6JFz@jIzB+(zFV(hnr{|4|U($!B(ScCn8 zk>Nxd^anIC5;k-?SlUDyo)PGaFbYgynHJ{2l`wWdFe!zteJm{tI-#MyfGliG82b_# z=g`$0w8VsE0MgThmDL(1AfP#ua&hJ`U4_VKeiCD-CXAwh@SmDVkhhR!aJpaL_S&|2 zp=wTOiVID5_x$k4dq-x3=CM7q&XPyPWmA?pVQpMkJ5#)FY|r(h;L`F7#%zyE%f_-E zgI{wz1|ymNIIn2T`WOjmk6pR14__OOxvFCJD%>As3saukg|`Z4vg^le^UeDg4Qm`t zap(52{o^NZ9+`Z8%73R}`kA}U_pI}-+&Nd-16SE2w->^_!t%KSU%bFKSFkExuxk4J z-OC>q?0i&EKK0D>{#eEO4+}Q@eL=}b1tkj^HgDb&vpqY9F^u1>eyibT!vojKd3WAq z`9#KqZ9co;yL;a{a`VW8?Anyb12+#m$gY~tEtqWi_O_}0(=Y$aBlFe1+h=c`ovYp% zuip7r*89D&<1fa}oQ)qpJG1Mhnd*-5){jmL=DkIe!HJjVJu4<(o;WhGf5~swq@y>FKFIcclv^@YGu{56;u#1%ODm_gOkcj2 zedp>-$yP`xy;~=Z^J}(DTILGs;stfnYabM>f<)50^>In*c=}_P=k=@Cu6}EDD*v4W za}C?#4clhRw>>T>8Mpp@e);3_+VMmGkS|P~n*Pdz>h1GYzDdX5=L_=%#oBg%Sg>xv zZ^W2<(rq-jy^o5jrrK`5ct7(bX zw9M4(optVeTu?k$P#Z6(g#e)BPRUHcGbFS{_qaW;f92X&VlH3I?!*0&C;!HQi35{u zZ@qN$r5R7dq9Mm|#yGa;V^`s1)%SC67vCy=C;aYL?|k*I%yYZi;=9^rPrW#IsxyA7 zGbZ?FUFYU=^KWea_U8Fw;jM!=4^H`S9vR>JaY4n@hIcC8t-Vt_ThR2Vc;(d4?W?!0 z{-A|mc(%A%iJ7a}6tCIzbu^+-mrNUa0dt=}JCzkjy;K^G zHtwl?$2%SU@%q0q-*5YA$NL>KTTjMbcrm{9#Tj4whn~})R8zN!MC|tbd(2^4KKlJv z!dL^6#L&bGAlbP18Umz81ec4&Oe_brftb`wS5jikzI{?^q@qmZV`I={*|;;$LVO_849_ zb(t<0x=d$*$VZ{T?bQlrk&CVoMWFI+({--}xkn+r?t;pfLYFb>(btap2Nf~4ER;rX z$=ww0Bt19vB^Egm3q7=PSdV#U^IlGumk?#u)(~(@gx^$@T7*{K44RDk+h^trXc`}< zu=rbul%%~92^Bz0v><6+QimnHK>5pYV++%&^&h&{PrW>Dx#5~{O&K4$D;~Jk$L#Ar z!-Gk3zfmAqk~$k5%{#ja0VWsJ>sq7C%>o*YQQyJ-!9J!66gu}ov3fi>2=feR#3B&y zhM5TleNMQu5noBomq0FIWzkxoOSn?ri54e)X-ev6D2y%UuNbgsV&#jw%BHL{u9Y$S z%B6s0m%r`zzSOr7n7wZ9GBZGfq>0G~A(I5SQ7@%jD!46^ZkGG3+XR600U#lnQb3Z0 z8Bo@Kac`1V6rBkZ7pOf|C1{v~kcESgh3QU>{)F)y8)^bnN&2n!BIQYuekUjXKlse> zcHGdz`w!3UZ;$V9kDczA+21+m^8e`6J^%geanFsiiL$Bene0j|dmXnr?mjnD{mcXR zrWuz%X7_)FN0<~Q-)U&J+LD+K5)QMOy#OG)0kxm1=j$b|A>~5HT2i9$Q|?v>ZL*QjYA2Pt_e(fAo) zHfI4d6XlpxfpmbrND(uu@4-OB`upZ(%3`IwX@bbw1QQN36X;6FW5ftEjLltaJh>6xwrYT&-eyJsCFg$0Fh{I+JYSi6AObBTs8Al7_3r`Ihqi`f92?VwX zQ3qPXmyuTROhLR4c9fYBFea5)J@L2c_Bt+v&u-g4>DGY8Bp<--YD44L}v&Bf5 zO%i!XKkRT8=Ut`5^M%dhzCqQ<2-lw}xj*)`A^s1!?^H zv1`Y`DlOz0a`JAJOq5J{=E~Q{@jrV#7@lnJTy|AFyK3rEJbM*>7nRNx)y0eIrZ>(M zt-E_FUbJ=0@=ez-pA;Ih3+LRGad+ix^@bl@jk#J#377xKl{X%qJTU93o-ZsWW!!}A zF#-iQ3#Q6%u9$l6X6d-?QO=6_oQkRSx3}NgKD9cwc5lqRZ{AyQcKg|zyji3bv*79W*xb}(zfXpqd4?id*uhY9OQ)Y1fk(d;Dnq;G^Ru^~*< zugt&ixV9sfQO>xulDM;E)>#HtI*Iv`f7?qqWkHq5mqMb#z;pTWA5IrqqfSD2tO?jhKyiWrd{ZuZOV!Q7LgNT@UPgWLLA8_|HiApLczlS@pXP9+S?p>iv(_j~`)FYb(~N*c zA}mnC5L;?lflkHpt&=QdX#z^NGi-M1&&gJwn+piDk}K257(v3qo==)CB2A2hJJoY<&_)ig^J$b(txTp|p~Duy8B?9H>&d){rm(|T{u+_q!!ZO3BIot%5F zEB;*9OjcmbJa5lyyi?CD%NuV1-#Wy~^PUNu*~Azr>=rW{h1S$iog?dCGdA7Jw5 z=FjD>jOVVLip=CTK&nz${HB)~zvUHuYOpv~U)wjfd)zdSC2IA=>ZyvEoa(WblwYo# z@vh&A&bvJ~vL>>)5zSP~bneuahwjD&2g<;A)YQ$b+!C z$s5^a=;VTCy=9pil#Lh5)?iDjLBE|^n~3GxV7%TkV%Ggm>qr_Wf3>swi(7pCPb?ZX z1+p1w9RMo;@E$QShJedy%>M5H**6EcMH;QFjJmx76Se-qu+SIuUt%&DnuMLq_?_F+ zYL;3O$-@j4=D=h-PX@qSxGg=OMS?)@3ZcQ^KyAh9NdMq!=!YT|4MN4`3Lnv(-D|ff z>#Y2WU)V0JTM~N?u}w*_Og&P&-ltZ0a6o8kT)U>eLAV?gV3a0+SL}j9iuQ;Q4G%hjy$r1aef%J>9rOZ9H9ahrFx(ItQW$s}Fu zVz%ZCp+k=%i|N=1o`Y%LMQnt?jgv!4X^j5X=3S*QB2wKrbtaf&caS3qI z>H}B9e16g7+KEf!mU(yKjm(M6$*x&<#e7ZuT+Q})&Gvg;@tWOnchz|M~evJ0H7& zOb}GSJ`C?G^sHJ4+l3Lj$cjHQf zB3?-!A%Ft;Bbs4uXB;4NLo%3zKLCw!qp%@r+6;;WZtxI)9Z4n;wDoZ*dS zln@Gg?zPMFC7UMFAD66$R?k~D=Uo~1uAJ_Qc~{PO*TJgVRT#4uF714UE`9-}E-N!B z#Z1%=nbiFw7WwyouXAq;Ji$(uMtfD zEea)=2Isgkp0P5P(U>GTB1WbMHF3N{CXPrjJ5WHLHWudIm|uDM%u@5D@DGpzbOg_G5j`BV1k zqG=~2kwg&)F;D{T+{waN<@&kGt?|mO_cz2Ux6V`^j8zP=sArI@>JxLWb%-K;SoVwqc&@%`Bi{w$3tTgl3jmMl67C zOLUzMbZcOMk%}^^4R&{94Qv#Dj4sd+j21ocl_ls3tcrg@ZxI}(%FbP>aF?v&OiBF? z!CN1S`R9YTd2ivIw;}Frn11PAb9~i~doRaVHQ#TV@$OH;zCLqnYb)j;3B^9gc27|l z5n4moHBX8m@vrGd>M|Be*hIF`D`BA=pa^J%6bI?{Q`|mxLV|T#+F5`0j)_Hz1X{>S z`Ua7tfjpK%i2WTrKF^k&3MlXLJU`=kzDY0r4c({`QHmwPJ({Jy*(`RQWf?wquvXA- zh>zJTG?ArnG=~TrxuOP;Ly@yS8yi9Xv4nv%?WB)GIva4-dcAZnZ3!il!j6tadWYnR z0AXiG$IIXjrIfUe4(y8U=n%=CUp!2=-^MNBV5@m3)E7=T`oy7*E)ao1E{+ROrf<>h zcj@*&(v3cxV^t==BT`FbsP!il`d@LoV_-9#b0zr{OE=Osh416`b;BnntJMx0uwr4p zta84zV!^T9+G2d1Rj^>C08}2iB@1aRXgA~(^W=qeLweR?2E{IZ#pt!>FE(B*ifn>B9{(KQP!pA11J=a=L z+Bukr5CO*ow-Ip&asZJj6Ddz)va14xs5!-&##|35?H)0~837m%)fvGsG|ti%1Vwa* zcgR1B=^gXigtT!bzu~jAf?b81hBlCV&H#2;!Wt7`)Pp^Re3zbu8kR`w3iS`ddP5@T zxwY&(ievLVg^VXajpnMRtjZ4O?-9D%gj6Q7^%g>Y>X7y_7ZlT8=Eu1!V!1UjXASOT zp(spQrY_vI-EW&MI7I1uDMBgAMPJ31qFo5kZeX^b)rkG9N-pfW1r45h$z+mhu#P3b zFc0d5m9IF6jKqv^)EfRYKhe2gA76Dr;gQ^fl zt=kdjRqgQ4og)ASgP>Y{C#p2`D2Zk}D1>SrMLS%3?yWVTg~o`vcHb z=PyJ6=K@*dLZ}k#x(FyIVlIRdAT%Nz#TuZLMns>cM9tSHX+jB%n~0?%ZKY->E%XqK zkxCs?{0^`^PGX_D@f$mCCD7Mq2c@h~=aRL01FY3ew7VADX@P>(o`zu(fqEzwbc1eUB!N3|V-a?4!JUba; zPh=)FAzH9puIbQ7$tnd8Fc0-HheI~m5j#Hcb6b*+zBWjG263}I&Z!P;W*=EHe!98 zu*>gD+_Lypd~3qOisbMhSw>{CYC0s%cT(pkrTm0CpLk=ovjI!!in6zSH+^$O4e_Fe znW9GQ#dfT~rs3wXm&dCTu3Shz-bnw%ipb9xPxG~^M&s?YMq|=!qFKNOh6Y0!7}S({ z$^0+|0n{9uY4jB-mz--ab|+p%`tO-;nEr*;YJe8Bn1<&W!}T<2h<>mSCjNv@GQQ`b(hB9(nxMVEpvnVgk0igjbVALkd&<=4dXYi9C&n9tzxXe8?%JAI#rop|$WHBH#?zt;AL_9i1^ zHzcx~Q_zH&qOKP7Avw=lN`*5@mJ!pOs*QRS;#Y09Lh}9%N<;B~hlluY>GpHF#puS! zPo+cHmy0u$aD;BF=*EyD-KOZPDgNt*k1X2(zva~+85bOx*2AC|vlpxs0KMocT}Wd= zJCTkoS)vyycCplDO<$}?v#xlOMG@m_T2l;=3Y?}N#FH;w}dHoWD6*1 zur7xL8SuhV(^e@lq*Sb+v4{T?c+Vg|XZX*+Yb}is(A#DIFbFJhC|KcR&HWW#=47Ds zE_<1vo>Qj0i2w^3PP&*{fcli6gW=A#G##MsWJ!|bFBlHP2NIfik<*VAQe!2-Uy|6l zNr8ng`H77cy#J_~*j|S|5t7>p$Wtbq+QfCgeiPSysV1r&DYuY|14w|8;L;8_I3aOo zD+B{N2sYtTXikiA7!3b^z^kb!_7@aKxBrb$EOE2nFnnTJ0ls{TA=C3)$L6zhU)ej> z^z~yLTT)Ja+_(lQI_$0`ncbyd0V`*D!P?pqj<)7N0KmF$Ai9!@$L;LDMlQg6f$D_Y6IK*D=03K(-AK2Jd+%T2{KRG?DYQfMGV(5b3N zILc3L=4I06$qfvI$YUbSBB4S%XOv3TuBjj{tAcc7d^Rcyo|^7ZC^%drgCbSl%0TIk zWX#JXQ%orJJ_bTp6ba!JJOcH>Us;Ya=wRh}oY@5M!=K?*i2xuu=$&+3i5IJZi3CaF z$>n7#Sz^_j4B{u0GljhPzbTGx@Uv(L@5YnJi!}>QgKyP*dG$g@w$=IAmA7EU6U0T9 zcOi{Jc0=*X1qX%F4c;=A3$LVSJ;_8IpC(jj(lntj!)07tI!yww41%P`f~OfzE1u-V z(}pK`@k|@GfsnB7CYufaRvEI{&oJ{wMF^9&Zi6$qs_#EZZkEW69<43>KP}tq{Bd5}*R+X_GA_QgMJB5;H+*j3H#CZl^RQI~n{a zkitl5R5i@pBmzBA`;e992(!51R1c;>8)f2>T%iq%4NVrj$ckfCGVuZbG|&ow%zbbu zC1(S&Mv2fCv7NZInBkLA$PnLT#cW(yQRsxOi=4^oGV@_j?)a_698nK=g)S^N!Z}Rt zi~PeI_i;d%HcMGq(_q@j5mK_=G8e2Kz2Cv!$M#@!T(po4R-;Z*HNaw#uo=YdE)N5p zFKlAI+(hMzSuIj4#eYI+$!*G$E0$l7npK?KtmIU<6E=vSqpUC2D5c*-e-B3J3tFV~ z63eaMOPZurzgkR-m!`Q3H|hWCW-r-C363) z_)F?LhF}OYC9nfN)VB;H^ciLSFLe8V=>~>(38s2N33Mw$jl z^V<^&aTG+kyYQ!6c#|&UVlD2;D|G;BVLcQyYz69Z90`R6S$9NtH4EpM?U=Oe@gHo0 zVIoWRcARMI#2Vg7bG8#+PWytLu#pN4HYV-yChuumPXuEFV9Ky=JLN{BrQHVA@aRq zn72FU>;-Xq!JNG;ZpY@tS^G*DLM{COMH6ri?~!c@L^C7=#VQOPfLTwv1{J8%{dh^b zke3LrluPy#3DFG+L5-pVpNPRC+LezOk)n6FDG!@IY`!GSgfFQ)qA$1zfaXP1SlR)} z=&FVKpaEIY2e}2HV2Eke6e>bAQj61cNeO}>HZXkBrVtfY8cn)wq&9hwmxzi(ix!J@&7#v}^*phg ztsK|0Z2v5CG;^P1iNojVjxRq?m312iLF7%AEPayJAR2TA>jDACK*!#ynp(emVukte@qZ9I-XT5LgH3Gi)>EC%>6(;&-9>}I=BG*L93LBAanj`^G# z?m2R5H0Iv$A01YBaWvx{xD|2h&>z2f-72l=j1N~pw@u7lrWa`~jaDqvi_C_AuNP#o zDSD_75;77)t>OeWLW79rO8gHf^hMZV*@YZOQW!+K9VFnEvBLqv4uy+OJFtTXH`WT4 zO&>c5XhX^hZk1MW8)qWm94akgZxe?(LfH7_5khA~9PZ@H{C_JVfK)Y;lkXz=8G`3j zxER8|B>qFj4758=EW%9(Gi1wOpdbZWgu=A38Azd|j`@v#ykg}t>$I}WMOlYo3gel2 zy}EkI@`pN7Fa3A*k;QV$ANx`^|CuCaxmjPVe6-GlAEfW!hSp)ilSNobJfkF*;hWDd zCimDa#&PpU?)*vfTj@8`$&`9yylUe;^ItfAvK;D)nYbLE0es3zh~rdX@`2lVYBRZtrj>9P!FtUTr$q*H%8{g3Qbz4h@g7lE zSBw#5IP{2+fN>IeUM)ICVEYi=DLlyMas9Dn* zWryX2={i)hqZXu=$t6H4g3~Zm^9s3@q)!x^e}%pgoa~-Zj#q|K=dsDKPn=Wbd*n(gWjb5U;?8v`YzlQNS$|=M)Cz zwUdjYVX*Tq}h3|r3)orfq-npmvcK#y@_ify5vX?p|%D@psF0ji0)A!puiZ@l#1()97CkOoE@UcPhJf)gKg9$UvF-mxK`!SJ* z-G1<^6G+&0Lx6E?Fk#~xz_2l?uP8sT?4g~Zc+M1%FXu3XSn7wS6n~w`k8BIzV#9|C4JX38F;JrX75a)FP2LdD)8%+ zRs1+JA9-H5_QJQ?X}|pCYnNk1yW*~0^Z6^j+XmZ`np-t*o{i<##oTpp_TVg7SYd!| zAq2Tp^yzD-zjgKz`7(Q}=4Q?Jn{GdI>zP?s{iDiFaHIyW>NAztO(cwGOrDs}ub4JZ z?T_Wx$K3VgfUjV|i!$K+Ei3!=_G|6mJTt#>%MUNUck#pA-FLHRJ-+Gkhn|L5?(VTY zxIHQ=0fgPFnl0HmQ?zsJ@JFs9@^TirefieqADDk=d(Spo{LFVw{P+Ym`{u8>c^qRp z9hfa^nki|D$jq6H^v{_N8XgQvyF`tc_XIeQB%NfaVMEH2|BZH7pu z5SQvy|wbD)M9A=-m>oU&FMNP~HksTc63TCyt1FPBLQP~LR;4e3@h8-g139O~#qT0O|41w(L z8Qb#$>)0_-`FeE*ns9WKwY?y)KH$MSg?M`LEW$Gn&lPy)<5?Wo5GcU2B(O11h-YcA zw7o3wOrQv1A+Ra10?+bbd3y!oEAdwq*c>QETyJ8PZPqYzs z2T7cCvK6x|!66YBWDz~&z6~~MJ}8l3=IyE11uH~F|6u? zMp6QIk_^6ci{OXwoJsa;iIIXLJLKc8G-TBlmCNMy$wp7yYEd>d&@~8PrL&!^I9F$o zWPzGGSdsW-QY_F?#FnUjNxds8>q&iqy|oZV;AJu(kQzf28LC64t6!%bmz|HH0Akh>DBOo+jAu4oX1Gy+%2wjFFVQB+10Uk5`V8xveNv%v$ z&ngmxzs6*Ey7Y*4Grew=T)zy>0 zRO&pU99*O4QG^Vf}wBBe`yVwaUoJRQm9u|)ZU%GDDLVYih04rpdIKiam;4p80v zs^=0tA0j9V{hE3OaH0cF_>&d`O)E%B%r1>gUEx?FC332sW3q@Kr^@A_kF({t^JE6D za3rd0FHJp459p>U{EBWW$!6K5o0_MjW+)-`tDZj6_T@(!U!?F!!?G(!E9iPhYa+Yl z*r~n8oA>YS021%mbMol33FDfm6H`;tx5Dd|>!^Ih7|s_O+#VzTp+n-Cu8WCWm_CZI!s0If z7&Z(iTp;_BBAm3Ebs(GALxuh?2xMNd$ZnxfQja4?qa@Nl}Hz4kJD{s_J)K0E{YsbwU_W~cRoy|Eq>umkF ztm4ffROooLe&^i!!}0Zp|8~#J`sYYsRWV=71Xl%5oQ8E9{sdlMX6?cw9F2C8LdB18 zjvC&?foYGbYrls$9Ij@A7t`-ne)rs4JvVz|wcGE7XG&XUiuTNSamv~^(?9$48N-S_ zMz;0qjl#X9mLxt*5vTkyy8DZ^yaS;Sl%`zrl!un635c2Y9P6MwJ+ubbOUk4ifxRU2 z)`#OjWaI=_GUiu|*BiiG!>Np14)O#lBHkdrQtYN1!6mV=?Ce+=O8p%o6KR-3G(+Vr zDvZN%&Ba__j7{Z=aAtQ!^bI}mdXE5-Pj)T`8iVp_Y3c5#MT`7ATU#LB1VqGD&nrjkKC10d*Bb@6L|gMcCGp# z@W0?gZy0)L2z6zl8fUka7-*QubN@a1>kMRGZsw~%2V^c8MZAsmxf*gDcplX+0RZ)N z)Wdc%D{gk7^3l|6xmb&iGG2G0Eo`?X%EUPUqgLdxBECG5hE8M`=mLBnT>%q&D-(Vf z%yh;l_BUIgs!`6lg^vZ@{m$}wwcPnnn*Nbn$=WocZ6)(~B(*XPt;88nXR!*=fqUfL z3uiOoj;9eJOSfqo+Iv0QVz~Yg?0hVI2ci2HC$m~p^s!VLaP+>f6=2RH7wamPO5yBJ|f|g%joh_;Vo- zFJbNn0Rl1u>rA?FA;}*gDq5C;M(7*0r;*MPKB++MSPydyIi4{aBpn6Cw5{i#td>0N z`pG4S2y3rhn)J*Su8QNoYt`M#du?;uTjTh@uJu!F1adE!k#~&t;^d{nzKxt``{G_7 zdBNCi#0hgXYd`etoig7&aWDJ5Q}>4cq+|TVWcC}UVxGNYdvW_Hw_x5~@F+KLeCUlW zQ`z6%8FRM9R&Kj@^4|XYJ3pxYpeR<=Hsfp?-#MR?f5SK7d!rs3%U9Oj4&4fUn0s(C zd@uLjb3e+vzv0iy##<(hZ|sZZ9waHsL5N03zp)$ojd@?=UGuc_Zunly-QHMD^SEu& zJb7|_2#`KP*Wre;cwxpH^{8@@&MUK`h?Gk| zw~WbXfdu}jXIN5y7rI4n;TvyA&L%HV&8_WypLGWZR%! zTca*Ar(!dWA9M~A-b^AuJAiyRdD@2}5>8olMJIo7W2h5|XC}z9$tW$TT+d1dRH75G zo4R}5le}v4<3ysBDN{i&(?8L4=tsHGles;TbG-F4?%FXc^k%Pber+>c_|JK&M<%^f-YMHdXAQSZK-J2nP+vLtye|bjx-6X@;Aj>;0y!PkmAWNS1}El{M4`x( zR>G`9&pD_37J$&ut1wcUw-klVa?bU7v(?;kNM2+mkP_V-Wxj}q*h05IM^2{w=GGDI z6h~0SP2-MJcFA-o8HAFluS8)oT*y`cII1RE`&0x*Gs_^h$Z$juR|q?X?U=77zZ6M1s_Fz@*&#rCBX+x zPQ5U^(wx_(Xoi0?_o*H(_?2;tK0u|I%WNU+Ls*4*bApTs#|n|h76uCHurpgq z1$}U!I1MOB#TI$V11_IZ6s>;hXv^6gG}JGle{_bLFi7fHayzq=Ml4`(c|uqqm;60X z1#x`KiFaST^Wwew`+=YKyx(*GY;4nsht4)24aZ4-5=inM22v=aa^oU_HXCH^C`nFLrfcrz+-jCRJ!C*g#x5oM`=`k@Zzjq0%u-HcDAX`{82SSpZ_(E`z(uR4;&GNYCO zC)%#ac~O{jv2{a{;mWa?qKX7njRO~~oCAuXG)G6DCoKdf8}WJ~GeBV47RkrEG$o0- zWwsO~fsqU)h(&56`-(sh4*bT!7`aq{-QR z*t|m%lcYcF7&?(qph%s~WvFJzpZF%cqm+-WNKBBq0a;|9OpGLHMS23KBhaoKQT%sE zPMzPh#6^o!U@VD{t|ky0#;cg+;9d%Ev`)0ndFtbydbsP%$$RAV{O$|i4Zn5y=Hbss9=f*{=IX8VQ4dkjbHe*cdoZFa%hl?Y`&8feE($LFy3@Y8hz(=%77J z3P||4PeYjCNANjpJY}OqHi|UBNp>69Qptu$DL7i1GJ+}TTWJi*PDS#kRr(;KlsUwz zjeBdSvvD5PoOfLujtG;^tD+;`mpz~gCGsJlVwa}~^}13;pv=$)-^~cE0TcwAZLpXI zX2zl2Fp-cvOmsBEK>jnmC7F3vWEf1f|!R86{TPu?v!Ig`JcrKlwG&^N%WsJ1 zH^kfx(gcq=bvO&^090gLq~Tw->zDCx`Lr%qp$xfny7`1b454eN$AUPEfWsierOWo% z5ooBE>oH0@MLkADgxX`|6|p*=D>3(Xjko${a+_k#rl)lY3gBHrt>$})TbYwuW|Fpa zK;^k1i6v68xis87m0ZuTHGt-Zn_<=+8K_1i8AZbKEI6Np4bvzHD`|RSvf=#KAlVWm zZ2S8{=U}>J!(m;}-Y4vEdxvdUh;cLz3`m(B%|qvsY4@#0GL6*WycB2268*;rF z^Up_ysHaO2DWV}th?ZiPo|Y(yC`nHxO32w_L?J%()Z}6@Q&SYkb*N9MDY7EUK^#yP zfUi;&_g^z=iKSjeL9V{9>a1k2zhDG0gckWM{yZ#as;3%maYNE>u1n=hl_X9B4MTK| zMl8)m^MYSC)oGTt7GfhV+tDb?%%wMp?vZ8Xjlyop5vo6(NF=(bbajs&Erc+TiS&RX z3J;xI6(OZst;!gYG@bc%f&v&OGE^|DR4I-r;QYK)X|?r>=X&5cUF?CG2{(heqBYE+ zMkM*hfQNgfI7*}o8JLf++I2dKlpzr6iPSc6uN3rKzTlc2hZV@bv`_ZN2FuF(2u)jK^JWtDu03c+J8$?Eo60e;|YukIG?O^NvMCP%> z9c{-t_Uzr)eDX+J!ilqC$nbbCxng4ocL}vBUO=IVbbu${ow4K)u5^MRX?JX4%HpLa z4G^09W3&ZCYsD84t-0^KaHk|zx-ss3ChpwC3D<*DJyWM+MQiV_pK)$VC0yaZ&+}%< z*dcP7T?D_)PcjYe+}E$Zc6C0tSl;ar&)xW_XytU-JWT;*MWm(Foc>sPBG3(6( z7WhzP$1brAC4Nbqh8{`1R42$VT%yB-v^7haftifgE1B@$hmz>f5k0}2O;7Gi#|Y`k zWn`^81poC=HX5Gppn24My$iX_#Bclm9^LS)v^k#80m=1Aw+M#-Aj{!M9;Xb+8b%Ek z>!f_lH-+*N!$iPVl7>K=nXn26Kgpd1JQALVReGn4{Gj6}`d&hyT%ZGa4#RywgjAV2 zJ3C=ke@TL7xjc@9<>EB#0$JCCWuR8_iBI1K%K>G|rqm(`$H<5;~ znl*!WmC_yvT?GIhC&tPtlp=KfZH&Kc%R|f`j#O6$E0Lk;>4bxd=Bgl~>JsUiM1o)m zIdjz}33;O{QqYN3cQio=<@fO#5K67VnGgPzrmU;{QEB;`!HJB=WxmNiJaS87xz#ae zHSXYZi%QA8^@g#7jM<{of2SSf@10Kl#W_wLH#_3w%DP~_6q-$A<{=}T!%O;}_PLVA zcuC`3+r9L;O$Xwe4m@-pq>WRDjE{>--&%ch^|WuMX#KUrthI+DuNM&K-(Qn!-J|lZbWNv{(wI-CZ=sKq#0hNw_!8}e4E=Ihe9ir9ng>ts$@;jfetPfS^*`M9 z-nNIXUB8Jv`Q>4g?Qzz0BimreaMIFlq_kzBQL|b4Z_$V1I6?XmyphBj{r3Z|wO<;z zL~g93&Q~H$mIEzHTcRkJL5r8f0qemR1C}rD@=aIY_585-z2b+i9m@k2kufOFxOfSH z<&d7rGJ&l`Zhn!a**l8WHB&g#n50}I<_n6Q1yo!LK_nwmk|(!D>lm%UJow0-?PQ;S zl9w*f>hSCd$si8#{12!D`eY^w`vt-r4GKKAo+7@1Tf#zYgY0C46Cm`tw3;!T2n$1* zq~A?dFHs;QC;uaL`$iOyBrBV7)r?sl<&{oGV)f6@=DmP(Xh>x&#O=aV{Zz@Uef1+( z&hHG3aW_**5UdcaZ0|s- znqSSwsUv1O|5D6Ber250dZDPZ$qN`%kTwvTYlzrX&PLIHM}ZK$L#LzbnmvRY*?CL4 zY0?=?I!a4sC8(>Jfk?n6Ll`UvWvA$w^4F**gI?h0EA3Y1^sgzRk zBB@|_h9gZt7k+Ns1xk!)D=xQYkjqdb5oX-dngCfQ*BJOZ`mv*iNu*%#?GMA`3_|QM ztgnMzj8;oHD8%8Kx}M+~7YFTZ8Fp2{cdCEBB{_aU06v3~Xrq4lTc>ZH9&^BF>swcE zUS+3q&5_|M%(XwlUccOY=pc#(*u7?!kJ}NND{hPzH%?!fDc&^hKyoa%a@ujX?LjSB z?c6vr0n_ZM;aShx`Fvr*f}<`o%VDeLDL}TEvl4e%Bk!3Uo^|@b9ggi`8E$O;_U4%L zR7_~%heA&c+}(riekDIDx#zn7@&}%uulU)D4>Dt=r)Hd|#y4Z9e9ToAvzOui$Xzh$ zc_WMeUb=q#hG4!`g!yE`L2>K39P$(CxQ`^%$A}eyU`K0U^Lxma$dZedfFv3DDG<`p zA~|A4l{}f14RHrH0lD50t=yriZz~{EqrM#i=n{ExV>`frC8PaS^I`%aiC&s zwHu`P^6QVDi1TfHBVE9dy6m_f4vUa}kX<-#OIerXlCVBysOjv;1c^altDAy7Ovx1TPx^;{m2llp@~H<%$80n?wXTfPS_UEl)_8b@QC`Be@Sv( z%jKkzAkI@JES|8?84nsGIPxRKg?!6eLy^r8+sn=Th$U=r77SS^J3$%#<`nvc8(LDc zxW6s3woKUjhq<{!BAs611fk9$R!$kackBy^OW1X7qJ!o%VG+z})$H>2=3AR*+^Zx5 zIMqSf_Z@ewv%|GM-yGms=Cht(nR7joE3ZckTBN&bk_7_Qsg25zPOby9$R^j#;I;PtAItN8Ql4 zZGCMkI}ocbURXE1{@ty2w%*?oE3BI-Y=yiKn}FKm&a$z+5Hq;FV@?>iWahkn@Y=!g z-dSfkSXw>ej0h%17?b(WuZx&eQxJV(O-4M-Z3m7qUrHVWX_&Ds!y>NIqOK&un+`5F z^eTu;GdXlEL`Q7uv}Myvsv(?QgGEEyKdpu=wLUqdQ-c*#)7tvEyx65UvDU73(y1i0 ztCh`6@n50;KW~&)$)~-jae|F$#3v`Ymtf8cxU7sZ3+lX_M$uZ5i(C0;V%Y?4kepM9 z>ye31K$?n6w!9@Wl*x3kd#T1#W-tzRSI|8B4*E!co;@mA2RXpz_cqUzY>(TE#xlpl zkKDO$SjVg%xr%R@A7Oc|j;(%awy=XkbQ?bxkrPGt+S%!d2jb2HV|yuoily1b&NQ|#h@>A zl*K9peHDlBuIKH z39^TZ_)k=!g(^&>)1C7ajA|19HD++ai9^}3&$utpg_6R=tuuC%-BQIZtlH(pEiAc_ z@FkH-N%)d)2Z3+NbO}2WcL|4~wA-l)EGeCeSsyQ~pWgHCkvm6b3O5t>kTfE(xCMe}*PpIFUBS

lQ%KJE@(6Xlu=3iWg=|!?kcY!tZZu3ZjHNxwDwxdw ze&y}@TlKS9jq`aWb9pP{c`K*HcwXJu{wHPws23^yVOHbENxAPn^TVC*?Tj_G;*GaX0^mW$%^U-xFJV0KYzH`+57%+GD3X zVn;hb*f_kOfXFEZ%|iqZvA{*K`WI&1FFrAw;5pL92%z6sW|)ZqnfP}p(Jp!9m&Jff zIbkMm765lkIpGX0QhvG&@HjcbULGh}G7ic8wPb)FFDC;;dA|wrSBAZ~jQQ8-d6HZ+ z0&8i{J0-!jUe~)6%IP?hlP@*Rl&L44e&Q*epIWYPz=XJ^|~nZP3DN-5dQ zN;bp70b8-9GanTJEQALtYrc&85?zi#Ie{Dt+cICRd>e5cdL)#tNJO)&D`Mv)4nx0( z>;bU#9SW&8P=q=aQ+l<7dcA5`4#@;4ggRv4$}Tw$1fcHdiO=cBgIL{7MA$NYqU_-r zrSsh#nr}6YI8@ScG<_s(nLe}YlAY;l4>7Vc?fM=F+>Lq;AldF6{n5}N#K55`yNSp> zjwEQYEd!_9f1O^3f*@m=uhkvvkqk|noN(D=8gWwFm{5oB_$F2`+C+pF_tgwXQZTcS z&z@waU<$=0iEA#f1H<`ZVH96O0lv(Ho#PdeTe9L9U2<_t0(<8BR3pq$Sw%)w6%w9f z(i;KbBr^6fQ$sDW^de+RIN%48%W2f@vZ`lYrqU;ZiETFE!hCL=!8~3uq6*d;%<(Q6 z0mx2x*?VCcWtFr-h*MXp#0So3mzVe;v7PWJ^(81P$WFGuwq2FqN8TO1GkWj2`0AZA z#k;N@eOxSz9mP53ukUzm$9z#KKYehz?cJB|@S~yOAMe^>SXqo6CfQ0!42A)RV+}1a zdrQpK^2k#>d3O5njAs+{2+3P7ryR5Py2q-xiIa87LZlzt?|c8u#RN$Q#;i~VfR3YP ztgIV@PxwllT0ZA2jysFzoI>0w%sMMRW^PzAsk!t`Le=feTbZPxIBOht;^&;OE-tLQ zyKP3;HJ&+Na(q1fQAzbw|Gh1KWQo|Ja^6UJ|#LLii(~g7nY74LbuHo z)yIp7R&Bl0`aycEsD7sC#Mt3~aCtze6;_Q~am2_|S@UQWtt}ALm z%zTLvWr$L`$+(`$r!0(F*I^CySaFqcf>2ja(5~PrauT94R?KQnZ{M;}&+QlRm?~&b zV{SWq5}wZ0eg_YOMbr_fLujDOc&lOrvbJ#EMKKJlBMn99ZL8^+`<`1)gawe zhW2?nF=ct)fhE5|Xtx@L0qttoRGe@zQ@Al^-#BI+w>?_121XljS}?O>D-H#L6!7H~ z%n1R!ZB}?@*1ky+g166lPd`NfFCDJ2DRs}YwBz}q^BI`RIW~RFWsOkWez6?hp82}- znsfXG?q>vuTx!YbS?`%&NgSWfxY19n1`il=FP(NUSvTvhUPdBM)Gg?6u4DT?>eIuGMm^l9QqFXSJP9J@`K(f= zq@mzx%YF;Jm~yH1)LQynjfHhKv<`5vj42_2B> z!Vm(<+R+uc(tln|SfM}cmk3cODeU2H2wJ^Ck?%Wy2_=bpspw={Gy!L5{d7A0FDb(b zx-maS8vP+tP4_m04(q{4+W8T#;_w~YUkjc>iy#$d>RdHw4oU=CWgvBYgv9mZL_jtQdgk>Rj6 zIzYsYIX=p8Gq*CP%+0A@WxTWQ)ns?f+nm z)NmI{E!HRfX|6%S{AavYEbo z_y#eRQBTrYa&i(IDpsh4s}L#78$bgneSg$JH82xIo+iCPBc|yaO{_E!lHJadm!b@$ zp!i>!Vxy`Kb}vm}ZD4};Wzv}Zb7>kIB`%*^n#kG#j&o@WtB#RHOVilc5_ZufHS#_F z|CYMs=X8tF4c3hY@%wc91G=4|-hBzTL}{{Lndb2DK$jM>J1Hkaoy?4$sZ_{EnAlHm zOMAvU6i9!WVsoV3p_+0M`BL1H3TW4nc!_RbryK3X;250j@KcWtoTSt#+!7k2Eb^=a z$4BD#D3O+3O(L%@BqlrxuGY4yP>EgAuJ@?4Kd0M$y8VQ1e}`Mbt-OQrI)ROJVvN#0 zqO=?(jg`lSW`@4w2;ElE?R)a=cR`$mZ{jUF?l1?364zE)_Zc7Oco(b`0B-cGSV&_* zyTMz&;9$XYgQspGg9Yha*2PQ;E*>%3taXd-0&Cvl4!gB>(POoiVgJU;oiR%Vok?@j zh;3!k!LM%XS)7xDlV2%-TIlFk7UXBYvLHYFl?8b%6kMz_p%S~x+PK(YLrrd@wGr>< zuYBd$qQ!v<^9)74m?dw%bXCl;MH4Bbv z>x#!&WeZk3X&tCqNTZP5kXOgjAjU|~U(7(<;!8#gs>7)|+FOMM=R!L3tir+?Lt$Oa z;-v}^lVkN_GfiPNVjiz(h*=8e;p5g)vS>+1YC$n>+pM@{vD*e@9xxgTs$v$;qQ!&w z2HX}J3`I3@3*V=~2294k3IdE~^QD81zHGowrcmEm-$pjo66qZs-9wQfoNL}8k~EzA zV*LSy;&j_d(6W_6FVgLK+!C&?P+uRsGeS-mZuFn)51r9DQf(eNd%&QDK7)HmAjJP@K=AQwwm*4gKE4laX?)`el=@d@y_x5QCRTEQ57Q zm0w!088C?QAbmvdRHq+&O<6Tm78(pPs>z~E(jk^g_FwUS!o_-{+PpkJ5Z`oJY5|#x zoH|6B3dGD1Hs4{vhGiRKHq6Twv0=%DoC6yUq+BTUp)iD`16c=j7xt(*usWCA6Mx=n*F)-XBD2rC6U46wZUs*$K1O$^gP%BzxG!7 z)oHR$mKFAhUC7KtILbuJYBHK2kb*QLZKN4#liB^V+CS~JS)7W|5CwkBKZCPNmOr!S z-s-9{#zfiCUaN22$GPX;x^>QX9{zW)*M*?`)1P-HB1ts4FE?Oh4mi+KsE3J_N@LVU=NUPwvUMi2)N)>YL zhc>D5BQ`49knF!vx{qE)S}Rpu;G-MH*wDKChK|f|TuBbgVlJ*G`ejAz&0;Z;9hPyt zPj1SMHT{V!3^AHJp2=j@xSGsnlnC^>uDIGCVl{8~nXYiGwLRQ=tRr$nV-IG=G$E2b zA*(|;19YsM84lSrcO=^;pBw7yOJ@2sPn$d(8A_$-2$^inW_pu-VT`j_3$&JY5056~ z9N7=J2*x0^%Z#?Z3PzmWlvAsk>V}>-9UqiqIki95n@q`xk^mt1{KyVJ=rfBiQ(-9B z2FU2LA~82gqu5rkWt^(P0U4Kg-lp=#BGqBE3e13Dw5_MX%Xqe-H+v8|&lR{)5W~Sn zgfNJX>NZvsP{DS?ruP_hsT!zQO63D8?-D8o=nUaTm$m>MN9nvk=QHVS#=gL-MdJ1S z)POk;Tng+GKAldamK(E@y}*5F`^e<+F2kWQM@Y!;I+)9)#>C`u;lc7>4atfsOX9gP z@$4ITDD$Q+SS@FpG@go>#>eE*q^j}qsGJzqIFPqN&7};T%i(N7R+N0%smx#|JCe~C zijj~@vn&1Cp_GJ)mP03tVF!&>WEHO>Bc2%L9Mc&aJ)OV1G)K1ylseXKnB04a*H{pF z&7PoQsPPh!RP2W-x`9GIwn)m8uC~2BJ9g~oI@6PmtMayZA}-14v7X%6HZ`scZtELL zN^&Zhk$Z+g?0b$U&-Extpv%P*gP>1J4{SpOfWPP*!&3j`27?7Tv_?u zKyco_cIxD{7jF9-9~hnbPoTTwL7;lxUo#cH?cexKVBNfb!!;PcweGh6z`ek#`Kk@q zBDeP33Kcgt&s4P({Vfk`f$1U~10_oGNZN>lAJriYjf?0abCJD@CYkToB#=py#{l85 zto($PxCu1DOt8JIWV_%w51MDKh?#2vwD`OapeNBUW|b!T#hdbFbO4Y=qyveiN=;NA z1e#f_g3H)}2|19`s#$rgp>){Qs z2$d#eQxg3?3b|u(e0&^lAmderQpBT`bTa~WfSp~vcG9%>b<~8$w2kp zIGXoX-|>pmUh(7AGu|x^5#wy0^8_v*n> zKG}ZhC?Tl+TJT?~x-A4BJArzE@Otu?gK^TVexg*vmLFCg+{Jzo2pl}XezA)`*zEY? z0DnkuL_!?iLSB$fW>n3NsYS#@jzTOBkqKK?!31o9(7YzR4F>R5sOUAFJuALSuL-Xq z3ZIp8Sp&4mh@IwPjG;iF*_KrIvqmPep#e;LhOyFlVH)Zy<+cB zCPBTNru-I)(k(hgQpq%^B9VM*f5b6a6|))fbTT7lN0g-?MCqTxOcR7eIKulZxpHtt zZ4>MSH^CF!>jj5XN4=9E9g&27RJ|q=Q%ex69`NdY{x3u@`*RS?@gW>!k_Q~)EDZU7 z!7={-hGUbC0n-VxUSDF3Q(?7$B#zj|^R`KDz;uNyS_2~sHi>VBVYSSd8z8oPS;5CG zM`xg7*(7u&yRkFyvr~RVS7}+1-eJ&>GUwTM_yP;-96w;4V5|drfzSh@3k8%!1?FSs z@0k@5{okI0=)Wu>x-*im(h*%3UMk4cx6gaTCBUvR@}K?;MXPS@{Biii!6V^V*NM*7 z@X3?0*3QV`jw71aoM`Pl5!TAP&UCkTM!L?l9t5Ai>qPi)$LkOa9PDlnxp6%l9us83 z;MO9FadUIXaONAIx;OMX*6W9(m$a|cb^H*MV zzvnKl-+k-Uts|ece7@=PU~%oyS^qJxG%$bvnKd7010j`|j)sPwxrO>^XA9Q4GE~?cX_HUUhZfPxsy5{^F-&*T+7q zo!Q=T<;Ya$bj8+hT`08WQ|EPOap#el#`a>ceacg;J~j7Bc*;=}_e=+0`qqvbLcdP_ zWAc`Lrv68;?C^V?UwSI9s6|ix)bNa_{_!?)T(E#p6Ke=3gLX~H8U|bUv0qdK+8pe^ z?Bm-w$GpB98L!{SAsy0$R{e2a(6zrk-DgDboPrm+# zea2(JNU4MhEa+ofwX@+Nf@)zW^4ArGP1Iguf(V+OetS7mIvT_rD%Q_OU&B0n$WfB! z(;bnv&eJC`u}Ql86S=0$@DXSkmY%+uNTv=$RkHMFMcvZZka1FxOHd+E!8z|cTo>PZ z_pSM|+M@W{Oj)ETM4mGKkGVB`-NU`eFJ5!pT=i-2dT_?K^TMGE?O(!3@aE1>U%CFu zjDHU}1Rc6VV7kG;AN+aUr~f%Lz%r2p2}(2P0emcnc!0d~vI1^_>8x5%{Vr2LD}wnF z6Jy1)113yc%nQs--hf2_KzqoM-yY5=Ls%ADOCgk)R78jwb20|I4bCW-<9M$sV=+!} zZmc&#K4SA4UaIM3PdaMjaXb+KXJe{#*w`heyq*MK7gaoi6H;tDq0 zV5LJQ%|yvhIEe=@kL$J3;ElC>#g&8x5)LKdV-N_6=?TI?N@d)36g^Lf3@fja8>M9> z0g^akNg-Uc=MSAGfr;1z86lBmgcLEJkwhXdU{R7dn@P(VRUD3EMO3q5T8>jOvvLG7 zICtfy2w3BZ()RR0oHTF`m_-s5zYp&8qzy7XFM_l3+}PNdG2LEkGs$*y z$pv281vweOl}Ns}O-{)w5mW1o)?4otNp8%QPAVkOi)9B(K?rUK&YFEF18EBw)0iBU zL(OR<&=iG;3ZSkYt@$Cr0Y=))uPOO0CD65Ywanyl;nDYmS^}E(~MY!z`J>-Fy3UUO$1S~*f2motbB%6^#f*w-& z6Ih1KjCDNC4qcLP5)6&g+Wpc-`=bWQI77_!OzSq0%!!XGJ3> zTSq2HH9{uefa-si#AGkba`j(3D&{NduAQ2x*fQb#Z;o|t2K(#uUG`1-iowQNAvEU> z+;Oj)cCRbCLq#qG_1xB-ceXZ9Z*890y8ptVi$~u%TJ$yk2N$BRp6^ug*j^X-G-5JM zA`vrL>yl}{RG-Vm6~!f|6j|?@;8aSo#UMDx>QJsRLs zZV&=yu?zI}D(XY$neWeR7&;0naGv<<79K#-vZVVGW2H`4!T^H@iBCRa^KRh7ItgAt zYk9Fu4_*fpE5rs6aau`HQ+<}H8#`boMy(xGl4GPr;&0lG$P$o?86Ymiva%H0F8wtJ zR3{-Hqr!hB^iqc~OY$<8{%!=7e04{DTPqb>OIh_aybeWAD>Q%+_|QY==ogLUIf805 zNts_yVD4#Ri{V}|&kw1++g=6?0B1arm4Gtjfou)Q+4#WiCJiG^iAjRS^2l>Z36ngB z?$A)YaM5H>j3k6Kp{u(+)*XJm8&oZq15aObbe^QSeDHQevW}>Z<{*F~C9x)$$U?Ru zlB~NurwJ*kK7gO{E2u!1{Hw-0=F6+^ls8P5H_ViW9w8^+FyVlqDaX%RuARAwiGO?J ztxwXQ?JjP5b-Mi331PwJa5l{ODlQi$3sXmCeRZbe-TeELp9g>UX0iFT;@ZfpzjMx0 zamQ0T?Wz4(xb>r=rxvn83j*?1-SMoQ_N<+H{kErmp&T|`s6d{|B3JRUBNHNvI-U?WN52mui4<8h?pzjJ z1?ors7zV-5TuFS?ZztSoSa{Xkx=daJZQsA$=0d}ze z&Co{85sOLLL@cJcVzJ&K_+O74i(z6Y@av?iClz&lW6*+c56d8PNCiu?t7AD3KTK?! z9uE+J(F6mfbYHs6=r-x;c;aX3{{bjBXLQIT|B|AKeBH?+m#6tm8XfF0xhT!z2}giz zJH4HagfY#oQOBG5TLjZ}!EWtr3u9vHsc7ObX>B6a2B@A0&FQQ(l#=&j1-b;;lwU&y zlEE+!1;jKyKpVe7-mg&LE9CqNx$mOjAJO({wEY2T?EWL#|26Bs%Wb&JHQnVJA=_V5 z`_LU^_Rl#33oJbEdDblO@Px?EQ@P+EEf=Z?E)bdp53;)!z0g{G$;;F)iZ3uP0YVE* u%VJd-b6{bs!^4dW3#(9B?bPU0>XS`Hchk7=(2hd8Z+QO;d6`Q3BL54YNR~_h literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/coverage/__pycache__/phystokens.cpython-312.pyc b/venv/Lib/site-packages/coverage/__pycache__/phystokens.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b75c5eb58112b56e877e5757b808da6c3b22f2fb GIT binary patch literal 7183 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 hS}*hJWJF5tqUd_-T7^mojyiZ{y!s4%Nzgz literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/coverage/__pycache__/plugin.cpython-312.pyc b/venv/Lib/site-packages/coverage/__pycache__/plugin.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5fc5444dedf1fccec78f2bb56ca3f9caa386d364 GIT binary patch literal 24593 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/coverage/__pycache__/plugin_support.cpython-312.pyc b/venv/Lib/site-packages/coverage/__pycache__/plugin_support.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..764303af1fafa90c1f5c08cd1d401b2c3bf3ecd5 GIT binary patch literal 17961 zcmdTse{dAndAs*>bvoTiI_acC0u}^F2hs@vHZ}+_AY_cGa0333GvqjKS8(`#C~r@I z&cekWw>>iP*tF9k*YQ~Cq?4m=TBS`J_1Bb6(;0W#ncVe6C2z>sZIYSvkIvwT)G6&u z`+e{2?%l0UvhgI1`{3TbeeZj3-+tfszF+UH{-L44#XLQ@Dwi>k`dLU)Yyy3AZF$!>zQ=mGCFq!fna+aCusKR3znMBVdWUZ7*6M(84CefsPVq#1N z5bOTbgen}2CK3pTV?03zJwylv_QaKRBASsS$+R>M09Ms=Fns|=Ju5Rn)jb?f$e)#Q zQZi9pcz1$CV=@UkRNIODB&If;IG9e2#?OwkxzwE)lFy8vJ(x+T&J#!DDS0FfAZ#=S zs5YHAo*pML`9%6TIfWofjA4xB8v6LfFT(wGPUgZqu#rXLfn7Y3SF*lr58LXv@;|9j zvb}5z+w~f$NwUMeLu!^BaCO4f30DEG0$l6hS_fB`4v$yKC%K@mUTOjI+rn@Bf^Djky@rzAdE%Tb#>NSeQ<=nsm`Fz@SrTLEL_&@seZ=%A+%ZIk#wH9> zj>I&sqHl_2cv*gNl)mU$Zjy`82F&7kr|~7n(Ke$mz0=U7wb}HJ686(pA;(SfaOcq5 zCb%2?NYJU;(?kMl+Ec(Ois~K(qDC^5s)|}4)2JyEMQwtcjC?5*Q7*ob46Y zB6Q_q26o{F@ki;TLg6~#bcj0zgt}~DY}A!PO=(aMs)#MlPG;6 z5!I?pD5_JNmg+n!gD}hxf~ZrQ;Vz=_NqUURNEl)*G?8v>cd&V?4%S_@!j){m7N-WT zIN?rYiHO+Ji0e9nZy8~|v+X*wnOkrqjt z%TtS1OI!0D&eGI;N8s94&-?p}{{9(9$-guA=)3N~)P`y2EqBkI06gnXC%3jMXS*eI zko7Q)DmjB3Nck6AH2wt*e%S(i0X(2Tyvjs%12#;9>w|GEDj^@THHF?WloFy>U16R&XT)K3$w|?T$cqzw*Pzy@q3IAx|jjM^D`sFC!vEr z54koK$OC{c(t+87h&g05eGzyNwMKuE^;e?2Tz?e%X{>L?_&{;pz%60h&llr=!Z0pP zfO!y)p@2sd;Ae*QPsIM(WDtEgnU#>=Srhp_M80X1vIMHp_~Z&?*jZe+^Oo??3S{Vk z-XLk0kw6k}k~X6Z)jtf!(9|Z!=ZE?Id5GFI9%^HDIi$LMJURHiL2b5o1g^C!9ecJ5|NP=`-iS z6RP0bDqSh=qN>oWs=>SUj4DXJzShS!0626%%mE#LPsXR?^PDE`5dBvPS-89AV)TVN^oI=N3 zB1i+(!QOj}wt!GoFQ&R0 z*5=nY&#&52T(u>Ccy84L3+~2Ok6b-6@9r$RJEvaAOY@=q#nApj-_XqiH!s{gUs(N_ zIrnF8w{4idn9t1jA1L-8D1;6bHV(gI`;P1HU8T0;xx=?xyYr42q15_tZutG~Z8^uB zuwkLA8?WMm;C|J0)io~!ib7y&)10slt!W$9y|%k3tX-)07wXq9xZCF4Yl`kQQx|Tz zH{G#v?k*;iDosC>Pw?Xxd!cZh%NT92YHFE+TKjR1An&7ZdnTPuP$J#nDP0r<(TrPQ z6Kt;bMOn=Y0JYcz*%j`#*Z11c^&{7gl)P&T!rGt5B!Mdlog_I1k2FmY8>^?tYF-3T z@)^v~JE-y-5H%ESA{3}LgR41B^2n~oAwZRF|E!!DCn;E(+NwG;AtJ+vKIEjFGxvr`BKp7= z2G5sv!$}0aB-ytHBovo`oCzY5vQ+`G2@cWr{b<4#Mfz3cs^HFxZAz2o4Z zjO z0O}vOC6tWV&Ln@9&q6E2X*7|9N*V5O@z6A(i72aTLtaxI zY$9}J0IOj9L$%X7ro-xCb{8THNWs~l%3vf##>o1QRw^|kruHhdDQH-O9P_W5+FkN@ z=O4&-7ySbeSgY3pXG;YL>LmJ#Lr|!C#bumukjIrMf?k;PU>FrLgUrsLuYlSg)xJ&+ z7O2u>P)fcip00YG(;)~~AR#F($QdZXdZRI-7?i6-QXQJV^dZ@{szE|V!*5_8lxtVG zA9y>bo}3=~+L5munX!GV{u}ir?_fa~WEe3>2cU*^EWyokB307Jei;IR=aEfJ3D}g& zAZ}?Y+jB$Q+ghD&;Q_{M*&Qd)G9nOfyMzb>rvbbNh#@eh>;(rec?RM(n)5*4SqZ5e z*hj8d<=k;5AT!t^dYt3w{7-?cdJ@?2GJn|up=Uk|^P03E)BYBJorjQ86NZ$&ZoPh!+UjuXXTW9X^ia6_E%$Gn{tG`OK32Wjzts7-jLDZ zw_zLw!-hZNetL!br@*lrm-1hj*$8p6rq_F>pS-ndb1`u2KPlh8)Y`@aHy{tFw)5$D zO0APq<4M^1$iT)4TQ~xH5FiTIo8iV+Pb1s8(j>3rjL_=1!oBZnyT1F{?&ReWVKnj)h zRb!2=;4ec@1*5fqEZz8{(WasK&i-O&f2nhz(6Mc1=S;8=7`!Fyp?E@{7L^m|dE_PX zEM{~zjaZT}`y6B-4a@CRaiS!+q3K@i1rP+G=?D~pS_J}Ct>h#&ox%)hz|<((()4}V zo11hbj!(tSm!Lo+I;bNbyKA%9I~E0teb1s`wLih%by@6t>8ns+oTQO}=9u_JRK&jq zl&{(htIKhtAR=CNgYI%^x~pE(U2dkkqz1YLCcwrSZh_Uo_8D%0xpLekK3ostp^*&3 z(Aiz+vWU9j#zOumDCp)6N)1qIxGxEU<03{U;+bv*LKGOAsM;#Q3B!+H+zW-@f7D#{jz))f2O?khq#|D_4;HP^)XSD-{~8 zW2s+esI{+RA5?1~Ep7`9SDg^roD)_p_&ZlrYlaa2XXtugtI=@1FJlnE2Hj2RJZ?gm z5{613*kGBoR0kU-EoAdDqZna<@=&mIgux19tzWj5ca;G3RwK*+&q1{@lC9hq?78O% zihX^1#d~%d5{K2c6UJ8SD`LnMG5{Bb>Zh0=Hg=3PuUI!r1nf%vVFwGa)BZXMHKPO- zNp?*Q5>`C-2EZt|&SAr^sr7otwT}6wo?=r^zP;20E~Vi|Qc*IP7p5m?9-Q4YyQ&Zv zniCGaTi>+M(vFVVd`nNUr6>QuO#6(x5ZqI0*_%6byLC%`c;-;4br)>rcw2J~OJ%dc z4gU>O_@vxWq0TKC9FZoSCW_?<2m5^7c_a9vLvY-AwEsz86dLEZf72tHUisH(R+Rapp*-c0TVGrJ@4x+`g-%vlzb27gxlW0 zRL@tB&Upt4!a%`0P|Z|(^F8^_nWttC&khy>hv$UH-mUko#8hqlGs0}o%|oTOBe}y1 z-nN`~1-5ztCiO|#Y8mlxUqS|ifpsL?Xo{UP(!yG%V!&%^Et65-AheI-u@u#*1@No3 z2g%DwqV+XaxoOxxMH?n59^EDkK(>;zVsUk5^5A91@_X z1_5e5=dY0r`xqHI*1om-_1*IwgT;=)*{4e#hYE18qSAOPbPUXN&veWV7Y-gT9C)e_ zczRBFW}zYmhNhvO&{n191Ims2VR~KSmCr?hdGWC|Y}thZ3z)LI%h-?WxyJ{A&C7C$ zGJKuUu6vLeP*N(*+*hC?+gSsrM%Qm+mww1V=2W6=YS;AWbU6QoLSSG{*oIB@SL^46 z)kR_T)aWf?BV*x8*9@Oa@rD~a_k%ax7&AhhY&{Eb(gMz7Kse(V&JiD_Ddbn7n5|_~ z={?@T4j6!emx9pU^H-mr_jVP%T_x|vf;M%{cwm7tidbqYmrZUP73*a~JliyESP@$I z3`l~azXGNiVRS74P!J#kpkF~CRH!sqcFzU1;G&G2st{kERBIE3`0V6b+ z8HYW43?=r8aK`S`DF_iAz%C~-Gr0+9#1K?NRL~JzYy`Q3ATrekZNni3ar{UG;>*;M zMU+3n%-CTB#q2BaQ-YBF6dj47pV+M6NVpt!KSs{l7n`m2wndi}!eQW1Z2C~(?QDCF z_KiU|BWX*HUwj0r%uWS36t<7=P)rCAdwd>-$p-c_ZvoCMo)u4*33j^LLoirXq|c^4 z0F+pwJ^;QtO}Ydcx1qW zh;TTNlgh+LC$J31R#4gY(r~1N$McM6Pv{>12%Oxsq!raR7R{Vfg)>n_v%*JVTS}7g z03cO4)HD}HBX~kDmDUkY*AuS1D#XL#6PV3KI4cFu)iimT0}KL`VM;gx3M+`6`C6~< zytZ@R*H!d&O&|K&(XSkx-!NF*Fj(^KfhbAys@GOc4NN`x*5_aUe4(?q;O#32eM=>v z4aTCBLL2UrNWjZzU*jmIw7f@F_NCC3MXHQs{R%zVY9O$Oc}*aQkcCyRsky9H^oU8F zGOD>nRm$Wo=$+lP3}c%HT>w~R2V}tC;MqKW?eY1R{$fjisbwHnSEXoBHwR}{7Xtg{ zghv*n~0ySJrc!nmY%Wj}v1IvL5sgdkv-GDa}?D=)%aHXZBFZbHGH=1@}Yq?tMk~zJ6+B(+nGo-}TrnWvBsGu4FC*}(_qe_TCwEPTgDU)dGtgLw_ zc1FUcCGBP6F`}_kqtrMM3O#n!&K5fZ}<;@RdDE^mLt?t5Tjnd&@MuZx}j?8 zRTIbn&2jkp^p$_H_E0;K;rpWa1s26Xld`O<5kYFx5`eO-fXx|{?Wz^db5v>QKY-fo z=4Fw$WXS&l_zKOOyqcW%^c6jQB~QOeju$$%&m5fDSqSW&69y|Z^QRECZWM%y37vLY zSw*;TJu{nTeY3#OkIV`CE6@z%80k}@rTnRM|9OZ;59XuF}zf~{sp|- z3e0O7silT^K;|uZ`b;QASG{Ydtq^#4PT2M7#%8m83BJsXrN>hlvzV=~Xj%!2e}!1w zu!4*=9uTO3!S14`yW|Nj!C+rLmItT%fjQy9Pd5hZ=^{5mN4Uz#pJ7IW@T-!76+g|i-FZm!E_n?U~g^nHHJU;9D<7Z}$ z%mUA|onk#E2j>>bZaiIxcTmJJ4N+^0JMr;U(ZntXB zz@NiV=r*A0fHG{75Ih4xFvzO<^pA8xV7trg+i~_$x$!Z3C>d8`1ova`G5X(ipx^qi zT};Xp7%GClkD#}+hkEciUd(XIi8Nu>j2Y5~9-*gyL8JVg0Xv~QjIA_kLy@}`Z`Wfc zLUvCWWXJsXk^-?8#M|AD~q9Y5sOf1j)SF6a3!=X#F|eUIySj}w2$ZTTM8 z^By-;%f8o0|Ly2Nycm{{@U-ZtVa7 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/coverage/__pycache__/python.cpython-312.pyc b/venv/Lib/site-packages/coverage/__pycache__/python.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..469dcd2bfeb9d656f4862a1a40caf407125e7d5c GIT binary patch literal 11852 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 XT`@II>tARRD%x@D`tK;5lAZrwqti~3 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/coverage/__pycache__/regions.cpython-312.pyc b/venv/Lib/site-packages/coverage/__pycache__/regions.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..43f5e1f1905778f5aefde22f9de5b96ab763a1ae GIT binary patch literal 6595 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 LhNM=ph424A9wx7K literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/coverage/__pycache__/report.cpython-312.pyc b/venv/Lib/site-packages/coverage/__pycache__/report.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0b37ea0f02871565a00f33587f14bf21bd6aff33 GIT binary patch literal 14788 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 NCc5t-g+7_!{{yXP$aw$& literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/coverage/__pycache__/report_core.cpython-312.pyc b/venv/Lib/site-packages/coverage/__pycache__/report_core.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a143be59ba5a3064ecacc893014a2bf55cdcc428 GIT binary patch literal 4787 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 HW}N>7^ar>N literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/coverage/__pycache__/results.cpython-312.pyc b/venv/Lib/site-packages/coverage/__pycache__/results.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7c30fed2bbc1065be06626d77ce001c112d7c30c GIT binary patch literal 22930 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/coverage/__pycache__/sqlitedb.cpython-312.pyc b/venv/Lib/site-packages/coverage/__pycache__/sqlitedb.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d57589df7cd261b3a3248e393e2aff25454cfa8a GIT binary patch literal 12765 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/coverage/__pycache__/sysmon.cpython-312.pyc b/venv/Lib/site-packages/coverage/__pycache__/sysmon.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..91b7f7e0c8be8770d0a5f60a0d00f0af89f594ea GIT binary patch literal 22079 zcmd6PdvIIVdFQ=&zeoZkNP;gBd=q+*dW({6+7d~)NjtN%dnKN8?s?yHzVp2g{;H(J#^L$)|K1)dKf!T7qZj3|$O--~r{}n9 z+|!)Ejc@`l=)(MjZbZjZS|8Sr=vizSF|gP;Vq~#t#KdBAF1CzV*t>Pa%3|Ay4Y46? zpKy#gSYO6)$wcW$DNCEeWfRU3=Y(s-HBmlNK2b4JG2tF@vodqIa-wRailr^#>Iu(? zXQF1LW}bAu8rd|ld1Uj%mXR$JT_ar+-6P#Rr{jXnLDPG@ zyhbA?yl=(dHvE~!1!W1fSSS_v_jK~R(n^U(cKpiLWuMV{CtB|cS_G${e-ArF&&Y1% zl?$%m9%amto?u0=r-sV*qRbtna941*P_DI7>+TEg5D)6OL!40YB~Eaw`H!H@BWP2p z=Ijpcp3@!WzQCizFn);*zAgwh2vzj&{p$A&a=}Og>OS^fuGUDe;0Zo1)I5rJp+l(s zl40Ztp;M?sykGDM^@tBlT2Ru!N_@fw$_YBy>>4pUuQC_4$39`B(1cq3tWGoPv;;S* zD>J}mdS%@t*^AKnew(r*!X}~pO9r*aLo<5c=GcM0KsbChFnYl|Ha$5S4Mip;Z(vgJ zO4CzQ5i#l=i-=xnM(Ub~OopNnF*JG3D+Hne?`R|(4zl`Pzo3zPeAXVAoQy;RtdBvw zTdCq`I3P)QvGhfR;PA{;&}Yo*_fO6s0|N)nhJ#u2VDKW9WGyGAsBs{iHH-$NC|c=* zlP~)8*^=P{VqkLg{ID1Zg{7<`5WX0gk^JM+6H`*QtG~$awd8{GKrx|LsBFhhz9)=5n(zUM2czQ|-Wcqw{tGFzIl)1+X*N8Ki+5$*I3MTytbl^J4sSVx#Bnp+yZj*b(0q}NDP*nc zU;q5G#~^gX3k8_Y>5V>3tP@j zhlF4ldw1qVfZLg4p|fYOrNJ%ugbRUlLFvqBEq=E@F4MJ?#ui&PSDNs^V}E~ z|4WWpU0gTB`SgRMR9Sh{M0T|Prrk9}r~o;9Bf0tZAzwht&V z1jKVv)^y<_iw(42;zsl<5$5!I=jOysNMJ#8TN>N^(1PZA^!vLW4M#=;VX3!EseOog zdIG^GbKI)4Vj+6%rK>L`s@j(V%Z8;FmxbkLZXCSPlW;$};(RPqz5VMK7o*qXuf>;# zQ`OsV^e3wOZXQfl_a&Tt;udt`Gl@G8iS+&AP9>E!iNWZ!ILWf##YV9Ci@T_NHv$G{ zpZ~m9{t&z0ug*7C`|00ukXjOa{L>uwk2+mRKfh+--1YM(*h~g76$Z#`Dg@10y@W;G zLn*^xWHRV8$TQf79F`p`SCG?lU0q$9y}(w_`%DC>wB~+)*5=RQHl!VX{|nQBaIVDc z_Y09xzhB%;o$aNdhXPs?_ElmVr6_nDe^MEOFL57PdhQx@){c7?qqSbHOEqGp>*>WX zoy@W6a5!7G9x+Q<>zGJW3kg!p0)+WoXfpbI)*%Ihlm6&=F&Ge}Q5C4svjcw&QT#n} z^8vmXxGQvSme)W#sseV@ioV!0&Ui)TO^%he<1}KyqZ|XANixQ*69y$ zY&UQ-r7c{vR_UXa6Txj3?=#w<)QWS;7Zw}kGFC&&9%592lcNyHy{Y&Hl##4{aHg~Dma-nPJy+GW*tvYhA?_?Kolcl))*`RfJRuR1(w zM}5*!zc{qwXj*lYEo@pGU3_|}X=z|-&vMKA7v7s#_9vW=tvGtuOvW19$DGYN$j=|X z=ith#7S1o}m(Q)ZcCT@Ul0p7v)0$528st~qHH$-w152)@T}vIy<;#7`yArkAZt~x8 zf9O7V-$+&E26sxRvrq1obDq|ZIsW0M?+@H)dGF}*qlwy{n=jllq}|8Y^i=J^eY4if z1BuAbcdPlnPUClLst)Wke0N`GUz_Cb%F^sil8g> zW`5I1y$x|u!rSc8<^mdSiTREPF3|ZJotC1JiBJqjN(@ekjQPijI*0cc1L5hQ)D<&7 zO7Q7@;(@N^Qq077y~iH#n~@}FM6%@p-|MqvZ6PT%36^^j3@~scm(>Aq&7%=Uj6|A9 z){Zw7giG0rC`E}8n~Y=)Gy#$Th;5*+%#l|M-u+Z2jw5ple^MC19QSj3<-+J2JuCLs z+vPQHG%pUl-m%oYSf6mVXWX@mJFY+S+9QiwmR?Ad`|bcHHr{sEzR|nf@~!T7yHj=h z619)reCno@e*CHA<4+}ePbE$bCCZ2I7>D@6Gv@utg|9&DGV|($KY$iQr9|QBMu2v zfxmkgiQnU?V~xoxM)NMK*p?KB|rZTv05P~oe4*u>K;FtJFD|e2Y)oBA= zw!~E-AEc&(b#*IT9NSjKyLE64K zYtm*VnB%5#Le>S(r_9I_H-Xrx-^*h~^ZK$Do2^Ft#|eZAp14Zxl{Haf)_0XsT~=B| z)%S6g;43w_JOVymujMMK@eN8!iv^p4&v@<&v`${74C@5NR^GSSwRxRJU4^s@+^p#( z(?wo+_cerJ8V=0GQ1rZ42uY+v5O9(r5HbayE31!%rm`jyNhbw{BxGVM&}gKP$r{EZp~Q2=^L_Ezn`foz!zJiB(jq}BX$SBrr#-%;$G1GZeDFropB}zB^e4w}Jf7It z_o1hM%}fozSyX!7*uE&d-m`Ro(Cl*6w;JDVOmyzO>AJb|&-eb>-tUa09yyuV_f+Dk zQwh(|ihKB@TJMrOQM+Z?@T1x-3w^iiHY^&VAoAB&Kxaw(7d$uJ#+y1oW+g*R$mEL|Vx&7F!z>4Q2q^`=^Pwtty`u2}G9h&{z zQQk#c+A?rRsUVn{)+xh`oFB*-S5(U z;N+41z~%1mG=9*gqjU#P=}tYRHyP0YLLKoQvV5sYnk3Y*Jg}NpMjHUkQr1X&CPCbM znaWAh6u*E#7B{_fvbbsUtGgZB@Y&EXON~iFgT%R7QuV6;vVZZJR7vYEbyX#`clBIp z+Zv_TwrXN1giJO#$skO~6j_Rd!*_LTHsWtnuXI)w0obO^Li{rFiT7ZFE{<7RlDF1G)l%lvQlo}G?*p@{?!1DZP1HOPcnA1^Xi zn8qvQk>5lf`-YcgnRhgQM)YlrxD$OK(hjn2P-tmv*RMm?ADYrS3QRL%WS*GIYtYXO zaU!U!EQ5muG7v7vCE!NBNwsHMT2 zM$V2yHQkjpoW(iHnhB+YcEl>z>r}mQFX>Tm;=s82Nmm_f5JIET=W^=X&EA|s_j#qJ zaQlM~o zg=$jpF8-u;1i#Jw)Ufw${rP%;YA!!L?yK@o-t(12H#F>aWEoW}Wn8I9Q{4mzmJ2^dZHYiG6zaW4l zojs%jT67>qDErR^Arl*DtF!vE+rwGYSqK_p6#EU5pEczq2=iGm3!u=2qH(sA#+8(P zlteoH{|SH6QwZj`+cw8sU&h<|PRUy(-zWn?G`1%#_4B3$!@^j`?n>GlR_nY=Rm<+> z7gBZGAcvN;FPJlpZ43H^Ltk}3xF~5~Golf8#8EbP^kHd%#34NV-IG{xfW$=kF|m`ue_{Cxht>Ssg7t@;4FVMN(*&D zd0q?GOo^{PYa5!8j>}wH?67RZ@rEX*!odmfjWXtlj6o$7^ooItvNelweX_iJa^U3d z-FpifKrkanR2~Bo>@&qv!p~ zyJ2DmiVPbeA6S6Kpa^uZv`lj(QferMQM?GjvA}dVN>~pP0+@T3qVPb6)Pn#eI3x!M z75>PicoJWmH5`jT`Y^!KaUpBOWTG%}_MJF(Y@l!0FWdDDtUt?`7@4>boB>XhrotiU zAN>IsWM2$LXM8%wXM*&B0Ma0|zzSX8jC8C*J0q&a>aS5C{UL%m?xz+DJ zMpL^_QSOG@H5*W^bk*H(ZSd+~%H2w>Z*Sg;$o6BqXSJr`dc$iC8Rtma`T3;t^C{=q zjMH`3sIM)B747JnjiZ^pQu;9aNbC+fmhU5xuPXt^hC@_LV=}HDEHueI3SjoD#85d* zuqDtK>?z)#T2m+JiYhboP-cKgLMdTnT7J`;W)Mj8dYufNeTWDA-R(#`2|Phu;qzd@ zFaecjLo1U@|2}_>hbF}Z6+QPy`inf{9^ZxT3X|o@ z1+o9yk*i0(RtlB3%lmfuQvWwR%gswA%cpLbmMs!108sYt$EVs+48F%p)W1CiPBTCGPVCBB;L zv0lm1Vzf}no0j{~T)bMDl0wU7C9TC;%V?{TJKnCOw0NA1z6Fo?x!gQScTn(PG{WZv zk2YGaWmiE3y#A_|lkjL_>+LPtcI(5r+NgQE4>VTzDEJjj6HFj}b!Xr?hksfxGkl>< zaE8y>1P!tBA!7N##f2pAFmto%8jOMX%AOi<(HQpCiGPHl#Tf)y`?N%o8>5Ee+m!u9 z$~I4-lMoz^Tw?HCNQwqUKk4B~|0n(?)oP+zCRxWPk|rZ-IeD7M@Tp;#vt`0PH6!y3 z;#*X1Mfs_L;ipdxW}R9EnMVO_KQcI=<}-$ccn{G|Eo~=H9~d~i|Cu8vPGwy=rzSCU z?tIiA4vxV#c;M9j!M?-(Q%4RR9tO7}51y+RgfbMKM)q8tV*>|=v#wkfjW5cUib0vZ z$$b%Rg@Rr!$`Xj*L+*O?N_>bIjX8%@&bo``d_pyMf(${MXyQuCUp;;K^c}9++PqrP zl&IK}aBM-i>ZnaS+LMmZj)quU2}l{l?YbNLOx5R&Ja>_;YIBmUOf&3Gam73f-vscHs#Yncnn7a?=wl&iyR+dfjVv)Zme1 z`y(sPM^`JVuJvB+O;_yrQN@mr-1QmHhO}o>(z7Y!sh1Uo_l)|=syk2;Hn6gdNzX>L z?3b*}#<^-1tCWr(O}3M>3HxYY(&1ZiY|1n=fpZzW-;IU2PpiY0=z&BUgjWvQ4>sw) z*JM1n(Y#J#SDBfUKx3apN^p=>WQz`Sk7R>%^w5Ed8kF)J*-_ueRmMiC!BB)nds9;R zWQQH#92WxVL+gzIy5PY)i}a@OFeE{(KmJi#9`=Z@=UXm|5+PMmP}*pk3`osjw%#`> zG9(C^N|GQ61IZO(^x$%($wwNe6@&ByCl9@NP;-0`EX>vgI~bTcTfSEn_53_gFSC~! zW{r70s=il({X##^5Pzjo>#=A(2N=haxB-@>eCyd(@O-@15-uCL-Qbb`Fao{up6XQL z8j41y*5gkm6&+j$g|wqsTmCLl!+e%Hj{PF@k}@7?c?N$$gNxrrKx#|)Y)s^-Egx7O z4F@L93W45O(>gp7nbPbON)AXz){TlhP* zD&5TL$=B2*!MQdlh~GqY__fd;i~oU|-lTv$P82K0&!~n~k&YpzV%9-PJVW(q~k1=8t6D4R7n02EJiSx;M@bd{pL!G?A#MSZ2Tzh3|LehNRR zH}*cm2A?F2@x6pm+`aJ>Z{M z=P|DGMspC03)iLu~`1f~Odlq>0`dpP*31 zSy;Vh4fr$0^)Gwk`eMR4`Oy>;%gLIupqqO;&KJ}4&XT{Es_oULS!{-y|H!O4Zk{{$ z+1@6~@t?KCEiZ5V_39QA%4aQ6qIT>N9dS$XI{1n@rIr@QO>v7N2-2L3e{0;tcFJ7L zlLf145MPL!i;3~G*0@#Xf6;G~HmZ_RR)D-~;#SmYR!X!O&b`%9RjaMEC|)$z>7~}B*K6N&!?V1Q}GiRRaFe*qy{Z`#&;HE;B^+x z%;V%IQSgw$4JADV*?1NIdvu?Yn}Z1ZIDLM>6St4|7G&Wy20Vn4CyJNMIxyyehs(sC zXupy>eo#qi@%Ujat)wJ&9WCy_Xh)SiEk^D!?M+G1>|)T^&N{Sj96zCyYB6R>c87uo zsvn0UF+shko#esd_((A0L_C4MP8D>7*WxW8Ey1&JeE8w|B;yd&k3aixSuuE3uq|Xr zsNM7B)9B}v9^n0&mu?|=_{`I&_^K7Epz?dk$x_zXmczI*7Y-I&lLil0+M@}^FX$Q)l)28L?tKyH_vso%3yx`4~oa z{|(BsdQerSH_z*Kj0(zg1}@p=H41kL+`WU7?i8FQV8xO>*4d4WtZB^8bYMTFc8>gZ za6Tt(8#+oy$oxD?Ls2MDA(i995bd4qFJIF3LEmFtd zqZ*F0I}-f}D&Sdgt~W`q#Yu9aRbU2%HXbOu)kieynPS-owwd;5FSi-zmg*Q`r$UEF-z z?Mb^^lkV0PcRPB!e&n?yOCd~e!Ma-2x-^`s+IUreyWWfbt7;eYKlgYup4LolOQyk_ z+2GAIG~TnDYD(vitd(-U?eqPwp16GCjRT9(*AHX0vHWRAL(I``^L7Udne-6n)>S-UfYna=}OjgEtoRctkYMaI(535 zu{76S`sz#h-iw)zE$NOu$&NiY+td4wCh@=H=&fgd_}urOOLly2(E&%z>yQ8I$5&h0 z-|2m;H{H_rqn5rzeP5!bFH_%{@wTVEJCfcVndZ)P^Uh@RPPnKv?Yg7aH*8ou263{c zBkkFf^z6B5_;c%@S--RMR{e_SnZK>ukZEj5H+Cl*yKlskjpPuM_U=x4ci(K4r;ynD znhJ{C&|lsN z){QSFB-n8m6fD1?n4mCTR``7&xvI-Jh_@;xKrGhB4*V)&C0j2PH)}}jIVTFJp>oz0 z(!4RB3z>jfWucF#kNA%i2ox|EfBW+ZPWs`baFtB>(S&)%F(p+6wDEb>1Y;1l7v1t zq}>~#%1OJslJ2fV_x_aofQmCK>KCKw#_h?*?THQB6XiSR`!bHow4*WUXiPg=l8%<8 zz7SryQ-zJj_3cKNZL1;BbBV(K&;I$aWQ3w2Y z)aW#76$ggjOaURhkaTn+&H=;Q2Z%-{-CO3#Z^~J>_++BA6E`m{uC%2tX{k$F8k3gB zCH;z}HE;iE&y`2r|E~ZF{vYi>5xOG#E{#CCQs$W#e=0fFba3m)alWiqj3}xE*O*U^ zo%CAlQ)mE&3{>rwv`Q^u<3#-t+NlCqzOTbbIeGjvd*`PyAcf=hp?=RkK z(U$7E%$CGUUT%dxrR2gMocu;g0gu`KWnZajM}Wu7u}wKVmhZ2@ep4sUIYZF}WG#w= zDx+jf^oQ-VWCC8JA%_9A)vdKB&liUt!ThQi2qWh+)pG zIp=eq)rGg=Z#%Q$=GeD&TwDT^h&4V;Vb}Sy{5b*N2MB;yZ4YyDv6Q2EDF6!|;^KI< zCSBJ4qq25#3n80ASsRIrwyU0oRN6Tv4wi~T*5~P>r+wK}Lf$oO1dsEWADedk~y1Nrw52W0E43U?&CY-Hs z9VG9TEy?OF>FR9|MiblnQ`G~BvVr+%qV!azsV(8yka4(PJ#zU-qN;Pn;af9u5WS1H zNS2{dTse3)Q&F|>NCuuO6`sXCUwfR*jYNKVhRb~` z?ya}ItqVs!!fm!Pw7OHyIcsiL)Tb+a$qHYhVpqbk3t`4pn|5_1T^(uHrlf0AVsmfG z_4xdOOqpx`r3`rC%1z12P3g++WF`CF%Dx3%rlNA8cj@f%fj>B(scTpi7dsdFiVY7h zV3PlsnG83O{*<7R^pNs}vPTFSb#j_?u}V5W|97?gSS4AZo&&sXh7V~}xtjxOPkJ17 z)tm_xqK95_xVdae?lORE$MRW<*0d9pn`AF;c`{^jUe_&S5~`JMMOI$CGY^_-6Oe0` z7rO=||KQ4BP@i9>;6n=jj)GrM@INW|2MX3GAT^Ol;*UuDsYq;zNav{V?$CA0qktMn zWV6JNwHQv_wX_-=?;hjz#%K9EHqPYy;+c{x@M*nOxx9KR!Z48TlL3wq^Oe?R{u`B+=VG+Z2VYfmy4kILF!IzevwTr zLh6o38F(mV#P3aX8>?|v*7Ou_-z&!_o?-EAYt_d_+==uHO%+h55dkH@aF%P8T(xay8m)6gsard37fQ*u;`OcSRgAdX*Z#mCX%0Z zz)2F`ld{5BEJv2;qJRhkPAgM{nphn%-^pC8rc*${$M};@q6x9bc4PUft6}NVifik< zDPt*>`P~&u!x|hf%G35n=p7S=#!v250O@_q8I9$D250$KtgH44cug3~Z?MnM6PjC%!|$ z7b*CJg5wB4PNlA?0Df17>B(sc#fy}0Cr^2HXB`eT1RGGx;}`TK`KLBaNJCjIf2Wxm zksvRUG$#LC4Lq4-O^)n%>QY|2=w=)_?8uiJ$s2=lB=|iWMfJmeYKQ%Phy3$36>5ok zPhOJ2g8(I4q83ktq*0L=&}^BSCj*TPW!ez(m(c2q#4d^qRe} zo%nxWXXkbIIVHH`{L+qNeKxpDNp)vhPTlcFyVV zz+tNL>d<1%V)^x&*J=_i`)=+})%2z+!4y3)R{~64Ub9%S7+&sAxwg%f{){hMSMrgqbbj`=b4!yqLf>(xHXKaZ4$Ya-v%!4d;4s$T;V7u*pj;nJ z?Ceif4$KW^>@^8{Tf)$W@OF92f-ha(k}Pk@SSm6lE_haD%B${KjTNT3C+|484Nb2< zJ$G!OGi7MLYp5~~@^`mdj5YV%jmFx$2YKFjlD}&*8EedWIR^hyey@UJ@f<06i^MmV^a*3qOP%8;Ko8(+vzBW^i|p5W~!_ z*5zbjmyICaM95yR$F*CvI8G|4k}50tNXZ|m+NvZ~j6ln{qpDSE{Sp01M%f3c#5w17 z_dF2DyS2BZ+js8c+kkM#$NME; zSd#nz5jx8weu?vic%g-p1VQ^b;vJU-ksA_2W8D7scJ7=%C`g^w5m_GZY-{s|B=6-= z=uLM>9BvbWZP4Rfo3~Y8UaJrshW_pmc`RVHKE!>xXeHMh42EQ{4AV-zE~8@T3Qj8Q zac>~ty&Mn}wm0;OAcw`EV)S_>*{5Ph&1vnkUI2v~ltA^cO|E_8QZYHx3U>5WTca4M}{pO+WLCNGUow#r`V z&DP&M zv5>t(eQw;vUkQ7;wKq zl10D^<8D`IUnW8}EQg1MAkyEF$j)qxzz~neeK-*Ec>~fBcdFkm04bF~_FvR*uTgh* zbi`|aC@pl{xRTh>@w;`z%G;%7G@im}$R89PQ1LM>R^TXHv33x^%w&Q{@fNf0#LktF z!7=0)UZ0qm0N8Q)XOkaIuI}hq$?sTZJJbWoL@Zt;$93!IndK}_OBG8qcDa6IAvaWQlSq!+>)%Yd~(a~rg^*JU7BRm{Rw8LlD` z`QF746e~sqo_WFw3p04IHk;)2S{DqKnjpJIJ+e_2`*q8VWsP+tSjR$r+_=VdB;a2~ z$I`yrCClYU?y*Oo<^)73LHrODZcrJ7xh3W6$c$}+_R$;wvni!XoQ5L-PN5^U>LEyi z4==SAI1jjjTQjN2WfV&R4#AIxG4J>|U`$6=jy5<8U%7}GtszpD2`{6e1K>)Cx@*)I zMO6#@T6J5Zx^1PXeL5$}+A?rhw#wEf^LI?=5wtx-D$8Q<6z*Pt79`rM^j2iMwG4^a z0UDE*k(0o7k@nL;pD1`GLOq->EP}cs!$pD;lF->uC=duu_=CfQfF-zp8G<4u_jGS} zPj8>+LSN5IBH_U_!-~j`k)lj=s2~qP{W?k#o&2?IP!^6>PVz-AJ2 zhI*Tp5d$PesWdzIsTd<`>M*(R7+oa=qGy2UnMh~PRlyeq#Xy%dK=&Y2M54QKUQjN5 zLJrI_|T!>49Zu%PQNN%&%EyYY3)$U9`G%BcVaX;19}*S>3T>>hz!E42L4O!kfBQHdSYZu_3JEc+Ouvpn~u@z1x8aD zh~VOqYiHM?&ArY-_Jp;VID!Wv%X~08l{6xv*Hzh(shIN?&^(Nw#aT5iE?3jyY?>Bl z2fZ!_^gI^yI@IATlV(?e!s^PZ_uqh~yL1C6rM>@wmJeQm7sBJ?AyI}q4irDYaM&k< z1t1Eb^y1|O^;f*2AB_;s>jN^AIy=*s=hPH5y1^ds5BY_3Av)v_fPtiaVlj0I9!Mr|nakbh|NVE+)VEprUi=tKNNtwKN&vR2y& zK(dEGk8Q>u4~>n1q)~A(;Rib~h{8HB*$O7o2sh>x-xS2`71eWnXf5iRgMT;}5&?hp z+9u8+n}bRrPY7Uh4ZWu|XmwunIPwQAQTw_% z?gdb6;M9h~ffmh34TS@0J2w#$d5M!l!1^PCSgW1}@R4fY3V?!)qxX4(UpyUxUaf-TSAW7UEU@r{N2UThZ+zosEK)RflECfXbC>&mK z61Rn8VCx*-KClii2g24bjR4jWm%-so`XCeGYM)Q7qG{KzT?8t!3Z}y&9Y;w+92)ogkibbnLIPDmrSrq%N8Sj93D<$}1lNUx{0Kv) zrpTaJ%ze0R{3+;gA}YbBA#ofmSUnKEa;lCjv91$$>{geX?%_%;hG6P>)f0fmwu;@R zsZ<99SciKCP<6cmUzdhLn$-P42({FzFYL&;v1A7RIjum6=k}qfhw@F48Ok>5zVj?W^0U8`X<*6J(xig z+0@pps7H__Um<%J!ov=z&+yrldn4?P%L>N_u6 zAZ)dUjGb=-!}zr@Oz9rO)c1<`ebYtL#X}5le#aIw4O20*jD8G~M$!7*l=vJl5m3W{ z6wBe+t8|QwS)w`J)SO}9Jk(jE)={*EHznSp&pXG64|wY(fDy}$*`iiH7d0i`hFV*U zjj|%cllm>8*mWo(J$q=InetM74gysn@J%Udjpjye71Su|6PuELN;-^~eWqv>D0Fk; z^P~2NnM!Gu;0^c=Lq$#cc+ez&k6A`hUD=d$2(i3q9$!FkQ;4_`{W2;xBt=tuCH*9}#Y=_0bJF9PG68lwpW1CD+3k%j zck9I6N-Xp(BD<)1=I*OAcd^a+R;E`8=h&$4F8zhM$T>nQo37PMI=63&g&KYQ`xMV@ zODVOPlmb2LKFE+nUrQ^@Y<#$z3vHYl6+Kw-qvc{Q5(c0ORbn1&0L<^$%&!hrZG(UYx(R^OTkdX(; z4Ez80ucL10^Gi6lt>njU9d~K?(EHa>i?}o6o&f2zIq}VW3&`hZNm%WUj#-06AoUBN zck^Z_AX~I5yjLV<&M>=gYk4fAsYnuFR=Y>_FAaCo>`O+Fe`4u2`)t*vMtYx{q&YPT z(tdN=nqwlM?SNhb=6rPZ|(^o2^ z9ZZspu5Go1E*Z6ieio938061%Z+?%wsco2%Q{&X$u!=+4@QjasV*Fmh@tUpxI$7!# zo!sTxAG!T(V`112j~Cn^Dzk$bp4mao6p+AL0NXZYBly800}nAi=RgVU1T-vAYaa^3 zYn%R*2s*3ew(1IX-w2va2xd4GQeDHUy$iM?*hs3^MFW|!gvd_t^k{FBo}qA%SSd}~ zy3nxWt)q{vQs=V1-;9h*YT_QBSH)Ex6U{NR?`(Kh>;OM{IHk-S_|sa$g3r zDI`iz*y#0thQePmnO@Az_(42Mv@w0~{KQ>WAG_QS zk-sB_Sr~0xThjjO-ChH)6|rE_t97^I@Ji@M)=mlw?5`Nu|ZF{n1{%95C8A0ufBTtzmRRv ztr2Fb9{x{7tSTv`@TD;RArTaJDpK4{-bBLSED|MIb=5@7Xqif#;z{TMj!f{gL`t9P zht&^Zp06VL=lv4A68q3FC!I~id7~JB7ey36FkLWG@)QJUj+l{OeV&LBE#`-KJ8aOM zidl7y^eF}~leZWNwY?F0Unm5w9B_rePbpHAxe8!q@K9_q5^LE@((X$^$i!+yLCLE@ zef3_#*Em#RU{Sfidm4{l+@J>{J2Y4Du3c_iV3W$uF1M1e+S@6~Aa(&@SGAI(jaJOz zaUNV4@Kz|u@MshWc)^X*C|`-YS45MUcwLnO5K>4YsU5mXv?nnX)(WdjXmJ2RSa5S>ed1a28+?Z#${;pC zG0UMhg`lLEB?vx{Mih%b=ur<_F_9+R5$sfagRBKNs+fomOkrUKo{15!jtsTT!d%3W zE}p^F;)^JGK-C3)`g`mc;c?<=jubw{+EE<&1K9HKz@wf=IV#|V+LkP<`I4e7uIZCW zTfy~nGw0T9H3?fyJm;RR>5%~%o|vinoj+-WzV`c2*bjxgCoEM^`Tf|m5#DUB+h^>{ zJC3ZfNAc+@cP4k%o-A>!l{6$u8dgenPFt`V`pk{3vJF~!dB^@$_P|y@=7Otilc*B6!53*q-W7YpAznsjn&PItoTUT*8YBdj{lB^|YEj;4g8X|ZJ0 z;Z8c7AKGu)<0DHYpVr)}xl?-QtpqeJu;)&A)pnbK898$!*ynHLW)zWmWu*iiMY>Q%=vZLVZ>L;Tgn zj*niCpIPLW_I-Nv*3more_r_6b1Qq#E$=!1^Ou*MFFr9a<-O7$sm*uwOcU}h!CoAjYSMOG~C7W8`JN>}PeR%ff*&m;O z;Bc-vb|)OWmz%rPUH!sw?y;FFsk-m1{_w=j6Y*~>F-tw4p1pPU@6WF|UzqRtytFdu zto`uZ&2#Vf&h`99u|uP}pLQ>n{^aa0O4=X2$ROcAeubfIrNF;yw(5kfdbwu*(&U|@ zTd`GJ&wsKdvw1Kx640XY@A5v*OE?Z7!=Iw3PbF=o>WFwz{8YSi@#1aAZQGr$<(d=s zY$uWVPtkyAc+@8>?p|fvQY?O-Yq)PKxnEfNL*I|~&W+4h|J1Qq|2NWNQ=)e7a>2g) z1;sGFu=KIPV9$HRQue|%n=@f^uHfq06Sj`^Tyw1zLu0DQ^`GTmMdc6fNqYS~(1 zI!fD8yeVn~d&e9#K%Vn~DQ1{;fjwrp0D*&K&1bh+Gn-nAX`3~hHm{ktjAC%vro>wz zK**%;Mz>K?(9L`nwD+h)HQQ1R^j+M+Znx`07|0zr;VCn5SrR9`N%FvpBw zG*o;B@%Ny6gN_n4mMC1kB0h9)&`le(YlC)+r{LF958`|O=&&U*0C3^ywiZ4L&1Xhw zi1knx*_pl}Q|d-q{Y^zI*?|f(B9@#77Yl#X7q9*BPZG|9zY`k}604aK~iH&jA(O#Y63=2r0E1c_|xro3!=Q|gC?;ZuX+D!Wv>5mSs6gR6ihvp9fsz9K3EbUnwS#?6o zL1-z3z?X&B;(OwJ{I$i7<>J=qZjclNxCsQt_1>A@g?;gopH+QSwQ6fi7MCw{&p!|2 z@{6Zco57dK?8v}GXw*R=(_W0yK>Bo)d7t?p_0aG(M!FmzVU(OVE1VQ8Yz%D~9FvCO z1reEs}$0GUw_elS39Q0}ljE6zb2hE9OlCwAizFXt{3xO5TBG_JArFGxrFpo4UsN;cH8MBIvnbzj|eE5FgwIIe1nE&jbXuVRWvpiDx($Al3@p7hv#W zcQk$niT(_x0wNhgmr2x#K9KYRk4DO+K@JMt7uiPzHe7ms2u~(+2mBBu2eV`hkCa{X zcv~Y~t^EUfp}qCc!0v`fLGPwf5Zf$X1_UT3Vla^dR16Tgr608sVJoPTgT61w*~g8n z9!W*)x>GiZ1CJU#R@bN}1yn)N^{X>immPbSx>xfLW!%~jCAVsOF3A>Lx6Rn*`tGq6 zgamrCF2@uNd=KDd(f|l=%_#*9sg$}3;H1SuIN>d*AoGL)%2qUhS1X$Wnkwk@MI!3n zeuWrNaLzyuvL5Yzd6EN@`zrD+d@MB{dF7dnA9#{e@4}ID=}g0oaU_ z2EjW9iz7Cg_)RPYA%n18-F>A-E2%bIo@VgrmK|~$$XnG-0ue%J4My@)%&S&3;8Au$ zPdLgvs-k*<|1fYfuu|SUo%eZeQPNhiW~)uuYU8_BZM#8It>rf)@*CpcTter4~@8&{RlG3Lq(X&o)@6x&& z5Tj=sKpla2$9hUm(MAI$?xiUU)JKVTEJg=i(2jIPlAKZjh8XBu7Bk8z#RFvuxON#- zDn3X(%0`WEgF+QEe#t?((L85D7I!R|e(>$j9aZn=%$pvSLBB^1s-Se*_S+}LR7u^J zl-XYMh%pt`fO;RVzjhwQk^KI z9Z3h;{~T*M%&tQgVL?!0blgB1v>I7%B#am-%?_XAq8x_hshtrDKzt8@A&HWM%UBWC zxvbmA+X*;k7&D@Wy^AG0-OYam(T#POq0f+n=DtHcW?D?_`X#!XFkW+Q z;5utHRX%Z8P2KbpN1mya@G}|f$|I3}_(v$JgWls2QL3m{idmi<2XUot0|`zOl zVtp<3=N&||Q2#1}g#MA0b&0c3)pJJ%=XWUw(9@DYVW#zulgfXyH>ZcVqO5p&4hEo9A( zFs59)gV$RtYBxbHZ(=!dRYY)A*u+()Dy|g7u2kiyWRxL6?q$oQc2g<;lPyx-;2+6* zUr*1B4hU3Q@V?jI@xJTb)BjLW;U@5W@;^_+UTGoZ*Z5*Tf`u>(RSY4Qh(f|dVH7sb zB-k*^AkM|PFh@&1%tOh?g@hyQplw3jnGnNb!WDKU++jDZbHqIfZ`hlt2v^W|XS_1u z3;QT8#;X$5;p#+9xF%5>uBCOZxIYmH2NJ<>kRdElYgFzEtOcJir&bJiSa>;9+^72p z(VTQu_2GKO6K+U)U~IMGwa50cFup$Az>+SaRJ=u$O11F{XHf|^L0`Axv-^gdX|F2i zRec4<;S*jp&V_2y8;&08J}5;~$${A5s3se+R8rDMM@CYbAq}K7XgQ;5@}Sx=GX50~ z3o)igP9{?Z?W*^{yLiBWF8y&e#F<=Ya@-X6$niKha^!}JxZj?3{;6)Hl)S9mmEc>rj50Hz?vM~39DoPZxS1tXP+$D)82 zpTZ3R-$2zRqLMHJyu&I?m>Xwd!M>v~WUm@cDJtDV`nk@eq`n?iN9YkoFi6rHO8i|+ z#Tkhxsc0l}nJnO1?;YE_t8dGeEr(C^CE%zw%TZZT6XSg&Cf~XjP>{FG3YlUM^D48>V5Y8K8~xht_ZGL3j%ElRso`m0Nx^B@GsoqMXlLxGKHfr z%SmI=u@#t$S@;}!Ej~6SsIYILh}9_^l!8*P@K8FG21S6hp{#U~`~N8thz1|_0nJJ%tCUv72W7RgMyZ0bMrl*3 zp{#|n21>uOR;h(DpsZ8;PzJ{Xp?0$hui9dM|HY8-6 zTxNQJ*~QeT7QvZKUXcwMYIt3Z4`^*LNJn~-q~V<*GsTiIBN9nBEM0$x-Lx48A%mYI zbM;b|f2U$@ZF`pgv5Q{vqNO^Z7hP%(Omc}B#f4=Uqqrr5hhFqCus?l&u%T1R|@C^uYmqn zh&_TKMvdJ|YXQmu`)wH6f64O(Z?6Kb#cMZa*g>Y37_05~VadiyrNX_z8(^2LXLyZ` zmCBEJTXRo1GGusdsl~>0HsjEm6=A|@thehj&f$=am&y!luhTBiGlQg;w2`euXU4$h zogfVSz4ONF+zB$qv=IY2fIe%9CNN}>MA;KCVvH1f`0XCUjW$*)VN~OUU?A7g$0*ob zLNbEA2D{7%=UIhJ2xo;c#-<-~^rVGRV_@^sXiU^3=v0uXblvauoitLZcwdLDqaN$) z$eap6Ct)Bi59;aX_sFP3Q3R#$w`vnq2vehmM8&gU#X6(|mXftZ6i6oGr$8winj8w4 z?wC$>a5AczLjSm->L#Zfn#rbgQ#>piLkDB0Rnsw`j{t7+sS!14@;IUCJk(1Szv&s0 z^$1dp$NEjDjR^6StmqnECD;nEv1PV`Dpr%#fUvI0T6D;Cbssx;^uWObJ^QF2YbXUU zYf1y99ul>6`07HbM=Jm;vr&^B7%^QqXT*RlfpOI|Q?wUkatVEqDsQRqP}QJq1#;=Y z(Ir}~1Gj}u`gZ`%k-H93)i@RUQ2nU)<29db{KdxHy60~Cp3jNT=X}rKt_@t;d2#2| ziG1z)tT@l}uHfzJz~sywlyn_T*P43EhSN*20;nMiU@m%ZfYbXA(|DWT37r$9|GW)`u zul6SsZ%=$6-U+@u$=wdNPPhH(h7UH(ypmtDJs;eWtJ*PF8^|8`wiTwKvw!y+U+^2k zxPnms_JMg0uP*+_Qsx)+p?0bAGnk2{oU!d-iPOK6{cuxTALOTJCdqVDD<~ z^VI_2i?k<71l68b0RbF=;R;a`6wmGa*8yrojNkiXNtimRH*kd4E zpu)-6Zl23=#UhZlTyDbDJjDMkElfRh-!>#$t4tVpj04-c2|xX;3Y}48Z0kX z*^^Q?Y)sa{p&d;so21x)Bqzr=Em|Nh65QRMR8s9&)PVadpGS#B4K(evJ_g=gX`)Rw z1)Kp~WNW^%jif74p%wL6$33QldZSgAD&`07i!_pk$OX|4gA{Uf^v#=*q z9MRN)*z2GS92RDJL4M;AyInvZx}bk}ilZIIUE*W82;Rt`YS<&vtClH*^5z#|wEp`5 z&XM_g5?Gh3U6&Q-f-T<=kEv{ji?7n$csA!n0 zYeKP}4otuHLH%sqhDm;|xplgAdd<`U5XI{D$-Q$;tEc&QcU|23H3#qWBB`hWtx(lG zbu{N|&55n^4KUAqkoa4Ed)Gz$%_zYQUsTnj$Tr;5t6^o+t(`9RhRf9%5N`MxfKd>+ z;)5;*6~PYy0bh%(Bxog91H-Ec4h8aDrBS4#uo>n(_798Vc%I2{=co$#Z5KJ5 zay|H(wB-y2Jsmywygezq!!9Z9hPRXzoTMwjOD#2pIISDbgc==fKVTsTwK!TA;HJYG zWb!zN#gzh`K}Q#la#Mkwc;uHA>i#%`MBFpmXEt2zzP|Q)eXixje9cR_?;Xo|kD(Pf&fL>ou#7(wo+N+S z-09;M@dzqG$|G4o?$0COVigpy%_XIK&G02vYWLC_OAaoy7aii2oLzeia2}W8k&8l1 zdb-Qfz2Fwb)y?$umBM9~X_vzgcYwzLp=R<-OiLwE4MQwnh?z042epLiiV}bbR04Hw zXh5_>It2BaWI(j87q#D5EFMRF3{fHE06-;67>#QZDAJ^=DyX8Z>z5?pm|i$CZ3BW$ z2+&K{FupLIh2>lpN?fHK#^&!KI08UN`$%b7ipgVX9=hFh^L_LZZG8<#KLY^Nl9zy% ziCv6MJ7;4JmFCorl0y!WQ3^|t@9nZU>FC+=Uk^Zwo0eZX7={;{H+qL2eM z#3>^@4aof&;Yz_)+iU>Jf*EHrY=%Dr{xj(F{g)XkTcM?UH<<{PrKLM}qwi)(T)Ju5 z#!HPESeO170Epabg12hgW^3A}yYe-gE;zGHb{mzax!U?UUtr3gJ)09(EsyAmd&C6P zL++7<7z$>4*N-=EG4D~E*4Q783#b<367R%_dd@oan4pjW~I2=MdKk( zT0TDGgggc_;XLas%VRjxZ|)gVqo={hLf-dPYGz-RKqXMCi^f$NgMW}S-DDe8Fk&=V zDA-dsHG%*NArBp>srqQ#z@C=jE<`tqs>d`i%1F0liF?xGAdl*VAQe)OGbSb2?Mv^XPPZMf>W_QW;y+KHUEYk^~Z?s>wx z+%%Y2^$p=F8g2(Ah-aq8ref2t%>-w}+?IXUW7kjS4j;+&zLGm~GUxAuadkC!>gwkO zwz>hb1u*KKUI%OZV`nErZZNLS_1q12edikP#u@?eMb0IYC)|FJxBLr>e$dmA@4OH$8|OBk|i{H zs+!(Y!HHls}9KYSWexXw0356w(hB{7aW6IzSXln2p*>gX4H?5UVG-&?%vtm zy}4tr=6!Nblr2S6er!BYrWbJ>kVm8&4`wYs=7{d z_we%-NR}3ZhNTXm6OIA1dn*{rLdz#O3KPHg)4#WUfm^=EW_{r9=6#Rn#K)H(L6H}) z1D!`Xf`{Wk^`=QU9M0UmpHf2&Aif zvJLyZI`?_;t zH$83m?!o%Wi~1Q3E5m8>n1_d!-7s0Y!v4PGBmus^zP8 zm)5(iapfV}k8z@<+t(_wwsOU7q0>&6r=Q=I z{KDf@;j}c&wv`TAYk}7XZ4{640qyBP*#Q^t^MOZ8cYOaV(~#94Fr#(>+v_xR*|mBE zn6TFx5ws)Nil7?-O=nUs_$0o*h~QNODuO`-!w9H%Fpe1JYBXv9-bCyz1V2RZHUhL< zl;N$En1rtga9aHe0Q|mY?}C>V)-Q;4LU7@U24T&@cDK;9Q11c| zuhOj_$SaC1iI^VMJV*w=%%F~%Sy^mFHrJ_KX!v=E={kJk_`V~(2M+aU+ps%Y3oU^l zhydA|-p!IryC*(leTnv*_6N`foqZjz8h%$`n6E{`Z2b?i=GVmaOXB(!S^Fgk&63c6 z66sfD0!Phw4nF#zr>qkuUs0qeQMN4VXDr2W=t#6l)MN|ZR=u4qO+Lyev z-?=lxAFUi?pF0H4+&TB&bI-l!-0z-y{-v)kEaCav|2<#)n`4sn4Sl$e-!6Fe|GP($ zzK}F2D`_6Br{pR3WP3bR_LjU^uXxK@8E?7dEBmwlav&QJ^}bTD9Lk2u;cQrZ`%As$ zNH$XL%l4I{*=RYIjg|Ye{T_)$xF7ukrFeNDJAm(~7G!-ZJtv?-c912Q_s$ttt@fKG zsJ2RjD#tdae z=StOJin+@5YGGBcno3^3$GBQx>9txq{9oiHDUa2wRw}xwnnk^0%%^&+;58GCmP^bE zTvylDij@MGp{raiGZJituj^X1B+4nD)tC9~#=E)M_urlU;M)9qmiJ1fW(8-}Qb|Y% zezZn%)shvw!F5y5>m`gBzFI_ouI3p}=Sd6qaU9pPPy68uiAh-xHgAvS$$CSQCWoZM zKh39kKlf$jT2IPv1v5hC)k=N%igKf7uId%Vq*0WWY9&v4r04TKcP!_PAwB!D7_KI5 zdFHnyOiDIbX~pub(3~wFyT>Y~j+ zT&^B>CzN(sFJN2)4lk`sdl4x)zK^2B;~=RpPf(e#j}L)ZUc=-Z&?xZ`k>eosXy!xc zGOsItrp)UVX8AcYtGx1U%pJ+8*z%DY6-+;uv%=l!C3=kSK-0 z>uuFWs8s7qMXmk}x$RuhSkqxbcvJn2D~eGpua%f`Pc2ok(_lie?KsQpT*J;$?yst* zV(N;fEBA}$s-h|@5R&FeNVVcRFBXc}9XVpsnpy*SwW3#waDW!?+b|628Z2h$Ri0<9 zN=shW^^)Zieop384Ja0GZ!ouGFAb3s&A63VRgIih!-&OvOVs?9k551?t`H%k@ktOX zmdkY@1dM&i&g7Gv$m{qUGax^Poo9ECk8H~$JHyFsIk_`5u`N&R^u~Vl!A^uod~#c! z+!-1FjqF&J`X6o8pQ7eFtSZA=K4C3CN&!&{qIB4n5%Bs%Z4{+gtJE(_aqtF2X+#_R zT+aHmq?W+juZ?I!cn7pm?HJxcd>_XX9Ac!z5zQsb6i;0~|P0Q}5VGP_A;hba}# z)^u(vKq3c_*aMcY3M{h1_NJRtP1TY$P@ckEt*|gsPUU&Sie?r7+$Bv6aJ6DZ4Do5( zv3|sI~Dbb6|P4dEyWi@ zn|%bX(x2OC`dkI$3#Pu-so%3klMOVi_qGPp*H$qMpCv-L{oIj8-&e5)tBf;cnX!tZ zR%(ihll5NFtiAP&DF)pJEnB#ZyMNf=BOfy?Ck@{k^QDxsQc?@gns1a-kDNPReaM-4 zH@otOsqym1;>d*UwW0_rR9;i`6$Q34i-uXu8`BCRFFIbIrB|o`t*E_G?{`#caemOH z4dO9A3XUa{Jm-Utf#a45GMsXKh%~anpja{8Et%>)3fB2q8t82z?+|&H$a_St5&3|~ zA)tSSctk$I-&h18pnq*QoY>1PvG(FE1>NoK^*fw ztp{+7V;%7z;JYTL0tih3Y%9e=RYbG(w-@v>I|R>bTz^ob^O?NL4aHD!x)Dw*w1H=7cM4F7Y40_^IBm~NrVbk5pz`9g=T{|^keB%)g3mE;5!cHgld-7JaoECYIl0r?HAxaAW<3|fD< zyL~)|36+mFV7ulo(W7m^`IIc=iNIMvV*Qw7`cEkK{s^~Q`%aVccQjmpkow8ahSw;_ zaTp8s9=cW$0{Mq%jNV7Y!2J89OoqrIOq?ekkssr4(A|VCIe)w_hkTKPV<0w`QHwnO zt*3=$bX7WpWwMQBxXjb_`bMdWJA#d5^|MD~*gae_9cy5p>58hXiC)iztoj(?Roj+! z)}YnBdqetdpPxC$HfCVrOv_ipD9_ zYG+X>g6@2TKy@{UvgDcd86 zs;+fw?NkMA53So#QAa2<$Cj&wS+i81zM|M=T%fpKQuO6JKpYVs8m(BQRJ;pUF`vIx z-=1T6+UNII5ydbdu&yAdw+7(TXh66da2ZJ`ido3@5Ihug#HP+kwd6#j?(yok0k~^7 z`|W!lY~ju!ZFBwsb8qk1=LJIbu|{5Zwg933Vkz$*iBH=B$!#PH&>N`MPTzW5*U?3Fcw1MsX zCILO~B@&?q5a#B(#Zk!nh}(-@ae2FBd<;86ar-7YXMd72B6~r-6x{BD_k~3F$UL?qnk_9{J*f|6(I8!7Z}QuJ$S=4kf#xJf%cl+WSQyV|T zpv}G)*TaxDF!JcaW~ec8rZIiFF><*Xe{(&$1Lo}JiAM6}$C*a*mFB>Cs5q=_rZ$t0 z^H1is?lexm-W-0TAq^e)M?IGwetIC`wKwNM792t&B=m*N!7tBjR+}T!kB#QY`6r+L zGXIO)&5?JWF8*qv@o~0s^G}-}=Ni{@&3BiYBTLP=Y7cvHbLz`Wk9}ByC*EfA!V~S6 z7k*J}Cf|Dc!c*f{bB*t3{^{D&A2#3lq?!DrIk31M+Z`nRqm5*`Ie4}q_3tE-P<^5? zdajvxr6I)+{Kq|!jrR{EybdnPo@pxMwKpJz1~(R(!Qpk^ZesLNX7kiz@8gBX!LQCX v$Idqs7pQMU_KZGUrS_u-39sj-hd$_i5D7q1@Ib=rAR%MzgTF&78#(?DXOf+> literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/coverage/__pycache__/version.cpython-312.pyc b/venv/Lib/site-packages/coverage/__pycache__/version.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..645abf3311ea0f4f2a1f6a1137e074d6980750c2 GIT binary patch literal 1217 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/coverage/annotate.py b/venv/Lib/site-packages/coverage/annotate.py new file mode 100644 index 0000000..2f6bd7d --- /dev/null +++ b/venv/Lib/site-packages/coverage/annotate.py @@ -0,0 +1,114 @@ +# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 +# For details: https://github.com/coveragepy/coveragepy/blob/main/NOTICE.txt + +"""Source file annotation for coverage.py.""" + +from __future__ import annotations + +import os +import re +from collections.abc import Iterable +from typing import TYPE_CHECKING + +from coverage.files import flat_rootname +from coverage.misc import ensure_dir, isolate_module +from coverage.plugin import FileReporter +from coverage.report_core import get_analysis_to_report +from coverage.results import Analysis +from coverage.types import TMorf + +if TYPE_CHECKING: + from coverage import Coverage + +os = isolate_module(os) + + +class AnnotateReporter: + """Generate annotated source files showing line coverage. + + This reporter creates annotated copies of the measured source files. Each + .py file is copied as a .py,cover file, with a left-hand margin annotating + each line:: + + > def h(x): + - if 0: #pragma: no cover + - pass + > if x == 1: + ! a = 1 + > else: + > a = 2 + + > h(2) + + Executed lines use ">", lines not executed use "!", lines excluded from + consideration use "-". + + """ + + def __init__(self, coverage: Coverage) -> None: + self.coverage = coverage + self.config = self.coverage.config + self.directory: str | None = None + + blank_re = re.compile(r"\s*(#|$)") + else_re = re.compile(r"\s*else\s*:\s*(#|$)") + + def report(self, morfs: Iterable[TMorf] | None, directory: str | None = None) -> None: + """Run the report. + + See `coverage.report()` for arguments. + + """ + self.directory = directory + self.coverage.get_data() + for fr, analysis in get_analysis_to_report(self.coverage, morfs): + self.annotate_file(fr, analysis) + + def annotate_file(self, fr: FileReporter, analysis: Analysis) -> None: + """Annotate a single file. + + `fr` is the FileReporter for the file to annotate. + + """ + statements = sorted(analysis.statements) + missing = sorted(analysis.missing) + excluded = sorted(analysis.excluded) + + if self.directory: + ensure_dir(self.directory) + dest_file = os.path.join(self.directory, flat_rootname(fr.relative_filename())) + assert dest_file.endswith("_py") + dest_file = dest_file[:-3] + ".py" + else: + dest_file = fr.filename + dest_file += ",cover" + + with open(dest_file, "w", encoding="utf-8") as dest: + i = j = 0 + covered = True + source = fr.source() + for lineno, line in enumerate(source.splitlines(True), start=1): + while i < len(statements) and statements[i] < lineno: + i += 1 + while j < len(missing) and missing[j] < lineno: + j += 1 + if i < len(statements) and statements[i] == lineno: + covered = j >= len(missing) or missing[j] > lineno + if self.blank_re.match(line): + dest.write(" ") + elif self.else_re.match(line): + # Special logic for lines containing only "else:". + if j >= len(missing): + dest.write("> ") + elif statements[i] == missing[j]: + dest.write("! ") + else: + dest.write("> ") + elif lineno in excluded: + dest.write("- ") + elif covered: + dest.write("> ") + else: + dest.write("! ") + + dest.write(line) diff --git a/venv/Lib/site-packages/coverage/bytecode.py b/venv/Lib/site-packages/coverage/bytecode.py new file mode 100644 index 0000000..a38d974 --- /dev/null +++ b/venv/Lib/site-packages/coverage/bytecode.py @@ -0,0 +1,196 @@ +# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 +# For details: https://github.com/coveragepy/coveragepy/blob/main/NOTICE.txt + +"""Bytecode analysis for coverage.py""" + +from __future__ import annotations + +import collections +import dis +from types import CodeType +from typing import Iterable, Mapping, Optional + +from coverage.types import TArc, TLineNo, TOffset + + +def code_objects(code: CodeType) -> Iterable[CodeType]: + """Iterate over all the code objects in `code`.""" + stack = [code] + while stack: + # We're going to return the code object on the stack, but first + # push its children for later returning. + code = stack.pop() + for c in code.co_consts: + if isinstance(c, CodeType): + stack.append(c) + yield code + + +def op_set(*op_names: str) -> set[int]: + """Make a set of opcodes from instruction names. + + The names might not exist in this version of Python, skip those if not. + """ + ops = {op for name in op_names if (op := dis.opmap.get(name))} + assert ops, f"At least one opcode must exist: {op_names}" + return ops + + +# Opcodes that are unconditional jumps elsewhere. +ALWAYS_JUMPS = op_set( + "JUMP_BACKWARD", + "JUMP_BACKWARD_NO_INTERRUPT", + "JUMP_FORWARD", +) + +# Opcodes that exit from a function. +RETURNS = op_set( + "RETURN_VALUE", + "RETURN_GENERATOR", +) + +# Opcodes that do nothing. +NOPS = op_set( + "NOP", + "NOT_TAKEN", +) + + +class InstructionWalker: + """Utility to step through trails of instructions. + + We have two reasons to need sequences of instructions from a code object: + First, in strict sequence to visit all the instructions in the object. + This is `walk(follow_jumps=False)`. Second, we want to follow jumps to + understand how execution will flow: `walk(follow_jumps=True)`. + """ + + def __init__(self, code: CodeType) -> None: + self.code = code + self.insts: dict[TOffset, dis.Instruction] = {} + + inst = None + for inst in dis.get_instructions(code): + self.insts[inst.offset] = inst + + assert inst is not None + self.max_offset = inst.offset + + def walk( + self, *, start_at: TOffset = 0, follow_jumps: bool = True + ) -> Iterable[dis.Instruction]: + """ + Yield instructions starting from `start_at`. Follow unconditional + jumps if `follow_jumps` is true. + """ + seen = set() + offset = start_at + while offset < self.max_offset + 1: + if offset in seen: + break + seen.add(offset) + if inst := self.insts.get(offset): + yield inst + if follow_jumps and inst.opcode in ALWAYS_JUMPS: + offset = inst.jump_target + continue + offset += 2 + + +TBranchTrailsOneSource = dict[Optional[TArc], set[TOffset]] +TBranchTrails = dict[TOffset, TBranchTrailsOneSource] + + +def branch_trails( + code: CodeType, + multiline_map: Mapping[TLineNo, TLineNo], +) -> TBranchTrails: + """ + Calculate branch trails for `code`. + + `multiline_map` maps line numbers to the first line number of a + multi-line statement. + + Instructions can have a jump_target, where they might jump to next. Some + instructions with a jump_target are unconditional jumps (ALWAYS_JUMPS), so + they aren't interesting to us, since they aren't the start of a branch + possibility. + + Instructions that might or might not jump somewhere else are branch + possibilities. For each of those, we track a trail of instructions. These + are lists of instruction offsets, the next instructions that can execute. + We follow the trail until we get to a new source line. That gives us the + arc from the original instruction's line to the new source line. + + """ + the_trails: TBranchTrails = collections.defaultdict(lambda: collections.defaultdict(set)) + iwalker = InstructionWalker(code) + for inst in iwalker.walk(follow_jumps=False): + if not inst.jump_target: + # We only care about instructions with jump targets. + continue + if inst.opcode in ALWAYS_JUMPS: + # We don't care about unconditional jumps. + continue + + from_line = inst.line_number + if from_line is None: + continue + from_line = multiline_map.get(from_line, from_line) + + def add_one_branch_trail( + trails: TBranchTrailsOneSource, + start_at: TOffset, + ) -> None: + # pylint: disable=cell-var-from-loop + inst_offsets: set[TOffset] = set() + to_line = None + for inst2 in iwalker.walk(start_at=start_at, follow_jumps=True): + inst_offsets.add(inst2.offset) + l2 = inst2.line_number + if l2 is not None: + l2 = multiline_map.get(l2, l2) + if l2 and l2 != from_line: + to_line = l2 + break + elif inst2.jump_target and (inst2.opcode not in ALWAYS_JUMPS): + break + elif inst2.opcode in RETURNS: + to_line = -code.co_firstlineno + break + if to_line is not None: + trails[(from_line, to_line)].update(inst_offsets) + else: + trails[None] = set() + + # Calculate two trails: one from the next instruction, and one from the + # jump_target instruction. + trails: TBranchTrailsOneSource = collections.defaultdict(set) + add_one_branch_trail(trails, start_at=inst.offset + 2) + add_one_branch_trail(trails, start_at=inst.jump_target) + the_trails[inst.offset] = trails + + # Sometimes we get BRANCH_RIGHT or BRANCH_LEFT events from instructions + # other than the original jump possibility instruction. Register each + # trail under all of their offsets so we can pick up in the middle of a + # trail if need be. + for arc, offsets in trails.items(): + for offset in offsets: + the_trails[offset][arc].update(offsets) + + return the_trails + + +def always_jumps(code: CodeType) -> dict[TOffset, TOffset]: + """Make a map of unconditional bytecodes jumping to others. + + Only include bytecodes that do no work and go to another bytecode. + """ + jumps = {} + iwalker = InstructionWalker(code) + for inst in iwalker.walk(follow_jumps=False): + if inst.opcode in ALWAYS_JUMPS: + jumps[inst.offset] = inst.jump_target + elif inst.opcode in NOPS: + jumps[inst.offset] = inst.offset + 2 + return jumps diff --git a/venv/Lib/site-packages/coverage/cmdline.py b/venv/Lib/site-packages/coverage/cmdline.py new file mode 100644 index 0000000..5287327 --- /dev/null +++ b/venv/Lib/site-packages/coverage/cmdline.py @@ -0,0 +1,1184 @@ +# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 +# For details: https://github.com/coveragepy/coveragepy/blob/main/NOTICE.txt + +"""Command-line support for coverage.py.""" + +from __future__ import annotations + +import glob +import optparse +import os +import os.path +import shlex +import signal +import sys +import textwrap +import traceback +import types +from typing import Any, NoReturn, cast + +import coverage +from coverage import Coverage, env +from coverage.config import CoverageConfig +from coverage.control import DEFAULT_DATAFILE +from coverage.core import CTRACER_FILE +from coverage.data import CoverageData, combinable_files, debug_data_file +from coverage.debug import info_header, short_stack, write_formatted_info +from coverage.exceptions import NoSource, CoverageException, _ExceptionDuringRun +from coverage.execfile import PyRunner +from coverage.results import display_covered, should_fail_under +from coverage.version import __url__ + +# When adding to this file, alphabetization is important. Look for +# "alphabetize" comments throughout. + + +def oneline(text: str) -> str: + """Turn a multi-line string into one line for help to reformat nicely.""" + return " ".join(text.split()) + + +class Opts: + """A namespace class for individual options we'll build parsers from.""" + + # Keep these entries alphabetized (roughly) by the option name as it + # appears on the command line. + + append = optparse.make_option( + "-a", + "--append", + action="store_true", + help="Append data to the data file. Otherwise it starts clean each time.", + ) + branch = optparse.make_option( + "", + "--branch", + action="store_true", + help="Measure branch coverage in addition to statement coverage.", + ) + concurrency = optparse.make_option( + "", + "--concurrency", + action="store", + metavar="LIBS", + help=oneline( + """ + Properly measure code using a concurrency library. + Valid values are: {}, or a comma-list of them. + """ + ).format(", ".join(sorted(CoverageConfig.CONCURRENCY_CHOICES))), + ) + context = optparse.make_option( + "", + "--context", + action="store", + metavar="LABEL", + help="The context label to record for this coverage run.", + ) + contexts = optparse.make_option( + "", + "--contexts", + action="store", + metavar="REGEX1,REGEX2,...", + help=oneline( + """ + Only display data from lines covered in the given contexts. + Accepts Python regexes, which must be quoted. + """ + ), + ) + datafile = optparse.make_option( + "", + "--data-file", + action="store", + metavar="DATAFILE", + help=oneline( + """ + Base name of the data files to operate on. + Defaults to '.coverage'. [env: COVERAGE_FILE] + """ + ), + ) + datafle_input = optparse.make_option( + "", + "--data-file", + action="store", + metavar="INFILE", + help=oneline( + """ + Read coverage data for report generation from this file. + Defaults to '.coverage'. [env: COVERAGE_FILE] + """ + ), + ) + datafile_output = optparse.make_option( + "", + "--data-file", + action="store", + metavar="OUTFILE", + help=oneline( + """ + Write the recorded coverage data to this file. + Defaults to '.coverage'. [env: COVERAGE_FILE] + """ + ), + ) + debug = optparse.make_option( + "", + "--debug", + action="store", + metavar="OPTS", + help="Debug options, separated by commas. [env: COVERAGE_DEBUG]", + ) + directory = optparse.make_option( + "-d", + "--directory", + action="store", + metavar="DIR", + help="Write the output files to DIR.", + ) + fail_under = optparse.make_option( + "", + "--fail-under", + action="store", + metavar="MIN", + type="float", + help="Exit with a status of 2 if the total coverage is less than MIN.", + ) + format = optparse.make_option( + "", + "--format", + action="store", + metavar="FORMAT", + help="Output format, either text (default), markdown, or total.", + ) + help = optparse.make_option( + "-h", + "--help", + action="store_true", + help="Get help on this command.", + ) + ignore_errors = optparse.make_option( + "-i", + "--ignore-errors", + action="store_true", + help="Ignore errors while reading source files.", + ) + include = optparse.make_option( + "", + "--include", + action="store", + metavar="PAT1,PAT2,...", + help=oneline( + """ + Include only files whose paths match one of these patterns. + Accepts shell-style wildcards, which must be quoted. + """ + ), + ) + keep = optparse.make_option( + "", + "--keep", + action="store_true", + help="Keep original coverage files, otherwise they are deleted.", + ) + pylib = optparse.make_option( + "-L", + "--pylib", + action="store_true", + help=oneline( + """ + Measure coverage even inside the Python installed library, + which isn't done by default. + """ + ), + ) + show_missing = optparse.make_option( + "-m", + "--show-missing", + action="store_true", + help="Show line numbers of statements in each module that weren't executed.", + ) + module = optparse.make_option( + "-m", + "--module", + action="store_true", + help=oneline( + """ + is an importable Python module, not a script path, + to be run as 'python -m' would run it. + """ + ), + ) + omit = optparse.make_option( + "", + "--omit", + action="store", + metavar="PAT1,PAT2,...", + help=oneline( + """ + Omit files whose paths match one of these patterns. + Accepts shell-style wildcards, which must be quoted. + """ + ), + ) + output_xml = optparse.make_option( + "-o", + "", + action="store", + dest="outfile", + metavar="OUTFILE", + help="Write the XML report to this file. Defaults to 'coverage.xml'", + ) + output_json = optparse.make_option( + "-o", + "", + action="store", + dest="outfile", + metavar="OUTFILE", + help="Write the JSON report to this file. Defaults to 'coverage.json'", + ) + output_lcov = optparse.make_option( + "-o", + "", + action="store", + dest="outfile", + metavar="OUTFILE", + help="Write the LCOV report to this file. Defaults to 'coverage.lcov'", + ) + json_pretty_print = optparse.make_option( + "", + "--pretty-print", + action="store_true", + help="Format the JSON for human readers.", + ) + parallel_mode = optparse.make_option( + "-p", + "--parallel-mode", + action="store_true", + help=oneline( + """ + Append a unique suffix to the data file name to collect separate + data from multiple processes. + """ + ), + ) + precision = optparse.make_option( + "", + "--precision", + action="store", + metavar="N", + type=int, + help=oneline( + """ + Number of digits after the decimal point to display for + reported coverage percentages. + """ + ), + ) + quiet = optparse.make_option( + "-q", + "--quiet", + action="store_true", + help="Don't print messages about what is happening.", + ) + rcfile = optparse.make_option( + "", + "--rcfile", + action="store", + help=oneline( + """ + Specify configuration file. + By default '.coveragerc', 'setup.cfg', 'tox.ini', and + 'pyproject.toml' are tried. [env: COVERAGE_RCFILE] + """ + ), + ) + save_signal = optparse.make_option( + "", + "--save-signal", + action="store", + metavar="SIGNAL", + choices=["USR1", "USR2"], + help=oneline( + """ + Specify a signal that will trigger coverage to write its collected data. + Supported values are: USR1, USR2. Not available on Windows. + """ + ), + ) + show_contexts = optparse.make_option( + "--show-contexts", + action="store_true", + help="Show contexts for covered lines.", + ) + skip_covered = optparse.make_option( + "--skip-covered", + action="store_true", + help="Skip files with 100% coverage.", + ) + no_skip_covered = optparse.make_option( + "--no-skip-covered", + action="store_false", + dest="skip_covered", + help="Disable --skip-covered.", + ) + skip_empty = optparse.make_option( + "--skip-empty", + action="store_true", + help="Skip files with no code.", + ) + sort = optparse.make_option( + "--sort", + action="store", + metavar="COLUMN", + help=oneline( + """ + Sort the report by the named column: name, stmts, miss, branch, brpart, or cover. + Default is name. + """ + ), + ) + source = optparse.make_option( + "", + "--source", + action="store", + metavar="SRC1,SRC2,...", + help="A list of directories or importable names of code to measure.", + ) + timid = optparse.make_option( + "", + "--timid", + action="store_true", + help="Use the slower Python trace function core.", + ) + title = optparse.make_option( + "", + "--title", + action="store", + metavar="TITLE", + help="A text string to use as the title on the HTML.", + ) + version = optparse.make_option( + "", + "--version", + action="store_true", + help="Display version information and exit.", + ) + + +class CoverageOptionParser(optparse.OptionParser): + """Base OptionParser for coverage.py. + + Problems don't exit the program. + Defaults are initialized for all options. + + """ + + def __init__(self, *args: Any, **kwargs: Any) -> None: + kwargs["add_help_option"] = False + super().__init__(*args, **kwargs) + self.set_defaults( + # Keep these arguments alphabetized by their names. + action=None, + append=None, + branch=None, + concurrency=None, + context=None, + contexts=None, + data_file=None, + debug=None, + directory=None, + fail_under=None, + format=None, + help=None, + ignore_errors=None, + include=None, + keep=None, + module=None, + omit=None, + parallel_mode=None, + precision=None, + pylib=None, + quiet=None, + rcfile=True, + save_signal=None, + show_contexts=None, + show_missing=None, + skip_covered=None, + skip_empty=None, + sort=None, + source=None, + timid=None, + title=None, + version=None, + ) + + self.disable_interspersed_args() + + class OptionParserError(Exception): + """Used to stop the optparse error handler ending the process.""" + + pass + + def parse_args_ok(self, args: list[str]) -> tuple[bool, optparse.Values | None, list[str]]: + """Call optparse.parse_args, but return a triple: + + (ok, options, args) + + """ + try: + options, args = super().parse_args(args) + except self.OptionParserError: + return False, None, [] + return True, options, args + + def error(self, msg: str) -> NoReturn: + """Override optparse.error so sys.exit doesn't get called.""" + show_help(msg) + raise self.OptionParserError + + +class GlobalOptionParser(CoverageOptionParser): + """Command-line parser for coverage.py global option arguments.""" + + def __init__(self) -> None: + super().__init__() + + self.add_options( + [ + Opts.help, + Opts.version, + ] + ) + + +class CmdOptionParser(CoverageOptionParser): + """Parse one of the new-style commands for coverage.py.""" + + def __init__( + self, + action: str, + options: list[optparse.Option], + description: str, + usage: str | None = None, + ): + """Create an OptionParser for a coverage.py command. + + `action` is the slug to put into `options.action`. + `options` is a list of Option's for the command. + `description` is the description of the command, for the help text. + `usage` is the usage string to display in help. + + """ + if usage: + usage = "%prog " + usage + super().__init__( + usage=usage, + description=description, + ) + self.set_defaults(action=action) + self.add_options(options) + self.cmd = action + + def __eq__(self, other: str) -> bool: # type: ignore[override] + # A convenience equality, so that I can put strings in unit test + # results, and they will compare equal to objects. + return other == f"" + + __hash__ = None # type: ignore[assignment] + + def get_prog_name(self) -> str: + """Override of an undocumented function in optparse.OptionParser.""" + program_name = super().get_prog_name() + + # Include the sub-command for this parser as part of the command. + return f"{program_name} {self.cmd}" + + +# In lists of Opts, keep them alphabetized by the option names as they appear +# on the command line, since these lists determine the order of the options in +# the help output. +# +# In COMMANDS, keep the keys (command names) alphabetized. + +GLOBAL_ARGS = [ + Opts.debug, + Opts.help, + Opts.rcfile, +] + +COMMANDS = { + "annotate": CmdOptionParser( + "annotate", + [ + Opts.directory, + Opts.datafle_input, + Opts.ignore_errors, + Opts.include, + Opts.omit, + ] + + GLOBAL_ARGS, + usage="[options] [modules]", + description=oneline( + """ + Make annotated copies of the given files, marking statements that are executed + with > and statements that are missed with !. + """ + ), + ), + "combine": CmdOptionParser( + "combine", + [ + Opts.append, + Opts.datafile, + Opts.keep, + Opts.quiet, + ] + + GLOBAL_ARGS, + usage="[options] ... ", + description=oneline( + """ + Combine data from multiple coverage files. + The combined results are written to a single + file representing the union of the data. The positional + arguments are data files or directories containing data files. + If no paths are provided, data files in the default data file's + directory are combined. + """ + ), + ), + "debug": CmdOptionParser( + "debug", + GLOBAL_ARGS, + usage="", + description=oneline( + """ + Display information about the internals of coverage.py, + for diagnosing problems. + Topics are: + 'data' to show a summary of the collected data; + 'sys' to show installation information; + 'config' to show the configuration; + 'premain' to show what is calling coverage; + 'pybehave' to show internal flags describing Python behavior; + 'sqlite' to show SQLite compilation options. + """ + ), + ), + "erase": CmdOptionParser( + "erase", + [ + Opts.datafile, + ] + + GLOBAL_ARGS, + description="Erase previously collected coverage data.", + ), + "help": CmdOptionParser( + "help", + GLOBAL_ARGS, + usage="[command]", + description="Describe how to use coverage.py", + ), + "html": CmdOptionParser( + "html", + [ + Opts.contexts, + Opts.directory, + Opts.datafle_input, + Opts.fail_under, + Opts.ignore_errors, + Opts.include, + Opts.omit, + Opts.precision, + Opts.quiet, + Opts.show_contexts, + Opts.skip_covered, + Opts.no_skip_covered, + Opts.skip_empty, + Opts.title, + ] + + GLOBAL_ARGS, + usage="[options] [modules]", + description=oneline( + """ + Create an HTML report of the coverage of the files. + Each file gets its own page, with the source decorated to show + executed, excluded, and missed lines. + """ + ), + ), + "json": CmdOptionParser( + "json", + [ + Opts.contexts, + Opts.datafle_input, + Opts.fail_under, + Opts.ignore_errors, + Opts.include, + Opts.omit, + Opts.output_json, + Opts.json_pretty_print, + Opts.quiet, + Opts.show_contexts, + ] + + GLOBAL_ARGS, + usage="[options] [modules]", + description="Generate a JSON report of coverage results.", + ), + "lcov": CmdOptionParser( + "lcov", + [ + Opts.datafle_input, + Opts.fail_under, + Opts.ignore_errors, + Opts.include, + Opts.output_lcov, + Opts.omit, + Opts.quiet, + ] + + GLOBAL_ARGS, + usage="[options] [modules]", + description="Generate an LCOV report of coverage results.", + ), + "report": CmdOptionParser( + "report", + [ + Opts.contexts, + Opts.datafle_input, + Opts.fail_under, + Opts.format, + Opts.ignore_errors, + Opts.include, + Opts.omit, + Opts.precision, + Opts.sort, + Opts.show_missing, + Opts.skip_covered, + Opts.no_skip_covered, + Opts.skip_empty, + ] + + GLOBAL_ARGS, + usage="[options] [modules]", + description="Report coverage statistics on modules.", + ), + "run": CmdOptionParser( + "run", + [ + Opts.append, + Opts.branch, + Opts.concurrency, + Opts.context, + Opts.datafile_output, + Opts.include, + Opts.module, + Opts.omit, + Opts.pylib, + Opts.parallel_mode, + Opts.save_signal, + Opts.source, + Opts.timid, + ] + + GLOBAL_ARGS, + usage="[options] [program options]", + description="Run a Python program, measuring code execution.", + ), + "xml": CmdOptionParser( + "xml", + [ + Opts.datafle_input, + Opts.fail_under, + Opts.ignore_errors, + Opts.include, + Opts.omit, + Opts.output_xml, + Opts.quiet, + Opts.skip_empty, + ] + + GLOBAL_ARGS, + usage="[options] [modules]", + description="Generate an XML report of coverage results.", + ), +} + + +def show_help( + error: str | None = None, + topic: str | None = None, + parser: optparse.OptionParser | None = None, +) -> None: + """Display an error message, or the named topic.""" + assert error or topic or parser + + program_path = sys.argv[0] + if program_path.endswith(os.path.sep + "__main__.py"): + # The path is the main module of a package; get that path instead. + program_path = os.path.dirname(program_path) + program_name = os.path.basename(program_path) + if env.WINDOWS: + # entry_points={"console_scripts":...} on Windows makes files + # called coverage.exe, coverage3.exe, and coverage-3.5.exe. These + # invoke coverage-script.py, coverage3-script.py, and + # coverage-3.5-script.py. argv[0] is the .py file, but we want to + # get back to the original form. + auto_suffix = "-script.py" + if program_name.endswith(auto_suffix): + program_name = program_name[: -len(auto_suffix)] + + help_params = dict(coverage.__dict__) + help_params["__url__"] = __url__ + help_params["program_name"] = program_name + if CTRACER_FILE: + help_params["extension_modifier"] = "with C extension" + else: + help_params["extension_modifier"] = "without C extension" + + if error: + print(error, file=sys.stderr) + print(f"Use '{program_name} help' for help.", file=sys.stderr) + elif parser: + print(parser.format_help().strip()) + print() + else: + assert topic is not None + help_msg = textwrap.dedent(HELP_TOPICS.get(topic, "")).strip() + if help_msg: + print(help_msg.format(**help_params)) + else: + print(f"Don't know topic {topic!r}") + print("Full documentation is at {__url__}".format(**help_params)) + + +OK, ERR, FAIL_UNDER = 0, 1, 2 + + +class CoverageScript: + """The command-line interface to coverage.py.""" + + def __init__(self) -> None: + self.global_option = False + self.coverage: Coverage + + def command_line(self, argv: list[str]) -> int: + """The bulk of the command line interface to coverage.py. + + `argv` is the argument list to process. + + Returns 0 if all is well, 1 if something went wrong. + + """ + # Collect the command-line options. + if not argv: + show_help(topic="minimum_help") + return OK + + # The command syntax we parse depends on the first argument. Global + # switch syntax always starts with an option. + parser: optparse.OptionParser | None + self.global_option = argv[0].startswith("-") + if self.global_option: + parser = GlobalOptionParser() + else: + parser = COMMANDS.get(argv[0]) + if not parser: + show_help(f"Unknown command: {argv[0]!r}") + return ERR + argv = argv[1:] + + ok, options, args = parser.parse_args_ok(argv) + if not ok: + return ERR + assert options is not None + + # Handle help and version. + if self.do_help(options, args, parser): + return OK + + # Listify the list options. + source = unshell_list(options.source) + omit = unshell_list(options.omit) + include = unshell_list(options.include) + debug = unshell_list(options.debug) + contexts = unshell_list(options.contexts) + + if options.concurrency is not None: + concurrency = options.concurrency.split(",") + else: + concurrency = None + + # Do something. + self.coverage = Coverage( + data_file=options.data_file or DEFAULT_DATAFILE, + data_suffix=options.parallel_mode, + cover_pylib=options.pylib, + timid=options.timid, + branch=options.branch, + config_file=options.rcfile, + source=source, + omit=omit, + include=include, + debug=debug, + concurrency=concurrency, + check_preimported=True, + context=options.context, + messages=not options.quiet, + ) + + if options.action == "debug": + return self.do_debug(args) + + elif options.action == "erase": + self.coverage.erase() + return OK + + elif options.action == "run": + return self.do_run(options, args) + + elif options.action == "combine": + if options.append: + self.coverage.load() + data_paths = args or None + self.coverage.combine(data_paths, strict=True, keep=bool(options.keep)) + self.coverage.save() + return OK + + # Remaining actions are reporting, with some common options. + report_args = dict( + morfs=unglob_args(args), + ignore_errors=options.ignore_errors, + omit=omit, + include=include, + contexts=contexts, + ) + + # We need to be able to import from the current directory, because + # plugins may try to, for example, to read Django settings. + sys.path.insert(0, "") + + self.coverage.load() + + total = None + if options.action == "report": + total = self.coverage.report( + precision=options.precision, + show_missing=options.show_missing, + skip_covered=options.skip_covered, + skip_empty=options.skip_empty, + sort=options.sort, + output_format=options.format, + **report_args, + ) + elif options.action == "annotate": + self.coverage.annotate(directory=options.directory, **report_args) + elif options.action == "html": + total = self.coverage.html_report( + directory=options.directory, + precision=options.precision, + skip_covered=options.skip_covered, + skip_empty=options.skip_empty, + show_contexts=options.show_contexts, + title=options.title, + **report_args, + ) + elif options.action == "xml": + total = self.coverage.xml_report( + outfile=options.outfile, + skip_empty=options.skip_empty, + **report_args, + ) + elif options.action == "json": + total = self.coverage.json_report( + outfile=options.outfile, + pretty_print=options.pretty_print, + show_contexts=options.show_contexts, + **report_args, + ) + elif options.action == "lcov": + total = self.coverage.lcov_report( + outfile=options.outfile, + **report_args, + ) + else: + # There are no other possible actions. + raise AssertionError + + if total is not None: + # Apply the command line fail-under options, and then use the config + # value, so we can get fail_under from the config file. + if options.fail_under is not None: + self.coverage.set_option("report:fail_under", options.fail_under) + if options.precision is not None: + self.coverage.set_option("report:precision", options.precision) + + fail_under = cast(float, self.coverage.get_option("report:fail_under")) + precision = cast(int, self.coverage.get_option("report:precision")) + if should_fail_under(total, fail_under, precision): + msg = "total of {total} is less than fail-under={fail_under:.{p}f}".format( + total=display_covered(total, precision), + fail_under=fail_under, + p=precision, + ) + print("Coverage failure:", msg) + return FAIL_UNDER + + return OK + + def do_help( + self, + options: optparse.Values, + args: list[str], + parser: optparse.OptionParser, + ) -> bool: + """Deal with help requests. + + Return True if it handled the request, False if not. + + """ + # Handle help. + if options.help: + if self.global_option: + show_help(topic="help") + else: + show_help(parser=parser) + return True + + if options.action == "help": + if args: + for a in args: + parser_maybe = COMMANDS.get(a) + if parser_maybe is not None: + show_help(parser=parser_maybe) + else: + show_help(topic=a) + else: + show_help(topic="help") + return True + + # Handle version. + if options.version: + show_help(topic="version") + return True + + return False + + def do_signal_save(self, _signum: int, _frame: types.FrameType | None) -> None: + """Signal handler to save coverage report""" + print("Saving coverage data...", flush=True) + self.coverage.save() + + def do_run(self, options: optparse.Values, args: list[str]) -> int: + """Implementation of 'coverage run'.""" + + if not args: + if options.module: + # Specified -m with nothing else. + show_help("No module specified for -m") + return ERR + command_line = cast(str, self.coverage.get_option("run:command_line")) + if command_line is not None: + args = shlex.split(command_line) + if args and args[0] in {"-m", "--module"}: + options.module = True + args = args[1:] + if not args: + show_help("Nothing to do.") + return ERR + + if options.append and self.coverage.get_option("run:parallel"): + show_help("Can't append to data files in parallel mode.") + return ERR + + if options.concurrency == "multiprocessing": + # Can't set other run-affecting command line options with + # multiprocessing. + for opt_name in ["branch", "include", "omit", "pylib", "source", "timid"]: + # As it happens, all of these options have no default, meaning + # they will be None if they have not been specified. + if getattr(options, opt_name) is not None: + show_help( + "Options affecting multiprocessing must only be specified " + + "in a configuration file.\n" + + f"Remove --{opt_name} from the command line.", + ) + return ERR + + os.environ["COVERAGE_RUN"] = "true" + + runner = PyRunner(args, as_module=bool(options.module)) + runner.prepare() + + if options.append: + self.coverage.load() + + if options.save_signal: + if env.WINDOWS: + show_help("--save-signal is not supported on Windows.") + return ERR + sig = getattr(signal, f"SIG{options.save_signal}") + signal.signal(sig, self.do_signal_save) + + # Run the script. + self.coverage.start() + code_ran = True + try: + runner.run() + except NoSource: + code_ran = False + raise + finally: + self.coverage.stop() + if code_ran: + self.coverage.save() + + return OK + + def do_debug(self, args: list[str]) -> int: + """Implementation of 'coverage debug'.""" + + if not args: + show_help( + "What information would you like: " + + "config, data, sys, premain, pybehave, sqlite?" + ) + return ERR + if args[1:]: + show_help("Only one topic at a time, please") + return ERR + + if args[0] == "sys": + write_formatted_info(print, "sys", self.coverage.sys_info()) + elif args[0] == "data": + print(info_header("data")) + data_file = self.coverage.config.data_file + debug_data_file(data_file) + for filename in combinable_files(data_file): + print("-----") + debug_data_file(filename) + elif args[0] == "config": + write_formatted_info(print, "config", self.coverage.config.debug_info()) + elif args[0] == "premain": + print(info_header("premain")) + print(short_stack(full=True)) + elif args[0] == "pybehave": + write_formatted_info(print, "pybehave", env.debug_info()) + elif args[0] == "sqlite": + write_formatted_info(print, "sqlite", CoverageData.sys_info()) + else: + show_help(f"Don't know what you mean by {args[0]!r}") + return ERR + + return OK + + +def unshell_list(s: str) -> list[str] | None: + """Turn a command-line argument into a list.""" + if not s: + return None + if env.WINDOWS: + # When running coverage.py as coverage.exe, some of the behavior + # of the shell is emulated: wildcards are expanded into a list of + # file names. So you have to single-quote patterns on the command + # line, but (not) helpfully, the single quotes are included in the + # argument, so we have to strip them off here. + s = s.strip("'") + return s.split(",") + + +def unglob_args(args: list[str]) -> list[str]: + """Interpret shell wildcards for platforms that need it.""" + if env.WINDOWS: + globbed = [] + for arg in args: + if "?" in arg or "*" in arg: + globbed.extend(glob.glob(arg)) + else: + globbed.append(arg) + args = globbed + return args + + +HELP_TOPICS = { + "help": """\ + Coverage.py, version {__version__} {extension_modifier} + Measure, collect, and report on code coverage in Python programs. + + usage: {program_name} [options] [args] + + Commands: + annotate Annotate source files with execution information. + combine Combine a number of data files. + debug Display information about the internals of coverage.py + erase Erase previously collected coverage data. + help Get help on using coverage.py. + html Create an HTML report. + json Create a JSON report of coverage results. + lcov Create an LCOV report of coverage results. + report Report coverage stats on modules. + run Run a Python program and measure code execution. + xml Create an XML report of coverage results. + + Use "{program_name} help " for detailed help on any command. + """, + "minimum_help": oneline( + """ + Code coverage for Python, version {__version__} {extension_modifier}. + Use '{program_name} help' for help. + """ + ), + "version": "Coverage.py, version {__version__} {extension_modifier}", +} + + +def main(argv: list[str] | None = None) -> int | None: + """The main entry point to coverage.py. + + This is installed as the script entry point. + + """ + if argv is None: + argv = sys.argv[1:] + try: + status = CoverageScript().command_line(argv) + except _ExceptionDuringRun as err: + # An exception was caught while running the product code. The + # sys.exc_info() return tuple is packed into an _ExceptionDuringRun + # exception. + traceback.print_exception(*err.args) # pylint: disable=no-value-for-parameter + status = ERR + except CoverageException as err: + # A controlled error inside coverage.py: print the message to the user. + msg = err.args[0] + if err.slug: + msg = f"{msg.rstrip('.')}; see {__url__}/messages.html#error-{err.slug}" + print(msg) + status = ERR + except SystemExit as err: + # The user called `sys.exit()`. Exit with their argument, if any. + if err.args: + status = err.args[0] + else: + status = None + return status + + +# Profiling using ox_profile. Install it from GitHub: +# pip install git+https://github.com/emin63/ox_profile.git +# +# $set_env.py: COVERAGE_PROFILE - Set to use ox_profile. +_profile = os.getenv("COVERAGE_PROFILE") +if _profile: # pragma: debugging + from ox_profile.core.launchers import SimpleLauncher # pylint: disable=import-error + + original_main = main + + def main( # pylint: disable=function-redefined + argv: list[str] | None = None, + ) -> int | None: + """A wrapper around main that profiles.""" + profiler = SimpleLauncher.launch() + try: + return original_main(argv) + finally: + data, _ = profiler.query(re_filter="coverage", max_records=100) + print(profiler.show(query=data, limit=100, sep="", col="")) + profiler.cancel() diff --git a/venv/Lib/site-packages/coverage/collector.py b/venv/Lib/site-packages/coverage/collector.py new file mode 100644 index 0000000..45ba7b2 --- /dev/null +++ b/venv/Lib/site-packages/coverage/collector.py @@ -0,0 +1,486 @@ +# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 +# For details: https://github.com/coveragepy/coveragepy/blob/main/NOTICE.txt + +"""Raw data collector for coverage.py.""" + +from __future__ import annotations + +import contextlib +import functools +import os +import sys +from collections.abc import Mapping +from types import FrameType +from typing import Any, Callable, TypeVar, cast + +from coverage import env +from coverage.core import Core +from coverage.data import CoverageData +from coverage.debug import short_stack +from coverage.exceptions import ConfigError +from coverage.misc import human_sorted_items, isolate_module +from coverage.plugin import CoveragePlugin +from coverage.types import ( + TArc, + TCheckIncludeFn, + TFileDisposition, + Tracer, + TShouldStartContextFn, + TShouldTraceFn, + TTraceData, + TTraceFn, + TWarnFn, +) + +os = isolate_module(os) + + +T = TypeVar("T") + + +class Collector: + """Collects trace data. + + Creates a Tracer object for each thread, since they track stack + information. Each Tracer points to the same shared data, contributing + traced data points. + + When the Collector is started, it creates a Tracer for the current thread, + and installs a function to create Tracers for each new thread started. + When the Collector is stopped, all active Tracers are stopped. + + Threads started while the Collector is stopped will never have Tracers + associated with them. + + """ + + # The stack of active Collectors. Collectors are added here when started, + # and popped when stopped. Collectors on the stack are paused when not + # the top, and resumed when they become the top again. + _collectors: list[Collector] = [] + + def __init__( + self, + core: Core, + should_trace: TShouldTraceFn, + check_include: TCheckIncludeFn, + should_start_context: TShouldStartContextFn | None, + file_mapper: Callable[[str], str], + branch: bool, + warn: TWarnFn, + concurrency: list[str], + ) -> None: + """Create a collector. + + `should_trace` is a function, taking a file name and a frame, and + returning a `coverage.FileDisposition object`. + + `check_include` is a function taking a file name and a frame. It returns + a boolean: True if the file should be traced, False if not. + + `should_start_context` is a function taking a frame, and returning a + string. If the frame should be the start of a new context, the string + is the new context. If the frame should not be the start of a new + context, return None. + + `file_mapper` is a function taking a filename, and returning a Unicode + filename. The result is the name that will be recorded in the data + file. + + If `branch` is true, then branches will be measured. This involves + collecting data on which statements followed each other (arcs). Use + `get_arc_data` to get the arc data. + + `warn` is a warning function, taking a single string message argument + and an optional slug argument which will be a string or None, to be + used if a warning needs to be issued. + + `concurrency` is a list of strings indicating the concurrency libraries + in use. Valid values are "greenlet", "eventlet", "gevent", or "thread" + (the default). "thread" can be combined with one of the other three. + Other values are ignored. + + """ + self.core = core + self.should_trace = should_trace + self.check_include = check_include + self.should_start_context = should_start_context + self.file_mapper = file_mapper + self.branch = branch + self.warn = warn + assert isinstance(concurrency, list), f"Expected a list: {concurrency!r}" + + self.pid = os.getpid() + + self.covdata: CoverageData + self.threading = None + self.static_context: str | None = None + + self.origin = short_stack() + + self.concur_id_func = None + + do_threading = False + + tried = "nothing" # to satisfy pylint + try: + if "greenlet" in concurrency: + tried = "greenlet" + import greenlet + + self.concur_id_func = greenlet.getcurrent + elif "eventlet" in concurrency: + tried = "eventlet" + import eventlet.greenthread + + self.concur_id_func = eventlet.greenthread.getcurrent + elif "gevent" in concurrency: + tried = "gevent" + import gevent + + self.concur_id_func = gevent.getcurrent + + if "thread" in concurrency: + do_threading = True + except ImportError as ex: + msg = f"Couldn't trace with concurrency={tried}, the module isn't installed." + raise ConfigError(msg) from ex + + if self.concur_id_func and not hasattr(core.tracer_class, "concur_id_func"): + raise ConfigError( + "Can't support concurrency={} with {}, only threads are supported.".format( + tried, + self.tracer_name(), + ), + ) + + if do_threading or not concurrency: + # It's important to import threading only if we need it. If + # it's imported early, and the program being measured uses + # gevent, then gevent's monkey-patching won't work properly. + import threading + + self.threading = threading + + self.reset() + + def __repr__(self) -> str: + return f"" + + def use_data(self, covdata: CoverageData, context: str | None) -> None: + """Use `covdata` for recording data.""" + self.covdata = covdata + self.static_context = context + self.covdata.set_context(self.static_context) + + def tracer_name(self) -> str: + """Return the class name of the tracer we're using.""" + return self.core.tracer_class.__name__ + + def _clear_data(self) -> None: + """Clear out existing data, but stay ready for more collection.""" + # We used to use self.data.clear(), but that would remove filename + # keys and data values that were still in use higher up the stack + # when we are called as part of switch_context. + with self.data_lock or contextlib.nullcontext(): + for d in self.data.values(): + d.clear() + + for tracer in self.tracers: + tracer.reset_activity() + + def reset(self) -> None: + """Clear collected data, and prepare to collect more.""" + self.data_lock = self.threading.Lock() if self.threading else None + + # The trace data we are collecting. + self.data: TTraceData = {} + + # A dictionary mapping file names to file tracer plugin names that will + # handle them. + self.file_tracers: dict[str, str] = {} + + self.disabled_plugins: set[str] = set() + + # The .should_trace_cache attribute is a cache from file names to + # coverage.FileDisposition objects, or None. When a file is first + # considered for tracing, a FileDisposition is obtained from + # Coverage.should_trace. Its .trace attribute indicates whether the + # file should be traced or not. If it should be, a plugin with dynamic + # file names can decide not to trace it based on the dynamic file name + # being excluded by the inclusion rules, in which case the + # FileDisposition will be replaced by None in the cache. + if env.PYPY: + import __pypy__ # pylint: disable=import-error + + # Alex Gaynor said: + # should_trace_cache is a strictly growing key: once a key is in + # it, it never changes. Further, the keys used to access it are + # generally constant, given sufficient context. That is to say, at + # any given point _trace() is called, pypy is able to know the key. + # This is because the key is determined by the physical source code + # line, and that's invariant with the call site. + # + # This property of a dict with immutable keys, combined with + # call-site-constant keys is a match for PyPy's module dict, + # which is optimized for such workloads. + # + # This gives a 20% benefit on the workload described at + # https://bitbucket.org/pypy/pypy/issue/1871/10x-slower-than-cpython-under-coverage + self.should_trace_cache = __pypy__.newdict("module") + else: + self.should_trace_cache = {} + + # Our active Tracers. + self.tracers: list[Tracer] = [] + + self._clear_data() + + def lock_data(self) -> None: + """Lock self.data_lock, for use by the C tracer.""" + if self.data_lock is not None: + self.data_lock.acquire() + + def unlock_data(self) -> None: + """Unlock self.data_lock, for use by the C tracer.""" + if self.data_lock is not None: + self.data_lock.release() + + def _start_tracer(self) -> TTraceFn | None: + """Start a new Tracer object, and store it in self.tracers.""" + tracer = self.core.tracer_class(**self.core.tracer_kwargs) + tracer.data = self.data + tracer.lock_data = self.lock_data + tracer.unlock_data = self.unlock_data + tracer.trace_arcs = self.branch + tracer.should_trace = self.should_trace + tracer.should_trace_cache = self.should_trace_cache + tracer.warn = self.warn + + if hasattr(tracer, "concur_id_func"): + tracer.concur_id_func = self.concur_id_func + if hasattr(tracer, "file_tracers"): + tracer.file_tracers = self.file_tracers + if hasattr(tracer, "threading"): + tracer.threading = self.threading + if hasattr(tracer, "check_include"): + tracer.check_include = self.check_include + if hasattr(tracer, "should_start_context"): + tracer.should_start_context = self.should_start_context + if hasattr(tracer, "switch_context"): + tracer.switch_context = self.switch_context + if hasattr(tracer, "disable_plugin"): + tracer.disable_plugin = self.disable_plugin + + fn = tracer.start() + self.tracers.append(tracer) + + return fn + + # The trace function has to be set individually on each thread before + # execution begins. Ironically, the only support the threading module has + # for running code before the thread main is the tracing function. So we + # install this as a trace function, and the first time it's called, it does + # the real trace installation. + # + # New in 3.12: threading.settrace_all_threads: https://github.com/python/cpython/pull/96681 + + def _installation_trace(self, frame: FrameType, event: str, arg: Any) -> TTraceFn | None: + """Called on new threads, installs the real tracer.""" + # Remove ourselves as the trace function. + sys.settrace(None) + # Install the real tracer. + fn: TTraceFn | None = self._start_tracer() + # Invoke the real trace function with the current event, to be sure + # not to lose an event. + if fn: + fn = fn(frame, event, arg) + # Return the new trace function to continue tracing in this scope. + return fn + + def start(self) -> None: + """Start collecting trace information.""" + # We may be a new collector in a forked process. The old process' + # collectors will be in self._collectors, but they won't be usable. + # Find them and discard them. + keep_collectors = [] + for c in self._collectors: + if c.pid == self.pid: + keep_collectors.append(c) + else: + c.post_fork() + self._collectors[:] = keep_collectors + + if self._collectors: + self._collectors[-1].pause() + + self.tracers = [] + + try: + # Install the tracer on this thread. + self._start_tracer() + except: + if self._collectors: + self._collectors[-1].resume() + raise + + # If _start_tracer succeeded, then we add ourselves to the global + # stack of collectors. + self._collectors.append(self) + + # Install our installation tracer in threading, to jump-start other + # threads. + if self.core.systrace and self.threading: + self.threading.settrace(self._installation_trace) + + def stop(self) -> None: + """Stop collecting trace information.""" + assert self._collectors + if self._collectors[-1] is not self: + print("self._collectors:") + for c in self._collectors: + print(f" {c!r}\n{c.origin}") + assert self._collectors[-1] is self, ( + f"Expected current collector to be {self!r}, but it's {self._collectors[-1]!r}" + ) + + self.pause() + + # Remove this Collector from the stack, and resume the one underneath (if any). + self._collectors.pop() + if self._collectors: + self._collectors[-1].resume() + + def pause(self) -> None: + """Pause tracing, but be prepared to `resume`.""" + for tracer in self.tracers: + tracer.stop() + stats = tracer.get_stats() + if stats: + print(f"\nCoverage.py {tracer.__class__.__name__} stats:") + for k, v in human_sorted_items(stats.items()): + print(f"{k:>20}: {v}") + if self.threading: + self.threading.settrace(None) + + def resume(self) -> None: + """Resume tracing after a `pause`.""" + for tracer in self.tracers: + tracer.start() + if self.core.systrace: + if self.threading: + self.threading.settrace(self._installation_trace) + else: + self._start_tracer() + + def post_fork(self) -> None: + """After a fork, tracers might need to adjust.""" + for tracer in self.tracers: + if hasattr(tracer, "post_fork"): + tracer.post_fork() + + def _activity(self) -> bool: + """Has any activity been traced? + + Returns a boolean, True if any trace function was invoked. + + """ + return any(tracer.activity() for tracer in self.tracers) + + def switch_context(self, new_context: str | None) -> None: + """Switch to a new dynamic context.""" + context: str | None + self.flush_data() + if self.static_context: + context = self.static_context + if new_context: + context += "|" + new_context + else: + context = new_context + self.covdata.set_context(context) + + def disable_plugin(self, disposition: TFileDisposition) -> None: + """Disable the plugin mentioned in `disposition`.""" + file_tracer = disposition.file_tracer + assert file_tracer is not None + plugin = file_tracer._coverage_plugin + plugin_name = plugin._coverage_plugin_name + self.warn(f"Disabling plug-in {plugin_name!r} due to previous exception") + plugin._coverage_enabled = False + disposition.trace = False + + @functools.cache # pylint: disable=method-cache-max-size-none + def cached_mapped_file(self, filename: str) -> str: + """A locally cached version of file names mapped through file_mapper.""" + return self.file_mapper(filename) + + def mapped_file_dict(self, d: Mapping[str, T]) -> dict[str, T]: + """Return a dict like d, but with keys modified by file_mapper.""" + # The call to list(items()) ensures that the GIL protects the dictionary + # iterator against concurrent modifications by tracers running + # in other threads. We try three times in case of concurrent + # access, hoping to get a clean copy. + runtime_err = None + for _ in range(3): # pragma: part covered + try: + items = list(d.items()) + except RuntimeError as ex: # pragma: cant happen + runtime_err = ex + else: + break + else: # pragma: cant happen + assert isinstance(runtime_err, Exception) + raise runtime_err + + return {self.cached_mapped_file(k): v for k, v in items if v} + + def plugin_was_disabled(self, plugin: CoveragePlugin) -> None: + """Record that `plugin` was disabled during the run.""" + self.disabled_plugins.add(plugin._coverage_plugin_name) + + def flush_data(self) -> bool: + """Save the collected data to our associated `CoverageData`. + + Data may have also been saved along the way. This forces the + last of the data to be saved. + + Returns True if there was data to save, False if not. + """ + if not self._activity(): + return False + + if self.branch: + if self.core.packed_arcs: + # Unpack the line number pairs packed into integers. See + # tracer.c:CTracer_record_pair for the C code that creates + # these packed ints. + arc_data: dict[str, list[TArc]] = {} + packed_data = cast(dict[str, set[int]], self.data) + + # The list() here and in the inner loop are to get a clean copy + # even as tracers are continuing to add data. + for fname, packeds in list(packed_data.items()): + tuples = [] + for packed in list(packeds): + l1 = packed & 0xFFFFF + l2 = (packed & (0xFFFFF << 20)) >> 20 + if packed & (1 << 40): + l1 *= -1 + if packed & (1 << 41): + l2 *= -1 + tuples.append((l1, l2)) + arc_data[fname] = tuples + else: + arc_data = cast(dict[str, list[TArc]], self.data) + self.covdata.add_arcs(self.mapped_file_dict(arc_data)) + else: + line_data = cast(dict[str, set[int]], self.data) + self.covdata.add_lines(self.mapped_file_dict(line_data)) + + file_tracers = { + k: v for k, v in self.file_tracers.items() if v not in self.disabled_plugins + } + self.covdata.add_file_tracers(self.mapped_file_dict(file_tracers)) + + self._clear_data() + return True diff --git a/venv/Lib/site-packages/coverage/config.py b/venv/Lib/site-packages/coverage/config.py new file mode 100644 index 0000000..274d71d --- /dev/null +++ b/venv/Lib/site-packages/coverage/config.py @@ -0,0 +1,731 @@ +# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 +# For details: https://github.com/coveragepy/coveragepy/blob/main/NOTICE.txt + +"""Config file for coverage.py""" + +from __future__ import annotations + +import base64 +import collections +import configparser +import copy +import json +import os +import os.path +import re +from collections.abc import Iterable +from typing import Any, Callable, Final, Mapping + +from coverage.exceptions import ConfigError +from coverage.misc import human_sorted_items, isolate_module, substitute_variables +from coverage.tomlconfig import TomlConfigParser, TomlDecodeError +from coverage.types import ( + TConfigSectionIn, + TConfigSectionOut, + TConfigurable, + TConfigValueIn, + TConfigValueOut, + TPluginConfig, +) + +os = isolate_module(os) + + +class HandyConfigParser(configparser.ConfigParser): + """Our specialization of ConfigParser.""" + + def __init__(self, our_file: bool) -> None: + """Create the HandyConfigParser. + + `our_file` is True if this config file is specifically for coverage, + False if we are examining another config file (tox.ini, setup.cfg) + for possible settings. + """ + + super().__init__(interpolation=None) + self.section_prefixes = ["coverage:"] + if our_file: + self.section_prefixes.append("") + + def read( # type: ignore[override] + self, + filenames: Iterable[str], + encoding_unused: str | None = None, + ) -> list[str]: + """Read a file name as UTF-8 configuration data.""" + return super().read(filenames, encoding="utf-8") + + def real_section(self, section: str) -> str | None: + """Get the actual name of a section.""" + for section_prefix in self.section_prefixes: + real_section = section_prefix + section + has = super().has_section(real_section) + if has: + return real_section + return None + + def has_option(self, section: str, option: str) -> bool: # type: ignore[override] + real_section = self.real_section(section) + if real_section is not None: + return super().has_option(real_section, option) + return False + + def has_section(self, section: str) -> bool: # type: ignore[override] + return bool(self.real_section(section)) + + def options(self, section: str) -> list[str]: # type: ignore[override] + real_section = self.real_section(section) + if real_section is not None: + return super().options(real_section) + raise ConfigError(f"No section: {section!r}") + + def get_section(self, section: str) -> TConfigSectionOut: + """Get the contents of a section, as a dictionary.""" + d: dict[str, TConfigValueOut] = {} + for opt in self.options(section): + d[opt] = self.get(section, opt) + return d + + def get(self, section: str, option: str, *args: Any, **kwargs: Any) -> str: # type: ignore + """Get a value, replacing environment variables also. + + The arguments are the same as `ConfigParser.get`, but in the found + value, ``$WORD`` or ``${WORD}`` are replaced by the value of the + environment variable ``WORD``. + + Returns the finished value. + + """ + for section_prefix in self.section_prefixes: + real_section = section_prefix + section + if super().has_option(real_section, option): + break + else: + raise ConfigError(f"No option {option!r} in section: {section!r}") + + v: str = super().get(real_section, option, *args, **kwargs) + v = substitute_variables(v, os.environ) + return v + + def getfile(self, section: str, option: str) -> str: + """Fix up a file path setting.""" + path = self.get(section, option) + return process_file_value(path) + + def getlist(self, section: str, option: str) -> list[str]: + """Read a list of strings. + + The value of `section` and `option` is treated as a comma- and newline- + separated list of strings. Each value is stripped of white space. + + Returns the list of strings. + + """ + value_list = self.get(section, option) + values = [] + for value_line in value_list.split("\n"): + for value in value_line.split(","): + value = value.strip() + if value: + values.append(value) + return values + + def getregexlist(self, section: str, option: str) -> list[str]: + """Read a list of full-line regexes. + + The value of `section` and `option` is treated as a newline-separated + list of regexes. Each value is stripped of white space. + + Returns the list of strings. + + """ + line_list = self.get(section, option) + return process_regexlist(section, option, line_list.splitlines()) + + +TConfigParser = HandyConfigParser | TomlConfigParser + + +# The default line exclusion regexes. +DEFAULT_EXCLUDE = [ + r"#\s*(pragma|PRAGMA)[:\s]?\s*(no|NO)\s*(cover|COVER)", + r"^\s*(((async )?def .*?)?\)(\s*->.*?)?:\s*)?\.\.\.\s*(#|$)", + r"if (typing\.)?TYPE_CHECKING:", +] + +# The default partial branch regexes, to be modified by the user. +DEFAULT_PARTIAL = [ + r"#\s*(pragma|PRAGMA)[:\s]?\s*(no|NO)\s*(branch|BRANCH)", +] + +# The default partial branch regexes, based on Python semantics. +# These are any Python branching constructs that can't actually execute all +# their branches. +DEFAULT_PARTIAL_ALWAYS = [ + "while (True|1|False|0):", + "if (True|1|False|0):", +] + + +class CoverageConfig(TConfigurable, TPluginConfig): + """Coverage.py configuration. + + The attributes of this class are the various settings that control the + operation of coverage.py. + + """ + + # pylint: disable=too-many-instance-attributes + + def __init__(self) -> None: + """Initialize the configuration attributes to their defaults.""" + # Metadata about the config. + # We tried to read these config files. + self.config_files_attempted: list[str] = [] + # We did read these config files, but maybe didn't find any content for us. + self.config_files_read: list[str] = [] + # The file that gave us our configuration. + self.config_file: str | None = None + self._config_contents: bytes | None = None + + # Defaults for [run] and [report] + self._include = None + self._omit = None + + # Defaults for [run] + self.branch = False + self.command_line: str | None = None + self.concurrency: list[str] = [] + self.context: str | None = None + self.core: str | None = None + self.cover_pylib = False + self.data_file = ".coverage" + self.debug: list[str] = [] + self.debug_file: str | None = None + self.disable_warnings: list[str] = [] + self.dynamic_context: str | None = None + self.parallel = False + self.patch: list[str] = [] + self.plugins: list[str] = [] + self.relative_files = False + self.run_include: list[str] = [] + self.run_omit: list[str] = [] + self.sigterm = False + self.source: list[str] | None = None + self.source_pkgs: list[str] = [] + self.source_dirs: list[str] = [] + self.timid = False + self._crash: str | None = None + + # Defaults for [report] + self.exclude_list = DEFAULT_EXCLUDE[:] + self.exclude_also: list[str] = [] + self.fail_under = 0.0 + self.format: str | None = None + self.ignore_errors = False + self.include_namespace_packages = False + self.report_include: list[str] | None = None + self.report_omit: list[str] | None = None + self.partial_always_list = DEFAULT_PARTIAL_ALWAYS[:] + self.partial_list = DEFAULT_PARTIAL[:] + self.partial_also: list[str] = [] + self.precision = 0 + self.report_contexts: list[str] | None = None + self.show_missing = False + self.skip_covered = False + self.skip_empty = False + self.sort: str | None = None + + # Defaults for [html] + self.extra_css: str | None = None + self.html_dir = "htmlcov" + self.html_skip_covered: bool | None = None + self.html_skip_empty: bool | None = None + self.html_title = "Coverage report" + self.show_contexts = False + + # Defaults for [xml] + self.xml_output = "coverage.xml" + self.xml_package_depth = 99 + + # Defaults for [json] + self.json_output = "coverage.json" + self.json_pretty_print = False + self.json_show_contexts = False + + # Defaults for [lcov] + self.lcov_output = "coverage.lcov" + self.lcov_line_checksums = False + + # Defaults for [paths] + self.paths: dict[str, list[str]] = {} + + # Options for plugins + self.plugin_options: dict[str, TConfigSectionOut] = {} + + MUST_BE_LIST = { + "debug", + "concurrency", + "plugins", + "report_omit", + "report_include", + "run_omit", + "run_include", + "patch", + } + + # File paths to make absolute during serialization. + # The pairs are (config_key, must_exist). + SERIALIZE_ABSPATH = { + ("data_file", False), + ("debug_file", False), + # `source` can be directories or modules, so don't abspath it if it + # doesn't exist. + ("source", True), + ("source_dirs", False), + } + + def from_args(self, **kwargs: TConfigValueIn) -> None: + """Read config values from `kwargs`.""" + for k, v in kwargs.items(): + if v is not None: + if k in self.MUST_BE_LIST and isinstance(v, str): + v = [v] + setattr(self, k, v) + + def from_file(self, filename: str, warn: Callable[[str], None], our_file: bool) -> bool: + """Read configuration from a .rc file. + + `filename` is a file name to read. + + `our_file` is True if this config file is specifically for coverage, + False if we are examining another config file (tox.ini, setup.cfg) + for possible settings. + + Returns True or False, whether the file could be read, and it had some + coverage.py settings in it. + + """ + _, ext = os.path.splitext(filename) + cp: TConfigParser + if ext == ".toml": + cp = TomlConfigParser(our_file) + else: + cp = HandyConfigParser(our_file) + + self.config_files_attempted.append(os.path.abspath(filename)) + + try: + files_read = cp.read(filename) + except (configparser.Error, TomlDecodeError) as err: + raise ConfigError(f"Couldn't read config file {filename}: {err}") from err + if not files_read: + return False + + self.config_files_read.extend(map(os.path.abspath, files_read)) + + any_set = False + try: + for option_spec in self.CONFIG_FILE_OPTIONS: + was_set = self._set_attr_from_config_option(cp, *option_spec) + if was_set: + any_set = True + except ValueError as err: + raise ConfigError(f"Couldn't read config file {filename}: {err}") from err + + # Check that there are no unrecognized options. + all_options = collections.defaultdict(set) + for option_spec in self.CONFIG_FILE_OPTIONS: + section, option = option_spec[1].split(":") + all_options[section].add(option) + + for section, options in all_options.items(): + real_section = cp.real_section(section) + if real_section: + for unknown in set(cp.options(section)) - options: + warn( + "Unrecognized option '[{}] {}=' in config file {}".format( + real_section, + unknown, + filename, + ), + ) + + # [paths] is special + if cp.has_section("paths"): + for option in cp.options("paths"): + self.paths[option] = cp.getlist("paths", option) + any_set = True + + # plugins can have options + for plugin in self.plugins: + if cp.has_section(plugin): + self.plugin_options[plugin] = cp.get_section(plugin) + any_set = True + + # Was this file used as a config file? If it's specifically our file, + # then it was used. If we're piggybacking on someone else's file, + # then it was only used if we found some settings in it. + if our_file: + used = True + else: + used = any_set + + if used: + self.config_file = os.path.abspath(filename) + with open(filename, "rb") as f: + self._config_contents = f.read() + + return used + + def copy(self) -> CoverageConfig: + """Return a copy of the configuration.""" + return copy.deepcopy(self) + + CONCURRENCY_CHOICES: Final[set[str]] = { + "thread", + "gevent", + "greenlet", + "eventlet", + "multiprocessing", + } + + # Mutually exclusive concurrency settings. + LIGHT_THREADS = {"greenlet", "eventlet", "gevent"} + + CONFIG_FILE_OPTIONS = [ + # These are *args for _set_attr_from_config_option: + # (attr, where, type_="") + # + # attr is the attribute to set on the CoverageConfig object. + # where is the section:name to read from the configuration file. + # type_ is the optional type to apply, by using .getTYPE to read the + # configuration value from the file. + # + # [run] + ("branch", "run:branch", "boolean"), + ("command_line", "run:command_line"), + ("concurrency", "run:concurrency", "list"), + ("context", "run:context"), + ("core", "run:core"), + ("cover_pylib", "run:cover_pylib", "boolean"), + ("data_file", "run:data_file", "file"), + ("debug", "run:debug", "list"), + ("debug_file", "run:debug_file", "file"), + ("disable_warnings", "run:disable_warnings", "list"), + ("dynamic_context", "run:dynamic_context"), + ("parallel", "run:parallel", "boolean"), + ("patch", "run:patch", "list"), + ("plugins", "run:plugins", "list"), + ("relative_files", "run:relative_files", "boolean"), + ("run_include", "run:include", "list"), + ("run_omit", "run:omit", "list"), + ("sigterm", "run:sigterm", "boolean"), + ("source", "run:source", "list"), + ("source_pkgs", "run:source_pkgs", "list"), + ("source_dirs", "run:source_dirs", "list"), + ("timid", "run:timid", "boolean"), + ("_crash", "run:_crash"), + # + # [report] + ("exclude_list", "report:exclude_lines", "regexlist"), + ("exclude_also", "report:exclude_also", "regexlist"), + ("fail_under", "report:fail_under", "float"), + ("format", "report:format"), + ("ignore_errors", "report:ignore_errors", "boolean"), + ("include_namespace_packages", "report:include_namespace_packages", "boolean"), + ("partial_always_list", "report:partial_branches_always", "regexlist"), + ("partial_list", "report:partial_branches", "regexlist"), + ("partial_also", "report:partial_also", "regexlist"), + ("precision", "report:precision", "int"), + ("report_contexts", "report:contexts", "list"), + ("report_include", "report:include", "list"), + ("report_omit", "report:omit", "list"), + ("show_missing", "report:show_missing", "boolean"), + ("skip_covered", "report:skip_covered", "boolean"), + ("skip_empty", "report:skip_empty", "boolean"), + ("sort", "report:sort"), + # + # [html] + ("extra_css", "html:extra_css"), + ("html_dir", "html:directory", "file"), + ("html_skip_covered", "html:skip_covered", "boolean"), + ("html_skip_empty", "html:skip_empty", "boolean"), + ("html_title", "html:title"), + ("show_contexts", "html:show_contexts", "boolean"), + # + # [xml] + ("xml_output", "xml:output", "file"), + ("xml_package_depth", "xml:package_depth", "int"), + # + # [json] + ("json_output", "json:output", "file"), + ("json_pretty_print", "json:pretty_print", "boolean"), + ("json_show_contexts", "json:show_contexts", "boolean"), + # + # [lcov] + ("lcov_output", "lcov:output", "file"), + ("lcov_line_checksums", "lcov:line_checksums", "boolean"), + ] + + def _set_attr_from_config_option( + self, + cp: TConfigParser, + attr: str, + where: str, + type_: str = "", + ) -> bool: + """Set an attribute on self if it exists in the ConfigParser. + + Returns True if the attribute was set. + + """ + section, option = where.split(":") + if cp.has_option(section, option): + method = getattr(cp, f"get{type_}") + setattr(self, attr, method(section, option)) + return True + return False + + def get_plugin_options(self, plugin: str) -> TConfigSectionOut: + """Get a dictionary of options for the plugin named `plugin`.""" + return self.plugin_options.get(plugin, {}) + + def set_option(self, option_name: str, value: TConfigValueIn | TConfigSectionIn) -> None: + """Set an option in the configuration. + + `option_name` is a colon-separated string indicating the section and + option name. For example, the ``branch`` option in the ``[run]`` + section of the config file would be indicated with `"run:branch"`. + + `value` is the new value for the option. + + """ + # Special-cased options. + if option_name == "paths": + # This is ugly, but type-checks and ensures the values are close + # to right. + self.paths = {} + assert isinstance(value, Mapping) + for k, v in value.items(): + assert isinstance(v, Iterable) + self.paths[k] = list(v) + return + + # Check all the hard-coded options. + for option_spec in self.CONFIG_FILE_OPTIONS: + attr, where = option_spec[:2] + if where == option_name: + setattr(self, attr, value) + return + + # See if it's a plugin option. + plugin_name, _, key = option_name.partition(":") + if key and plugin_name in self.plugins: + self.plugin_options.setdefault(plugin_name, {})[key] = value # type: ignore[index] + return + + # If we get here, we didn't find the option. + raise ConfigError(f"No such option: {option_name!r}") + + def get_option(self, option_name: str) -> TConfigValueOut | None: + """Get an option from the configuration. + + `option_name` is a colon-separated string indicating the section and + option name. For example, the ``branch`` option in the ``[run]`` + section of the config file would be indicated with `"run:branch"`. + + Returns the value of the option. + + """ + # Special-cased options. + if option_name == "paths": + return self.paths + + # Check all the hard-coded options. + for option_spec in self.CONFIG_FILE_OPTIONS: + attr, where = option_spec[:2] + if where == option_name: + return getattr(self, attr) # type: ignore[no-any-return] + + # See if it's a plugin option. + plugin_name, _, key = option_name.partition(":") + if key and plugin_name in self.plugins: + return self.plugin_options.get(plugin_name, {}).get(key) + + # If we get here, we didn't find the option. + raise ConfigError(f"No such option: {option_name!r}") + + def post_process(self) -> None: + """Make final adjustments to settings to make them usable.""" + self.paths = {k: [process_file_value(f) for f in v] for k, v in self.paths.items()} + + self.exclude_list += self.exclude_also + self.partial_list += self.partial_also + + if "subprocess" in self.patch: + self.parallel = True + + # We can handle a few concurrency options here, but only one at a time. + concurrencies = set(self.concurrency) + unknown = concurrencies - self.CONCURRENCY_CHOICES + if unknown: + show = ", ".join(sorted(unknown)) + raise ConfigError(f"Unknown concurrency choices: {show}") + light_threads = concurrencies & self.LIGHT_THREADS + if len(light_threads) > 1: + show = ", ".join(sorted(light_threads)) + raise ConfigError(f"Conflicting concurrency settings: {show}") + + def debug_info(self) -> list[tuple[str, Any]]: + """Make a list of (name, value) pairs for writing debug info.""" + return human_sorted_items((k, v) for k, v in self.__dict__.items() if not k.startswith("_")) + + def serialize(self) -> str: + """Convert to a string that can be ingested with `deserialize`. + + File paths used by `coverage run` are made absolute to ensure the + deserialized config will refer to the same files. + """ + data = {k: v for k, v in self.__dict__.items() if not k.startswith("_")} + for k, must_exist in self.SERIALIZE_ABSPATH: + abs_fn = abs_path_if_exists if must_exist else os.path.abspath + v = data[k] + if isinstance(v, list): + v = list(map(abs_fn, v)) + elif isinstance(v, str): + v = abs_fn(v) + data[k] = v + return base64.b64encode(json.dumps(data).encode()).decode() + + @classmethod + def deserialize(cls, config_str: str) -> CoverageConfig: + """Take a string from `serialize`, and make a CoverageConfig.""" + data = json.loads(base64.b64decode(config_str.encode()).decode()) + config = cls() + config.__dict__.update(data) + return config + + +def process_file_value(path: str) -> str: + """Make adjustments to a file path to make it usable.""" + return os.path.expanduser(path) + + +def abs_path_if_exists(path: str) -> str: + """os.path.abspath, but only if the path exists.""" + if os.path.exists(path): + return os.path.abspath(path) + else: + return path + + +def process_regexlist(name: str, option: str, values: list[str]) -> list[str]: + """Check the values in a regex list and keep the non-blank ones.""" + value_list = [] + for value in values: + value = value.strip() + try: + re.compile(value) + except re.error as e: + raise ConfigError(f"Invalid [{name}].{option} value {value!r}: {e}") from e + if value: + value_list.append(value) + return value_list + + +def config_files_to_try(config_file: bool | str) -> list[tuple[str, bool, bool]]: + """What config files should we try to read? + + Returns a list of tuples: + (filename, is_our_file, was_file_specified) + """ + + # Some API users were specifying ".coveragerc" to mean the same as + # True, so make it so. + if config_file == ".coveragerc": + config_file = True + specified_file = config_file is not True + if not specified_file: + # No file was specified. Check COVERAGE_RCFILE. + rcfile = os.getenv("COVERAGE_RCFILE") + if rcfile: + config_file = rcfile + specified_file = True + if not specified_file: + # Still no file specified. Default to .coveragerc + config_file = ".coveragerc" + assert isinstance(config_file, str) + files_to_try = [ + (config_file, True, specified_file), + ("setup.cfg", False, False), + ("tox.ini", False, False), + ("pyproject.toml", False, False), + ] + return files_to_try + + +def read_coverage_config( + config_file: bool | str, + warn: Callable[[str], None], + **kwargs: TConfigValueIn, +) -> CoverageConfig: + """Read the coverage.py configuration. + + Arguments: + config_file: a boolean or string, see the `Coverage` class for the + tricky details. + warn: a function to issue warnings. + all others: keyword arguments from the `Coverage` class, used for + setting values in the configuration. + + Returns: + config: + config is a CoverageConfig object read from the appropriate + configuration file. + + """ + # Build the configuration from a number of sources: + # 1) defaults: + config = CoverageConfig() + + # 2) from a file: + if config_file: + files_to_try = config_files_to_try(config_file) + + for fname, our_file, specified_file in files_to_try: + config_read = config.from_file(fname, warn, our_file=our_file) + if config_read: + break + if specified_file: + raise ConfigError(f"Couldn't read {fname!r} as a config file") + + # 3) from environment variables: + env_data_file = os.getenv("COVERAGE_FILE") + if env_data_file: + config.data_file = env_data_file + + # $set_env.py: COVERAGE_DEBUG - Debug options: https://coverage.rtfd.io/cmd.html#debug + debugs = os.getenv("COVERAGE_DEBUG") + if debugs: + config.debug.extend(d.strip() for d in debugs.split(",")) + + # Read the COVERAGE_CORE environment variable for backward compatibility, + # and because we use it in the test suite to pick a specific core. + env_core = os.getenv("COVERAGE_CORE") + if env_core: + config.core = env_core + + # 4) from constructor arguments: + config.from_args(**kwargs) + + # 5) for our benchmark, force settings using a secret environment variable: + force_file = os.getenv("COVERAGE_FORCE_CONFIG") + if force_file: + config.from_file(force_file, warn, our_file=True) + + # Once all the config has been collected, there's a little post-processing + # to do. + config.post_process() + + return config diff --git a/venv/Lib/site-packages/coverage/context.py b/venv/Lib/site-packages/coverage/context.py new file mode 100644 index 0000000..bb4b3a1 --- /dev/null +++ b/venv/Lib/site-packages/coverage/context.py @@ -0,0 +1,74 @@ +# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 +# For details: https://github.com/coveragepy/coveragepy/blob/main/NOTICE.txt + +"""Determine contexts for coverage.py""" + +from __future__ import annotations + +from collections.abc import Sequence +from types import FrameType + +from coverage.types import TShouldStartContextFn + + +def combine_context_switchers( + context_switchers: Sequence[TShouldStartContextFn], +) -> TShouldStartContextFn | None: + """Create a single context switcher from multiple switchers. + + `context_switchers` is a list of functions that take a frame as an + argument and return a string to use as the new context label. + + Returns a function that composites `context_switchers` functions, or None + if `context_switchers` is an empty list. + + When invoked, the combined switcher calls `context_switchers` one-by-one + until a string is returned. The combined switcher returns None if all + `context_switchers` return None. + """ + if not context_switchers: + return None + + if len(context_switchers) == 1: + return context_switchers[0] + + def should_start_context(frame: FrameType) -> str | None: + """The combiner for multiple context switchers.""" + for switcher in context_switchers: + new_context = switcher(frame) + if new_context is not None: + return new_context + return None + + return should_start_context + + +def should_start_context_test_function(frame: FrameType) -> str | None: + """Is this frame calling a test_* function?""" + co_name = frame.f_code.co_name + if co_name.startswith("test") or co_name == "runTest": + return qualname_from_frame(frame) + return None + + +def qualname_from_frame(frame: FrameType) -> str | None: + """Get a qualified name for the code running in `frame`.""" + co = frame.f_code + fname = co.co_name + method = None + if co.co_argcount and co.co_varnames[0] == "self": + self = frame.f_locals.get("self", None) + method = getattr(self, fname, None) + + if method is None: + func = frame.f_globals.get(fname) + if func is None: + return None + return f"{func.__module__}.{fname}" + + func = getattr(method, "__func__", None) + if func is None: + cls = self.__class__ + return f"{cls.__module__}.{cls.__name__}.{fname}" + + return f"{func.__module__}.{func.__qualname__}" diff --git a/venv/Lib/site-packages/coverage/control.py b/venv/Lib/site-packages/coverage/control.py new file mode 100644 index 0000000..81308d3 --- /dev/null +++ b/venv/Lib/site-packages/coverage/control.py @@ -0,0 +1,1481 @@ +# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 +# For details: https://github.com/coveragepy/coveragepy/blob/main/NOTICE.txt + +"""Central control stuff for coverage.py.""" + +from __future__ import annotations + +import atexit +import collections +import contextlib +import datetime +import functools +import os +import os.path +import platform +import signal +import sys +import threading +import time +import warnings +from collections.abc import Iterable, Iterator +from types import FrameType +from typing import IO, Any, Callable, cast + +from coverage import env +from coverage.annotate import AnnotateReporter +from coverage.collector import Collector +from coverage.config import CoverageConfig, read_coverage_config +from coverage.context import combine_context_switchers, should_start_context_test_function +from coverage.core import CTRACER_FILE, Core +from coverage.data import CoverageData, combine_parallel_data +from coverage.debug import ( + DebugControl, + NoDebugging, + relevant_environment_display, + short_stack, + write_formatted_info, +) +from coverage.disposition import disposition_debug_msg +from coverage.exceptions import ConfigError, CoverageException, CoverageWarning, PluginError +from coverage.files import PathAliases, abs_file, relative_filename, set_relative_directory +from coverage.html import HtmlReporter +from coverage.inorout import InOrOut +from coverage.jsonreport import JsonReporter +from coverage.lcovreport import LcovReporter +from coverage.misc import ( + DefaultValue, + bool_or_none, + ensure_dir_for_file, + isolate_module, + join_regex, +) +from coverage.multiproc import patch_multiprocessing +from coverage.patch import apply_patches +from coverage.plugin import FileReporter +from coverage.plugin_support import Plugins, TCoverageInit +from coverage.python import PythonFileReporter +from coverage.report import SummaryReporter +from coverage.report_core import render_report +from coverage.results import Analysis, analysis_from_file_reporter +from coverage.types import ( + FilePath, + TConfigSectionIn, + TConfigurable, + TConfigValueIn, + TConfigValueOut, + TFileDisposition, + TLineNo, + TMorf, +) +from coverage.version import __url__ +from coverage.xmlreport import XmlReporter + +os = isolate_module(os) + + +@contextlib.contextmanager +def override_config(cov: Coverage, **kwargs: TConfigValueIn) -> Iterator[None]: + """Temporarily tweak the configuration of `cov`. + + The arguments are applied to `cov.config` with the `from_args` method. + At the end of the with-statement, the old configuration is restored. + """ + original_config = cov.config + cov.config = cov.config.copy() + try: + cov.config.from_args(**kwargs) + yield + finally: + cov.config = original_config + + +DEFAULT_DATAFILE = DefaultValue("MISSING") +_DEFAULT_DATAFILE = DEFAULT_DATAFILE # Just in case, for backwards compatibility +CONFIG_DATA_PREFIX = ":data:" + + +class Coverage(TConfigurable): + """Programmatic access to coverage.py. + + To use:: + + from coverage import Coverage + + cov = Coverage() + cov.start() + #.. call your code .. + cov.stop() + cov.html_report(directory="covhtml") + + A context manager is available to do the same thing:: + + cov = Coverage() + with cov.collect(): + #.. call your code .. + cov.html_report(directory="covhtml") + + Note: in keeping with Python custom, names starting with underscore are + not part of the public API. They might stop working at any point. Please + limit yourself to documented methods to avoid problems. + + Methods can raise any of the exceptions described in :ref:`api_exceptions`. + + """ + + # The stack of started Coverage instances. + _instances: list[Coverage] = [] + + @classmethod + def current(cls) -> Coverage | None: + """Get the latest started `Coverage` instance, if any. + + Returns: a `Coverage` instance, or None. + + .. versionadded:: 5.0 + + """ + if cls._instances: + return cls._instances[-1] + else: + return None + + def __init__( # pylint: disable=too-many-arguments + self, + data_file: FilePath | DefaultValue | None = DEFAULT_DATAFILE, + data_suffix: str | bool | None = None, + cover_pylib: bool | None = None, + auto_data: bool = False, + timid: bool | None = None, + branch: bool | None = None, + config_file: FilePath | bool = True, + source: Iterable[str] | None = None, + source_pkgs: Iterable[str] | None = None, + source_dirs: Iterable[str] | None = None, + omit: str | Iterable[str] | None = None, + include: str | Iterable[str] | None = None, + debug: Iterable[str] | None = None, + concurrency: str | Iterable[str] | None = None, + check_preimported: bool = False, + context: str | None = None, + messages: bool = False, + plugins: Iterable[Callable[..., None]] | None = None, + ) -> None: + """ + Many of these arguments duplicate and override values that can be + provided in a configuration file. Parameters that are missing here + will use values from the config file. + + `data_file` is the base name of the data file to use. The config value + defaults to ".coverage". None can be provided to prevent writing a data + file. `data_suffix` is appended (with a dot) to `data_file` to create + the final file name. If `data_suffix` is simply True, then a suffix is + created with the machine and process identity included. + + `cover_pylib` is a boolean determining whether Python code installed + with the Python interpreter is measured. This includes the Python + standard library and any packages installed with the interpreter. + + If `auto_data` is true, then any existing data file will be read when + coverage measurement starts, and data will be saved automatically when + measurement stops. + + If `timid` is true, then a slower and simpler trace function will be + used. This is important for some environments where manipulation of + tracing functions breaks the faster trace function. + + If `branch` is true, then branch coverage will be measured in addition + to the usual statement coverage. + + `config_file` determines what configuration file to read: + + * If it is ".coveragerc", it is interpreted as if it were True, + for backward compatibility. + + * If it is a string, it is the name of the file to read. If the + file can't be read, it is an error. + + * If it is True, then a few standard files names are tried + (".coveragerc", "setup.cfg", "tox.ini"). It is not an error for + these files to not be found. + + * If it is False, then no configuration file is read. + + `source` is a list of file paths or package names. Only code located + in the trees indicated by the file paths or package names will be + measured. + + `source_pkgs` is a list of package names. It works the same as + `source`, but can be used to name packages where the name can also be + interpreted as a file path. + + `source_dirs` is a list of file paths. It works the same as + `source`, but raises an error if the path doesn't exist, rather + than being treated as a package name. + + `include` and `omit` are lists of file name patterns. Files that match + `include` will be measured, files that match `omit` will not. Each + will also accept a single string argument. + + `debug` is a list of strings indicating what debugging information is + desired. + + `concurrency` is a string indicating the concurrency library being used + in the measured code. Without this, coverage.py will get incorrect + results if these libraries are in use. Valid strings are "greenlet", + "eventlet", "gevent", "multiprocessing", or "thread" (the default). + This can also be a list of these strings. + + If `check_preimported` is true, then when coverage is started, the + already-imported files will be checked to see if they should be + measured by coverage. Importing measured files before coverage is + started can mean that code is missed. + + `context` is a string to use as the :ref:`static context + ` label for collected data. + + If `messages` is true, some messages will be printed to stdout + indicating what is happening. + + If `plugins` are passed, they are an iterable of function objects + accepting a `reg` object to register plugins, as described in + :ref:`api_plugin`. When they are provided, they will override the + plugins found in the coverage configuration file. + + .. versionadded:: 4.0 + The `concurrency` parameter. + + .. versionadded:: 4.2 + The `concurrency` parameter can now be a list of strings. + + .. versionadded:: 5.0 + The `check_preimported` and `context` parameters. + + .. versionadded:: 5.3 + The `source_pkgs` parameter. + + .. versionadded:: 6.0 + The `messages` parameter. + + .. versionadded:: 7.7 + The `plugins` parameter. + + .. versionadded:: 7.8 + The `source_dirs` parameter. + """ + # Start self.config as a usable default configuration. It will soon be + # replaced with the real configuration. + self.config = CoverageConfig() + + # data_file=None means no disk file at all. data_file missing means + # use the value from the config file. + self._no_disk = data_file is None + if isinstance(data_file, DefaultValue): + data_file = None + if data_file is not None: + data_file = os.fspath(data_file) + + # This is injectable by tests. + self._debug_file: IO[str] | None = None + + self._auto_load = self._auto_save = auto_data + self._data_suffix_specified = data_suffix + + # Is it ok for no data to be collected? + self._warn_no_data = True + self._warn_unimported_source = True + self._warn_preimported_source = check_preimported + self._no_warn_slugs: set[str] = set() + self._messages = messages + + # If we're invoked from a .pth file, we shouldn't try to make another one. + self._make_pth_file = True + + # A record of all the warnings that have been issued. + self._warnings: list[str] = [] + + # Other instance attributes, set with placebos or placeholders. + # More useful objects will be created later. + self._debug: DebugControl = NoDebugging() + self._inorout: InOrOut | None = None + self._plugins: Plugins = Plugins() + self._plugin_override = cast(Iterable[TCoverageInit] | None, plugins) + self._data: CoverageData | None = None + self._data_to_close: list[CoverageData] = [] + self._core: Core | None = None + self._collector: Collector | None = None + self._metacov = False + + self._file_mapper: Callable[[str], str] = abs_file + self._data_suffix = self._run_suffix = None + self._exclude_re: dict[str, str] = {} + self._old_sigterm: Callable[[int, FrameType | None], Any] | None = None + + # State machine variables: + # Have we initialized everything? + self._inited = False + self._inited_for_start = False + # Have we started collecting and not stopped it? + self._started = False + # Should we write the debug output? + self._should_write_debug = True + + # Build our configuration from a number of sources. + if isinstance(config_file, str) and config_file.startswith(CONFIG_DATA_PREFIX): + self.config = CoverageConfig.deserialize(config_file[len(CONFIG_DATA_PREFIX) :]) + else: + if not isinstance(config_file, bool): + config_file = os.fspath(config_file) + self.config = read_coverage_config( + config_file=config_file, + warn=self._warn, + data_file=data_file, + cover_pylib=cover_pylib, + timid=timid, + branch=branch, + parallel=bool_or_none(data_suffix), + source=source, + source_pkgs=source_pkgs, + source_dirs=source_dirs, + run_omit=omit, + run_include=include, + debug=debug, + report_omit=omit, + report_include=include, + concurrency=concurrency, + context=context, + ) + + # If we have subprocess measurement happening automatically, then we + # want any explicit creation of a Coverage object to mean, this process + # is already coverage-aware, so don't auto-measure it. By now, the + # auto-creation of a Coverage object has already happened. But we can + # find it and tell it not to save its data. + if not env.METACOV: + _prevent_sub_process_measurement() + + def _init(self) -> None: + """Set all the initial state. + + This is called by the public methods to initialize state. This lets us + construct a :class:`Coverage` object, then tweak its state before this + function is called. + + """ + if self._inited: + return + + self._inited = True + + # Create and configure the debugging controller. + self._debug = DebugControl(self.config.debug, self._debug_file, self.config.debug_file) + if self._debug.should("process"): + self._debug.write("Coverage._init") + + if "multiprocessing" in (self.config.concurrency or ()): + # Multi-processing uses parallel for the subprocesses, so also use + # it for the main process. + self.config.parallel = True + + # _exclude_re is a dict that maps exclusion list names to compiled regexes. + self._exclude_re = {} + + set_relative_directory() + if self.config.relative_files: + self._file_mapper = relative_filename + + # Load plugins + self._plugins = Plugins(self._debug) + if self._plugin_override: + self._plugins.load_from_callables(self._plugin_override) + else: + self._plugins.load_from_config(self.config.plugins, self.config) + + # Run configuring plugins. + for plugin in self._plugins.configurers: + # We need an object with set_option and get_option. Either self or + # self.config will do. Choosing randomly stops people from doing + # other things with those objects, against the public API. Yes, + # this is a bit childish. :) + plugin.configure([self, self.config][int(time.time()) % 2]) + + def _post_init(self) -> None: + """Stuff to do after everything is initialized.""" + if self._should_write_debug: + self._should_write_debug = False + self._write_startup_debug() + + # "[run] _crash" will raise an exception if the value is close by in + # the call stack, for testing error handling. + if self.config._crash and self.config._crash in short_stack(): + raise RuntimeError(f"Crashing because called by {self.config._crash}") + + def _write_startup_debug(self) -> None: + """Write out debug info at startup if needed.""" + wrote_any = False + with self._debug.without_callers(): + if self._debug.should("config"): + write_formatted_info(self._debug.write, "config", self.config.debug_info()) + wrote_any = True + + if self._debug.should("sys"): + write_formatted_info(self._debug.write, "sys", self.sys_info()) + for plugin in self._plugins: + header = "sys: " + plugin._coverage_plugin_name + write_formatted_info(self._debug.write, header, plugin.sys_info()) + wrote_any = True + + if self._debug.should("pybehave"): + write_formatted_info(self._debug.write, "pybehave", env.debug_info()) + wrote_any = True + + if self._debug.should("sqlite"): + write_formatted_info(self._debug.write, "sqlite", CoverageData.sys_info()) + wrote_any = True + + if wrote_any: + write_formatted_info(self._debug.write, "end", ()) + + def _should_trace(self, filename: str, frame: FrameType) -> TFileDisposition: + """Decide whether to trace execution in `filename`. + + Calls `_should_trace_internal`, and returns the FileDisposition. + + """ + assert self._inorout is not None + disp = self._inorout.should_trace(filename, frame) + if self._debug.should("trace"): + self._debug.write(disposition_debug_msg(disp)) + return disp + + def _check_include_omit_etc(self, filename: str, frame: FrameType) -> bool: + """Check a file name against the include/omit/etc, rules, verbosely. + + Returns a boolean: True if the file should be traced, False if not. + + """ + assert self._inorout is not None + reason = self._inorout.check_include_omit_etc(filename, frame) + if self._debug.should("trace"): + if not reason: + msg = f"Including {filename!r}" + else: + msg = f"Not including {filename!r}: {reason}" + self._debug.write(msg) + + return not reason + + def _warn(self, msg: str, slug: str | None = None, once: bool = False) -> None: + """Use `msg` as a warning. + + For warning suppression, use `slug` as the shorthand. + + If `once` is true, only show this warning once (determined by the + slug.) + + """ + if not self._no_warn_slugs: + self._no_warn_slugs = set(self.config.disable_warnings) + + if slug in self._no_warn_slugs: + # Don't issue the warning + return + + self._warnings.append(msg) + if slug: + msg = f"{msg} ({slug}); see {__url__}/messages.html#warning-{slug}" + if self._debug.should("pid"): + msg = f"[{os.getpid()}] {msg}" + warnings.warn(msg, category=CoverageWarning, stacklevel=2) + + if once: + assert slug is not None + self._no_warn_slugs.add(slug) + + def _message(self, msg: str) -> None: + """Write a message to the user, if configured to do so.""" + if self._messages: + print(msg) + + def get_option(self, option_name: str) -> TConfigValueOut | None: + """Get an option from the configuration. + + `option_name` is a colon-separated string indicating the section and + option name. For example, the ``branch`` option in the ``[run]`` + section of the config file would be indicated with `"run:branch"`. + + Returns the value of the option. The type depends on the option + selected. + + As a special case, an `option_name` of ``"paths"`` will return an + dictionary with the entire ``[paths]`` section value. + + .. versionadded:: 4.0 + + """ + return self.config.get_option(option_name) + + def set_option(self, option_name: str, value: TConfigValueIn | TConfigSectionIn) -> None: + """Set an option in the configuration. + + `option_name` is a colon-separated string indicating the section and + option name. For example, the ``branch`` option in the ``[run]`` + section of the config file would be indicated with ``"run:branch"``. + + `value` is the new value for the option. This should be an + appropriate Python value. For example, use True for booleans, not the + string ``"True"``. + + As an example, calling: + + .. code-block:: python + + cov.set_option("run:branch", True) + + has the same effect as this configuration file: + + .. code-block:: ini + + [run] + branch = True + + As a special case, an `option_name` of ``"paths"`` will replace the + entire ``[paths]`` section. The value should be a dictionary. + + .. versionadded:: 4.0 + + """ + self.config.set_option(option_name, value) + + def load(self) -> None: + """Load previously-collected coverage data from the data file.""" + self._init() + if self._collector is not None: + self._collector.reset() + should_skip = self.config.parallel and not os.path.exists(self.config.data_file) + if not should_skip: + self._init_data(suffix=None) + self._post_init() + if not should_skip: + assert self._data is not None + self._data.read() + + def _init_for_start(self) -> None: + """Initialization for start()""" + # Construct the collector. + concurrency: list[str] = self.config.concurrency + if "multiprocessing" in concurrency: + if self.config.config_file is None: + raise ConfigError("multiprocessing requires a configuration file") + patch_multiprocessing(rcfile=self.config.config_file) + + dycon = self.config.dynamic_context + if not dycon or dycon == "none": + context_switchers = [] + elif dycon == "test_function": + context_switchers = [should_start_context_test_function] + else: + raise ConfigError(f"Don't understand dynamic_context setting: {dycon!r}") + + context_switchers.extend( + plugin.dynamic_context for plugin in self._plugins.context_switchers + ) + + should_start_context = combine_context_switchers(context_switchers) + + self._core = Core( + warn=self._warn, + debug=(self._debug if self._debug.should("core") else None), + config=self.config, + dynamic_contexts=(should_start_context is not None), + metacov=self._metacov, + ) + self._collector = Collector( + core=self._core, + should_trace=self._should_trace, + check_include=self._check_include_omit_etc, + should_start_context=should_start_context, + file_mapper=self._file_mapper, + branch=self.config.branch, + warn=self._warn, + concurrency=concurrency, + ) + + suffix = self._data_suffix_specified + if suffix: + if not isinstance(suffix, str): + # if data_suffix=True, use .machinename.pid.random + suffix = True + elif self.config.parallel: + if suffix is None: + suffix = True + elif not isinstance(suffix, str): + suffix = bool(suffix) + else: + suffix = None + + self._init_data(suffix) + + assert self._data is not None + self._collector.use_data(self._data, self.config.context) + + # Early warning if we aren't going to be able to support plugins. + if self._plugins.file_tracers and not self._core.supports_plugins: + self._warn( + "Plugin file tracers ({}) aren't supported with {}".format( + ", ".join( + plugin._coverage_plugin_name for plugin in self._plugins.file_tracers + ), + self._collector.tracer_name(), + ), + ) + for plugin in self._plugins.file_tracers: + plugin._coverage_enabled = False + + # Create the file classifying substructure. + self._inorout = InOrOut( + config=self.config, + warn=self._warn, + debug=(self._debug if self._debug.should("trace") else None), + include_namespace_packages=self.config.include_namespace_packages, + ) + self._inorout.plugins = self._plugins + self._inorout.disp_class = self._core.file_disposition_class + + # It's useful to write debug info after initing for start. + self._should_write_debug = True + + # Register our clean-up handlers. + atexit.register(self._atexit) + if self.config.sigterm: + is_main = (threading.current_thread() == threading.main_thread()) # fmt: skip + if is_main and not env.WINDOWS: + # The Python docs seem to imply that SIGTERM works uniformly even + # on Windows, but that's not my experience, and this agrees: + # https://stackoverflow.com/questions/35772001/x/35792192#35792192 + self._old_sigterm = signal.signal( # type: ignore[assignment] + signal.SIGTERM, + self._on_sigterm, + ) + + def _init_data(self, suffix: str | bool | None) -> None: + """Create a data file if we don't have one yet.""" + if self._data is None: + # Create the data file. We do this at construction time so that the + # data file will be written into the directory where the process + # started rather than wherever the process eventually chdir'd to. + ensure_dir_for_file(self.config.data_file) + self._data = CoverageData( + basename=self.config.data_file, + suffix=suffix, + warn=self._warn, + debug=self._debug, + no_disk=self._no_disk, + ) + self._data_to_close.append(self._data) + + def start(self) -> None: + """Start measuring code coverage. + + Coverage measurement is only collected in functions called after + :meth:`start` is invoked. Statements in the same scope as + :meth:`start` won't be measured. + + Once you invoke :meth:`start`, you must also call :meth:`stop` + eventually, or your process might not shut down cleanly. + + The :meth:`collect` method is a context manager to handle both + starting and stopping collection. + + """ + self._init() + if not self._inited_for_start: + self._inited_for_start = True + self._init_for_start() + self._post_init() + + assert self._collector is not None + assert self._inorout is not None + + # Issue warnings for possible problems. + self._inorout.warn_conflicting_settings() + + # See if we think some code that would eventually be measured has + # already been imported. + if self._warn_preimported_source: + self._inorout.warn_already_imported_files() + + if self._auto_load: + self.load() + + apply_patches(self, self.config, self._debug, make_pth_file=self._make_pth_file) + + self._collector.start() + self._started = True + self._instances.append(self) + + def stop(self) -> None: + """Stop measuring code coverage.""" + if self._instances: + if self._instances[-1] is self: + self._instances.pop() + if self._started: + assert self._collector is not None + self._collector.stop() + self._started = False + + @contextlib.contextmanager + def collect(self) -> Iterator[None]: + """A context manager to start/stop coverage measurement collection. + + .. versionadded:: 7.3 + + """ + self.start() + try: + yield + finally: + self.stop() # pragma: nested + + def _atexit(self, event: str = "atexit") -> None: + """Clean up on process shutdown.""" + if self._debug.should("process"): + self._debug.write(f"{event}: pid: {os.getpid()}, instance: {self!r}") + if self._started: + self.stop() + if self._auto_save or event == "sigterm": + self.save() + for d in self._data_to_close: + d.close(force=True) + + def _on_sigterm(self, signum_unused: int, frame_unused: FrameType | None) -> None: + """A handler for signal.SIGTERM.""" + self._atexit("sigterm") + # Statements after here won't be seen by metacov because we just wrote + # the data, and are about to kill the process. + signal.signal(signal.SIGTERM, self._old_sigterm) # pragma: not covered + os.kill(os.getpid(), signal.SIGTERM) # pragma: not covered + + def erase(self) -> None: + """Erase previously collected coverage data. + + This removes the in-memory data collected in this session as well as + discarding the data file. + + """ + self._init() + self._post_init() + if self._collector is not None: + self._collector.reset() + self._init_data(suffix=None) + assert self._data is not None + self._data.erase(parallel=self.config.parallel) + self._data = None + self._inited_for_start = False + + def switch_context(self, new_context: str) -> None: + """Switch to a new dynamic context. + + `new_context` is a string to use as the :ref:`dynamic context + ` label for collected data. If a :ref:`static + context ` is in use, the static and dynamic context + labels will be joined together with a pipe character. + + Coverage collection must be started already. + + .. versionadded:: 5.0 + + """ + if not self._started: # pragma: part started + raise CoverageException("Cannot switch context, coverage is not started") + + assert self._collector is not None + if self._collector.should_start_context: + self._warn("Conflicting dynamic contexts", slug="dynamic-conflict", once=True) + + self._collector.switch_context(new_context) + + def clear_exclude(self, which: str = "exclude") -> None: + """Clear the exclude list.""" + self._init() + setattr(self.config, f"{which}_list", []) + self._exclude_regex_stale() + + def exclude(self, regex: str, which: str = "exclude") -> None: + """Exclude source lines from execution consideration. + + A number of lists of regular expressions are maintained. Each list + selects lines that are treated differently during reporting. + + `which` determines which list is modified. The "exclude" list selects + lines that are not considered executable at all. The "partial" list + indicates lines with branches that are not taken. + + `regex` is a regular expression. The regex is added to the specified + list. If any of the regexes in the list is found in a line, the line + is marked for special treatment during reporting. + + """ + self._init() + excl_list = getattr(self.config, f"{which}_list") + excl_list.append(regex) + self._exclude_regex_stale() + + def _exclude_regex_stale(self) -> None: + """Drop all the compiled exclusion regexes, a list was modified.""" + self._exclude_re.clear() + + def _exclude_regex(self, which: str) -> str: + """Return a regex string for the given exclusion list.""" + if which not in self._exclude_re: + excl_list = getattr(self.config, f"{which}_list") + self._exclude_re[which] = join_regex(excl_list) + return self._exclude_re[which] + + def get_exclude_list(self, which: str = "exclude") -> list[str]: + """Return a list of excluded regex strings. + + `which` indicates which list is desired. See :meth:`exclude` for the + lists that are available, and their meaning. + + """ + self._init() + return cast(list[str], getattr(self.config, f"{which}_list")) + + def save(self) -> None: + """Save the collected coverage data to the data file.""" + data = self.get_data() + data.write() + + def _make_aliases(self) -> PathAliases: + """Create a PathAliases from our configuration.""" + aliases = PathAliases( + debugfn=(self._debug.write if self._debug.should("pathmap") else None), + relative=self.config.relative_files, + ) + for paths in self.config.paths.values(): + result = paths[0] + for pattern in paths[1:]: + aliases.add(pattern, result) + return aliases + + def combine( + self, + data_paths: Iterable[str] | None = None, + strict: bool = False, + keep: bool = False, + ) -> None: + """Combine together a number of similarly-named coverage data files. + + All coverage data files whose name starts with `data_file` (from the + coverage() constructor) will be read, and combined together into the + current measurements. + + `data_paths` is a list of files or directories from which data should + be combined. If no list is passed, then the data files from the + directory indicated by the current data file (probably the current + directory) will be combined. + + If `strict` is true, then it is an error to attempt to combine when + there are no data files to combine. + + If `keep` is true, then original input data files won't be deleted. + + .. versionadded:: 4.0 + The `data_paths` parameter. + + .. versionadded:: 4.3 + The `strict` parameter. + + .. versionadded: 5.5 + The `keep` parameter. + """ + self._init() + self._init_data(suffix=None) + self._post_init() + self.get_data() + + assert self._data is not None + combine_parallel_data( + self._data, + aliases=self._make_aliases(), + data_paths=data_paths, + strict=strict, + keep=keep, + message=self._message, + ) + + def get_data(self) -> CoverageData: + """Get the collected data. + + Also warn about various problems collecting data. + + Returns a :class:`coverage.CoverageData`, the collected coverage data. + + .. versionadded:: 4.0 + + """ + self._init() + self._init_data(suffix=None) + self._post_init() + + if self._collector is not None: + for plugin in self._plugins: + if not plugin._coverage_enabled: + self._collector.plugin_was_disabled(plugin) + + if self._collector.flush_data(): + self._post_save_work() + + assert self._data is not None + return self._data + + def _post_save_work(self) -> None: + """After saving data, look for warnings, post-work, etc. + + Warn about things that should have happened but didn't. + Look for un-executed files. + + """ + assert self._data is not None + assert self._inorout is not None + + # If there are still entries in the source_pkgs_unmatched list, + # then we never encountered those packages. + if self._warn_unimported_source: + self._inorout.warn_unimported_source() + + # Find out if we got any data. + if not self._data and self._warn_no_data: + self._warn("No data was collected.", slug="no-data-collected") + + # Touch all the files that could have executed, so that we can + # mark completely un-executed files as 0% covered. + file_paths = collections.defaultdict(list) + for file_path, plugin_name in self._inorout.find_possibly_unexecuted_files(): + file_path = self._file_mapper(file_path) + file_paths[plugin_name].append(file_path) + for plugin_name, paths in file_paths.items(): + self._data.touch_files(paths, plugin_name) + + # Backward compatibility with version 1. + def analysis(self, morf: TMorf) -> tuple[str, list[TLineNo], list[TLineNo], str]: + """Like `analysis2` but doesn't return excluded line numbers.""" + f, s, _, m, mf = self.analysis2(morf) + return f, s, m, mf + + def analysis2( + self, + morf: TMorf, + ) -> tuple[str, list[TLineNo], list[TLineNo], list[TLineNo], str]: + """Analyze a module. + + `morf` is a module or a file name. It will be analyzed to determine + its coverage statistics. The return value is a 5-tuple: + + * The file name for the module. + * A list of line numbers of executable statements. + * A list of line numbers of excluded statements. + * A list of line numbers of statements not run (missing from + execution). + * A readable formatted string of the missing line numbers. + + The analysis uses the source file itself and the current measured + coverage data. + + """ + analysis = self._analyze(morf) + return ( + analysis.filename, + sorted(analysis.statements), + sorted(analysis.excluded), + sorted(analysis.missing), + analysis.missing_formatted(), + ) + + @functools.lru_cache(maxsize=1) + def _analyze(self, morf: TMorf) -> Analysis: + """Analyze a module or file. Private for now.""" + self._init() + self._post_init() + + data = self.get_data() + file_reporter = self._get_file_reporter(morf) + filename = self._file_mapper(file_reporter.filename) + return analysis_from_file_reporter(data, self.config.precision, file_reporter, filename) + + def branch_stats(self, morf: TMorf) -> dict[TLineNo, tuple[int, int]]: + """Get branch statistics about a module. + + `morf` is a module or a file name. + + Returns a dict mapping line numbers to a tuple: + (total_exits, taken_exits). + + .. versionadded:: 7.7 + + """ + analysis = self._analyze(morf) + return analysis.branch_stats() + + @functools.lru_cache(maxsize=1) + def _get_file_reporter(self, morf: TMorf) -> FileReporter: + """Get a FileReporter for a module or file name.""" + assert self._data is not None + plugin = None + file_reporter: str | FileReporter = "python" + + if isinstance(morf, str): + mapped_morf = self._file_mapper(morf) + plugin_name = self._data.file_tracer(mapped_morf) + if plugin_name: + plugin = self._plugins.get(plugin_name) + + if plugin: + file_reporter = plugin.file_reporter(mapped_morf) + if file_reporter is None: + raise PluginError( + "Plugin {!r} did not provide a file reporter for {!r}.".format( + plugin._coverage_plugin_name, + morf, + ), + ) + + if file_reporter == "python": + file_reporter = PythonFileReporter(morf, self) + + assert isinstance(file_reporter, FileReporter) + return file_reporter + + def _get_file_reporters( + self, + morfs: Iterable[TMorf] | None = None, + ) -> list[tuple[FileReporter, TMorf]]: + """Get FileReporters for a list of modules or file names. + + For each module or file name in `morfs`, find a FileReporter. Return + a list pairing FileReporters with the morfs. + + If `morfs` is a single module or file name, this returns a list of one + FileReporter. If `morfs` is empty or None, then the list of all files + measured is used to find the FileReporters. + + """ + assert self._data is not None + if not morfs: + morfs = self._data.measured_files() + + # Be sure we have a collection. + if not isinstance(morfs, (list, tuple, set)): + morfs = [morfs] # type: ignore[list-item] + + morfs = sorted(morfs, key=lambda m: m if isinstance(m, str) else m.__name__) + return [(self._get_file_reporter(morf), morf) for morf in morfs] + + def _prepare_data_for_reporting(self) -> None: + """Re-map data before reporting, to get implicit "combine" behavior.""" + if self.config.paths: + mapped_data = CoverageData(warn=self._warn, debug=self._debug, no_disk=True) + if self._data is not None: + mapped_data.update(self._data, map_path=self._make_aliases().map) + self._data = mapped_data + self._data_to_close.append(mapped_data) + + def report( + self, + morfs: Iterable[TMorf] | None = None, + show_missing: bool | None = None, + ignore_errors: bool | None = None, + file: IO[str] | None = None, + omit: str | list[str] | None = None, + include: str | list[str] | None = None, + skip_covered: bool | None = None, + contexts: list[str] | None = None, + skip_empty: bool | None = None, + precision: int | None = None, + sort: str | None = None, + output_format: str | None = None, + ) -> float: + """Write a textual summary report to `file`. + + Each module in `morfs` is listed, with counts of statements, executed + statements, missing statements, and a list of lines missed. + + If `show_missing` is true, then details of which lines or branches are + missing will be included in the report. If `ignore_errors` is true, + then a failure while reporting a single file will not stop the entire + report. + + `file` is a file-like object, suitable for writing. + + `output_format` determines the format, either "text" (the default), + "markdown", or "total". + + `include` is a list of file name patterns. Files that match will be + included in the report. Files matching `omit` will not be included in + the report. + + If `skip_covered` is true, don't report on files with 100% coverage. + + If `skip_empty` is true, don't report on empty files (those that have + no statements). + + `contexts` is a list of regular expression strings. Only data from + :ref:`dynamic contexts ` that match one of those + expressions (using :func:`re.search `) will be + included in the report. + + `precision` is the number of digits to display after the decimal + point for percentages. + + All of the arguments default to the settings read from the + :ref:`configuration file `. + + Returns a float, the total percentage covered. + + .. versionadded:: 4.0 + The `skip_covered` parameter. + + .. versionadded:: 5.0 + The `contexts` and `skip_empty` parameters. + + .. versionadded:: 5.2 + The `precision` parameter. + + .. versionadded:: 7.0 + The `format` parameter. + + """ + self._prepare_data_for_reporting() + with override_config( + self, + ignore_errors=ignore_errors, + report_omit=omit, + report_include=include, + show_missing=show_missing, + skip_covered=skip_covered, + report_contexts=contexts, + skip_empty=skip_empty, + precision=precision, + sort=sort, + format=output_format, + ): + reporter = SummaryReporter(self) + return reporter.report(morfs, outfile=file) + + def annotate( + self, + morfs: Iterable[TMorf] | None = None, + directory: str | None = None, + ignore_errors: bool | None = None, + omit: str | list[str] | None = None, + include: str | list[str] | None = None, + contexts: list[str] | None = None, + ) -> None: + """Annotate a list of modules. + + Each module in `morfs` is annotated. The source is written to a new + file, named with a ",cover" suffix, with each line prefixed with a + marker to indicate the coverage of the line. Covered lines have ">", + excluded lines have "-", and missing lines have "!". + + See :meth:`report` for other arguments. + + """ + self._prepare_data_for_reporting() + with override_config( + self, + ignore_errors=ignore_errors, + report_omit=omit, + report_include=include, + report_contexts=contexts, + ): + reporter = AnnotateReporter(self) + reporter.report(morfs, directory=directory) + + def html_report( + self, + morfs: Iterable[TMorf] | None = None, + directory: str | None = None, + ignore_errors: bool | None = None, + omit: str | list[str] | None = None, + include: str | list[str] | None = None, + extra_css: str | None = None, + title: str | None = None, + skip_covered: bool | None = None, + show_contexts: bool | None = None, + contexts: list[str] | None = None, + skip_empty: bool | None = None, + precision: int | None = None, + ) -> float: + """Generate an HTML report. + + The HTML is written to `directory`. The file "index.html" is the + overview starting point, with links to more detailed pages for + individual modules. + + `extra_css` is a path to a file of other CSS to apply on the page. + It will be copied into the HTML directory. + + `title` is a text string (not HTML) to use as the title of the HTML + report. + + See :meth:`report` for other arguments. + + Returns a float, the total percentage covered. + + .. note:: + + The HTML report files are generated incrementally based on the + source files and coverage results. If you modify the report files, + the changes will not be considered. You should be careful about + changing the files in the report folder. + + """ + self._prepare_data_for_reporting() + with override_config( + self, + ignore_errors=ignore_errors, + report_omit=omit, + report_include=include, + html_dir=directory, + extra_css=extra_css, + html_title=title, + html_skip_covered=skip_covered, + show_contexts=show_contexts, + report_contexts=contexts, + html_skip_empty=skip_empty, + precision=precision, + ): + reporter = HtmlReporter(self) + return reporter.report(morfs) + + def xml_report( + self, + morfs: Iterable[TMorf] | None = None, + outfile: str | None = None, + ignore_errors: bool | None = None, + omit: str | list[str] | None = None, + include: str | list[str] | None = None, + contexts: list[str] | None = None, + skip_empty: bool | None = None, + ) -> float: + """Generate an XML report of coverage results. + + The report is compatible with Cobertura reports. + + Each module in `morfs` is included in the report. `outfile` is the + path to write the file to, "-" will write to stdout. + + See :meth:`report` for other arguments. + + Returns a float, the total percentage covered. + + """ + self._prepare_data_for_reporting() + with override_config( + self, + ignore_errors=ignore_errors, + report_omit=omit, + report_include=include, + xml_output=outfile, + report_contexts=contexts, + skip_empty=skip_empty, + ): + return render_report(self.config.xml_output, XmlReporter(self), morfs, self._message) + + def json_report( + self, + morfs: Iterable[TMorf] | None = None, + outfile: str | None = None, + ignore_errors: bool | None = None, + omit: str | list[str] | None = None, + include: str | list[str] | None = None, + contexts: list[str] | None = None, + pretty_print: bool | None = None, + show_contexts: bool | None = None, + ) -> float: + """Generate a JSON report of coverage results. + + Each module in `morfs` is included in the report. `outfile` is the + path to write the file to, "-" will write to stdout. + + `pretty_print` is a boolean, whether to pretty-print the JSON output or not. + + See :meth:`report` for other arguments. + + Returns a float, the total percentage covered. + + .. versionadded:: 5.0 + + """ + self._prepare_data_for_reporting() + with override_config( + self, + ignore_errors=ignore_errors, + report_omit=omit, + report_include=include, + json_output=outfile, + report_contexts=contexts, + json_pretty_print=pretty_print, + json_show_contexts=show_contexts, + ): + return render_report(self.config.json_output, JsonReporter(self), morfs, self._message) + + def lcov_report( + self, + morfs: Iterable[TMorf] | None = None, + outfile: str | None = None, + ignore_errors: bool | None = None, + omit: str | list[str] | None = None, + include: str | list[str] | None = None, + contexts: list[str] | None = None, + ) -> float: + """Generate an LCOV report of coverage results. + + Each module in `morfs` is included in the report. `outfile` is the + path to write the file to, "-" will write to stdout. + + See :meth:`report` for other arguments. + + .. versionadded:: 6.3 + """ + self._prepare_data_for_reporting() + with override_config( + self, + ignore_errors=ignore_errors, + report_omit=omit, + report_include=include, + lcov_output=outfile, + report_contexts=contexts, + ): + return render_report(self.config.lcov_output, LcovReporter(self), morfs, self._message) + + def sys_info(self) -> Iterable[tuple[str, Any]]: + """Return a list of (key, value) pairs showing internal information.""" + + import coverage as covmod + + self._init() + self._post_init() + + def plugin_info(plugins: list[Any]) -> list[str]: + """Make an entry for the sys_info from a list of plug-ins.""" + entries = [] + for plugin in plugins: + entry = plugin._coverage_plugin_name + if not plugin._coverage_enabled: + entry += " (disabled)" + entries.append(entry) + return entries + + info = [ + ("coverage_version", covmod.__version__), + ("coverage_module", covmod.__file__), + ("core", self._collector.tracer_name() if self._collector is not None else "-none-"), + ("CTracer", f"available from {CTRACER_FILE}" if CTRACER_FILE else "unavailable"), + ("plugins.file_tracers", plugin_info(self._plugins.file_tracers)), + ("plugins.configurers", plugin_info(self._plugins.configurers)), + ("plugins.context_switchers", plugin_info(self._plugins.context_switchers)), + ("configs_attempted", self.config.config_files_attempted), + ("configs_read", self.config.config_files_read), + ("config_file", self.config.config_file), + ( + "config_contents", + repr(self.config._config_contents) if self.config._config_contents else "-none-", + ), + ("data_file", self._data.data_filename() if self._data is not None else "-none-"), + ("python", sys.version.replace("\n", "")), + ("platform", platform.platform()), + ("implementation", platform.python_implementation()), + ("build", platform.python_build()), + ("gil_enabled", getattr(sys, "_is_gil_enabled", lambda: True)()), + ("executable", sys.executable), + ("def_encoding", sys.getdefaultencoding()), + ("fs_encoding", sys.getfilesystemencoding()), + ("pid", os.getpid()), + ("cwd", os.getcwd()), + ("path", sys.path), + ("environment", [f"{k} = {v}" for k, v in relevant_environment_display(os.environ)]), + ("command_line", " ".join(getattr(sys, "argv", ["-none-"]))), + ("time", f"{datetime.datetime.now():%Y-%m-%d %H:%M:%S}"), + ] + + if self._inorout is not None: + info.extend(self._inorout.sys_info()) + + return info + + +# Mega debugging... +# $set_env.py: COVERAGE_DEBUG_CALLS - Lots and lots of output about calls to Coverage. +if int(os.getenv("COVERAGE_DEBUG_CALLS", 0)): # pragma: debugging + from coverage.debug import decorate_methods, show_calls + + Coverage = decorate_methods( # type: ignore[misc] + show_calls(show_args=True), + butnot=["get_data"], + )(Coverage) + + +def process_startup(*, force: bool = False) -> Coverage | None: + """Call this at Python start-up to perhaps measure coverage. + + If the environment variable COVERAGE_PROCESS_START is defined, coverage + measurement is started. The value of the variable is the config file + to use. + + For details, see https://coverage.readthedocs.io/en/latest/subprocess.html. + + Returns the :class:`Coverage` instance that was started, or None if it was + not started by this call. + + """ + config_data = os.getenv("COVERAGE_PROCESS_CONFIG") + cps = os.getenv("COVERAGE_PROCESS_START") + if config_data is not None: + config_file = CONFIG_DATA_PREFIX + config_data + elif cps is not None: + config_file = cps + else: + # No request for coverage, nothing to do. + return None + + # This function can be called more than once in a process. This happens + # because some virtualenv configurations make the same directory visible + # twice in sys.path. This means that the .pth file will be found twice, + # and executed twice, executing this function twice. We set a global + # flag (an attribute on this function) to indicate that coverage.py has + # already been started, so we can avoid doing it twice. + # + # https://github.com/coveragepy/coveragepy/issues/340 has more details. + + if not force and hasattr(process_startup, "coverage"): + # We've annotated this function before, so we must have already + # auto-started coverage.py in this process. Nothing to do. + return None + + cov = Coverage(config_file=config_file) + process_startup.coverage = cov # type: ignore[attr-defined] + cov._warn_no_data = False + cov._warn_unimported_source = False + cov._warn_preimported_source = False + cov._auto_save = True + cov._make_pth_file = False + cov.start() + + return cov + + +def _after_fork_in_child() -> None: + """Used by patch=fork in the child process to restart coverage.""" + if cov := Coverage.current(): + cov.stop() + process_startup(force=True) + + +def _prevent_sub_process_measurement() -> None: + """Stop any subprocess auto-measurement from writing data.""" + auto_created_coverage = getattr(process_startup, "coverage", None) + if auto_created_coverage is not None: + auto_created_coverage._auto_save = False diff --git a/venv/Lib/site-packages/coverage/core.py b/venv/Lib/site-packages/coverage/core.py new file mode 100644 index 0000000..64616a6 --- /dev/null +++ b/venv/Lib/site-packages/coverage/core.py @@ -0,0 +1,139 @@ +# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 +# For details: https://github.com/coveragepy/coveragepy/blob/main/NOTICE.txt + +"""Management of core choices.""" + +from __future__ import annotations + +import os +import sys +from typing import Any + +from coverage import env +from coverage.config import CoverageConfig +from coverage.disposition import FileDisposition +from coverage.exceptions import ConfigError +from coverage.misc import isolate_module +from coverage.pytracer import PyTracer +from coverage.sysmon import SysMonitor +from coverage.types import TDebugCtl, TFileDisposition, Tracer, TWarnFn + +os = isolate_module(os) + +IMPORT_ERROR: str = "" + +try: + # Use the C extension code when we can, for speed. + import coverage.tracer + + CTRACER_FILE: str | None = getattr(coverage.tracer, "__file__", "unknown") +except ImportError as imp_err: + # Couldn't import the C extension, maybe it isn't built. + # We still need to check the environment variable directly here, + # as this code runs before configuration is loaded. + if os.getenv("COVERAGE_CORE") == "ctrace": # pragma: part covered + # During testing, we use the COVERAGE_CORE environment variable + # to indicate that we've fiddled with the environment to test this + # fallback code. If we thought we had a C tracer, but couldn't import + # it, then exit quickly and clearly instead of dribbling confusing + # errors. I'm using sys.exit here instead of an exception because an + # exception here causes all sorts of other noise in unittest. + sys.stderr.write("*** COVERAGE_CORE is 'ctrace' but can't import CTracer!\n") + sys.exit(1) + IMPORT_ERROR = str(imp_err) + CTRACER_FILE = None + + +class Core: + """Information about the central technology enabling execution measurement.""" + + tracer_class: type[Tracer] + tracer_kwargs: dict[str, Any] + file_disposition_class: type[TFileDisposition] + supports_plugins: bool + packed_arcs: bool + systrace: bool + + def __init__( + self, + *, + warn: TWarnFn, + debug: TDebugCtl | None, + config: CoverageConfig, + dynamic_contexts: bool, + metacov: bool, + ) -> None: + def _debug(msg: str) -> None: + if debug: + debug.write(msg) + + _debug("in core.py") + + # Check the conditions that preclude us from using sys.monitoring. + reason_no_sysmon = "" + if not env.PYBEHAVIOR.pep669: + reason_no_sysmon = "sys.monitoring isn't available in this version" + elif config.branch and not env.PYBEHAVIOR.branch_right_left: + reason_no_sysmon = "sys.monitoring can't measure branches in this version" + elif dynamic_contexts: + reason_no_sysmon = "it doesn't yet support dynamic contexts" + elif any((bad := c) in config.concurrency for c in ["greenlet", "eventlet", "gevent"]): + reason_no_sysmon = f"it doesn't support concurrency={bad}" + + core_name: str | None = None + if config.timid: + core_name = "pytrace" + _debug("core.py: Using pytrace because timid=True") + elif core_name is None: + # This could still leave core_name as None. + core_name = config.core + _debug(f"core.py: core from config is {core_name!r}") + + if core_name == "sysmon" and reason_no_sysmon: + _debug(f"core.py: defaulting because sysmon not usable: {reason_no_sysmon}") + warn(f"Can't use core=sysmon: {reason_no_sysmon}, using default core", slug="no-sysmon") + core_name = None + + if core_name is None: + if env.SYSMON_DEFAULT and not reason_no_sysmon: + core_name = "sysmon" + _debug("core.py: Using sysmon because SYSMON_DEFAULT is set") + else: + core_name = "ctrace" + _debug("core.py: Defaulting to ctrace core") + + if core_name == "ctrace": + if not CTRACER_FILE: + if IMPORT_ERROR and env.SHIPPING_WHEELS: + warn(f"Couldn't import C tracer: {IMPORT_ERROR}", slug="no-ctracer", once=True) + core_name = "pytrace" + _debug("core.py: Falling back to pytrace because C tracer not available") + + _debug(f"core.py: Using core={core_name}") + + self.tracer_kwargs = {} + + if core_name == "sysmon": + self.tracer_class = SysMonitor + self.tracer_kwargs["tool_id"] = 3 if metacov else 1 + self.file_disposition_class = FileDisposition + self.supports_plugins = False + self.packed_arcs = False + self.systrace = False + elif core_name == "ctrace": + self.tracer_class = coverage.tracer.CTracer + self.file_disposition_class = coverage.tracer.CFileDisposition + self.supports_plugins = True + self.packed_arcs = True + self.systrace = True + elif core_name == "pytrace": + self.tracer_class = PyTracer + self.file_disposition_class = FileDisposition + self.supports_plugins = False + self.packed_arcs = False + self.systrace = True + else: + raise ConfigError(f"Unknown core value: {core_name!r}") + + def __repr__(self) -> str: + return f"" diff --git a/venv/Lib/site-packages/coverage/data.py b/venv/Lib/site-packages/coverage/data.py new file mode 100644 index 0000000..4310470 --- /dev/null +++ b/venv/Lib/site-packages/coverage/data.py @@ -0,0 +1,227 @@ +# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 +# For details: https://github.com/coveragepy/coveragepy/blob/main/NOTICE.txt + +"""Coverage data for coverage.py. + +This file had the 4.x JSON data support, which is now gone. This file still +has storage-agnostic helpers, and is kept to avoid changing too many imports. +CoverageData is now defined in sqldata.py, and imported here to keep the +imports working. + +""" + +from __future__ import annotations + +import functools +import glob +import hashlib +import os.path +from collections.abc import Iterable +from typing import Callable + +from coverage.exceptions import CoverageException, NoDataError +from coverage.files import PathAliases +from coverage.misc import Hasher, file_be_gone, human_sorted, plural +from coverage.sqldata import CoverageData as CoverageData # pylint: disable=useless-import-alias + + +def line_counts(data: CoverageData, fullpath: bool = False) -> dict[str, int]: + """Return a dict summarizing the line coverage data. + + Keys are based on the file names, and values are the number of executed + lines. If `fullpath` is true, then the keys are the full pathnames of + the files, otherwise they are the basenames of the files. + + Returns a dict mapping file names to counts of lines. + + """ + summ = {} + filename_fn: Callable[[str], str] + if fullpath: + # pylint: disable=unnecessary-lambda-assignment + filename_fn = lambda f: f + else: + filename_fn = os.path.basename + for filename in data.measured_files(): + lines = data.lines(filename) + assert lines is not None + summ[filename_fn(filename)] = len(lines) + return summ + + +def add_data_to_hash(data: CoverageData, filename: str, hasher: Hasher) -> None: + """Contribute `filename`'s data to the `hasher`. + + `hasher` is a `coverage.misc.Hasher` instance to be updated with + the file's data. It should only get the results data, not the run + data. + + """ + if data.has_arcs(): + hasher.update(sorted(data.arcs(filename) or [])) + else: + hasher.update(sorted_lines(data, filename)) + hasher.update(data.file_tracer(filename)) + + +def combinable_files(data_file: str, data_paths: Iterable[str] | None = None) -> list[str]: + """Make a list of data files to be combined. + + `data_file` is a path to a data file. `data_paths` is a list of files or + directories of files. + + Returns a list of absolute file paths. + """ + data_dir, local = os.path.split(os.path.abspath(data_file)) + + data_paths = data_paths or [data_dir] + files_to_combine = [] + for p in data_paths: + if os.path.isfile(p): + files_to_combine.append(os.path.abspath(p)) + elif os.path.isdir(p): + pattern = glob.escape(os.path.join(os.path.abspath(p), local)) + ".*" + files_to_combine.extend(glob.glob(pattern)) + else: + raise NoDataError(f"Couldn't combine from non-existent path '{p}'") + + # SQLite might have made journal files alongside our database files. + # We never want to combine those. + files_to_combine = [fnm for fnm in files_to_combine if not fnm.endswith("-journal")] + + # Sorting isn't usually needed, since it shouldn't matter what order files + # are combined, but sorting makes tests more predictable, and makes + # debugging more understandable when things go wrong. + return sorted(files_to_combine) + + +def combine_parallel_data( + data: CoverageData, + aliases: PathAliases | None = None, + data_paths: Iterable[str] | None = None, + strict: bool = False, + keep: bool = False, + message: Callable[[str], None] | None = None, +) -> None: + """Combine a number of data files together. + + `data` is a CoverageData. + + Treat `data.filename` as a file prefix, and combine the data from all + of the data files starting with that prefix plus a dot. + + If `aliases` is provided, it's a `PathAliases` object that is used to + re-map paths to match the local machine's. + + If `data_paths` is provided, it is a list of directories or files to + combine. Directories are searched for files that start with + `data.filename` plus dot as a prefix, and those files are combined. + + If `data_paths` is not provided, then the directory portion of + `data.filename` is used as the directory to search for data files. + + Unless `keep` is True every data file found and combined is then deleted + from disk. If a file cannot be read, a warning will be issued, and the + file will not be deleted. + + If `strict` is true, and no files are found to combine, an error is + raised. + + `message` is a function to use for printing messages to the user. + + """ + files_to_combine = combinable_files(data.base_filename(), data_paths) + + if strict and not files_to_combine: + raise NoDataError("No data to combine") + + if aliases is None: + map_path = None + else: + map_path = functools.cache(aliases.map) + + file_hashes = set() + combined_any = False + + for f in files_to_combine: + if f == data.data_filename(): + # Sometimes we are combining into a file which is one of the + # parallel files. Skip that file. + if data._debug.should("dataio"): + data._debug.write(f"Skipping combining ourself: {f!r}") + continue + + try: + rel_file_name = os.path.relpath(f) + except ValueError: + # ValueError can be raised under Windows when os.getcwd() returns a + # folder from a different drive than the drive of f, in which case + # we print the original value of f instead of its relative path + rel_file_name = f + + with open(f, "rb") as fobj: + hasher = hashlib.new("sha3_256", usedforsecurity=False) + hasher.update(fobj.read()) + sha = hasher.digest() + combine_this_one = sha not in file_hashes + + delete_this_one = not keep + if combine_this_one: + if data._debug.should("dataio"): + data._debug.write(f"Combining data file {f!r}") + file_hashes.add(sha) + try: + new_data = CoverageData(f, debug=data._debug) + new_data.read() + except CoverageException as exc: + if data._warn: + # The CoverageException has the file name in it, so just + # use the message as the warning. + data._warn(str(exc)) + if message: + message(f"Couldn't combine data file {rel_file_name}: {exc}") + delete_this_one = False + else: + data.update(new_data, map_path=map_path) + combined_any = True + if message: + message(f"Combined data file {rel_file_name}") + else: + if message: + message(f"Skipping duplicate data {rel_file_name}") + + if delete_this_one: + if data._debug.should("dataio"): + data._debug.write(f"Deleting data file {f!r}") + file_be_gone(f) + + if strict and not combined_any: + raise NoDataError("No usable data files") + + +def debug_data_file(filename: str) -> None: + """Implementation of 'coverage debug data'.""" + data = CoverageData(filename) + filename = data.data_filename() + print(f"path: {filename}") + if not os.path.exists(filename): + print("No data collected: file doesn't exist") + return + data.read() + print(f"has_arcs: {data.has_arcs()!r}") + summary = line_counts(data, fullpath=True) + filenames = human_sorted(summary.keys()) + nfiles = len(filenames) + print(f"{nfiles} file{plural(nfiles)}:") + for f in filenames: + line = f"{f}: {summary[f]} line{plural(summary[f])}" + plugin = data.file_tracer(f) + if plugin: + line += f" [{plugin}]" + print(line) + + +def sorted_lines(data: CoverageData, filename: str) -> list[int]: + """Get the sorted lines for a file, for tests.""" + lines = data.lines(filename) + return sorted(lines or []) diff --git a/venv/Lib/site-packages/coverage/debug.py b/venv/Lib/site-packages/coverage/debug.py new file mode 100644 index 0000000..a134990 --- /dev/null +++ b/venv/Lib/site-packages/coverage/debug.py @@ -0,0 +1,669 @@ +# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 +# For details: https://github.com/coveragepy/coveragepy/blob/main/NOTICE.txt + +"""Control of and utilities for debugging.""" + +from __future__ import annotations + +import _thread +import atexit +import contextlib +import datetime +import functools +import inspect +import itertools +import os +import pprint +import re +import reprlib +import sys +import traceback +import types +from collections.abc import Iterable, Iterator, Mapping +from typing import IO, Any, Callable, Final, overload + +from coverage.misc import human_sorted_items, isolate_module +from coverage.types import AnyCallable, TWritable + +os = isolate_module(os) + + +# When debugging, it can be helpful to force some options, especially when +# debugging the configuration mechanisms you usually use to control debugging! +# This is a list of forced debugging options. +FORCED_DEBUG: list[str] = [] +FORCED_DEBUG_FILE = None + + +class DebugControl: + """Control and output for debugging.""" + + show_repr_attr = False # For auto_repr + + def __init__( + self, + options: Iterable[str], + output: IO[str] | None, + file_name: str | None = None, + ) -> None: + """Configure the options and output file for debugging.""" + self.options = list(options) + FORCED_DEBUG + self.suppress_callers = False + + filters = [] + if self.should("process"): + filters.append(CwdTracker().filter) + filters.append(ProcessTracker().filter) + if self.should("pytest"): + filters.append(PytestTracker().filter) + if self.should("pid"): + filters.append(add_pid_and_tid) + + self.output = DebugOutputFile.get_one( + output, + file_name=file_name, + filters=filters, + ) + self.raw_output = self.output.outfile + + def __repr__(self) -> str: + return f"" + + def should(self, option: str) -> bool: + """Decide whether to output debug information in category `option`.""" + if option == "callers" and self.suppress_callers: + return False + return option in self.options + + @contextlib.contextmanager + def without_callers(self) -> Iterator[None]: + """A context manager to prevent call stacks from being logged.""" + old = self.suppress_callers + self.suppress_callers = True + try: + yield + finally: + self.suppress_callers = old + + def write(self, msg: str, *, exc: BaseException | None = None) -> None: + """Write a line of debug output. + + `msg` is the line to write. A newline will be appended. + + If `exc` is provided, a stack trace of the exception will be written + after the message. + + """ + self.output.write(msg + "\n") + if exc is not None: + self.output.write("".join(traceback.format_exception(None, exc, exc.__traceback__))) + if self.should("self"): + caller_self = inspect.stack()[1][0].f_locals.get("self") + if caller_self is not None: + self.output.write(f"self: {caller_self!r}\n") + if self.should("callers"): + dump_stack_frames(out=self.output, skip=1) + self.output.flush() + + +class NoDebugging(DebugControl): + """A replacement for DebugControl that will never try to do anything.""" + + def __init__(self) -> None: + # pylint: disable=super-init-not-called + pass + + def should(self, option: str) -> bool: + """Should we write debug messages? Never.""" + return False + + @contextlib.contextmanager + def without_callers(self) -> Iterator[None]: + """A dummy context manager to satisfy the api.""" + yield # pragma: never called + + def write(self, msg: str, *, exc: BaseException | None = None) -> None: + """This will never be called.""" + raise AssertionError("NoDebugging.write should never be called.") + + +class DevNullDebug(NoDebugging): + """A DebugControl that won't write anywhere.""" + + def write(self, msg: str, *, exc: BaseException | None = None) -> None: + pass + + +def info_header(label: str) -> str: + """Make a nice header string.""" + return "--{:-<60s}".format(" " + label + " ") + + +def info_formatter(info: Iterable[tuple[str, Any]]) -> Iterable[str]: + """Produce a sequence of formatted lines from info. + + `info` is a sequence of pairs (label, data). The produced lines are + nicely formatted, ready to print. + + """ + info = list(info) + if not info: + return + LABEL_LEN = 30 + assert all(len(l) < LABEL_LEN for l, _ in info) + for label, data in info: + if data == []: + data = "-none-" + prefix = f"{label:>{LABEL_LEN}}: " + match data: + case tuple() if len(str(data)) < 30: + yield f"{prefix}{data}" + case tuple() | list() | set(): + for e in data: + yield f"{prefix}{e}" + prefix = " " * (LABEL_LEN + 2) + case _: + yield f"{prefix}{data}" + + +def write_formatted_info( + write: Callable[[str], None], + header: str, + info: Iterable[tuple[str, Any]], +) -> None: + """Write a sequence of (label,data) pairs nicely. + + `write` is a function write(str) that accepts each line of output. + `header` is a string to start the section. `info` is a sequence of + (label, data) pairs, where label is a str, and data can be a single + value, or a list/set/tuple. + + """ + write(info_header(header)) + for line in info_formatter(info): + write(f" {line}") + + +def exc_one_line(exc: Exception) -> str: + """Get a one-line summary of an exception, including class name and message.""" + lines = traceback.format_exception_only(type(exc), exc) + return "|".join(l.rstrip() for l in lines) + + +_FILENAME_REGEXES: list[tuple[str, str]] = [ + (r".*[/\\]pytest-of-.*[/\\]pytest-\d+([/\\]popen-gw\d+)?", "tmp:"), +] +_FILENAME_SUBS: list[tuple[str, str]] = [] + + +@overload +def short_filename(filename: str) -> str: + pass + + +@overload +def short_filename(filename: None) -> None: + pass + + +def short_filename(filename: str | None) -> str | None: + """Shorten a file name. Directories are replaced by prefixes like 'syspath:'""" + if not _FILENAME_SUBS: + for pathdir in sys.path: + _FILENAME_SUBS.append((pathdir, "syspath:")) + import coverage + + _FILENAME_SUBS.append((os.path.dirname(coverage.__file__), "cov:")) + _FILENAME_SUBS.sort(key=(lambda pair: len(pair[0])), reverse=True) + if filename is not None: + for pat, sub in _FILENAME_REGEXES: + filename = re.sub(pat, sub, filename) + for before, after in _FILENAME_SUBS: + filename = filename.replace(before, after) + return filename + + +def file_summary(filename: str) -> str: + """A one-line summary of a file, for log messages.""" + try: + s = os.stat(filename) + except FileNotFoundError: + summary = "does not exist" + except Exception as e: + summary = f"error: {e}" + else: + mod = datetime.datetime.fromtimestamp(s.st_mtime) + summary = f"{s.st_size} bytes, modified {mod}" + return summary + + +def short_stack( + skip: int = 0, + full: bool = False, + frame_ids: bool = False, + short_filenames: bool = False, +) -> str: + """Return a string summarizing the call stack. + + The string is multi-line, with one line per stack frame. Each line shows + the function name, the file name, and the line number: + + ... + start_import_stop : /Users/ned/coverage/trunk/tests/coveragetest.py:95 + import_local_file : /Users/ned/coverage/trunk/tests/coveragetest.py:81 + import_local_file : /Users/ned/coverage/trunk/coverage/backward.py:159 + ... + + `skip` is the number of closest immediate frames to skip, so that debugging + functions can call this and not be included in the result. + + If `full` is true, then include all frames. Otherwise, initial "boring" + frames (ones in site-packages and earlier) are omitted. + + `short_filenames` will shorten filenames using `short_filename`, to reduce + the amount of repetitive noise in stack traces. + + """ + # Regexes in initial frames that we don't care about. + # fmt: off + BORING_PRELUDE = [ + "", # pytest-xdist has string execution. + r"\bigor.py$", # Our test runner. + r"\bsite-packages\b", # pytest etc getting to our tests. + ] + # fmt: on + + stack: Iterable[inspect.FrameInfo] = inspect.stack()[:skip:-1] + if not full: + for pat in BORING_PRELUDE: + stack = itertools.dropwhile( + (lambda fi, pat=pat: re.search(pat, fi.filename)), # type: ignore[misc] + stack, + ) + lines = [] + for frame_info in stack: + line = f"{frame_info.function:>30s} : " + if frame_ids: + line += f"{id(frame_info.frame):#x} " + filename = frame_info.filename + if short_filenames: + filename = short_filename(filename) + line += f"{filename}:{frame_info.lineno}" + lines.append(line) + return "\n".join(lines) + + +def dump_stack_frames(out: TWritable, skip: int = 0) -> None: + """Print a summary of the stack to `out`.""" + out.write(short_stack(skip=skip + 1) + "\n") + + +def clipped_repr(text: str, numchars: int = 50) -> str: + """`repr(text)`, but limited to `numchars`.""" + r = reprlib.Repr() + r.maxstring = numchars + return r.repr(text) + + +def short_id(id64: int) -> int: + """Given a 64-bit id, make a shorter 16-bit one.""" + id16 = 0 + for offset in range(0, 64, 16): + id16 ^= id64 >> offset + return id16 & 0xFFFF + + +def add_pid_and_tid(text: str) -> str: + """A filter to add pid and tid to debug messages.""" + # Thread ids are useful, but too long. Make a shorter one. + tid = f"{short_id(_thread.get_ident()):04x}" + text = f"{os.getpid():5d}.{tid}: {text}" + return text + + +AUTO_REPR_IGNORE = {"$coverage.object_id"} + + +def auto_repr(self: Any) -> str: + """A function implementing an automatic __repr__ for debugging.""" + show_attrs = ( + (k, v) + for k, v in self.__dict__.items() + if getattr(v, "show_repr_attr", True) + and not inspect.ismethod(v) + and k not in AUTO_REPR_IGNORE + ) + return "<{klass} @{id:#x}{attrs}>".format( + klass=self.__class__.__name__, + id=id(self), + attrs="".join(f" {k}={v!r}" for k, v in show_attrs), + ) + + +def simplify(v: Any) -> Any: # pragma: debugging + """Turn things which are nearly dict/list/etc into dict/list/etc.""" + if isinstance(v, dict): + return {k: simplify(vv) for k, vv in v.items()} + elif isinstance(v, (list, tuple)): + return type(v)(simplify(vv) for vv in v) + elif hasattr(v, "__dict__"): + return simplify({"." + k: v for k, v in v.__dict__.items()}) + else: + return v + + +def ppformat(v: Any) -> str: # pragma: debugging + """Debug helper to pretty-print data, including SimpleNamespace objects.""" + return pprint.pformat(simplify(v), indent=4, compact=True, sort_dicts=True, width=140) + + +def pp(v: Any) -> None: # pragma: debugging + """Debug helper to pretty-print data, including SimpleNamespace objects.""" + print(ppformat(v)) + + +def filter_text(text: str, filters: Iterable[Callable[[str], str]]) -> str: + """Run `text` through a series of filters. + + `filters` is a list of functions. Each takes a string and returns a + string. Each is run in turn. After each filter, the text is split into + lines, and each line is passed through the next filter. + + Returns: the final string that results after all of the filters have + run. + + """ + clean_text = text.rstrip() + ending = text[len(clean_text) :] + text = clean_text + for filter_fn in filters: + lines = [] + for line in text.splitlines(): + lines.extend(filter_fn(line).splitlines()) + text = "\n".join(lines) + return text + ending + + +class CwdTracker: + """A class to add cwd info to debug messages.""" + + def __init__(self) -> None: + self.cwd: str | None = None + + def filter(self, text: str) -> str: + """Add a cwd message for each new cwd.""" + cwd = os.getcwd() + if cwd != self.cwd: + text = f"cwd is now {cwd!r}\n{text}" + self.cwd = cwd + return text + + +class ProcessTracker: + """Track process creation for debug logging.""" + + def __init__(self) -> None: + self.pid: int = os.getpid() + self.did_welcome = False + + def filter(self, text: str) -> str: + """Add a message about how new processes came to be.""" + welcome = "" + pid = os.getpid() + if self.pid != pid: + welcome = f"New process: forked {self.pid} -> {pid}\n" + self.pid = pid + elif not self.did_welcome: + argv = getattr(sys, "argv", None) + welcome = ( + f"New process: {pid=}, executable: {sys.executable!r}\n" + + f"New process: cmd: {argv!r}\n" + + f"New process parent pid: {os.getppid()!r}\n" + ) + + if welcome: + self.did_welcome = True + return welcome + text + else: + return text + + +class PytestTracker: + """Track the current pytest test name to add to debug messages.""" + + def __init__(self) -> None: + self.test_name: str | None = None + + def filter(self, text: str) -> str: + """Add a message when the pytest test changes.""" + test_name = os.getenv("PYTEST_CURRENT_TEST") + if test_name != self.test_name: + text = f"Pytest context: {test_name}\n{text}" + self.test_name = test_name + return text + + +class DebugOutputFile: + """A file-like object that includes pid and cwd information.""" + + def __init__( + self, + outfile: IO[str] | None, + filters: Iterable[Callable[[str], str]], + ): + self.outfile = outfile + self.filters = list(filters) + self.pid = os.getpid() + + @classmethod + def get_one( + cls, + fileobj: IO[str] | None = None, + file_name: str | None = None, + filters: Iterable[Callable[[str], str]] = (), + interim: bool = False, + ) -> DebugOutputFile: + """Get a DebugOutputFile. + + If `fileobj` is provided, then a new DebugOutputFile is made with it. + + If `fileobj` isn't provided, then a file is chosen (`file_name` if + provided, or COVERAGE_DEBUG_FILE, or stderr), and a process-wide + singleton DebugOutputFile is made. + + `filters` are the text filters to apply to the stream to annotate with + pids, etc. + + If `interim` is true, then a future `get_one` can replace this one. + + """ + if fileobj is not None: + # Make DebugOutputFile around the fileobj passed. + return cls(fileobj, filters) + + the_one, is_interim = cls._get_singleton_data() + if the_one is None or is_interim: + if file_name is not None: + fileobj = open(file_name, "a", encoding="utf-8") + else: + # $set_env.py: COVERAGE_DEBUG_FILE - Where to write debug output + file_name = os.getenv("COVERAGE_DEBUG_FILE", FORCED_DEBUG_FILE) + if file_name in ["stdout", "stderr"]: + fileobj = getattr(sys, file_name) + elif file_name: + fileobj = open(file_name, "a", encoding="utf-8") + atexit.register(fileobj.close) + else: + fileobj = sys.stderr + the_one = cls(fileobj, filters) + cls._set_singleton_data(the_one, interim) + + if not (the_one.filters): + the_one.filters = list(filters) + return the_one + + # Because of the way igor.py deletes and re-imports modules, + # this class can be defined more than once. But we really want + # a process-wide singleton. So stash it in sys.modules instead of + # on a class attribute. Yes, this is aggressively gross. + + SYS_MOD_NAME: Final[str] = "$coverage.debug.DebugOutputFile.the_one" + SINGLETON_ATTR: Final[str] = "the_one_and_is_interim" + + @classmethod + def _set_singleton_data(cls, the_one: DebugOutputFile, interim: bool) -> None: + """Set the one DebugOutputFile to rule them all.""" + singleton_module = types.ModuleType(cls.SYS_MOD_NAME) + setattr(singleton_module, cls.SINGLETON_ATTR, (the_one, interim)) + sys.modules[cls.SYS_MOD_NAME] = singleton_module + + @classmethod + def _get_singleton_data(cls) -> tuple[DebugOutputFile | None, bool]: + """Get the one DebugOutputFile.""" + singleton_module = sys.modules.get(cls.SYS_MOD_NAME) + return getattr(singleton_module, cls.SINGLETON_ATTR, (None, True)) + + @classmethod + def _del_singleton_data(cls) -> None: + """Delete the one DebugOutputFile, just for tests to use.""" + if cls.SYS_MOD_NAME in sys.modules: + del sys.modules[cls.SYS_MOD_NAME] + + def write(self, text: str) -> None: + """Just like file.write, but filter through all our filters.""" + assert self.outfile is not None + if not self.outfile.closed: + self.outfile.write(filter_text(text, self.filters)) + self.outfile.flush() + + def flush(self) -> None: + """Flush our file.""" + assert self.outfile is not None + if not self.outfile.closed: + self.outfile.flush() + + +def log(msg: str, stack: bool = False) -> None: # pragma: debugging + """Write a log message as forcefully as possible.""" + out = DebugOutputFile.get_one(interim=True) + out.write(msg + "\n") + if stack: + dump_stack_frames(out=out, skip=1) + + +def decorate_methods( + decorator: Callable[..., Any], + butnot: Iterable[str] = (), + private: bool = False, +) -> Callable[..., Any]: # pragma: debugging + """A class decorator to apply a decorator to methods.""" + + def _decorator(cls): # type: ignore[no-untyped-def] + for name, meth in inspect.getmembers(cls, inspect.isroutine): + if name not in cls.__dict__: + continue + if name != "__init__": + if not private and name.startswith("_"): + continue + if name in butnot: + continue + setattr(cls, name, decorator(meth)) + return cls + + return _decorator + + +def break_in_pudb(func: AnyCallable) -> AnyCallable: # pragma: debugging + """A function decorator to stop in the debugger for each call.""" + + @functools.wraps(func) + def _wrapper(*args: Any, **kwargs: Any) -> Any: + import pudb + + sys.stdout = sys.__stdout__ + pudb.set_trace() + return func(*args, **kwargs) + + return _wrapper + + +OBJ_IDS = itertools.count() +CALLS = itertools.count() +OBJ_ID_ATTR = "$coverage.object_id" + + +def show_calls( + show_args: bool = True, + show_stack: bool = False, + show_return: bool = False, +) -> Callable[..., Any]: # pragma: debugging + """A method decorator to debug-log each call to the function.""" + + def _decorator(func: AnyCallable) -> AnyCallable: + @functools.wraps(func) + def _wrapper(self: Any, *args: Any, **kwargs: Any) -> Any: + oid = getattr(self, OBJ_ID_ATTR, None) + if oid is None: + oid = f"{os.getpid():08d} {next(OBJ_IDS):04d}" + setattr(self, OBJ_ID_ATTR, oid) + extra = "" + if show_args: + eargs = ", ".join(map(repr, args)) + ekwargs = ", ".join("{}={!r}".format(*item) for item in kwargs.items()) + extra += "(" + extra += eargs + if eargs and ekwargs: + extra += ", " + extra += ekwargs + extra += ")" + if show_stack: + extra += " @ " + extra += "; ".join(short_stack(short_filenames=True).splitlines()) + callid = next(CALLS) + msg = f"{oid} {callid:04d} {func.__name__}{extra}\n" + DebugOutputFile.get_one(interim=True).write(msg) + ret = func(self, *args, **kwargs) + if show_return: + msg = f"{oid} {callid:04d} {func.__name__} return {ret!r}\n" + DebugOutputFile.get_one(interim=True).write(msg) + return ret + + return _wrapper + + return _decorator + + +def relevant_environment_display(env: Mapping[str, str]) -> list[tuple[str, str]]: + """Filter environment variables for a debug display. + + Select variables to display (with COV or PY in the name, or HOME, TEMP, or + TMP), and also cloak sensitive values with asterisks. + + Arguments: + env: a dict of environment variable names and values. + + Returns: + A list of pairs (name, value) to show. + + """ + SLUGS = {"COV", "PY"} + INCLUDE = {"HOME", "TEMP", "TMP"} + CLOAK = {"API", "TOKEN", "KEY", "SECRET", "PASS", "SIGNATURE"} + TRUNCATE = {"COVERAGE_PROCESS_CONFIG"} + TRUNCATE_LEN = 60 + + to_show = [] + for name, val in env.items(): + show = False + if name in INCLUDE: + show = True + elif any(slug in name for slug in SLUGS): + show = True + if show: + if any(slug in name for slug in CLOAK): + val = re.sub(r"\w", "*", val) + if name in TRUNCATE: + if len(val) > TRUNCATE_LEN: + val = val[: TRUNCATE_LEN - 3] + "..." + to_show.append((name, val)) + return human_sorted_items(to_show) diff --git a/venv/Lib/site-packages/coverage/disposition.py b/venv/Lib/site-packages/coverage/disposition.py new file mode 100644 index 0000000..0ecdfad --- /dev/null +++ b/venv/Lib/site-packages/coverage/disposition.py @@ -0,0 +1,59 @@ +# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 +# For details: https://github.com/coveragepy/coveragepy/blob/main/NOTICE.txt + +"""Simple value objects for tracking what to do with files.""" + +from __future__ import annotations + +from typing import TYPE_CHECKING + +from coverage.types import TFileDisposition + +if TYPE_CHECKING: + from coverage.plugin import FileTracer + + +class FileDisposition: + """A simple value type for recording what to do with a file.""" + + original_filename: str + canonical_filename: str + source_filename: str | None + trace: bool + reason: str + file_tracer: FileTracer | None + has_dynamic_filename: bool + + def __repr__(self) -> str: + return f"" + + +# FileDisposition "methods": FileDisposition is a pure value object, so it can +# be implemented in either C or Python. Acting on them is done with these +# functions. + + +def disposition_init(cls: type[TFileDisposition], original_filename: str) -> TFileDisposition: + """Construct and initialize a new FileDisposition object.""" + disp = cls() + disp.original_filename = original_filename + disp.canonical_filename = original_filename + disp.source_filename = None + disp.trace = False + disp.reason = "" + disp.file_tracer = None + disp.has_dynamic_filename = False + return disp + + +def disposition_debug_msg(disp: TFileDisposition) -> str: + """Make a nice debug message of what the FileDisposition is doing.""" + if disp.trace: + msg = f"Tracing {disp.original_filename!r}" + if disp.original_filename != disp.source_filename: + msg += f" as {disp.source_filename!r}" + if disp.file_tracer: + msg += f": will be traced by {disp.file_tracer!r}" + else: + msg = f"Not tracing {disp.original_filename!r}: {disp.reason}" + return msg diff --git a/venv/Lib/site-packages/coverage/env.py b/venv/Lib/site-packages/coverage/env.py new file mode 100644 index 0000000..8cf8230 --- /dev/null +++ b/venv/Lib/site-packages/coverage/env.py @@ -0,0 +1,135 @@ +# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 +# For details: https://github.com/coveragepy/coveragepy/blob/main/NOTICE.txt + +"""Determine facts about the environment.""" + +from __future__ import annotations + +import os +import platform +import sys +from collections.abc import Iterable +from typing import Any, Final + +# debug_info() at the bottom wants to show all the globals, but not imports. +# Grab the global names here to know which names to not show. Nothing defined +# above this line will be in the output. +_UNINTERESTING_GLOBALS = list(globals()) +# These names also shouldn't be shown. +_UNINTERESTING_GLOBALS += ["PYBEHAVIOR", "debug_info"] + +# Operating systems. +WINDOWS = sys.platform == "win32" +LINUX = sys.platform.startswith("linux") +MACOS = sys.platform == "darwin" + +# Python implementations. +CPYTHON = (platform.python_implementation() == "CPython") # fmt: skip +PYPY = (platform.python_implementation() == "PyPy") # fmt: skip + +# Python versions. We amend version_info with one more value, a zero if an +# official version, or 1 if built from source beyond an official version. +# Only use sys.version_info directly where tools like mypy need it to understand +# version-specfic code, otherwise use PYVERSION. +PYVERSION = sys.version_info + (int(platform.python_version()[-1] == "+"),) + +if PYPY: + # Minimum now is 7.3.16 + PYPYVERSION = tuple(sys.pypy_version_info) # type: ignore[attr-defined] +else: + PYPYVERSION = (0,) + +# Do we have a GIL? +GIL = getattr(sys, "_is_gil_enabled", lambda: True)() + +# Do we ship compiled coveragepy wheels for this version? +SHIPPING_WHEELS = CPYTHON and PYVERSION[:2] <= (3, 14) + +# Should we default to sys.monitoring? +SYSMON_DEFAULT = CPYTHON and PYVERSION >= (3, 14) + + +# Python behavior. +class PYBEHAVIOR: + """Flags indicating this Python's behavior.""" + + # When leaving a with-block, do we visit the with-line exactly, + # or the context managers in inner-out order? + # + # mwith.py: + # with ( + # open("/tmp/one", "w") as f2, + # open("/tmp/two", "w") as f3, + # open("/tmp/three", "w") as f4, + # ): + # print("hello 6") + # + # % python3.11 -m trace -t mwith.py | grep mwith + # --- modulename: mwith, funcname: + # mwith.py(2): open("/tmp/one", "w") as f2, + # mwith.py(1): with ( + # mwith.py(2): open("/tmp/one", "w") as f2, + # mwith.py(3): open("/tmp/two", "w") as f3, + # mwith.py(1): with ( + # mwith.py(3): open("/tmp/two", "w") as f3, + # mwith.py(4): open("/tmp/three", "w") as f4, + # mwith.py(1): with ( + # mwith.py(4): open("/tmp/three", "w") as f4, + # mwith.py(6): print("hello 6") + # mwith.py(1): with ( + # + # % python3.12 -m trace -t mwith.py | grep mwith + # --- modulename: mwith, funcname: + # mwith.py(2): open("/tmp/one", "w") as f2, + # mwith.py(3): open("/tmp/two", "w") as f3, + # mwith.py(4): open("/tmp/three", "w") as f4, + # mwith.py(6): print("hello 6") + # mwith.py(4): open("/tmp/three", "w") as f4, + # mwith.py(3): open("/tmp/two", "w") as f3, + # mwith.py(2): open("/tmp/one", "w") as f2, + + exit_with_through_ctxmgr = (PYVERSION >= (3, 12, 6)) # fmt: skip + + # f-strings are parsed as code, pep 701 + fstring_syntax = (PYVERSION >= (3, 12)) # fmt: skip + + # PEP669 Low Impact Monitoring: https://peps.python.org/pep-0669/ + pep669: Final[bool] = bool(getattr(sys, "monitoring", None)) + + # Where does frame.f_lasti point when yielding from a generator? + # It used to point at the YIELD, in 3.13 it points at the RESUME, + # then it went back to the YIELD. + # https://github.com/python/cpython/issues/113728 + lasti_is_yield = (PYVERSION[:2] != (3, 13)) # fmt: skip + + # PEP649 and PEP749: Deferred annotations + deferred_annotations = (PYVERSION >= (3, 14)) # fmt: skip + + # Does sys.monitoring support BRANCH_RIGHT and BRANCH_LEFT? The names + # were added in early 3.14 alphas, but didn't work entirely correctly until + # after 3.14.0a5. + branch_right_left = pep669 and (PYVERSION > (3, 14, 0, "alpha", 5, 0)) + + +# Coverage.py specifics, about testing scenarios. See tests/testenv.py also. + +# Are we coverage-measuring ourselves? +METACOV = os.getenv("COVERAGE_COVERAGE") is not None + +# Are we running our test suite? +# Even when running tests, you can use COVERAGE_TESTING=0 to disable the +# test-specific behavior like AST checking. +TESTING = os.getenv("COVERAGE_TESTING") == "True" + + +def debug_info() -> Iterable[tuple[str, Any]]: + """Return a list of (name, value) pairs for printing debug information.""" + info = [ + (name, value) + for name, value in globals().items() + if not name.startswith("_") and name not in _UNINTERESTING_GLOBALS + ] + info += [ + (name, value) for name, value in PYBEHAVIOR.__dict__.items() if not name.startswith("_") + ] + return sorted(info) diff --git a/venv/Lib/site-packages/coverage/exceptions.py b/venv/Lib/site-packages/coverage/exceptions.py new file mode 100644 index 0000000..233788b --- /dev/null +++ b/venv/Lib/site-packages/coverage/exceptions.py @@ -0,0 +1,85 @@ +# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 +# For details: https://github.com/coveragepy/coveragepy/blob/main/NOTICE.txt + +"""Exceptions coverage.py can raise.""" + +from __future__ import annotations + +from typing import Any + + +class CoverageException(Exception): + """The base class of all exceptions raised by Coverage.py.""" + + def __init__( + self, + *args: Any, + slug: str | None = None, + ) -> None: + """Create an exception. + + Args: + slug: A short string identifying the exception, will be used for + linking to documentation. + """ + + super().__init__(*args) + self.slug = slug + + +class ConfigError(CoverageException): + """A problem with a config file, or a value in one.""" + + pass + + +class DataError(CoverageException): + """An error in using a data file.""" + + pass + + +class NoDataError(CoverageException): + """We didn't have data to work with.""" + + pass + + +class NoSource(CoverageException): + """We couldn't find the source for a module.""" + + pass + + +class NoCode(NoSource): + """We couldn't find any code at all.""" + + pass + + +class NotPython(CoverageException): + """A source file turned out not to be parsable Python.""" + + pass + + +class PluginError(CoverageException): + """A plugin misbehaved.""" + + pass + + +class _ExceptionDuringRun(CoverageException): + """An exception happened while running customer code. + + Construct it with three arguments, the values from `sys.exc_info`. + + """ + + pass + + +class CoverageWarning(Warning): + """A warning from Coverage.py.""" + + pass diff --git a/venv/Lib/site-packages/coverage/execfile.py b/venv/Lib/site-packages/coverage/execfile.py new file mode 100644 index 0000000..1f8996c --- /dev/null +++ b/venv/Lib/site-packages/coverage/execfile.py @@ -0,0 +1,329 @@ +# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 +# For details: https://github.com/coveragepy/coveragepy/blob/main/NOTICE.txt + +"""Execute files of Python code.""" + +from __future__ import annotations + +import importlib.machinery +import importlib.util +import inspect +import marshal +import os +import struct +import sys +from importlib.machinery import ModuleSpec +from types import CodeType, ModuleType +from typing import Any + +from coverage.exceptions import CoverageException, NoCode, NoSource, _ExceptionDuringRun +from coverage.files import canonical_filename, python_reported_file +from coverage.misc import isolate_module +from coverage.python import get_python_source + +os = isolate_module(os) + + +PYC_MAGIC_NUMBER = importlib.util.MAGIC_NUMBER + + +class DummyLoader: + """A shim for the pep302 __loader__, emulating pkgutil.ImpLoader. + + Currently only implements the .fullname attribute + """ + + def __init__(self, fullname: str, *_args: Any) -> None: + self.fullname = fullname + + +def find_module( + modulename: str, +) -> tuple[str | None, str, ModuleSpec]: + """Find the module named `modulename`. + + Returns the file path of the module, the name of the enclosing + package, and the spec. + """ + try: + spec = importlib.util.find_spec(modulename) + except ImportError as err: + raise NoSource(str(err)) from err + if not spec: + raise NoSource(f"No module named {modulename!r}") + pathname = spec.origin + packagename = spec.name + if spec.submodule_search_locations: + mod_main = modulename + ".__main__" + spec = importlib.util.find_spec(mod_main) + if not spec: + raise NoSource( + f"No module named {mod_main}; " + + f"{modulename!r} is a package and cannot be directly executed", + ) + pathname = spec.origin + packagename = spec.name + packagename = packagename.rpartition(".")[0] + return pathname, packagename, spec + + +class PyRunner: + """Multi-stage execution of Python code. + + This is meant to emulate real Python execution as closely as possible. + + """ + + def __init__(self, args: list[str], as_module: bool = False) -> None: + self.args = args + self.as_module = as_module + + self.arg0 = args[0] + self.package: str | None = None + self.modulename: str | None = None + self.pathname: str | None = None + self.loader: DummyLoader | None = None + self.spec: ModuleSpec | None = None + + def prepare(self) -> None: + """Set sys.path properly. + + This needs to happen before any importing, and without importing anything. + """ + path0: str | None + if getattr(sys.flags, "safe_path", False): + # See https://docs.python.org/3/using/cmdline.html#cmdoption-P + path0 = None + elif self.as_module: + path0 = os.getcwd() + elif os.path.isdir(self.arg0): + # Running a directory means running the __main__.py file in that + # directory. + path0 = self.arg0 + else: + path0 = os.path.abspath(os.path.dirname(self.arg0)) + + if os.path.isdir(sys.path[0]): + # sys.path fakery. If we are being run as a command, then sys.path[0] + # is the directory of the "coverage" script. If this is so, replace + # sys.path[0] with the directory of the file we're running, or the + # current directory when running modules. If it isn't so, then we + # don't know what's going on, and just leave it alone. + top_file = inspect.stack()[-1][0].f_code.co_filename + sys_path_0_abs = os.path.abspath(sys.path[0]) + top_file_dir_abs = os.path.abspath(os.path.dirname(top_file)) + sys_path_0_abs = canonical_filename(sys_path_0_abs) + top_file_dir_abs = canonical_filename(top_file_dir_abs) + if sys_path_0_abs != top_file_dir_abs: + path0 = None + + else: + # sys.path[0] is a file. Is the next entry the directory containing + # that file? + if sys.path[1] == os.path.dirname(sys.path[0]): + # Can it be right to always remove that? + del sys.path[1] + + if path0 is not None: + sys.path[0] = python_reported_file(path0) + + def _prepare2(self) -> None: + """Do more preparation to run Python code. + + Includes finding the module to run and adjusting sys.argv[0]. + This method is allowed to import code. + + """ + if self.as_module: + self.modulename = self.arg0 + pathname, self.package, self.spec = find_module(self.modulename) + if self.spec is not None: + self.modulename = self.spec.name + self.loader = DummyLoader(self.modulename) + assert pathname is not None + self.pathname = os.path.abspath(pathname) + self.args[0] = self.arg0 = self.pathname + elif os.path.isdir(self.arg0): + # Running a directory means running the __main__.py file in that + # directory. + for ext in [".py", ".pyc", ".pyo"]: + try_filename = os.path.join(self.arg0, f"__main__{ext}") + # 3.8.10 changed how files are reported when running a + # directory. + try_filename = os.path.abspath(try_filename) + if os.path.exists(try_filename): + self.arg0 = try_filename + break + else: + raise NoSource(f"Can't find '__main__' module in '{self.arg0}'") + + # Make a spec. I don't know if this is the right way to do it. + try_filename = python_reported_file(try_filename) + self.spec = importlib.machinery.ModuleSpec("__main__", None, origin=try_filename) + self.spec.has_location = True + self.package = "" + self.loader = DummyLoader("__main__") + else: + self.loader = DummyLoader("__main__") + + self.arg0 = python_reported_file(self.arg0) + + def run(self) -> None: + """Run the Python code!""" + + self._prepare2() + + # Create a module to serve as __main__ + main_mod = ModuleType("__main__") + + from_pyc = self.arg0.endswith((".pyc", ".pyo")) + main_mod.__file__ = self.arg0 + if from_pyc: + main_mod.__file__ = main_mod.__file__[:-1] + if self.package is not None: + main_mod.__package__ = self.package + main_mod.__loader__ = self.loader # type: ignore[assignment] + if self.spec is not None: + main_mod.__spec__ = self.spec + + main_mod.__builtins__ = sys.modules["builtins"] # type: ignore[attr-defined] + + sys.modules["__main__"] = main_mod + + # Set sys.argv properly. + sys.argv = self.args + + try: + # Make a code object somehow. + if from_pyc: + code = make_code_from_pyc(self.arg0) + else: + code = make_code_from_py(self.arg0) + except CoverageException: + raise + except Exception as exc: + msg = f"Couldn't run '{self.arg0}' as Python code: {exc.__class__.__name__}: {exc}" + raise CoverageException(msg) from exc + + # Execute the code object. + # Return to the original directory in case the test code exits in + # a non-existent directory. + cwd = os.getcwd() + try: + exec(code, main_mod.__dict__) + except SystemExit: # pylint: disable=try-except-raise + # The user called sys.exit(). Just pass it along to the upper + # layers, where it will be handled. + raise + except Exception: + # Something went wrong while executing the user code. + # Get the exc_info, and pack them into an exception that we can + # throw up to the outer loop. We peel one layer off the traceback + # so that the coverage.py code doesn't appear in the final printed + # traceback. + typ, err, tb = sys.exc_info() + assert typ is not None + assert err is not None + assert tb is not None + + # PyPy3 weirdness. If I don't access __context__, then somehow it + # is non-None when the exception is reported at the upper layer, + # and a nested exception is shown to the user. This getattr fixes + # it somehow? https://bitbucket.org/pypy/pypy/issue/1903 + getattr(err, "__context__", None) + + # Call the excepthook. + try: + assert err.__traceback__ is not None + err.__traceback__ = err.__traceback__.tb_next + sys.excepthook(typ, err, tb.tb_next) + except SystemExit: # pylint: disable=try-except-raise + raise + except Exception as exc: + # Getting the output right in the case of excepthook + # shenanigans is kind of involved. + sys.stderr.write("Error in sys.excepthook:\n") + typ2, err2, tb2 = sys.exc_info() + assert typ2 is not None + assert err2 is not None + assert tb2 is not None + err2.__suppress_context__ = True + assert err2.__traceback__ is not None + err2.__traceback__ = err2.__traceback__.tb_next + sys.__excepthook__(typ2, err2, tb2.tb_next) + sys.stderr.write("\nOriginal exception was:\n") + raise _ExceptionDuringRun(typ, err, tb.tb_next) from exc + else: + sys.exit(1) + finally: + os.chdir(cwd) + + +def run_python_module(args: list[str]) -> None: + """Run a Python module, as though with ``python -m name args...``. + + `args` is the argument array to present as sys.argv, including the first + element naming the module being executed. + + This is a helper for tests, to encapsulate how to use PyRunner. + + """ + runner = PyRunner(args, as_module=True) + runner.prepare() + runner.run() + + +def run_python_file(args: list[str]) -> None: + """Run a Python file as if it were the main program on the command line. + + `args` is the argument array to present as sys.argv, including the first + element naming the file being executed. `package` is the name of the + enclosing package, if any. + + This is a helper for tests, to encapsulate how to use PyRunner. + + """ + runner = PyRunner(args, as_module=False) + runner.prepare() + runner.run() + + +def make_code_from_py(filename: str) -> CodeType: + """Get source from `filename` and make a code object of it.""" + try: + source = get_python_source(filename) + except (OSError, NoSource) as exc: + raise NoSource(f"No file to run: '{filename}'") from exc + + code = compile(source, filename, mode="exec", dont_inherit=True) + return code + + +def make_code_from_pyc(filename: str) -> CodeType: + """Get a code object from a .pyc file.""" + try: + fpyc = open(filename, "rb") + except OSError as exc: + raise NoCode(f"No file to run: '{filename}'") from exc + + with fpyc: + # First four bytes are a version-specific magic number. It has to + # match or we won't run the file. + magic = fpyc.read(4) + if magic != PYC_MAGIC_NUMBER: + raise NoCode(f"Bad magic number in .pyc file: {magic!r} != {PYC_MAGIC_NUMBER!r}") + + flags = struct.unpack(" None: + """Set the directory that `relative_filename` will be relative to.""" + global RELATIVE_DIR, CANONICAL_FILENAME_CACHE + + # The current directory + abs_curdir = abs_file(os.curdir) + if not abs_curdir.endswith(os.sep): + # Suffix with separator only if not at the system root + abs_curdir = abs_curdir + os.sep + + # The absolute path to our current directory. + RELATIVE_DIR = os.path.normcase(abs_curdir) + + # Cache of results of calling the canonical_filename() method, to + # avoid duplicating work. + CANONICAL_FILENAME_CACHE = {} + + +def relative_directory() -> str: + """Return the directory that `relative_filename` is relative to.""" + return RELATIVE_DIR + + +def relative_filename(filename: str) -> str: + """Return the relative form of `filename`. + + The file name will be relative to the current directory when the + `set_relative_directory` was called. + + """ + fnorm = os.path.normcase(filename) + if fnorm.startswith(RELATIVE_DIR): + filename = filename[len(RELATIVE_DIR) :] + return filename + + +def canonical_filename(filename: str) -> str: + """Return a canonical file name for `filename`. + + An absolute path with no redundant components and normalized case. + + """ + if filename not in CANONICAL_FILENAME_CACHE: + cf = filename + if not os.path.isabs(filename): + for path in [os.curdir] + sys.path: + if path is None: + continue # type: ignore[unreachable] + f = os.path.join(path, filename) + try: + exists = os.path.exists(f) + except UnicodeError: + exists = False + if exists: + cf = f + break + cf = abs_file(cf) + CANONICAL_FILENAME_CACHE[filename] = cf + return CANONICAL_FILENAME_CACHE[filename] + + +def flat_rootname(filename: str) -> str: + """A base for a flat file name to correspond to this file. + + Useful for writing files about the code where you want all the files in + the same directory, but need to differentiate same-named files from + different directories. + + For example, the file a/b/c.py will return 'z_86bbcbe134d28fd2_c_py' + + """ + dirname, basename = ntpath.split(filename) + if dirname: + fp = hashlib.new( + "sha3_256", + dirname.encode("UTF-8"), + usedforsecurity=False, + ).hexdigest()[:16] + prefix = f"z_{fp}_" + else: + prefix = "" + return prefix + basename.replace(".", "_") + + +if env.WINDOWS: + _ACTUAL_PATH_CACHE: dict[str, str] = {} + _ACTUAL_PATH_LIST_CACHE: dict[str, list[str]] = {} + + def actual_path(path: str) -> str: + """Get the actual path of `path`, including the correct case.""" + if path in _ACTUAL_PATH_CACHE: + return _ACTUAL_PATH_CACHE[path] + + head, tail = os.path.split(path) + if not tail: + # This means head is the drive spec: normalize it. + actpath = head.upper() + elif not head: + actpath = tail + else: + head = actual_path(head) + if head in _ACTUAL_PATH_LIST_CACHE: + files = _ACTUAL_PATH_LIST_CACHE[head] + else: + try: + files = os.listdir(head) + except Exception: + # This will raise OSError, or this bizarre TypeError: + # https://bugs.python.org/issue1776160 + files = [] + _ACTUAL_PATH_LIST_CACHE[head] = files + normtail = os.path.normcase(tail) + for f in files: + if os.path.normcase(f) == normtail: + tail = f + break + actpath = os.path.join(head, tail) + _ACTUAL_PATH_CACHE[path] = actpath + return actpath + +else: + + def actual_path(path: str) -> str: + """The actual path for non-Windows platforms.""" + return path + + +def abs_file(path: str) -> str: + """Return the absolute normalized form of `path`.""" + return actual_path(os.path.abspath(os.path.realpath(path))) + + +def zip_location(filename: str) -> tuple[str, str] | None: + """Split a filename into a zipfile / inner name pair. + + Only return a pair if the zipfile exists. No check is made if the inner + name is in the zipfile. + + """ + for ext in [".zip", ".whl", ".egg", ".pex", ".par"]: + zipbase, extension, inner = filename.partition(ext + sep(filename)) + if extension: + zipfile = zipbase + ext + if os.path.exists(zipfile): + return zipfile, inner + return None + + +def source_exists(path: str) -> bool: + """Determine if a source file path exists.""" + if os.path.exists(path): + return True + + if zip_location(path): + # If zip_location returns anything, then it's a zipfile that + # exists. That's good enough for us. + return True + + return False + + +def python_reported_file(filename: str) -> str: + """Return the string as Python would describe this file name.""" + return os.path.abspath(filename) + + +def isabs_anywhere(filename: str) -> bool: + """Is `filename` an absolute path on any OS?""" + return ntpath.isabs(filename) or posixpath.isabs(filename) + + +def prep_patterns(patterns: Iterable[str]) -> list[str]: + """Prepare the file patterns for use in a `GlobMatcher`. + + If a pattern starts with a wildcard, it is used as a pattern + as-is. If it does not start with a wildcard, then it is made + absolute with the current directory. + + If `patterns` is None, an empty list is returned. + + """ + prepped = [] + for p in patterns or []: + prepped.append(p) + if not p.startswith(("*", "?")): + prepped.append(abs_file(p)) + return prepped + + +class TreeMatcher: + """A matcher for files in a tree. + + Construct with a list of paths, either files or directories. Paths match + with the `match` method if they are one of the files, or if they are + somewhere in a subtree rooted at one of the directories. + + """ + + def __init__(self, paths: Iterable[str], name: str = "unknown") -> None: + self.original_paths: list[str] = human_sorted(paths) + self.paths = [os.path.normcase(p) for p in paths] + self.name = name + + def __repr__(self) -> str: + return f"" + + def info(self) -> list[str]: + """A list of strings for displaying when dumping state.""" + return self.original_paths + + def match(self, fpath: str) -> bool: + """Does `fpath` indicate a file in one of our trees?""" + fpath = os.path.normcase(fpath) + for p in self.paths: + if fpath.startswith(p): + if fpath == p: + # This is the same file! + return True + if fpath[len(p)] == os.sep: + # This is a file in the directory + return True + return False + + +class ModuleMatcher: + """A matcher for modules in a tree.""" + + def __init__(self, module_names: Iterable[str], name: str = "unknown") -> None: + self.modules = list(module_names) + self.name = name + + def __repr__(self) -> str: + return f"" + + def info(self) -> list[str]: + """A list of strings for displaying when dumping state.""" + return self.modules + + def match(self, module_name: str) -> bool: + """Does `module_name` indicate a module in one of our packages?""" + if not module_name: + return False + + for m in self.modules: + if module_name.startswith(m): + if module_name == m: + return True + if module_name[len(m)] == ".": + # This is a module in the package + return True + + return False + + +class GlobMatcher: + """A matcher for files by file name pattern.""" + + def __init__(self, pats: Iterable[str], name: str = "unknown") -> None: + self.pats = list(pats) + self.re = globs_to_regex(self.pats, case_insensitive=env.WINDOWS) + self.name = name + + def __repr__(self) -> str: + return f"" + + def info(self) -> list[str]: + """A list of strings for displaying when dumping state.""" + return self.pats + + def match(self, fpath: str) -> bool: + """Does `fpath` match one of our file name patterns?""" + return self.re.match(fpath) is not None + + +def sep(s: str) -> str: + """Find the path separator used in this string, or os.sep if none.""" + if sep_match := re.search(r"[\\/]", s): + the_sep = sep_match[0] + else: + the_sep = os.sep + return the_sep + + +# Tokenizer for _glob_to_regex. +# None as a sub means disallowed. +# fmt: off +G2RX_TOKENS = [(re.compile(rx), sub) for rx, sub in [ + (r"\*\*\*+", None), # Can't have *** + (r"[^/]+\*\*+", None), # Can't have x** + (r"\*\*+[^/]+", None), # Can't have **x + (r"\*\*/\*\*", None), # Can't have **/** + (r"^\*+/", r"(.*[/\\\\])?"), # ^*/ matches any prefix-slash, or nothing. + (r"/\*+$", r"[/\\\\].*"), # /*$ matches any slash-suffix. + (r"\*\*/", r"(.*[/\\\\])?"), # **/ matches any subdirs, including none + (r"/", r"[/\\\\]"), # / matches either slash or backslash + (r"\*", r"[^/\\\\]*"), # * matches any number of non slash-likes + (r"\?", r"[^/\\\\]"), # ? matches one non slash-like + (r"\[.*?\]", r"\g<0>"), # [a-f] matches [a-f] + (r"[a-zA-Z0-9_-]+", r"\g<0>"), # word chars match themselves + (r"[\[\]]", None), # Can't have single square brackets + (r".", r"\\\g<0>"), # Anything else is escaped to be safe +]] +# fmt: on + + +def _glob_to_regex(pattern: str) -> str: + """Convert a file-path glob pattern into a regex.""" + # Turn all backslashes into slashes to simplify the tokenizer. + pattern = pattern.replace("\\", "/") + if "/" not in pattern: + pattern = f"**/{pattern}" + path_rx = [] + pos = 0 + while pos < len(pattern): + for rx, sub in G2RX_TOKENS: # pragma: always breaks + if m := rx.match(pattern, pos=pos): + if sub is None: + raise ConfigError(f"File pattern can't include {m[0]!r}") + path_rx.append(m.expand(sub)) + pos = m.end() + break + return "".join(path_rx) + + +def globs_to_regex( + patterns: Iterable[str], + case_insensitive: bool = False, + partial: bool = False, +) -> re.Pattern[str]: + """Convert glob patterns to a compiled regex that matches any of them. + + Slashes are always converted to match either slash or backslash, for + Windows support, even when running elsewhere. + + If the pattern has no slash or backslash, then it is interpreted as + matching a file name anywhere it appears in the tree. Otherwise, the glob + pattern must match the whole file path. + + If `partial` is true, then the pattern will match if the target string + starts with the pattern. Otherwise, it must match the entire string. + + Returns: a compiled regex object. Use the .match method to compare target + strings. + + """ + flags = 0 + if case_insensitive: + flags |= re.IGNORECASE + rx = join_regex(map(_glob_to_regex, patterns)) + if not partial: + rx = rf"(?:{rx})\Z" + compiled = re.compile(rx, flags=flags) + return compiled + + +class PathAliases: + """A collection of aliases for paths. + + When combining data files from remote machines, often the paths to source + code are different, for example, due to OS differences, or because of + serialized checkouts on continuous integration machines. + + A `PathAliases` object tracks a list of pattern/result pairs, and can + map a path through those aliases to produce a unified path. + + """ + + def __init__( + self, + debugfn: Callable[[str], None] | None = None, + relative: bool = False, + ) -> None: + # A list of (original_pattern, regex, result) + self.aliases: list[tuple[str, re.Pattern[str], str]] = [] + self.debugfn = debugfn or (lambda msg: 0) + self.relative = relative + self.pprinted = False + + def pprint(self) -> None: + """Dump the important parts of the PathAliases, for debugging.""" + self.debugfn(f"Aliases (relative={self.relative}):") + for original_pattern, regex, result in self.aliases: + self.debugfn(f" Rule: {original_pattern!r} -> {result!r} using regex {regex.pattern!r}") + + def add(self, pattern: str, result: str) -> None: + """Add the `pattern`/`result` pair to the list of aliases. + + `pattern` is an `glob`-style pattern. `result` is a simple + string. When mapping paths, if a path starts with a match against + `pattern`, then that match is replaced with `result`. This models + isomorphic source trees being rooted at different places on two + different machines. + + `pattern` can't end with a wildcard component, since that would + match an entire tree, and not just its root. + + """ + original_pattern = pattern + pattern_sep = sep(pattern) + + if len(pattern) > 1: + pattern = pattern.rstrip(r"\/") + + # The pattern can't end with a wildcard component. + if pattern.endswith("*"): + raise ConfigError("Pattern must not end with wildcards.") + + # The pattern is meant to match a file path. Let's make it absolute + # unless it already is, or is meant to match any prefix. + if not self.relative: + if not pattern.startswith("*") and not isabs_anywhere(pattern + pattern_sep): + pattern = abs_file(pattern) + if not pattern.endswith(pattern_sep): + pattern += pattern_sep + + # Make a regex from the pattern. + regex = globs_to_regex([pattern], case_insensitive=True, partial=True) + + # Normalize the result: it must end with a path separator. + result_sep = sep(result) + result = result.rstrip(r"\/") + result_sep + self.aliases.append((original_pattern, regex, result)) + + def map(self, path: str, exists: Callable[[str], bool] = source_exists) -> str: + """Map `path` through the aliases. + + `path` is checked against all of the patterns. The first pattern to + match is used to replace the root of the path with the result root. + Only one pattern is ever used. If no patterns match, `path` is + returned unchanged. + + The separator style in the result is made to match that of the result + in the alias. + + `exists` is a function to determine if the resulting path actually + exists. + + Returns the mapped path. If a mapping has happened, this is a + canonical path. If no mapping has happened, it is the original value + of `path` unchanged. + + """ + if not self.pprinted: + self.pprint() + self.pprinted = True + + for original_pattern, regex, result in self.aliases: + if m := regex.match(path): + new = path.replace(m[0], result) + new = new.replace(sep(path), sep(result)) + if not self.relative: + new = canonical_filename(new) + dot_start = result.startswith(("./", ".\\")) and len(result) > 2 + if new.startswith(("./", ".\\")) and not dot_start: + new = new[2:] + if not exists(new): + self.debugfn( + f"Rule {original_pattern!r} changed {path!r} to {new!r} " + + "which doesn't exist, continuing", + ) + continue + self.debugfn( + f"Matched path {path!r} to rule {original_pattern!r} -> {result!r}, " + + f"producing {new!r}", + ) + return new + + # If we get here, no pattern matched. + + if self.relative: + path = relative_filename(path) + + if self.relative and not isabs_anywhere(path): + # Auto-generate a pattern to implicitly match relative files + parts = re.split(r"[/\\]", path) + if len(parts) > 1: + dir1 = parts[0] + pattern = f"*/{dir1}" + regex_pat = rf"^(.*[\\/])?{re.escape(dir1)}[\\/]" + result = f"{dir1}{os.sep}" + # Only add a new pattern if we don't already have this pattern. + if not any(p == pattern for p, _, _ in self.aliases): + self.debugfn( + f"Generating rule: {pattern!r} -> {result!r} using regex {regex_pat!r}", + ) + self.aliases.append((pattern, re.compile(regex_pat), result)) + return self.map(path, exists=exists) + + self.debugfn(f"No rules match, path {path!r} is unchanged") + return path + + +def find_python_files(dirname: str, include_namespace_packages: bool) -> Iterable[str]: + """Yield all of the importable Python files in `dirname`, recursively. + + To be importable, the files have to be in a directory with a __init__.py, + except for `dirname` itself, which isn't required to have one. The + assumption is that `dirname` was specified directly, so the user knows + best, but sub-directories are checked for a __init__.py to be sure we only + find the importable files. + + If `include_namespace_packages` is True, then the check for __init__.py + files is skipped. + + Files with strange characters are skipped, since they couldn't have been + imported, and are probably editor side-files. + + """ + for i, (dirpath, dirnames, filenames) in enumerate(os.walk(dirname)): + if not include_namespace_packages: + if i > 0 and "__init__.py" not in filenames: + # If a directory doesn't have __init__.py, then it isn't + # importable and neither are its files + del dirnames[:] + continue + for filename in filenames: + # We're only interested in files that look like reasonable Python + # files: Must end with .py or .pyw, and must not have certain funny + # characters that probably mean they are editor junk. + if re.match(r"^[^.#~!$@%^&*()+=,]+\.pyw?$", filename): + yield os.path.join(dirpath, filename) + + +# Globally set the relative directory. +set_relative_directory() diff --git a/venv/Lib/site-packages/coverage/html.py b/venv/Lib/site-packages/coverage/html.py new file mode 100644 index 0000000..807661c --- /dev/null +++ b/venv/Lib/site-packages/coverage/html.py @@ -0,0 +1,860 @@ +# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 +# For details: https://github.com/coveragepy/coveragepy/blob/main/NOTICE.txt + +"""HTML reporting for coverage.py.""" + +from __future__ import annotations + +import collections +import dataclasses +import datetime +import functools +import json +import os +import re +import string +from collections.abc import Iterable +from dataclasses import dataclass, field +from typing import TYPE_CHECKING, Any + +import coverage +from coverage.data import CoverageData, add_data_to_hash +from coverage.exceptions import NoDataError +from coverage.files import flat_rootname +from coverage.misc import ( + Hasher, + ensure_dir, + file_be_gone, + format_local_datetime, + human_sorted, + isolate_module, + plural, + stdout_link, +) +from coverage.report_core import get_analysis_to_report +from coverage.results import Analysis, AnalysisNarrower, Numbers +from coverage.templite import Templite +from coverage.types import TLineNo, TMorf +from coverage.version import __url__ + +if TYPE_CHECKING: + from coverage import Coverage + from coverage.plugins import FileReporter + + +os = isolate_module(os) + + +def data_filename(fname: str) -> str: + """Return the path to an "htmlfiles" data file of ours.""" + static_dir = os.path.join(os.path.dirname(__file__), "htmlfiles") + static_filename = os.path.join(static_dir, fname) + return static_filename + + +def read_data(fname: str) -> str: + """Return the contents of a data file of ours.""" + with open(data_filename(fname), encoding="utf-8") as data_file: + return data_file.read() + + +def write_html(fname: str, html: str) -> None: + """Write `html` to `fname`, properly encoded.""" + html = re.sub(r"(\A\s+)|(\s+$)", "", html, flags=re.MULTILINE) + "\n" + with open(fname, "wb") as fout: + fout.write(html.encode("ascii", "xmlcharrefreplace")) + + +@dataclass +class LineData: + """The data for each source line of HTML output.""" + + tokens: list[tuple[str, str]] + number: TLineNo + category: str + contexts: list[str] + contexts_label: str + context_list: list[str] + short_annotations: list[str] + long_annotations: list[str] + html: str = "" + context_str: str | None = None + annotate: str | None = None + annotate_long: str | None = None + css_class: str = "" + + +@dataclass +class FileData: + """The data for each source file of HTML output.""" + + relative_filename: str + nums: Numbers + lines: list[LineData] + + +@dataclass +class IndexItem: + """Information for each index entry, to render an index page.""" + + url: str = "" + file: str = "" + description: str = "" + nums: Numbers = field(default_factory=Numbers) + + +@dataclass +class IndexPage: + """Data for each index page.""" + + noun: str + plural: str + filename: str + summaries: list[IndexItem] + totals: Numbers + skipped_covered_count: int + skipped_empty_count: int + + +class HtmlDataGeneration: + """Generate structured data to be turned into HTML reports.""" + + EMPTY = "(empty)" + + def __init__(self, cov: Coverage) -> None: + self.coverage = cov + self.config = self.coverage.config + self.data = self.coverage.get_data() + self.has_arcs = self.data.has_arcs() + if self.config.show_contexts: + if self.data.measured_contexts() == {""}: + self.coverage._warn("No contexts were measured") + self.data.set_query_contexts(self.config.report_contexts) + + def data_for_file(self, fr: FileReporter, analysis: Analysis) -> FileData: + """Produce the data needed for one file's report.""" + if self.has_arcs: + missing_branch_arcs = analysis.missing_branch_arcs() + arcs_executed = analysis.arcs_executed + else: + missing_branch_arcs = {} + arcs_executed = [] + + if self.config.show_contexts: + contexts_by_lineno = self.data.contexts_by_lineno(analysis.filename) + + lines = [] + branch_stats = analysis.branch_stats() + multiline_map = {} + if hasattr(fr, "multiline_map"): + multiline_map = fr.multiline_map() + + for lineno, tokens in enumerate(fr.source_token_lines(), start=1): + # Figure out how to mark this line. + category = category2 = "" + short_annotations = [] + long_annotations = [] + + if lineno in analysis.excluded: + category = "exc" + elif lineno in analysis.missing: + category = "mis" + elif self.has_arcs and lineno in missing_branch_arcs: + category = "par" + mba = missing_branch_arcs[lineno] + if len(mba) == branch_stats[lineno][0]: + # None of the branches were taken from this line. + short_annotations.append("anywhere") + long_annotations.append( + f"line {lineno} didn't jump anywhere: it always raised an exception." + ) + else: + for b in missing_branch_arcs[lineno]: + if b < 0: + short_annotations.append("exit") + else: + short_annotations.append(str(b)) + long_annotations.append( + fr.missing_arc_description(lineno, b, arcs_executed) + ) + elif lineno in analysis.statements: + category = "run" + elif first_line := multiline_map.get(lineno): + if first_line in analysis.excluded: + category2 = "exc2" + elif first_line in analysis.missing: + category2 = "mis2" + elif self.has_arcs and first_line in missing_branch_arcs: + category2 = "par2" + # I don't understand why this last condition is marked as + # partial. If I add an else with an exception, the exception + # is raised. + elif first_line in analysis.statements: # pragma: part covered + category2 = "run2" + + contexts = [] + contexts_label = "" + context_list = [] + if category and self.config.show_contexts: + contexts = human_sorted(c or self.EMPTY for c in contexts_by_lineno.get(lineno, ())) + if contexts == [self.EMPTY]: + contexts_label = self.EMPTY + else: + contexts_label = f"{len(contexts)} ctx" + context_list = contexts + + lines.append( + LineData( + tokens=tokens, + number=lineno, + category=category or category2, + contexts=contexts, + contexts_label=contexts_label, + context_list=context_list, + short_annotations=short_annotations, + long_annotations=long_annotations, + ) + ) + + file_data = FileData( + relative_filename=fr.relative_filename(), + nums=analysis.numbers, + lines=lines, + ) + + return file_data + + +class FileToReport: + """A file we're considering reporting.""" + + def __init__(self, fr: FileReporter, analysis: Analysis) -> None: + self.fr = fr + self.analysis = analysis + self.rootname = flat_rootname(fr.relative_filename()) + self.html_filename = self.rootname + ".html" + self.prev_html = self.next_html = "" + + +HTML_SAFE = string.ascii_letters + string.digits + "!#$%'()*+,-./:;=?@[]^_`{|}~" + + +@functools.cache +def encode_int(n: int) -> str: + """Create a short HTML-safe string from an integer, using HTML_SAFE.""" + if n == 0: + return HTML_SAFE[0] + + r = [] + while n: + n, t = divmod(n, len(HTML_SAFE)) + r.append(HTML_SAFE[t]) + return "".join(r) + + +def copy_with_cache_bust(src: str, dest_dir: str) -> str: + """Copy `src` to `dest_dir`, adding a hash to the name. + + Returns the updated destination file name with hash. + """ + with open(src, "rb") as f: + text = f.read() + h = Hasher() + h.update(text) + cache_bust = h.hexdigest()[:8] + src_base = os.path.basename(src) + dest = src_base.replace(".", f"_cb_{cache_bust}.") + with open(os.path.join(dest_dir, dest), "wb") as f: + f.write(text) + return dest + + +class HtmlReporter: + """HTML reporting.""" + + # These files will be copied from the htmlfiles directory to the output + # directory. + STATIC_FILES = [ + "style.css", + "coverage_html.js", + "keybd_closed.png", + "favicon_32.png", + ] + + def __init__(self, cov: Coverage) -> None: + self.coverage = cov + self.config = self.coverage.config + self.directory = self.config.html_dir + + self.skip_covered = self.config.html_skip_covered + if self.skip_covered is None: + self.skip_covered = self.config.skip_covered + self.skip_empty = self.config.html_skip_empty + if self.skip_empty is None: + self.skip_empty = self.config.skip_empty + + title = self.config.html_title + + self.extra_css = bool(self.config.extra_css) + + self.data = self.coverage.get_data() + self.has_arcs = self.data.has_arcs() + + self.index_pages: dict[str, IndexPage] = { + "file": self.new_index_page("file", "files"), + } + self.incr = IncrementalChecker(self.directory) + self.datagen = HtmlDataGeneration(self.coverage) + self.directory_was_empty = False + self.first_fr = None + self.final_fr = None + + self.template_globals = { + # Functions available in the templates. + "escape": escape, + "pair": pair, + "pretty_file": pretty_file, + # Constants for this report. + "__url__": __url__, + "__version__": coverage.__version__, + "title": title, + "time_stamp": format_local_datetime(datetime.datetime.now()), + "extra_css": self.extra_css, + "has_arcs": self.has_arcs, + "statics": {}, + # Constants for all reports. + # These css classes determine which lines are highlighted by default. + "category": { + "exc": "exc show_exc", + "mis": "mis show_mis", + "par": "par run show_par", + "run": "run", + "exc2": "exc exc2 show_exc", + "mis2": "mis mis2 show_mis", + "par2": "par par2 ru2 show_par", + "run2": "run run2", + }, + } + self.index_tmpl = Templite(read_data("index.html"), self.template_globals) + self.pyfile_html_source = read_data("pyfile.html") + self.source_tmpl = Templite(self.pyfile_html_source, self.template_globals) + + def new_index_page(self, noun: str, plural_noun: str) -> IndexPage: + """Create an IndexPage for a kind of region.""" + return IndexPage( + noun=noun, + plural=plural_noun, + filename="index.html" if noun == "file" else f"{noun}_index.html", + summaries=[], + totals=Numbers(precision=self.config.precision), + skipped_covered_count=0, + skipped_empty_count=0, + ) + + def report(self, morfs: Iterable[TMorf] | None) -> float: + """Generate an HTML report for `morfs`. + + `morfs` is a list of modules or file names. + + """ + # Read the status data and check that this run used the same + # global data as the last run. + self.incr.read() + self.incr.check_global_data(self.config, self.pyfile_html_source) + + # Process all the files. For each page we need to supply a link + # to the next and previous page. + files_to_report = [] + + have_data = False + for fr, analysis in get_analysis_to_report(self.coverage, morfs): + have_data = True + ftr = FileToReport(fr, analysis) + if self.should_report(analysis, self.index_pages["file"]): + files_to_report.append(ftr) + else: + file_be_gone(os.path.join(self.directory, ftr.html_filename)) + + if not have_data: + raise NoDataError("No data to report.") + + self.make_directory() + self.make_local_static_report_files() + + if files_to_report: + for ftr1, ftr2 in zip(files_to_report[:-1], files_to_report[1:]): + ftr1.next_html = ftr2.html_filename + ftr2.prev_html = ftr1.html_filename + files_to_report[0].prev_html = "index.html" + files_to_report[-1].next_html = "index.html" + + for ftr in files_to_report: + self.write_html_page(ftr) + for noun, plural_noun in ftr.fr.code_region_kinds(): + if noun not in self.index_pages: + self.index_pages[noun] = self.new_index_page(noun, plural_noun) + + # Write the index page. + if files_to_report: + first_html = files_to_report[0].html_filename + final_html = files_to_report[-1].html_filename + else: + first_html = final_html = "index.html" + self.write_file_index_page(first_html, final_html) + + # Write function and class index pages. + self.write_region_index_pages(files_to_report) + + return ( + self.index_pages["file"].totals.n_statements + and self.index_pages["file"].totals.pc_covered + ) + + def make_directory(self) -> None: + """Make sure our htmlcov directory exists.""" + ensure_dir(self.directory) + if not os.listdir(self.directory): + self.directory_was_empty = True + + def copy_static_file(self, src: str, slug: str = "") -> None: + """Copy a static file into the output directory with cache busting.""" + dest = copy_with_cache_bust(src, self.directory) + if not slug: + slug = os.path.basename(src).replace(".", "_") + self.template_globals["statics"][slug] = dest # type: ignore + + def make_local_static_report_files(self) -> None: + """Make local instances of static files for HTML report.""" + + # The files we provide must always be copied. + for static in self.STATIC_FILES: + self.copy_static_file(data_filename(static)) + + # The user may have extra CSS they want copied. + if self.extra_css: + assert self.config.extra_css is not None + self.copy_static_file(self.config.extra_css, slug="extra_css") + + # Only write the .gitignore file if the directory was originally empty. + # .gitignore can't be copied from the source tree because if it was in + # the source tree, it would stop the static files from being checked in. + if self.directory_was_empty: + with open(os.path.join(self.directory, ".gitignore"), "w", encoding="utf-8") as fgi: + fgi.write("# Created by coverage.py\n*\n") + + def should_report(self, analysis: Analysis, index_page: IndexPage) -> bool: + """Determine if we'll report this file or region.""" + # Get the numbers for this file. + nums = analysis.numbers + index_page.totals += nums + + if self.skip_covered: + # Don't report on 100% files. + no_missing_lines = (nums.n_missing == 0) # fmt: skip + no_missing_branches = (nums.n_partial_branches == 0) # fmt: skip + if no_missing_lines and no_missing_branches: + index_page.skipped_covered_count += 1 + return False + + if self.skip_empty: + # Don't report on empty files. + if nums.n_statements == 0: + index_page.skipped_empty_count += 1 + return False + + return True + + def write_html_page(self, ftr: FileToReport) -> None: + """Generate an HTML page for one source file. + + If the page on disk is already correct based on our incremental status + checking, then the page doesn't have to be generated, and this function + only does page summary bookkeeping. + + """ + # Find out if the page on disk is already correct. + if self.incr.can_skip_file(self.data, ftr.fr, ftr.rootname): + self.index_pages["file"].summaries.append(self.incr.index_info(ftr.rootname)) + return + + # Write the HTML page for this source file. + file_data = self.datagen.data_for_file(ftr.fr, ftr.analysis) + + contexts = collections.Counter(c for cline in file_data.lines for c in cline.contexts) + context_codes = {y: i for (i, y) in enumerate(x[0] for x in contexts.most_common())} + if context_codes: + contexts_json = json.dumps( + {encode_int(v): k for (k, v) in context_codes.items()}, + indent=2, + ) + else: + contexts_json = None + + for ldata in file_data.lines: + # Build the HTML for the line. + html_parts = [] + for tok_type, tok_text in ldata.tokens: + if tok_type == "ws": + html_parts.append(escape(tok_text)) + else: + tok_html = escape(tok_text) or " " + html_parts.append(f'{tok_html}') + ldata.html = "".join(html_parts) + if ldata.context_list: + encoded_contexts = [ + encode_int(context_codes[c_context]) for c_context in ldata.context_list + ] + code_width = max(len(ec) for ec in encoded_contexts) + ldata.context_str = str(code_width) + "".join( + ec.ljust(code_width) for ec in encoded_contexts + ) + else: + ldata.context_str = "" + + if ldata.short_annotations: + # 202F is NARROW NO-BREAK SPACE. + # 219B is RIGHTWARDS ARROW WITH STROKE. + ldata.annotate = ",   ".join( + f"{ldata.number} ↛ {d}" for d in ldata.short_annotations + ) + else: + ldata.annotate = None + + if ldata.long_annotations: + longs = ldata.long_annotations + # A line can only have two branch destinations. If there were + # two missing, we would have written one as "always raised." + assert len(longs) == 1, ( + f"Had long annotations in {ftr.fr.relative_filename()}: {longs}" + ) + ldata.annotate_long = longs[0] + else: + ldata.annotate_long = None + + css_classes = [] + if ldata.category: + css_classes.append( + self.template_globals["category"][ldata.category], # type: ignore[index] + ) + ldata.css_class = " ".join(css_classes) or "pln" + + html_path = os.path.join(self.directory, ftr.html_filename) + html = self.source_tmpl.render( + { + **file_data.__dict__, + "contexts_json": contexts_json, + "prev_html": ftr.prev_html, + "next_html": ftr.next_html, + } + ) + write_html(html_path, html) + + # Save this file's information for the index page. + index_info = IndexItem( + url=ftr.html_filename, + file=escape(ftr.fr.relative_filename()), + nums=ftr.analysis.numbers, + ) + self.index_pages["file"].summaries.append(index_info) + self.incr.set_index_info(ftr.rootname, index_info) + + def write_file_index_page(self, first_html: str, final_html: str) -> None: + """Write the file index page for this report.""" + index_file = self.write_index_page( + self.index_pages["file"], + first_html=first_html, + final_html=final_html, + ) + + print_href = stdout_link(index_file, f"file://{os.path.abspath(index_file)}") + self.coverage._message(f"Wrote HTML report to {print_href}") + + # Write the latest hashes for next time. + self.incr.write() + + def write_region_index_pages(self, files_to_report: Iterable[FileToReport]) -> None: + """Write the other index pages for this report.""" + for ftr in files_to_report: + region_nouns = [pair[0] for pair in ftr.fr.code_region_kinds()] + num_lines = len(ftr.fr.source().splitlines()) + regions = ftr.fr.code_regions() + + for noun in region_nouns: + page_data = self.index_pages[noun] + + outside_lines = set(range(1, num_lines + 1)) + for region in regions: + if region.kind != noun: + continue + outside_lines -= region.lines + + narrower = AnalysisNarrower(ftr.analysis) + narrower.add_regions(r.lines for r in regions if r.kind == noun) + narrower.add_regions([outside_lines]) + + for region in regions: + if region.kind != noun: + continue + analysis = narrower.narrow(region.lines) + if not self.should_report(analysis, page_data): + continue + sorting_name = region.name.rpartition(".")[-1].lstrip("_") + page_data.summaries.append( + IndexItem( + url=f"{ftr.html_filename}#t{region.start}", + file=escape(ftr.fr.relative_filename()), + description=( + f"" + + escape(region.name) + + "" + ), + nums=analysis.numbers, + ) + ) + + analysis = narrower.narrow(outside_lines) + if self.should_report(analysis, page_data): + page_data.summaries.append( + IndexItem( + url=ftr.html_filename, + file=escape(ftr.fr.relative_filename()), + description=( + "" + + f"(no {escape(noun)})" + + "" + ), + nums=analysis.numbers, + ) + ) + + for noun, index_page in self.index_pages.items(): + if noun != "file": + self.write_index_page(index_page) + + def write_index_page(self, index_page: IndexPage, **kwargs: str) -> str: + """Write an index page specified by `index_page`. + + Returns the filename created. + """ + skipped_covered_msg = skipped_empty_msg = "" + if n := index_page.skipped_covered_count: + word = plural(n, index_page.noun, index_page.plural) + skipped_covered_msg = f"{n} {word} skipped due to complete coverage." + if n := index_page.skipped_empty_count: + word = plural(n, index_page.noun, index_page.plural) + skipped_empty_msg = f"{n} empty {word} skipped." + + index_buttons = [ + { + "label": ip.plural.title(), + "url": ip.filename if ip.noun != index_page.noun else "", + "current": ip.noun == index_page.noun, + } + for ip in self.index_pages.values() + ] + render_data = { + "regions": index_page.summaries, + "totals": index_page.totals, + "noun": index_page.noun, + "region_noun": index_page.noun if index_page.noun != "file" else "", + "skip_covered": self.skip_covered, + "skipped_covered_msg": skipped_covered_msg, + "skipped_empty_msg": skipped_empty_msg, + "first_html": "", + "final_html": "", + "index_buttons": index_buttons, + } + render_data.update(kwargs) + html = self.index_tmpl.render(render_data) + + index_file = os.path.join(self.directory, index_page.filename) + write_html(index_file, html) + return index_file + + +@dataclass +class FileInfo: + """Summary of the information from last rendering, to avoid duplicate work.""" + + hash: str = "" + index: IndexItem = field(default_factory=IndexItem) + + +class IncrementalChecker: + """Logic and data to support incremental reporting. + + When generating an HTML report, often only a few of the source files have + changed since the last time we made the HTML report. This means previously + created HTML pages can be reused without generating them again, speeding + the command. + + This class manages a JSON data file that captures enough information to + know whether an HTML page for a .py file needs to be regenerated or not. + The data file also needs to store all the information needed to create the + entry for the file on the index page so that if the HTML page is reused, + the index page can still be created to refer to it. + + The data looks like:: + + { + "note": "This file is an internal implementation detail ...", + // A fixed number indicating the data format. STATUS_FORMAT + "format": 5, + // The version of coverage.py + "version": "7.4.4", + // A hash of a number of global things, including the configuration + // settings and the pyfile.html template itself. + "globals": "540ee119c15d52a68a53fe6f0897346d", + "files": { + // An entry for each source file keyed by the flat_rootname(). + "z_7b071bdc2a35fa80___init___py": { + // Hash of the source, the text of the .py file. + "hash": "e45581a5b48f879f301c0f30bf77a50c", + // Information for the index.html file. + "index": { + "url": "z_7b071bdc2a35fa80___init___py.html", + "file": "cogapp/__init__.py", + "description": "", + // The Numbers for this file. + "nums": { "precision": 2, "n_files": 1, "n_statements": 43, ... } + } + }, + ... + } + } + + """ + + STATUS_FILE = "status.json" + STATUS_FORMAT = 5 + NOTE = ( + "This file is an internal implementation detail to speed up HTML report" + + " generation. Its format can change at any time. You might be looking" + + " for the JSON report: https://coverage.rtfd.io/cmd.html#cmd-json" + ) + + def __init__(self, directory: str) -> None: + self.directory = directory + self._reset() + + def _reset(self) -> None: + """Initialize to empty. Causes all files to be reported.""" + self.globals = "" + self.files: dict[str, FileInfo] = {} + + def read(self) -> None: + """Read the information we stored last time.""" + try: + status_file = os.path.join(self.directory, self.STATUS_FILE) + with open(status_file, encoding="utf-8") as fstatus: + status = json.load(fstatus) + except (OSError, ValueError): + # Status file is missing or malformed. + usable = False + else: + if status["format"] != self.STATUS_FORMAT: + usable = False + elif status["version"] != coverage.__version__: + usable = False + else: + usable = True + + if usable: + self.files = {} + for filename, filedict in status["files"].items(): + indexdict = filedict["index"] + index_item = IndexItem(**indexdict) + index_item.nums = Numbers(**indexdict["nums"]) + fileinfo = FileInfo( + hash=filedict["hash"], + index=index_item, + ) + self.files[filename] = fileinfo + self.globals = status["globals"] + else: + self._reset() + + def write(self) -> None: + """Write the current status.""" + status_file = os.path.join(self.directory, self.STATUS_FILE) + status_data = { + "note": self.NOTE, + "format": self.STATUS_FORMAT, + "version": coverage.__version__, + "globals": self.globals, + "files": {fname: dataclasses.asdict(finfo) for fname, finfo in self.files.items()}, + } + with open(status_file, "w", encoding="utf-8") as fout: + json.dump(status_data, fout, separators=(",", ":")) + + def check_global_data(self, *data: Any) -> None: + """Check the global data that can affect incremental reporting. + + Pass in whatever global information could affect the content of the + HTML pages. If the global data has changed since last time, this will + clear the data so that all files are regenerated. + + """ + m = Hasher() + for d in data: + m.update(d) + these_globals = m.hexdigest() + if self.globals != these_globals: + self._reset() + self.globals = these_globals + + def can_skip_file(self, data: CoverageData, fr: FileReporter, rootname: str) -> bool: + """Can we skip reporting this file? + + `data` is a CoverageData object, `fr` is a `FileReporter`, and + `rootname` is the name being used for the file. + + Returns True if the HTML page is fine as-is, False if we need to recreate + the HTML page. + + """ + m = Hasher() + m.update(fr.source().encode("utf-8")) + add_data_to_hash(data, fr.filename, m) + this_hash = m.hexdigest() + + file_info = self.files.setdefault(rootname, FileInfo()) + + if this_hash == file_info.hash: + # Nothing has changed to require the file to be reported again. + return True + else: + # File has changed, record the latest hash and force regeneration. + file_info.hash = this_hash + return False + + def index_info(self, fname: str) -> IndexItem: + """Get the information for index.html for `fname`.""" + return self.files.get(fname, FileInfo()).index + + def set_index_info(self, fname: str, info: IndexItem) -> None: + """Set the information for index.html for `fname`.""" + self.files.setdefault(fname, FileInfo()).index = info + + +# Helpers for templates and generating HTML + + +def escape(t: str) -> str: + """HTML-escape the text in `t`. + + This is only suitable for HTML text, not attributes. + + """ + # Convert HTML special chars into HTML entities. + return t.replace("&", "&").replace("<", "<") + + +def pair(ratio: tuple[int, int]) -> str: + """Format a pair of numbers so JavaScript can read them in an attribute.""" + return "{} {}".format(*ratio) + + +def pretty_file(filename: str) -> str: + """Return a prettier version of `filename` for display.""" + return re.sub(r"[/\\]", "\N{THIN SPACE}\\g<0>\N{THIN SPACE}", filename) diff --git a/venv/Lib/site-packages/coverage/htmlfiles/coverage_html.js b/venv/Lib/site-packages/coverage/htmlfiles/coverage_html.js new file mode 100644 index 0000000..6f87174 --- /dev/null +++ b/venv/Lib/site-packages/coverage/htmlfiles/coverage_html.js @@ -0,0 +1,735 @@ +// Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 +// For details: https://github.com/coveragepy/coveragepy/blob/main/NOTICE.txt + +// Coverage.py HTML report browser code. +/*jslint browser: true, sloppy: true, vars: true, plusplus: true, maxerr: 50, indent: 4 */ +/*global coverage: true, document, window, $ */ + +coverage = {}; + +// General helpers +function debounce(callback, wait) { + let timeoutId = null; + return function(...args) { + clearTimeout(timeoutId); + timeoutId = setTimeout(() => { + callback.apply(this, args); + }, wait); + }; +}; + +function checkVisible(element) { + const rect = element.getBoundingClientRect(); + const viewBottom = Math.max(document.documentElement.clientHeight, window.innerHeight); + const viewTop = 30; + return !(rect.bottom < viewTop || rect.top >= viewBottom); +} + +function on_click(sel, fn) { + const elt = document.querySelector(sel); + if (elt) { + elt.addEventListener("click", fn); + } +} + +// Helpers for table sorting +function getCellValue(row, column = 0) { + const cell = row.cells[column] // nosemgrep: eslint.detect-object-injection + if (cell.childElementCount == 1) { + var child = cell.firstElementChild; + if (child.tagName === "A") { + child = child.firstElementChild; + } + if (child instanceof HTMLDataElement && child.value) { + return child.value; + } + } + return cell.innerText || cell.textContent; +} + +function rowComparator(rowA, rowB, column = 0) { + let valueA = getCellValue(rowA, column); + let valueB = getCellValue(rowB, column); + if (!isNaN(valueA) && !isNaN(valueB)) { + return valueA - valueB; + } + return valueA.localeCompare(valueB, undefined, {numeric: true}); +} + +function sortColumn(th) { + // Get the current sorting direction of the selected header, + // clear state on other headers and then set the new sorting direction. + const currentSortOrder = th.getAttribute("aria-sort"); + [...th.parentElement.cells].forEach(header => header.setAttribute("aria-sort", "none")); + var direction; + if (currentSortOrder === "none") { + direction = th.dataset.defaultSortOrder || "ascending"; + } + else if (currentSortOrder === "ascending") { + direction = "descending"; + } + else { + direction = "ascending"; + } + th.setAttribute("aria-sort", direction); + + const column = [...th.parentElement.cells].indexOf(th) + + // Sort all rows and afterwards append them in order to move them in the DOM. + Array.from(th.closest("table").querySelectorAll("tbody tr")) + .sort((rowA, rowB) => rowComparator(rowA, rowB, column) * (direction === "ascending" ? 1 : -1)) + .forEach(tr => tr.parentElement.appendChild(tr)); + + // Save the sort order for next time. + if (th.id !== "region") { + let th_id = "file"; // Sort by file if we don't have a column id + let current_direction = direction; + const stored_list = localStorage.getItem(coverage.INDEX_SORT_STORAGE); + if (stored_list) { + ({th_id, direction} = JSON.parse(stored_list)) + } + localStorage.setItem(coverage.INDEX_SORT_STORAGE, JSON.stringify({ + "th_id": th.id, + "direction": current_direction + })); + if (th.id !== th_id || document.getElementById("region")) { + // Sort column has changed, unset sorting by function or class. + localStorage.setItem(coverage.SORTED_BY_REGION, JSON.stringify({ + "by_region": false, + "region_direction": current_direction + })); + } + } + else { + // Sort column has changed to by function or class, remember that. + localStorage.setItem(coverage.SORTED_BY_REGION, JSON.stringify({ + "by_region": true, + "region_direction": direction + })); + } +} + +// Find all the elements with data-shortcut attribute, and use them to assign a shortcut key. +coverage.assign_shortkeys = function () { + document.querySelectorAll("[data-shortcut]").forEach(element => { + document.addEventListener("keypress", event => { + if (event.target.tagName.toLowerCase() === "input") { + return; // ignore keypress from search filter + } + if (event.key === element.dataset.shortcut) { + element.click(); + } + }); + }); +}; + +// Create the events for the filter box. +coverage.wire_up_filter = function () { + // Populate the filter and hide100 inputs if there are saved values for them. + const saved_filter_value = localStorage.getItem(coverage.FILTER_STORAGE); + if (saved_filter_value) { + document.getElementById("filter").value = saved_filter_value; + } + const saved_hide100_value = localStorage.getItem(coverage.HIDE100_STORAGE); + if (saved_hide100_value) { + document.getElementById("hide100").checked = JSON.parse(saved_hide100_value); + } + + // Cache elements. + const table = document.querySelector("table.index"); + const table_body_rows = table.querySelectorAll("tbody tr"); + const no_rows = document.getElementById("no_rows"); + + const footer = table.tFoot.rows[0]; + const ratio_columns = Array.from(footer.cells).map(cell => Boolean(cell.dataset.ratio)); + + // Observe filter keyevents. + const filter_handler = (event => { + // Keep running total of each metric, first index contains number of shown rows + const totals = ratio_columns.map( + is_ratio => is_ratio ? {"numer": 0, "denom": 0} : 0 + ); + + var text = document.getElementById("filter").value; + // Store filter value + localStorage.setItem(coverage.FILTER_STORAGE, text); + const casefold = (text === text.toLowerCase()); + const hide100 = document.getElementById("hide100").checked; + // Store hide value. + localStorage.setItem(coverage.HIDE100_STORAGE, JSON.stringify(hide100)); + + // Hide / show elements. + table_body_rows.forEach(row => { + var show = false; + // Check the text filter. + for (let column = 0; column < totals.length; column++) { + cell = row.cells[column]; + if (cell.classList.contains("name")) { + var celltext = cell.textContent; + if (casefold) { + celltext = celltext.toLowerCase(); + } + if (celltext.includes(text)) { + show = true; + } + } + } + + // Check the "hide covered" filter. + if (show && hide100) { + const [numer, denom] = row.cells[row.cells.length - 1].dataset.ratio.split(" "); + show = (numer !== denom); + } + + if (!show) { + // hide + row.classList.add("hidden"); + return; + } + + // show + row.classList.remove("hidden"); + totals[0]++; + + for (let column = 0; column < totals.length; column++) { + // Accumulate dynamic totals + cell = row.cells[column] // nosemgrep: eslint.detect-object-injection + if (cell.matches(".name, .spacer")) { + continue; + } + if (ratio_columns[column] && cell.dataset.ratio) { + // Column stores a ratio + const [numer, denom] = cell.dataset.ratio.split(" "); + totals[column]["numer"] += parseInt(numer, 10); // nosemgrep: eslint.detect-object-injection + totals[column]["denom"] += parseInt(denom, 10); // nosemgrep: eslint.detect-object-injection + } + else { + totals[column] += parseInt(cell.textContent, 10); // nosemgrep: eslint.detect-object-injection + } + } + }); + + // Show placeholder if no rows will be displayed. + if (!totals[0]) { + // Show placeholder, hide table. + no_rows.style.display = "block"; + table.style.display = "none"; + return; + } + + // Hide placeholder, show table. + no_rows.style.display = null; + table.style.display = null; + + // Calculate new dynamic sum values based on visible rows. + for (let column = 0; column < totals.length; column++) { + // Get footer cell element. + const cell = footer.cells[column]; // nosemgrep: eslint.detect-object-injection + if (cell.matches(".name, .spacer")) { + continue; + } + + // Set value into dynamic footer cell element. + if (ratio_columns[column]) { + // Percentage column uses the numerator and denominator, + // and adapts to the number of decimal places. + const match = /\.([0-9]+)/.exec(cell.textContent); + const places = match ? match[1].length : 0; + const { numer, denom } = totals[column]; // nosemgrep: eslint.detect-object-injection + cell.dataset.ratio = `${numer} ${denom}`; + // Check denom to prevent NaN if filtered files contain no statements + cell.textContent = denom + ? `${(numer * 100 / denom).toFixed(places)}%` + : `${(100).toFixed(places)}%`; + } + else { + cell.textContent = totals[column]; // nosemgrep: eslint.detect-object-injection + } + } + }); + + document.getElementById("filter").addEventListener("input", debounce(filter_handler)); + document.getElementById("hide100").addEventListener("input", debounce(filter_handler)); + + // Trigger change event on setup, to force filter on page refresh + // (filter value may still be present). + document.getElementById("filter").dispatchEvent(new Event("input")); + document.getElementById("hide100").dispatchEvent(new Event("input")); +}; +coverage.FILTER_STORAGE = "COVERAGE_FILTER_VALUE"; +coverage.HIDE100_STORAGE = "COVERAGE_HIDE100_VALUE"; + +// Set up the click-to-sort columns. +coverage.wire_up_sorting = function () { + document.querySelectorAll("[data-sortable] th[aria-sort]").forEach( + th => th.addEventListener("click", e => sortColumn(e.target)) + ); + + // Look for a localStorage item containing previous sort settings: + let th_id = "file", direction = "ascending"; + const stored_list = localStorage.getItem(coverage.INDEX_SORT_STORAGE); + if (stored_list) { + ({th_id, direction} = JSON.parse(stored_list)); + } + let by_region = false, region_direction = "ascending"; + const sorted_by_region = localStorage.getItem(coverage.SORTED_BY_REGION); + if (sorted_by_region) { + ({ + by_region, + region_direction + } = JSON.parse(sorted_by_region)); + } + + const region_id = "region"; + if (by_region && document.getElementById(region_id)) { + direction = region_direction; + } + // If we are in a page that has a column with id of "region", sort on + // it if the last sort was by function or class. + let th; + if (document.getElementById(region_id)) { + th = document.getElementById(by_region ? region_id : th_id); + } + else { + th = document.getElementById(th_id); + } + th.setAttribute("aria-sort", direction === "ascending" ? "descending" : "ascending"); + th.click() +}; + +coverage.INDEX_SORT_STORAGE = "COVERAGE_INDEX_SORT_2"; +coverage.SORTED_BY_REGION = "COVERAGE_SORT_REGION"; + +// Loaded on index.html +coverage.index_ready = function () { + coverage.assign_shortkeys(); + coverage.wire_up_filter(); + coverage.wire_up_sorting(); + + on_click(".button_prev_file", coverage.to_prev_file); + on_click(".button_next_file", coverage.to_next_file); + + on_click(".button_show_hide_help", coverage.show_hide_help); +}; + +// -- pyfile stuff -- + +coverage.LINE_FILTERS_STORAGE = "COVERAGE_LINE_FILTERS"; + +coverage.pyfile_ready = function () { + // If we're directed to a particular line number, highlight the line. + var frag = location.hash; + if (frag.length > 2 && frag[1] === "t") { + document.querySelector(frag).closest(".n").classList.add("highlight"); + coverage.set_sel(parseInt(frag.substr(2), 10)); + } + else { + coverage.set_sel(0); + } + + on_click(".button_toggle_run", coverage.toggle_lines); + on_click(".button_toggle_mis", coverage.toggle_lines); + on_click(".button_toggle_exc", coverage.toggle_lines); + on_click(".button_toggle_par", coverage.toggle_lines); + + on_click(".button_next_chunk", coverage.to_next_chunk_nicely); + on_click(".button_prev_chunk", coverage.to_prev_chunk_nicely); + on_click(".button_top_of_page", coverage.to_top); + on_click(".button_first_chunk", coverage.to_first_chunk); + + on_click(".button_prev_file", coverage.to_prev_file); + on_click(".button_next_file", coverage.to_next_file); + on_click(".button_to_index", coverage.to_index); + + on_click(".button_show_hide_help", coverage.show_hide_help); + + coverage.filters = undefined; + try { + coverage.filters = localStorage.getItem(coverage.LINE_FILTERS_STORAGE); + } catch(err) {} + + if (coverage.filters) { + coverage.filters = JSON.parse(coverage.filters); + } + else { + coverage.filters = {run: false, exc: true, mis: true, par: true}; + } + + for (cls in coverage.filters) { + coverage.set_line_visibilty(cls, coverage.filters[cls]); // nosemgrep: eslint.detect-object-injection + } + + coverage.assign_shortkeys(); + coverage.init_scroll_markers(); + coverage.wire_up_sticky_header(); + + document.querySelectorAll("[id^=ctxs]").forEach( + cbox => cbox.addEventListener("click", coverage.expand_contexts) + ); + + // Rebuild scroll markers when the window height changes. + window.addEventListener("resize", coverage.build_scroll_markers); +}; + +coverage.toggle_lines = function (event) { + const btn = event.target.closest("button"); + const category = btn.value + const show = !btn.classList.contains("show_" + category); + coverage.set_line_visibilty(category, show); + coverage.build_scroll_markers(); + coverage.filters[category] = show; + try { + localStorage.setItem(coverage.LINE_FILTERS_STORAGE, JSON.stringify(coverage.filters)); + } catch(err) {} +}; + +coverage.set_line_visibilty = function (category, should_show) { + const cls = "show_" + category; + const btn = document.querySelector(".button_toggle_" + category); + if (btn) { + if (should_show) { + document.querySelectorAll("#source ." + category).forEach(e => e.classList.add(cls)); + btn.classList.add(cls); + } + else { + document.querySelectorAll("#source ." + category).forEach(e => e.classList.remove(cls)); + btn.classList.remove(cls); + } + } +}; + +// Return the nth line div. +coverage.line_elt = function (n) { + return document.getElementById("t" + n)?.closest("p"); +}; + +// Set the selection. b and e are line numbers. +coverage.set_sel = function (b, e) { + // The first line selected. + coverage.sel_begin = b; + // The next line not selected. + coverage.sel_end = (e === undefined) ? b+1 : e; +}; + +coverage.to_top = function () { + coverage.set_sel(0, 1); + coverage.scroll_window(0); +}; + +coverage.to_first_chunk = function () { + coverage.set_sel(0, 1); + coverage.to_next_chunk(); +}; + +coverage.to_prev_file = function () { + window.location = document.getElementById("prevFileLink").href; +} + +coverage.to_next_file = function () { + window.location = document.getElementById("nextFileLink").href; +} + +coverage.to_index = function () { + location.href = document.getElementById("indexLink").href; +} + +coverage.show_hide_help = function () { + const helpCheck = document.getElementById("help_panel_state") + helpCheck.checked = !helpCheck.checked; +} + +// Return a string indicating what kind of chunk this line belongs to, +// or null if not a chunk. +coverage.chunk_indicator = function (line_elt) { + const classes = line_elt?.className; + if (!classes) { + return null; + } + const match = classes.match(/\bshow_\w+\b/); + if (!match) { + return null; + } + return match[0]; +}; + +coverage.to_next_chunk = function () { + const c = coverage; + + // Find the start of the next colored chunk. + var probe = c.sel_end; + var chunk_indicator, probe_line; + while (true) { + probe_line = c.line_elt(probe); + if (!probe_line) { + return; + } + chunk_indicator = c.chunk_indicator(probe_line); + if (chunk_indicator) { + break; + } + probe++; + } + + // There's a next chunk, `probe` points to it. + var begin = probe; + + // Find the end of this chunk. + var next_indicator = chunk_indicator; + while (next_indicator === chunk_indicator) { + probe++; + probe_line = c.line_elt(probe); + next_indicator = c.chunk_indicator(probe_line); + } + c.set_sel(begin, probe); + c.show_selection(); +}; + +coverage.to_prev_chunk = function () { + const c = coverage; + + // Find the end of the prev colored chunk. + var probe = c.sel_begin-1; + var probe_line = c.line_elt(probe); + if (!probe_line) { + return; + } + var chunk_indicator = c.chunk_indicator(probe_line); + while (probe > 1 && !chunk_indicator) { + probe--; + probe_line = c.line_elt(probe); + if (!probe_line) { + return; + } + chunk_indicator = c.chunk_indicator(probe_line); + } + + // There's a prev chunk, `probe` points to its last line. + var end = probe+1; + + // Find the beginning of this chunk. + var prev_indicator = chunk_indicator; + while (prev_indicator === chunk_indicator) { + probe--; + if (probe <= 0) { + return; + } + probe_line = c.line_elt(probe); + prev_indicator = c.chunk_indicator(probe_line); + } + c.set_sel(probe+1, end); + c.show_selection(); +}; + +// Returns 0, 1, or 2: how many of the two ends of the selection are on +// the screen right now? +coverage.selection_ends_on_screen = function () { + if (coverage.sel_begin === 0) { + return 0; + } + + const begin = coverage.line_elt(coverage.sel_begin); + const end = coverage.line_elt(coverage.sel_end-1); + + return ( + (checkVisible(begin) ? 1 : 0) + + (checkVisible(end) ? 1 : 0) + ); +}; + +coverage.to_next_chunk_nicely = function () { + if (coverage.selection_ends_on_screen() === 0) { + // The selection is entirely off the screen: + // Set the top line on the screen as selection. + + // This will select the top-left of the viewport + // As this is most likely the span with the line number we take the parent + const line = document.elementFromPoint(0, 0).parentElement; + if (line.parentElement !== document.getElementById("source")) { + // The element is not a source line but the header or similar + coverage.select_line_or_chunk(1); + } + else { + // We extract the line number from the id + coverage.select_line_or_chunk(parseInt(line.id.substring(1), 10)); + } + } + coverage.to_next_chunk(); +}; + +coverage.to_prev_chunk_nicely = function () { + if (coverage.selection_ends_on_screen() === 0) { + // The selection is entirely off the screen: + // Set the lowest line on the screen as selection. + + // This will select the bottom-left of the viewport + // As this is most likely the span with the line number we take the parent + const line = document.elementFromPoint(document.documentElement.clientHeight-1, 0).parentElement; + if (line.parentElement !== document.getElementById("source")) { + // The element is not a source line but the header or similar + coverage.select_line_or_chunk(coverage.lines_len); + } + else { + // We extract the line number from the id + coverage.select_line_or_chunk(parseInt(line.id.substring(1), 10)); + } + } + coverage.to_prev_chunk(); +}; + +// Select line number lineno, or if it is in a colored chunk, select the +// entire chunk +coverage.select_line_or_chunk = function (lineno) { + var c = coverage; + var probe_line = c.line_elt(lineno); + if (!probe_line) { + return; + } + var the_indicator = c.chunk_indicator(probe_line); + if (the_indicator) { + // The line is in a highlighted chunk. + // Search backward for the first line. + var probe = lineno; + var indicator = the_indicator; + while (probe > 0 && indicator === the_indicator) { + probe--; + probe_line = c.line_elt(probe); + if (!probe_line) { + break; + } + indicator = c.chunk_indicator(probe_line); + } + var begin = probe + 1; + + // Search forward for the last line. + probe = lineno; + indicator = the_indicator; + while (indicator === the_indicator) { + probe++; + probe_line = c.line_elt(probe); + indicator = c.chunk_indicator(probe_line); + } + + coverage.set_sel(begin, probe); + } + else { + coverage.set_sel(lineno); + } +}; + +coverage.show_selection = function () { + // Highlight the lines in the chunk + document.querySelectorAll("#source .highlight").forEach(e => e.classList.remove("highlight")); + for (let probe = coverage.sel_begin; probe < coverage.sel_end; probe++) { + coverage.line_elt(probe).querySelector(".n").classList.add("highlight"); + } + + coverage.scroll_to_selection(); +}; + +coverage.scroll_to_selection = function () { + // Scroll the page if the chunk isn't fully visible. + if (coverage.selection_ends_on_screen() < 2) { + const element = coverage.line_elt(coverage.sel_begin); + coverage.scroll_window(element.offsetTop - 60); + } +}; + +coverage.scroll_window = function (to_pos) { + window.scroll({top: to_pos, behavior: "smooth"}); +}; + +coverage.init_scroll_markers = function () { + // Init some variables + coverage.lines_len = document.querySelectorAll("#source > p").length; + + // Build html + coverage.build_scroll_markers(); +}; + +coverage.build_scroll_markers = function () { + const temp_scroll_marker = document.getElementById("scroll_marker") + if (temp_scroll_marker) temp_scroll_marker.remove(); + // Don't build markers if the window has no scroll bar. + if (document.body.scrollHeight <= window.innerHeight) { + return; + } + + const marker_scale = window.innerHeight / document.body.scrollHeight; + const line_height = Math.min(Math.max(3, window.innerHeight / coverage.lines_len), 10); + + let previous_line = -99, last_mark, last_top; + + const scroll_marker = document.createElement("div"); + scroll_marker.id = "scroll_marker"; + document.getElementById("source").querySelectorAll( + "p.show_run, p.show_mis, p.show_exc, p.show_exc, p.show_par" + ).forEach(element => { + const line_top = Math.floor(element.offsetTop * marker_scale); + const line_number = parseInt(element.querySelector(".n a").id.substr(1)); + + if (line_number === previous_line + 1) { + // If this solid missed block just make previous mark higher. + last_mark.style.height = `${line_top + line_height - last_top}px`; + } + else { + // Add colored line in scroll_marker block. + last_mark = document.createElement("div"); + last_mark.id = `m${line_number}`; + last_mark.classList.add("marker"); + last_mark.style.height = `${line_height}px`; + last_mark.style.top = `${line_top}px`; + scroll_marker.append(last_mark); + last_top = line_top; + } + + previous_line = line_number; + }); + + // Append last to prevent layout calculation + document.body.append(scroll_marker); +}; + +coverage.wire_up_sticky_header = function () { + const header = document.querySelector("header"); + const header_bottom = ( + header.querySelector(".content h2").getBoundingClientRect().top - + header.getBoundingClientRect().top + ); + + function updateHeader() { + if (window.scrollY > header_bottom) { + header.classList.add("sticky"); + } + else { + header.classList.remove("sticky"); + } + } + + window.addEventListener("scroll", updateHeader); + updateHeader(); +}; + +coverage.expand_contexts = function (e) { + var ctxs = e.target.parentNode.querySelector(".ctxs"); + + if (!ctxs.classList.contains("expanded")) { + var ctxs_text = ctxs.textContent; + var width = Number(ctxs_text[0]); + ctxs.textContent = ""; + for (var i = 1; i < ctxs_text.length; i += width) { + key = ctxs_text.substring(i, i + width).trim(); + ctxs.appendChild(document.createTextNode(contexts[key])); + ctxs.appendChild(document.createElement("br")); + } + ctxs.classList.add("expanded"); + } +}; + +document.addEventListener("DOMContentLoaded", () => { + if (document.body.classList.contains("indexfile")) { + coverage.index_ready(); + } + else { + coverage.pyfile_ready(); + } +}); diff --git a/venv/Lib/site-packages/coverage/htmlfiles/favicon_32.png b/venv/Lib/site-packages/coverage/htmlfiles/favicon_32.png new file mode 100644 index 0000000000000000000000000000000000000000..8649f0475d8d20793b2ec431fe25a186a414cf10 GIT binary patch literal 1732 zcmV;#20QtQP)K2KOkBOVxIZChq#W-v7@TU%U6P(wycKT1hUJUToW3ke1U1ONa4 z000000000000000bb)GRa9mqwR9|UWHy;^RUrt?IT__Y0JUcxmBP0(51q1>E00030 z|NrOz)aw7%8sJzM<5^g%z7^qE`}_Ot|JUUG(NUkWzR|7K?Zo%@_v-8G-1N%N=D$;; zw;keH4dGY$`1t4M=HK_s*zm^0#KgqfwWhe3qO_HtvXYvtjgX>;-~C$L`&k>^R)9)7 zdPh2TL^pCnHC#0+_4D)M`p?qp!pq{jO_{8;$fbaflbx`Tn52n|n}8VFRTA1&ugOP< zPd{uvFjz7t*Vot1&d$l-xWCk}s;sQL&#O(Bskh6gqNJv>#iB=ypG1e3K!K4yc7!~M zfj4S*g^zZ7eP$+_Sl07Z646l;%urinP#D8a6TwRtnLIRcI!r4f@bK~9-`~;E(N?Lv zSEst7s;rcxsi~}{Nsytfz@MtUoR*iFc8!#vvx}Umhm4blk(_~MdVD-@dW&>!Nn~ro z_E~-ESVQAj6Wmn;(olz(O&_{U2*pZBc1aYjMh>Dq3z|6`jW`RDHV=t3I6yRKJ~LOX zz_z!!vbVXPqob#=pj3^VMT?x6t(irRmSKsMo1~LLkB&=#j!=M%NP35mfqim$drWb9 zYIb>no_LUwc!r^NkDzs4YHu@=ZHRzrafWDZd1EhEVq=tGX?tK$pIa)DTh#bkvh!J- z?^%@YS!U*0E8$q$_*aOTQ&)Ra64g>ep;BdcQgvlg8qQHrP*E$;P{-m=A*@axn@$bO zO-Y4JzS&EAi%YG}N?cn?YFS7ivPY=EMV6~YH;+Xxu|tefLS|Aza)Cg6us#)=JW!uH zQa?H>d^j+YHCtyjL^LulF*05|F$RG!AX_OHVI&MtA~_@=5_lU|0000rbW%=J06GH4 z^5LD8b8apw8vNh1ua1mF{{Hy)_U`NA;Nacc+sCpuHXa-V{r&yz?c(9#+}oX+NmiRW z+W-IqK1oDDR5;6GfCDCOP5}iL5fK(cB~ET81`MFgF2kGa9AjhSIk~-E-4&*tPPKdiilQJ11k_J082ZS z>@TvivP!5ZFG?t@{t+GpR3XR&@*hA_VE1|Lo8@L@)l*h(Z@=?c-NS$Fk&&61IzUU9 z*nPqBM=OBZ-6ka1SJgGAS-Us5EN)r#dUX%>wQZLa2ytPCtMKp)Ob z*xcu38Z&d5<-NBS)@jRD+*!W*cf-m_wmxDEqBf?czI%3U0J$Xik;lA`jg}VH?(S(V zE!M3;X2B8w0TnnW&6(8;_Uc)WD;Ms6PKP+s(sFgO!}B!^ES~GDt4qLPxwYB)^7)XA zZwo9zDy-B0B+jT6V=!=bo(zs_8{eBA78gT9GH$(DVhz;4VAYwz+bOIdZ-PNb|I&rl z^XG=vFLF)1{&nT2*0vMz#}7^9hXzzf&ZdKlEj{LihP;|;Ywqn35ajP?H?7t|i-Un% z&&kxee@9B{nwgv1+S-~0)E1{ob1^Wn`F2isurqThKK=3%&;`@{0{!D- z&CSj80t;uPu&FaJFtSXKH#ajgGj}=sEad7US6jP0|Db@0j)?(5@sf<7`~a9>s;wCa zm^)spe{uxGFmrJYI9cOh7s$>8Npkt-5EWB1UKc`{W{y5Ce$1+nM9Cr;);=Ju#N^62OSlJMn7omiUgP&ErsYzT~iGxcW aE(`!K@+CXylaC4j0000 + + + + {{ title|escape }} + + + {% if extra_css %} + + {% endif %} + + + + +

+ +
+ +
+ + + {# The title="" attr doesn't work in Safari. #} + {% if has_arcs %} + + + {% if region_noun %} + + {% endif %} + + + + + + + + {% endif %} + + + {% if region_noun %} + + {% endif %} + + {% if has_arcs %} + + {% endif %} + + + + {% if has_arcs %} + + + + + {% endif %} + + + + + + {% for region in regions %} + + + {% if region_noun %} + + {% endif %} + + {% if has_arcs %} + + {% endif %} + + + + {% if has_arcs %} + + + + + {% endif %} + + + + {% endfor %} + + + + + {% if region_noun %} + + {% endif %} + + {% if has_arcs %} + + {% endif %} + + + + {% if has_arcs %} + + + + + {% endif %} + + + + +
   Statements Branches Total
File{{ region_noun }} coveragestatementsmissingexcluded coveragebranchespartial coverage
{{region.file|escape|pretty_file}}{{region.description}} {{region.nums.pc_statements_str}}%{{region.nums.n_statements}}{{region.nums.n_missing}}{{region.nums.n_excluded}} {{region.nums.pc_branches_str}}%{{region.nums.n_branches}}{{region.nums.n_partial_branches}} {{region.nums.pc_covered_str}}%
Total  {{totals.pc_statements_str}}%{{totals.n_statements}}{{totals.n_missing}}{{totals.n_excluded}} {{totals.pc_branches_str}}%{{totals.n_branches}}{{totals.n_partial_branches}} {{totals.pc_covered_str}}%
+ +

+ No items found using the specified filter. +

+ + {% if skipped_covered_msg %} +

{{ skipped_covered_msg }}

+ {% endif %} + {% if skipped_empty_msg %} +

{{ skipped_empty_msg }}

+ {% endif %} +
+ + + + + diff --git a/venv/Lib/site-packages/coverage/htmlfiles/keybd_closed.png b/venv/Lib/site-packages/coverage/htmlfiles/keybd_closed.png new file mode 100644 index 0000000000000000000000000000000000000000..ba119c47df81ed2bbd27a06988abf700139c4f99 GIT binary patch literal 9004 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/coverage/htmlfiles/pyfile.html b/venv/Lib/site-packages/coverage/htmlfiles/pyfile.html new file mode 100644 index 0000000..ce9f26d --- /dev/null +++ b/venv/Lib/site-packages/coverage/htmlfiles/pyfile.html @@ -0,0 +1,149 @@ +{# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 #} +{# For details: https://github.com/coveragepy/coveragepy/blob/main/NOTICE.txt #} + + + + + + Coverage for {{relative_filename|escape}}: {{nums.pc_covered_str}}% + + + {% if extra_css %} + + {% endif %} + + {% if contexts_json %} + + {% endif %} + + + + + +
+
+

+ Coverage for {{relative_filename|escape|pretty_file}}: + {{nums.pc_covered_str}}% +

+ + + +

+ {{nums.n_statements}} statements   + + + + {% if has_arcs %} + + {% endif %} +

+ +

+ « prev     + ^ index     + » next +       + coverage.py v{{__version__}}, + created at {{ time_stamp }} +

+ + +
+
+ +
+ {% for line in lines -%} + {% joined %} +

+ {{line.number}} + {{line.html}}  + {% if line.context_list %} + + {% endif %} + {# Things that should float right in the line. #} + + {% if line.annotate %} + {{line.annotate}} + {{line.annotate_long}} + {% endif %} + {% if line.contexts %} + + {% endif %} + + {# Things that should appear below the line. #} + {% if line.context_str %} + {{ line.context_str }} + {% endif %} +

+ {% endjoined %} + {% endfor %} +
+ + + + + diff --git a/venv/Lib/site-packages/coverage/htmlfiles/style.css b/venv/Lib/site-packages/coverage/htmlfiles/style.css new file mode 100644 index 0000000..1cb379a --- /dev/null +++ b/venv/Lib/site-packages/coverage/htmlfiles/style.css @@ -0,0 +1,385 @@ +@charset "UTF-8"; +/* Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 */ +/* For details: https://github.com/coveragepy/coveragepy/blob/main/NOTICE.txt */ +/* Don't edit this .css file. Edit the .scss file instead! */ +html, body, h1, h2, h3, p, table, td, th { margin: 0; padding: 0; border: 0; font-weight: inherit; font-style: inherit; font-size: 100%; font-family: inherit; vertical-align: baseline; } + +body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; font-size: 1em; background: #fff; color: #000; } + +@media (prefers-color-scheme: dark) { body { background: #1e1e1e; } } + +@media (prefers-color-scheme: dark) { body { color: #eee; } } + +html > body { font-size: 16px; } + +a:active, a:focus { outline: 2px dashed #007acc; } + +p { font-size: .875em; line-height: 1.4em; } + +table { border-collapse: collapse; } + +td { vertical-align: top; } + +table tr.hidden { display: none !important; } + +p#no_rows { display: none; font-size: 1.15em; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; } + +a.nav { text-decoration: none; color: inherit; } + +a.nav:hover { text-decoration: underline; color: inherit; } + +.hidden { display: none; } + +header { background: #f8f8f8; width: 100%; z-index: 2; border-bottom: 1px solid #ccc; } + +@media (prefers-color-scheme: dark) { header { background: black; } } + +@media (prefers-color-scheme: dark) { header { border-color: #333; } } + +header .content { padding: 1rem 3.5rem; } + +header h2 { margin-top: .5em; font-size: 1em; } + +header h2 a.button { font-family: inherit; font-size: inherit; border: 1px solid; border-radius: .2em; background: #eee; color: inherit; text-decoration: none; padding: .1em .5em; margin: 1px calc(.1em + 1px); cursor: pointer; border-color: #ccc; } + +@media (prefers-color-scheme: dark) { header h2 a.button { background: #333; } } + +@media (prefers-color-scheme: dark) { header h2 a.button { border-color: #444; } } + +header h2 a.button.current { border: 2px solid; background: #fff; border-color: #999; cursor: default; } + +@media (prefers-color-scheme: dark) { header h2 a.button.current { background: #1e1e1e; } } + +@media (prefers-color-scheme: dark) { header h2 a.button.current { border-color: #777; } } + +header p.text { margin: .5em 0 -.5em; color: #666; font-style: italic; } + +@media (prefers-color-scheme: dark) { header p.text { color: #aaa; } } + +header.sticky { position: fixed; left: 0; right: 0; height: 2.5em; } + +header.sticky .text { display: none; } + +header.sticky h1, header.sticky h2 { font-size: 1em; margin-top: 0; display: inline-block; } + +header.sticky .content { padding: 0.5rem 3.5rem; } + +header.sticky .content p { font-size: 1em; } + +header.sticky ~ #source { padding-top: 6.5em; } + +main { position: relative; z-index: 1; } + +footer { margin: 1rem 3.5rem; } + +footer .content { padding: 0; color: #666; font-style: italic; } + +@media (prefers-color-scheme: dark) { footer .content { color: #aaa; } } + +#index { margin: 1rem 0 0 3.5rem; } + +h1 { font-size: 1.25em; display: inline-block; } + +#filter_container { float: right; margin: 0 2em 0 0; line-height: 1.66em; } + +#filter_container #filter { width: 10em; padding: 0.2em 0.5em; border: 2px solid #ccc; background: #fff; color: #000; } + +@media (prefers-color-scheme: dark) { #filter_container #filter { border-color: #444; } } + +@media (prefers-color-scheme: dark) { #filter_container #filter { background: #1e1e1e; } } + +@media (prefers-color-scheme: dark) { #filter_container #filter { color: #eee; } } + +#filter_container #filter:focus { border-color: #007acc; } + +#filter_container :disabled ~ label { color: #ccc; } + +@media (prefers-color-scheme: dark) { #filter_container :disabled ~ label { color: #444; } } + +#filter_container label { font-size: .875em; color: #666; } + +@media (prefers-color-scheme: dark) { #filter_container label { color: #aaa; } } + +header button { font-family: inherit; font-size: inherit; border: 1px solid; border-radius: .2em; background: #eee; color: inherit; text-decoration: none; padding: .1em .5em; margin: 1px calc(.1em + 1px); cursor: pointer; border-color: #ccc; } + +@media (prefers-color-scheme: dark) { header button { background: #333; } } + +@media (prefers-color-scheme: dark) { header button { border-color: #444; } } + +header button:active, header button:focus { outline: 2px dashed #007acc; } + +header button.run { background: #eeffee; } + +@media (prefers-color-scheme: dark) { header button.run { background: #373d29; } } + +header button.run.show_run { background: #dfd; border: 2px solid #00dd00; margin: 0 .1em; } + +@media (prefers-color-scheme: dark) { header button.run.show_run { background: #373d29; } } + +header button.mis { background: #ffeeee; } + +@media (prefers-color-scheme: dark) { header button.mis { background: #4b1818; } } + +header button.mis.show_mis { background: #fdd; border: 2px solid #ff0000; margin: 0 .1em; } + +@media (prefers-color-scheme: dark) { header button.mis.show_mis { background: #4b1818; } } + +header button.exc { background: #f7f7f7; } + +@media (prefers-color-scheme: dark) { header button.exc { background: #333; } } + +header button.exc.show_exc { background: #eee; border: 2px solid #808080; margin: 0 .1em; } + +@media (prefers-color-scheme: dark) { header button.exc.show_exc { background: #333; } } + +header button.par { background: #ffffd5; } + +@media (prefers-color-scheme: dark) { header button.par { background: #650; } } + +header button.par.show_par { background: #ffa; border: 2px solid #bbbb00; margin: 0 .1em; } + +@media (prefers-color-scheme: dark) { header button.par.show_par { background: #650; } } + +#help_panel, #source p .annotate.long { display: none; position: absolute; z-index: 999; background: #ffffcc; border: 1px solid #888; border-radius: .2em; color: #333; padding: .25em .5em; } + +#source p .annotate.long { white-space: normal; float: right; top: 1.75em; right: 1em; height: auto; } + +#help_panel_wrapper { float: right; position: relative; } + +#keyboard_icon { margin: 5px; } + +#help_panel_state { display: none; } + +#help_panel { top: 25px; right: 0; padding: .75em; border: 1px solid #883; color: #333; } + +#help_panel .keyhelp p { margin-top: .75em; } + +#help_panel .legend { font-style: italic; margin-bottom: 1em; } + +.indexfile #help_panel { width: 25em; } + +.pyfile #help_panel { width: 18em; } + +#help_panel_state:checked ~ #help_panel { display: block; } + +kbd { border: 1px solid black; border-color: #888 #333 #333 #888; padding: .1em .35em; font-family: SFMono-Regular, Menlo, Monaco, Consolas, monospace; font-weight: bold; background: #eee; border-radius: 3px; } + +#source { padding: 1em 0 1em 3.5rem; font-family: SFMono-Regular, Menlo, Monaco, Consolas, monospace; } + +#source p { position: relative; white-space: pre; } + +#source p * { box-sizing: border-box; } + +#source p .n { float: left; text-align: right; width: 3.5rem; box-sizing: border-box; margin-left: -3.5rem; padding-right: 1em; color: #999; user-select: none; } + +@media (prefers-color-scheme: dark) { #source p .n { color: #777; } } + +#source p .n.highlight { background: #ffdd00; } + +#source p .n a { scroll-margin-top: 6em; text-decoration: none; color: #999; } + +@media (prefers-color-scheme: dark) { #source p .n a { color: #777; } } + +#source p .n a:hover { text-decoration: underline; color: #999; } + +@media (prefers-color-scheme: dark) { #source p .n a:hover { color: #777; } } + +#source p .t { display: inline-block; width: 100%; box-sizing: border-box; margin-left: -.5em; padding-left: 0.3em; border-left: 0.2em solid #fff; } + +@media (prefers-color-scheme: dark) { #source p .t { border-color: #1e1e1e; } } + +#source p .t:hover { background: #f2f2f2; } + +@media (prefers-color-scheme: dark) { #source p .t:hover { background: #282828; } } + +#source p .t:hover ~ .r .annotate.long { display: block; } + +#source p .t .com { color: #008000; font-style: italic; line-height: 1px; } + +@media (prefers-color-scheme: dark) { #source p .t .com { color: #6a9955; } } + +#source p .t .key { font-weight: bold; line-height: 1px; } + +#source p .t .str, #source p .t .fst { color: #0451a5; } + +@media (prefers-color-scheme: dark) { #source p .t .str, #source p .t .fst { color: #9cdcfe; } } + +#source p.mis .t { border-left: 0.2em solid #ff0000; } + +#source p.mis.show_mis .t { background: #fdd; } + +@media (prefers-color-scheme: dark) { #source p.mis.show_mis .t { background: #4b1818; } } + +#source p.mis.show_mis .t:hover { background: #f2d2d2; } + +@media (prefers-color-scheme: dark) { #source p.mis.show_mis .t:hover { background: #532323; } } + +#source p.mis.mis2 .t { border-left: 0.2em dotted #ff0000; } + +#source p.mis.mis2.show_mis .t { background: #ffeeee; } + +@media (prefers-color-scheme: dark) { #source p.mis.mis2.show_mis .t { background: #351b1b; } } + +#source p.mis.mis2.show_mis .t:hover { background: #f2d2d2; } + +@media (prefers-color-scheme: dark) { #source p.mis.mis2.show_mis .t:hover { background: #532323; } } + +#source p.run .t { border-left: 0.2em solid #00dd00; } + +#source p.run.show_run .t { background: #dfd; } + +@media (prefers-color-scheme: dark) { #source p.run.show_run .t { background: #373d29; } } + +#source p.run.show_run .t:hover { background: #d2f2d2; } + +@media (prefers-color-scheme: dark) { #source p.run.show_run .t:hover { background: #404633; } } + +#source p.run.run2 .t { border-left: 0.2em dotted #00dd00; } + +#source p.run.run2.show_run .t { background: #eeffee; } + +@media (prefers-color-scheme: dark) { #source p.run.run2.show_run .t { background: #2b2e24; } } + +#source p.run.run2.show_run .t:hover { background: #d2f2d2; } + +@media (prefers-color-scheme: dark) { #source p.run.run2.show_run .t:hover { background: #404633; } } + +#source p.exc .t { border-left: 0.2em solid #808080; } + +#source p.exc.show_exc .t { background: #eee; } + +@media (prefers-color-scheme: dark) { #source p.exc.show_exc .t { background: #333; } } + +#source p.exc.show_exc .t:hover { background: #e2e2e2; } + +@media (prefers-color-scheme: dark) { #source p.exc.show_exc .t:hover { background: #3c3c3c; } } + +#source p.exc.exc2 .t { border-left: 0.2em dotted #808080; } + +#source p.exc.exc2.show_exc .t { background: #f7f7f7; } + +@media (prefers-color-scheme: dark) { #source p.exc.exc2.show_exc .t { background: #292929; } } + +#source p.exc.exc2.show_exc .t:hover { background: #e2e2e2; } + +@media (prefers-color-scheme: dark) { #source p.exc.exc2.show_exc .t:hover { background: #3c3c3c; } } + +#source p.par .t { border-left: 0.2em solid #bbbb00; } + +#source p.par.show_par .t { background: #ffa; } + +@media (prefers-color-scheme: dark) { #source p.par.show_par .t { background: #650; } } + +#source p.par.show_par .t:hover { background: #f2f2a2; } + +@media (prefers-color-scheme: dark) { #source p.par.show_par .t:hover { background: #6d5d0c; } } + +#source p.par.par2 .t { border-left: 0.2em dotted #bbbb00; } + +#source p.par.par2.show_par .t { background: #ffffd5; } + +@media (prefers-color-scheme: dark) { #source p.par.par2.show_par .t { background: #423a0f; } } + +#source p.par.par2.show_par .t:hover { background: #f2f2a2; } + +@media (prefers-color-scheme: dark) { #source p.par.par2.show_par .t:hover { background: #6d5d0c; } } + +#source p .r { position: absolute; top: 0; right: 2.5em; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; } + +#source p .annotate { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; color: #666; padding-right: .5em; } + +@media (prefers-color-scheme: dark) { #source p .annotate { color: #ddd; } } + +#source p .annotate.short:hover ~ .long { display: block; } + +#source p .annotate.long { width: 30em; right: 2.5em; } + +#source p input { display: none; } + +#source p input ~ .r label.ctx { cursor: pointer; border-radius: .25em; } + +#source p input ~ .r label.ctx::before { content: "▶ "; } + +#source p input ~ .r label.ctx:hover { background: #e8f4ff; color: #666; } + +@media (prefers-color-scheme: dark) { #source p input ~ .r label.ctx:hover { background: #0f3a42; } } + +@media (prefers-color-scheme: dark) { #source p input ~ .r label.ctx:hover { color: #aaa; } } + +#source p input:checked ~ .r label.ctx { background: #d0e8ff; color: #666; border-radius: .75em .75em 0 0; padding: 0 .5em; margin: -.25em 0; } + +@media (prefers-color-scheme: dark) { #source p input:checked ~ .r label.ctx { background: #056; } } + +@media (prefers-color-scheme: dark) { #source p input:checked ~ .r label.ctx { color: #aaa; } } + +#source p input:checked ~ .r label.ctx::before { content: "▼ "; } + +#source p input:checked ~ .ctxs { padding: .25em .5em; overflow-y: scroll; max-height: 10.5em; } + +#source p label.ctx { color: #999; display: inline-block; padding: 0 .5em; font-size: .8333em; } + +@media (prefers-color-scheme: dark) { #source p label.ctx { color: #777; } } + +#source p .ctxs { display: block; max-height: 0; overflow-y: hidden; transition: all .2s; padding: 0 .5em; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; white-space: nowrap; background: #d0e8ff; border-radius: .25em; margin-right: 1.75em; text-align: right; } + +@media (prefers-color-scheme: dark) { #source p .ctxs { background: #056; } } + +#index { font-family: SFMono-Regular, Menlo, Monaco, Consolas, monospace; font-size: 0.875em; } + +#index table.index { margin-left: -.5em; } + +#index td, #index th { text-align: right; vertical-align: baseline; padding: .25em .5em; border-bottom: 1px solid #eee; } + +@media (prefers-color-scheme: dark) { #index td, #index th { border-color: #333; } } + +#index td.name, #index th.name { text-align: left; width: auto; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; min-width: 15em; } + +#index td.left, #index th.left { text-align: left; } + +#index td.spacer, #index th.spacer { border: none; padding: 0; } + +#index td.spacer:hover, #index th.spacer:hover { background: inherit; } + +#index th { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; font-style: italic; color: #333; border-color: #ccc; cursor: pointer; } + +@media (prefers-color-scheme: dark) { #index th { color: #ddd; } } + +#index th:hover { background: #eee; } + +@media (prefers-color-scheme: dark) { #index th:hover { background: #333; } } + +#index th .arrows { color: #666; font-size: 85%; font-family: sans-serif; font-style: normal; pointer-events: none; } + +#index th[aria-sort="ascending"], #index th[aria-sort="descending"] { white-space: nowrap; background: #eee; padding-left: .5em; } + +@media (prefers-color-scheme: dark) { #index th[aria-sort="ascending"], #index th[aria-sort="descending"] { background: #333; } } + +#index th[aria-sort="ascending"] .arrows::after { content: " ▲"; } + +#index th[aria-sort="descending"] .arrows::after { content: " ▼"; } + +#index tr.grouphead th { cursor: default; font-style: normal; border-color: #999; } + +#index td.name { font-size: 1.15em; } + +#index td.name a { text-decoration: none; color: inherit; } + +#index td.name .no-noun { font-style: italic; } + +#index tr.total td, #index tr.total_dynamic td { font-weight: bold; border-bottom: none; } + +#index tr.region:hover { background: #eee; } + +@media (prefers-color-scheme: dark) { #index tr.region:hover { background: #333; } } + +#index tr.region:hover td.name { text-decoration: underline; color: inherit; } + +#scroll_marker { position: fixed; z-index: 3; right: 0; top: 0; width: 16px; height: 100%; background: #fff; border-left: 1px solid #eee; will-change: transform; } + +@media (prefers-color-scheme: dark) { #scroll_marker { background: #1e1e1e; } } + +@media (prefers-color-scheme: dark) { #scroll_marker { border-color: #333; } } + +#scroll_marker .marker { background: #ccc; position: absolute; min-height: 3px; width: 100%; } + +@media (prefers-color-scheme: dark) { #scroll_marker .marker { background: #444; } } diff --git a/venv/Lib/site-packages/coverage/htmlfiles/style.scss b/venv/Lib/site-packages/coverage/htmlfiles/style.scss new file mode 100644 index 0000000..7888e79 --- /dev/null +++ b/venv/Lib/site-packages/coverage/htmlfiles/style.scss @@ -0,0 +1,842 @@ +/* Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 */ +/* For details: https://github.com/coveragepy/coveragepy/blob/main/NOTICE.txt */ + +// CSS styles for coverage.py HTML reports. + +// When you edit this file, you need to run "make css" to get the CSS file +// generated, and then check in both the .scss and the .css files. + +// When working on the file, this command is useful: +// sass --watch --style=compact --sourcemap=none --no-cache coverage/htmlfiles/style.scss:htmlcov/style.css +// +// OR you can process sass purely in python with `pip install pysass`, then: +// pysassc --style=compact coverage/htmlfiles/style.scss coverage/htmlfiles/style.css + +// Ignore this comment, it's for the CSS output file: +/* Don't edit this .css file. Edit the .scss file instead! */ + +// Dimensions +$left-gutter: 3.5rem; + +// +// Declare colors and variables +// + +$font-normal: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; +$font-code: SFMono-Regular, Menlo, Monaco, Consolas, monospace; + +$off-button-lighten: 50%; +$hover-dark-amt: 95%; +$multi-dim-amt: 50%; + +$focus-color: #007acc; + +$mis-color: #ff0000; +$run-color: #00dd00; +$exc-color: #808080; +$par-color: #bbbb00; + +$light-bg: #fff; +$light-fg: #000; +$light-gray1: #f8f8f8; +$light-gray2: #eee; +$light-gray3: #ccc; +$light-gray4: #999; +$light-gray5: #666; +$light-gray6: #333; +$light-pln-bg: $light-bg; +$light-mis-bg: #fdd; +$light-run-bg: #dfd; +$light-exc-bg: $light-gray2; +$light-par-bg: #ffa; +$light-token-com: #008000; +$light-token-str: #0451a5; +$light-context-bg-color: #d0e8ff; + +$dark-bg: #1e1e1e; +$dark-fg: #eee; +$dark-gray1: #222; +$dark-gray2: #333; +$dark-gray3: #444; +$dark-gray4: #777; +$dark-gray5: #aaa; +$dark-gray6: #ddd; +$dark-pln-bg: $dark-bg; +$dark-mis-bg: #4b1818; +$dark-run-bg: #373d29; +$dark-exc-bg: $dark-gray2; +$dark-par-bg: #650; +$dark-token-com: #6a9955; +$dark-token-str: #9cdcfe; +$dark-context-bg-color: #056; + +// +// Mixins and utilities +// + +@mixin background-dark($color) { + @media (prefers-color-scheme: dark) { + background: $color; + } +} +@mixin color-dark($color) { + @media (prefers-color-scheme: dark) { + color: $color; + } +} +@mixin border-color-dark($color) { + @media (prefers-color-scheme: dark) { + border-color: $color; + } +} + +// Add visual outline to navigable elements on focus improve accessibility. +@mixin focus-border { + &:active, &:focus { + outline: 2px dashed $focus-color; + } +} + +@mixin button-shape { + font-family: inherit; + font-size: inherit; + border: 1px solid; + border-radius: .2em; + background: $light-gray2; + @include background-dark($dark-gray2); + color: inherit; + text-decoration: none; + padding: .1em .5em; + margin: 1px calc(.1em + 1px); + cursor: pointer; + border-color: $light-gray3; + @include border-color-dark($dark-gray3); +} + +// Page-wide styles +html, body, h1, h2, h3, p, table, td, th { + margin: 0; + padding: 0; + border: 0; + font-weight: inherit; + font-style: inherit; + font-size: 100%; + font-family: inherit; + vertical-align: baseline; +} + +// Set baseline grid to 16 pt. +body { + font-family: $font-normal; + font-size: 1em; + background: $light-bg; + color: $light-fg; + @include background-dark($dark-bg); + @include color-dark($dark-fg); +} + +html>body { + font-size: 16px; +} + +a { + @include focus-border; +} + +p { + font-size: .875em; + line-height: 1.4em; +} + +table { + border-collapse: collapse; +} +td { + vertical-align: top; +} +table tr.hidden { + display: none !important; +} + +p#no_rows { + display: none; + font-size: 1.15em; + font-family: $font-normal; +} + +a.nav { + text-decoration: none; + color: inherit; + + &:hover { + text-decoration: underline; + color: inherit; + } +} + +.hidden { + display: none; +} + +// Page structure +header { + background: $light-gray1; + @include background-dark(black); + width: 100%; + z-index: 2; + border-bottom: 1px solid $light-gray3; + @include border-color-dark($dark-gray2); + + .content { + padding: 1rem $left-gutter; + } + + h2 { + margin-top: .5em; + font-size: 1em; + + a.button { + @include button-shape; + &.current { + border: 2px solid; + background: $light-bg; + @include background-dark($dark-bg); + border-color: $light-gray4; + @include border-color-dark($dark-gray4); + cursor: default; + } + } + } + + p.text { + margin: .5em 0 -.5em; + color: $light-gray5; + @include color-dark($dark-gray5); + font-style: italic; + } + + &.sticky { + position: fixed; + left: 0; + right: 0; + height: 2.5em; + + .text { + display: none; + } + + h1, h2 { + font-size: 1em; + margin-top: 0; + display: inline-block; + } + + .content { + padding: .5rem $left-gutter; + p { + font-size: 1em; + } + } + + & ~ #source { + padding-top: 6.5em; + } + } +} + +main { + position: relative; + z-index: 1; +} + +footer { + margin: 1rem $left-gutter; + + .content { + padding: 0; + color: $light-gray5; + @include color-dark($dark-gray5); + font-style: italic; + } +} + +#index { + margin: 1rem 0 0 $left-gutter; +} + +// Header styles + +h1 { + font-size: 1.25em; + display: inline-block; +} + +#filter_container { + float: right; + margin: 0 2em 0 0; + line-height: 1.66em; + + #filter { + width: 10em; + padding: 0.2em 0.5em; + border: 2px solid $light-gray3; + background: $light-bg; + color: $light-fg; + @include border-color-dark($dark-gray3); + @include background-dark($dark-bg); + @include color-dark($dark-fg); + &:focus { + border-color: $focus-color; + } + } + + :disabled ~ label{ + color: $light-gray3; + @include color-dark($dark-gray3); + } + + label { + font-size: .875em; + color: $light-gray5; + @include color-dark($dark-gray5); + } +} + +header button { + @include button-shape; + @include focus-border; + + &.run { + background: mix($light-run-bg, $light-bg, $off-button-lighten); + @include background-dark($dark-run-bg); + &.show_run { + background: $light-run-bg; + @include background-dark($dark-run-bg); + border: 2px solid $run-color; + margin: 0 .1em; + } + } + &.mis { + background: mix($light-mis-bg, $light-bg, $off-button-lighten); + @include background-dark($dark-mis-bg); + &.show_mis { + background: $light-mis-bg; + @include background-dark($dark-mis-bg); + border: 2px solid $mis-color; + margin: 0 .1em; + } + } + &.exc { + background: mix($light-exc-bg, $light-bg, $off-button-lighten); + @include background-dark($dark-exc-bg); + &.show_exc { + background: $light-exc-bg; + @include background-dark($dark-exc-bg); + border: 2px solid $exc-color; + margin: 0 .1em; + } + } + &.par { + background: mix($light-par-bg, $light-bg, $off-button-lighten); + @include background-dark($dark-par-bg); + &.show_par { + background: $light-par-bg; + @include background-dark($dark-par-bg); + border: 2px solid $par-color; + margin: 0 .1em; + } + } +} + +// Yellow post-it things. +%popup { + display: none; + position: absolute; + z-index: 999; + background: #ffffcc; + border: 1px solid #888; + border-radius: .2em; + color: #333; + padding: .25em .5em; +} + +// Yellow post-it's in the text listings. +%in-text-popup { + @extend %popup; + white-space: normal; + float: right; + top: 1.75em; + right: 1em; + height: auto; +} + +// Help panel +#help_panel_wrapper { + float: right; + position: relative; +} + +#keyboard_icon { + margin: 5px; +} + +#help_panel_state { + display: none; +} + +#help_panel { + @extend %popup; + top: 25px; + right: 0; + padding: .75em; + border: 1px solid #883; + + color: #333; + + .keyhelp p { + margin-top: .75em; + } + + .legend { + font-style: italic; + margin-bottom: 1em; + } + + .indexfile & { + width: 25em; + } + + .pyfile & { + width: 18em; + } + + #help_panel_state:checked ~ & { + display: block; + } +} + +kbd { + border: 1px solid black; + border-color: #888 #333 #333 #888; + padding: .1em .35em; + font-family: $font-code; + font-weight: bold; + background: #eee; + border-radius: 3px; +} + +// Source file styles + +// The slim bar at the left edge of the source lines, colored by coverage. +$border-indicator-width: .2em; + +#source { + padding: 1em 0 1em $left-gutter; + font-family: $font-code; + + p { + // position relative makes position:absolute pop-ups appear in the right place. + position: relative; + white-space: pre; + + * { + box-sizing: border-box; + } + + .n { + float: left; + text-align: right; + width: $left-gutter; + box-sizing: border-box; + margin-left: -$left-gutter; + padding-right: 1em; + color: $light-gray4; + user-select: none; + @include color-dark($dark-gray4); + + &.highlight { + background: #ffdd00; + } + + a { + // Make anchors to the line scroll the line to be + // visible beneath the fixed-position header. + scroll-margin-top: 6em; + text-decoration: none; + color: $light-gray4; + @include color-dark($dark-gray4); + &:hover { + text-decoration: underline; + color: $light-gray4; + @include color-dark($dark-gray4); + } + } + } + + .t { + display: inline-block; + width: 100%; + box-sizing: border-box; + margin-left: -.5em; + padding-left: .5em - $border-indicator-width; + border-left: $border-indicator-width solid $light-bg; + @include border-color-dark($dark-bg); + + &:hover { + background: mix($light-pln-bg, $light-fg, $hover-dark-amt); + @include background-dark(mix($dark-pln-bg, $dark-fg, $hover-dark-amt)); + + & ~ .r .annotate.long { + display: block; + } + } + + // Syntax coloring + .com { + color: $light-token-com; + @include color-dark($dark-token-com); + font-style: italic; + line-height: 1px; + } + .key { + font-weight: bold; + line-height: 1px; + } + .str, .fst { + color: $light-token-str; + @include color-dark($dark-token-str); + } + } + + &.mis { + .t { + border-left: $border-indicator-width solid $mis-color; + } + + &.show_mis .t { + background: $light-mis-bg; + @include background-dark($dark-mis-bg); + + &:hover { + background: mix($light-mis-bg, $light-fg, $hover-dark-amt); + @include background-dark(mix($dark-mis-bg, $dark-fg, $hover-dark-amt)); + } + } + + &.mis2 { + .t { + border-left: $border-indicator-width dotted $mis-color; + } + + &.show_mis .t { + background: mix($light-mis-bg, $light-bg, $multi-dim-amt); + @include background-dark(mix($dark-mis-bg, $dark-bg, $multi-dim-amt)); + + &:hover { + background: mix($light-mis-bg, $light-fg, $hover-dark-amt); + @include background-dark(mix($dark-mis-bg, $dark-fg, $hover-dark-amt)); + } + } + } + } + + &.run { + .t { + border-left: $border-indicator-width solid $run-color; + } + + &.show_run .t { + background: $light-run-bg; + @include background-dark($dark-run-bg); + + &:hover { + background: mix($light-run-bg, $light-fg, $hover-dark-amt); + @include background-dark(mix($dark-run-bg, $dark-fg, $hover-dark-amt)); + } + } + + &.run2 { + .t { + border-left: $border-indicator-width dotted $run-color; + } + + &.show_run .t { + background: mix($light-run-bg, $light-bg, $multi-dim-amt); + @include background-dark(mix($dark-run-bg, $dark-bg, $multi-dim-amt)); + + &:hover { + background: mix($light-run-bg, $light-fg, $hover-dark-amt); + @include background-dark(mix($dark-run-bg, $dark-fg, $hover-dark-amt)); + } + } + } + } + + &.exc { + .t { + border-left: $border-indicator-width solid $exc-color; + } + + &.show_exc .t { + background: $light-exc-bg; + @include background-dark($dark-exc-bg); + + &:hover { + background: mix($light-exc-bg, $light-fg, $hover-dark-amt); + @include background-dark(mix($dark-exc-bg, $dark-fg, $hover-dark-amt)); + } + } + + &.exc2 { + .t { + border-left: $border-indicator-width dotted $exc-color; + } + + &.show_exc .t { + background: mix($light-exc-bg, $light-bg, $multi-dim-amt); + @include background-dark(mix($dark-exc-bg, $dark-bg, $multi-dim-amt)); + + &:hover { + background: mix($light-exc-bg, $light-fg, $hover-dark-amt); + @include background-dark(mix($dark-exc-bg, $dark-fg, $hover-dark-amt)); + } + } + } + } + + &.par { + .t { + border-left: $border-indicator-width solid $par-color; + } + + &.show_par .t { + background: $light-par-bg; + @include background-dark($dark-par-bg); + + &:hover { + background: mix($light-par-bg, $light-fg, $hover-dark-amt); + @include background-dark(mix($dark-par-bg, $dark-fg, $hover-dark-amt)); + } + } + + &.par2 { + .t { + border-left: $border-indicator-width dotted $par-color; + } + + &.show_par .t { + background: mix($light-par-bg, $light-bg, $multi-dim-amt); + @include background-dark(mix($dark-par-bg, $dark-bg, $multi-dim-amt)); + + &:hover { + background: mix($light-par-bg, $light-fg, $hover-dark-amt); + @include background-dark(mix($dark-par-bg, $dark-fg, $hover-dark-amt)); + } + } + } + } + + .r { + position: absolute; + top: 0; + right: 2.5em; + font-family: $font-normal; + } + + .annotate { + font-family: $font-normal; + color: $light-gray5; + @include color-dark($dark-gray6); + padding-right: .5em; + + &.short:hover ~ .long { + display: block; + } + + &.long { + @extend %in-text-popup; + width: 30em; + right: 2.5em; + } + } + + input { + display: none; + + & ~ .r label.ctx { + cursor: pointer; + border-radius: .25em; + &::before { + content: "▶ "; + } + &:hover { + background: mix($light-context-bg-color, $light-bg, $off-button-lighten); + @include background-dark(mix($dark-context-bg-color, $dark-bg, $off-button-lighten)); + color: $light-gray5; + @include color-dark($dark-gray5); + } + } + + &:checked ~ .r label.ctx { + background: $light-context-bg-color; + @include background-dark($dark-context-bg-color); + color: $light-gray5; + @include color-dark($dark-gray5); + border-radius: .75em .75em 0 0; + padding: 0 .5em; + margin: -.25em 0; + &::before { + content: "▼ "; + } + } + + &:checked ~ .ctxs { + padding: .25em .5em; + overflow-y: scroll; + max-height: 10.5em; + } + } + + label.ctx { + color: $light-gray4; + @include color-dark($dark-gray4); + display: inline-block; + padding: 0 .5em; + font-size: .8333em; // 10/12 + } + + .ctxs { + display: block; + max-height: 0; + overflow-y: hidden; + transition: all .2s; + padding: 0 .5em; + font-family: $font-normal; + white-space: nowrap; + background: $light-context-bg-color; + @include background-dark($dark-context-bg-color); + border-radius: .25em; + margin-right: 1.75em; + text-align: right; + } + } +} + + +// index styles +#index { + font-family: $font-code; + font-size: 0.875em; + + table.index { + margin-left: -.5em; + } + td, th { + text-align: right; + vertical-align: baseline; + padding: .25em .5em; + border-bottom: 1px solid $light-gray2; + @include border-color-dark($dark-gray2); + &.name { + text-align: left; + width: auto; + font-family: $font-normal; + min-width: 15em; + } + &.left { + text-align: left; + } + &.spacer { + border: none; + padding: 0; + &:hover { + background: inherit; + } + } + } + th { + font-family: $font-normal; + font-style: italic; + color: $light-gray6; + @include color-dark($dark-gray6); + border-color: #ccc; + cursor: pointer; + &:hover { + background: $light-gray2; + @include background-dark($dark-gray2); + } + .arrows { + color: #666; + font-size: 85%; + font-family: sans-serif; + font-style: normal; + pointer-events: none; + } + &[aria-sort="ascending"], &[aria-sort="descending"] { + white-space: nowrap; + background: $light-gray2; + @include background-dark($dark-gray2); + padding-left: .5em; + } + &[aria-sort="ascending"] .arrows::after { + content: " ▲"; + } + &[aria-sort="descending"] .arrows::after { + content: " ▼"; + } + } + tr.grouphead { + th { + cursor: default; + font-style: normal; + border-color: #999; + } + } + td.name { + font-size: 1.15em; + a { + text-decoration: none; + color: inherit; + } + & .no-noun { + font-style: italic; + } + } + + tr.total td, + tr.total_dynamic td { + font-weight: bold; + border-bottom: none; + } + tr.region:hover { + background: $light-gray2; + @include background-dark($dark-gray2); + td.name { + text-decoration: underline; + color: inherit; + } + } +} + +// scroll marker styles +#scroll_marker { + position: fixed; + z-index: 3; + right: 0; + top: 0; + width: 16px; + height: 100%; + background: $light-bg; + border-left: 1px solid $light-gray2; + @include background-dark($dark-bg); + @include border-color-dark($dark-gray2); + will-change: transform; // for faster scrolling of fixed element in Chrome + + .marker { + background: $light-gray3; + @include background-dark($dark-gray3); + position: absolute; + min-height: 3px; + width: 100%; + } +} diff --git a/venv/Lib/site-packages/coverage/inorout.py b/venv/Lib/site-packages/coverage/inorout.py new file mode 100644 index 0000000..1addb22 --- /dev/null +++ b/venv/Lib/site-packages/coverage/inorout.py @@ -0,0 +1,614 @@ +# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 +# For details: https://github.com/coveragepy/coveragepy/blob/main/NOTICE.txt + +"""Determining whether files are being measured/reported or not.""" + +from __future__ import annotations + +import importlib.util +import inspect +import itertools +import os +import os.path +import platform +import re +import sys +import sysconfig +import traceback +from collections.abc import Iterable +from types import FrameType, ModuleType +from typing import TYPE_CHECKING, Any, cast + +from coverage import env +from coverage.disposition import FileDisposition, disposition_init +from coverage.exceptions import ConfigError, CoverageException, PluginError +from coverage.files import ( + GlobMatcher, + ModuleMatcher, + TreeMatcher, + canonical_filename, + find_python_files, + prep_patterns, +) +from coverage.misc import isolate_module, sys_modules_saved +from coverage.python import source_for_file, source_for_morf +from coverage.types import TDebugCtl, TFileDisposition, TMorf, TWarnFn + +if TYPE_CHECKING: + from coverage.config import CoverageConfig + from coverage.plugin_support import Plugins + + +modules_we_happen_to_have: list[ModuleType] = [ + inspect, + itertools, + os, + platform, + re, + sysconfig, + traceback, +] + +if env.PYPY: + # Pypy has some unusual stuff in the "stdlib". Consider those locations + # when deciding where the stdlib is. These modules are not used for anything, + # they are modules importable from the pypy lib directories, so that we can + # find those directories. + import _pypy_irc_topic # pylint: disable=import-error + import _structseq # pylint: disable=import-error + + modules_we_happen_to_have.extend([_structseq, _pypy_irc_topic]) + + +os = isolate_module(os) + + +def canonical_path(morf: TMorf, directory: bool = False) -> str: + """Return the canonical path of the module or file `morf`. + + If the module is a package, then return its directory. If it is a + module, then return its file, unless `directory` is True, in which + case return its enclosing directory. + + """ + morf_path = canonical_filename(source_for_morf(morf)) + if morf_path.endswith("__init__.py") or directory: + morf_path = os.path.split(morf_path)[0] + return morf_path + + +def name_for_module(filename: str, frame: FrameType | None) -> str | None: + """Get the name of the module for a filename and frame. + + For configurability's sake, we allow __main__ modules to be matched by + their importable name. + + If loaded via runpy (aka -m), we can usually recover the "original" + full dotted module name, otherwise, we resort to interpreting the + file name to get the module's name. In the case that the module name + can't be determined, None is returned. + + """ + module_globals = frame.f_globals if frame is not None else {} + dunder_name: str | None = module_globals.get("__name__", None) + + if isinstance(dunder_name, str) and dunder_name != "__main__": + # This is the usual case: an imported module. + return dunder_name + + spec = module_globals.get("__spec__", None) + if spec: + fullname = spec.name + if isinstance(fullname, str) and fullname != "__main__": + # Module loaded via: runpy -m + return fullname + + # Script as first argument to Python command line. + inspectedname = inspect.getmodulename(filename) + if inspectedname is not None: + return inspectedname + else: + return dunder_name + + +def module_is_namespace(mod: ModuleType) -> bool: + """Is the module object `mod` a PEP420 namespace module?""" + return hasattr(mod, "__path__") and getattr(mod, "__file__", None) is None + + +def module_has_file(mod: ModuleType) -> bool: + """Does the module object `mod` have an existing __file__ ?""" + mod__file__ = getattr(mod, "__file__", None) + if mod__file__ is None: + return False + return os.path.exists(mod__file__) + + +def file_and_path_for_module(modulename: str) -> tuple[str | None, list[str]]: + """Find the file and search path for `modulename`. + + Returns: + filename: The filename of the module, or None. + path: A list (possibly empty) of directories to find submodules in. + + """ + filename = None + path = [] + try: + spec = importlib.util.find_spec(modulename) + except Exception: + pass + else: + if spec is not None: + filename = spec.origin + path = list(spec.submodule_search_locations or ()) + return filename, path + + +def add_stdlib_paths(paths: set[str]) -> None: + """Add paths where the stdlib can be found to the set `paths`.""" + # Look at where some standard modules are located. That's the + # indication for "installed with the interpreter". In some + # environments (virtualenv, for example), these modules may be + # spread across a few locations. Look at all the candidate modules + # we've imported, and take all the different ones. + for m in modules_we_happen_to_have: + if hasattr(m, "__file__"): + paths.add(canonical_path(m, directory=True)) + + +def add_third_party_paths(paths: set[str]) -> None: + """Add locations for third-party packages to the set `paths`.""" + # Get the paths that sysconfig knows about. + scheme_names = set(sysconfig.get_scheme_names()) + + for scheme in scheme_names: + # https://foss.heptapod.net/pypy/pypy/-/issues/3433 + better_scheme = "pypy_posix" if scheme == "pypy" else scheme + if os.name in better_scheme.split("_"): + config_paths = sysconfig.get_paths(scheme) + for path_name in ["platlib", "purelib", "scripts"]: + paths.add(config_paths[path_name]) + + +def add_coverage_paths(paths: set[str]) -> None: + """Add paths where coverage.py code can be found to the set `paths`.""" + cover_path = canonical_path(__file__, directory=True) + paths.add(cover_path) + if env.TESTING: + # Don't include our own test code. + paths.add(os.path.join(cover_path, "tests")) + + +class InOrOut: + """Machinery for determining what files to measure.""" + + def __init__( + self, + config: CoverageConfig, + warn: TWarnFn, + debug: TDebugCtl | None, + include_namespace_packages: bool, + ) -> None: + self.warn = warn + self.debug = debug + self.include_namespace_packages = include_namespace_packages + + self.source_pkgs: list[str] = [] + self.source_pkgs.extend(config.source_pkgs) + self.source_dirs: list[str] = [] + self.source_dirs.extend(config.source_dirs) + for src in config.source or []: + if os.path.isdir(src): + self.source_dirs.append(src) + else: + self.source_pkgs.append(src) + + # Canonicalize everything in `source_dirs`. + # Also confirm that they actually are directories. + for i, src in enumerate(self.source_dirs): + if not os.path.isdir(src): + raise ConfigError(f"Source dir is not a directory: {src!r}") + self.source_dirs[i] = canonical_filename(src) + + self.source_pkgs_unmatched = self.source_pkgs[:] + + self.include = prep_patterns(config.run_include) + self.omit = prep_patterns(config.run_omit) + + # The directories for files considered "installed with the interpreter". + self.pylib_paths: set[str] = set() + if not config.cover_pylib: + add_stdlib_paths(self.pylib_paths) + + # To avoid tracing the coverage.py code itself, we skip anything + # located where we are. + self.cover_paths: set[str] = set() + add_coverage_paths(self.cover_paths) + + # Find where third-party packages are installed. + self.third_paths: set[str] = set() + add_third_party_paths(self.third_paths) + + def _debug(msg: str) -> None: + if self.debug: + self.debug.write(msg) + + # The matchers for should_trace. + + # Generally useful information + _debug("sys.path:" + "".join(f"\n {p}" for p in sys.path)) + + # Create the matchers we need for should_trace + self.source_match = None + self.source_pkgs_match = None + self.pylib_match = None + self.include_match = self.omit_match = None + + if self.source_dirs or self.source_pkgs: + against = [] + if self.source_dirs: + self.source_match = TreeMatcher(self.source_dirs, "source") + against.append(f"trees {self.source_match!r}") + if self.source_pkgs: + self.source_pkgs_match = ModuleMatcher(self.source_pkgs, "source_pkgs") + against.append(f"modules {self.source_pkgs_match!r}") + _debug("Source matching against " + " and ".join(against)) + else: + if self.pylib_paths: + self.pylib_match = TreeMatcher(self.pylib_paths, "pylib") + _debug(f"Python stdlib matching: {self.pylib_match!r}") + if self.include: + self.include_match = GlobMatcher(self.include, "include") + _debug(f"Include matching: {self.include_match!r}") + if self.omit: + self.omit_match = GlobMatcher(self.omit, "omit") + _debug(f"Omit matching: {self.omit_match!r}") + + self.cover_match = TreeMatcher(self.cover_paths, "coverage") + _debug(f"Coverage code matching: {self.cover_match!r}") + + self.third_match = TreeMatcher(self.third_paths, "third") + _debug(f"Third-party lib matching: {self.third_match!r}") + + # Check if the source we want to measure has been installed as a + # third-party package. + # Is the source inside a third-party area? + self.source_in_third_paths = set() + with sys_modules_saved(): + for pkg in self.source_pkgs: + try: + modfile, path = file_and_path_for_module(pkg) + _debug(f"Imported source package {pkg!r} as {modfile!r}") + except CoverageException as exc: + _debug(f"Couldn't import source package {pkg!r}: {exc}") + continue + if modfile: + if self.third_match.match(modfile): + _debug( + f"Source in third-party: source_pkg {pkg!r} at {modfile!r}", + ) + self.source_in_third_paths.add(canonical_path(source_for_file(modfile))) + else: + for pathdir in path: + if self.third_match.match(pathdir): + _debug( + f"Source in third-party: {pkg!r} path directory at {pathdir!r}", + ) + self.source_in_third_paths.add(pathdir) + + for src in self.source_dirs: + if self.third_match.match(src): + _debug(f"Source in third-party: source directory {src!r}") + self.source_in_third_paths.add(src) + self.source_in_third_match = TreeMatcher(self.source_in_third_paths, "source_in_third") + _debug(f"Source in third-party matching: {self.source_in_third_match}") + + self.plugins: Plugins + self.disp_class: type[TFileDisposition] = FileDisposition + + def should_trace(self, filename: str, frame: FrameType | None = None) -> TFileDisposition: + """Decide whether to trace execution in `filename`, with a reason. + + This function is called from the trace function. As each new file name + is encountered, this function determines whether it is traced or not. + + Returns a FileDisposition object. + + """ + original_filename = filename + disp = disposition_init(self.disp_class, filename) + + def nope(disp: TFileDisposition, reason: str) -> TFileDisposition: + """Simple helper to make it easy to return NO.""" + disp.trace = False + disp.reason = reason + return disp + + if original_filename.startswith("<"): + return nope(disp, "original file name is not real") + + if frame is not None: + # Compiled Python files have two file names: frame.f_code.co_filename is + # the file name at the time the .pyc was compiled. The second name is + # __file__, which is where the .pyc was actually loaded from. Since + # .pyc files can be moved after compilation (for example, by being + # installed), we look for __file__ in the frame and prefer it to the + # co_filename value. + dunder_file = frame.f_globals and frame.f_globals.get("__file__") + if dunder_file: + # Danger: __file__ can (rarely?) be of type Path. + filename = source_for_file(str(dunder_file)) + if original_filename and not original_filename.startswith("<"): + orig = os.path.basename(original_filename) + if orig != os.path.basename(filename): + # Files shouldn't be renamed when moved. This happens when + # exec'ing code. If it seems like something is wrong with + # the frame's file name, then just use the original. + filename = original_filename + + if not filename: + # Empty string is pretty useless. + return nope(disp, "empty string isn't a file name") + + if filename.startswith("memory:"): + return nope(disp, "memory isn't traceable") + + if filename.startswith("<"): + # Lots of non-file execution is represented with artificial + # file names like "", "", or + # "". Don't ever trace these executions, since we + # can't do anything with the data later anyway. + return nope(disp, "file name is not real") + + canonical = canonical_filename(filename) + disp.canonical_filename = canonical + + # Try the plugins, see if they have an opinion about the file. + plugin = None + for plugin in self.plugins.file_tracers: + if not plugin._coverage_enabled: + continue + + try: + file_tracer = plugin.file_tracer(canonical) + if file_tracer is not None: + file_tracer._coverage_plugin = plugin + disp.trace = True + disp.file_tracer = file_tracer + if file_tracer.has_dynamic_source_filename(): + disp.has_dynamic_filename = True + else: + disp.source_filename = canonical_filename( + file_tracer.source_filename(), + ) + break + except Exception: + plugin_name = plugin._coverage_plugin_name + tb = traceback.format_exc() + self.warn(f"Disabling plug-in {plugin_name!r} due to an exception:\n{tb}") + plugin._coverage_enabled = False + continue + else: + # No plugin wanted it: it's Python. + disp.trace = True + disp.source_filename = canonical + + if not disp.has_dynamic_filename: + if not disp.source_filename: + raise PluginError( + f"Plugin {plugin!r} didn't set source_filename for '{disp.original_filename}'", + ) + reason = self.check_include_omit_etc(disp.source_filename, frame) + if reason: + nope(disp, reason) + + return disp + + def check_include_omit_etc(self, filename: str, frame: FrameType | None) -> str | None: + """Check a file name against the include, omit, etc, rules. + + Returns a string or None. String means, don't trace, and is the reason + why. None means no reason found to not trace. + + """ + modulename = name_for_module(filename, frame) + + # If the user specified source or include, then that's authoritative + # about the outer bound of what to measure and we don't have to apply + # any canned exclusions. If they didn't, then we have to exclude the + # stdlib and coverage.py directories. + if self.source_match or self.source_pkgs_match: + extra = "" + ok = False + if self.source_pkgs_match: + if isinstance(modulename, str) and self.source_pkgs_match.match(modulename): + ok = True + if modulename in self.source_pkgs_unmatched: + self.source_pkgs_unmatched.remove(modulename) + else: + extra = f"module {modulename!r} " + if not ok and self.source_match: + if self.source_match.match(filename): + ok = True + if not ok: + return extra + "falls outside the --source spec" + if self.third_match.match(filename) and not self.source_in_third_match.match(filename): + return "inside --source, but is third-party" + elif self.include_match: + if not self.include_match.match(filename): + return "falls outside the --include trees" + else: + # We exclude the coverage.py code itself, since a little of it + # will be measured otherwise. + if self.cover_match.match(filename): + return "is part of coverage.py" + + # If we aren't supposed to trace installed code, then check if this + # is near the Python standard library and skip it if so. + if self.pylib_match and self.pylib_match.match(filename): + return "is in the stdlib" + + # Exclude anything in the third-party installation areas. + if self.third_match.match(filename): + return "is a third-party module" + + # Check the file against the omit pattern. + if self.omit_match and self.omit_match.match(filename): + return "is inside an --omit pattern" + + # No point tracing a file we can't later write to SQLite. + try: + filename.encode("utf-8") + except UnicodeEncodeError: + return "non-encodable filename" + + # No reason found to skip this file. + return None + + def warn_conflicting_settings(self) -> None: + """Warn if there are settings that conflict.""" + if self.include: + if self.source_dirs or self.source_pkgs: + self.warn("--include is ignored because --source is set", slug="include-ignored") + + def warn_already_imported_files(self) -> None: + """Warn if files have already been imported that we will be measuring.""" + if self.include or self.source_dirs or self.source_pkgs: + warned = set() + for mod in list(sys.modules.values()): + filename = getattr(mod, "__file__", None) + if filename is None: + continue + if filename in warned: + continue + + if len(getattr(mod, "__path__", ())) > 1: + # A namespace package, which confuses this code, so ignore it. + continue + + disp = self.should_trace(filename) + if disp.has_dynamic_filename: + # A plugin with dynamic filenames: the Python file + # shouldn't cause a warning, since it won't be the subject + # of tracing anyway. + continue + if disp.trace: + msg = f"Already imported a file that will be measured: {filename}" + self.warn(msg, slug="already-imported") + warned.add(filename) + elif self.debug and self.debug.should("trace"): + self.debug.write( + "Didn't trace already imported file {!r}: {}".format( + disp.original_filename, + disp.reason, + ), + ) + + def warn_unimported_source(self) -> None: + """Warn about source packages that were of interest, but never traced.""" + for pkg in self.source_pkgs_unmatched: + self._warn_about_unmeasured_code(pkg) + + def _warn_about_unmeasured_code(self, pkg: str) -> None: + """Warn about a package or module that we never traced. + + `pkg` is a string, the name of the package or module. + + """ + mod = sys.modules.get(pkg) + if mod is None: + self.warn(f"Module {pkg} was never imported.", slug="module-not-imported") + return + + if module_is_namespace(mod): + # A namespace package. It's OK for this not to have been traced, + # since there is no code directly in it. + return + + if not module_has_file(mod): + self.warn(f"Module {pkg} has no Python source.", slug="module-not-python") + return + + # The module was in sys.modules, and seems like a module with code, but + # we never measured it. I guess that means it was imported before + # coverage even started. + msg = f"Module {pkg} was previously imported, but not measured" + self.warn(msg, slug="module-not-measured") + + def find_possibly_unexecuted_files(self) -> Iterable[tuple[str, str | None]]: + """Find files in the areas of interest that might be untraced. + + Yields pairs: file path, and responsible plug-in name. + """ + for pkg in self.source_pkgs: + if pkg not in sys.modules or not module_has_file(sys.modules[pkg]): + continue + pkg_file = source_for_file(cast(str, sys.modules[pkg].__file__)) + yield from self._find_executable_files(canonical_path(pkg_file)) + + for src in self.source_dirs: + yield from self._find_executable_files(src) + + def _find_plugin_files(self, src_dir: str) -> Iterable[tuple[str, str]]: + """Get executable files from the plugins.""" + for plugin in self.plugins.file_tracers: + for x_file in plugin.find_executable_files(src_dir): + yield x_file, plugin._coverage_plugin_name + + def _find_executable_files(self, src_dir: str) -> Iterable[tuple[str, str | None]]: + """Find executable files in `src_dir`. + + Search for files in `src_dir` that can be executed because they + are probably importable. Don't include ones that have been omitted + by the configuration. + + Yield the file path, and the plugin name that handles the file. + + """ + py_files = ( + (py_file, None) + for py_file in find_python_files(src_dir, self.include_namespace_packages) + ) + plugin_files = self._find_plugin_files(src_dir) + + for file_path, plugin_name in itertools.chain(py_files, plugin_files): + file_path = canonical_filename(file_path) + if self.omit_match and self.omit_match.match(file_path): + # Turns out this file was omitted, so don't pull it back + # in as un-executed. + continue + yield file_path, plugin_name + + def sys_info(self) -> Iterable[tuple[str, Any]]: + """Our information for Coverage.sys_info. + + Returns a list of (key, value) pairs. + """ + info = [ + ("coverage_paths", self.cover_paths), + ("stdlib_paths", self.pylib_paths), + ("third_party_paths", self.third_paths), + ("source_in_third_party_paths", self.source_in_third_paths), + ] + + matcher_names = [ + "source_match", + "source_pkgs_match", + "include_match", + "omit_match", + "cover_match", + "pylib_match", + "third_match", + "source_in_third_match", + ] + + for matcher_name in matcher_names: + matcher = getattr(self, matcher_name) + if matcher: + matcher_info = matcher.info() + else: + matcher_info = "-none-" + info.append((matcher_name, matcher_info)) + + return info diff --git a/venv/Lib/site-packages/coverage/jsonreport.py b/venv/Lib/site-packages/coverage/jsonreport.py new file mode 100644 index 0000000..1aad2e1 --- /dev/null +++ b/venv/Lib/site-packages/coverage/jsonreport.py @@ -0,0 +1,192 @@ +# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 +# For details: https://github.com/coveragepy/coveragepy/blob/main/NOTICE.txt + +"""Json reporting for coverage.py""" + +from __future__ import annotations + +import datetime +import json +import sys +from collections.abc import Iterable +from typing import IO, TYPE_CHECKING, Any + +from coverage import __version__ +from coverage.report_core import get_analysis_to_report +from coverage.results import Analysis, AnalysisNarrower, Numbers +from coverage.types import TLineNo, TMorf + +if TYPE_CHECKING: + from coverage import Coverage + from coverage.data import CoverageData + from coverage.plugin import FileReporter + + +# A type for data that can be JSON-serialized. +JsonObj = dict[str, Any] + +# "Version 1" had no format number at all. +# 2: add the meta.format field. +# 3: add region information (functions, classes) +FORMAT_VERSION = 3 + + +class JsonReporter: + """A reporter for writing JSON coverage results.""" + + report_type = "JSON report" + + def __init__(self, coverage: Coverage) -> None: + self.coverage = coverage + self.config = self.coverage.config + self.total = Numbers(self.config.precision) + self.report_data: JsonObj = {} + + def make_summary(self, nums: Numbers) -> JsonObj: + """Create a dict summarizing `nums`.""" + return { + "covered_lines": nums.n_executed, + "num_statements": nums.n_statements, + "percent_covered": nums.pc_covered, + "percent_covered_display": nums.pc_covered_str, + "missing_lines": nums.n_missing, + "excluded_lines": nums.n_excluded, + "percent_statements_covered": nums.pc_statements, + "percent_statements_covered_display": nums.pc_statements_str, + } + + def make_branch_summary(self, nums: Numbers) -> JsonObj: + """Create a dict summarizing the branch info in `nums`.""" + return { + "num_branches": nums.n_branches, + "num_partial_branches": nums.n_partial_branches, + "covered_branches": nums.n_executed_branches, + "missing_branches": nums.n_missing_branches, + "percent_branches_covered": nums.pc_branches, + "percent_branches_covered_display": nums.pc_branches_str, + } + + def report(self, morfs: Iterable[TMorf] | None, outfile: IO[str]) -> float: + """Generate a json report for `morfs`. + + `morfs` is a list of modules or file names. + + `outfile` is a file object to write the json to. + + """ + outfile = outfile or sys.stdout + coverage_data = self.coverage.get_data() + coverage_data.set_query_contexts(self.config.report_contexts) + self.report_data["meta"] = { + "format": FORMAT_VERSION, + "version": __version__, + "timestamp": datetime.datetime.now().isoformat(), + "branch_coverage": coverage_data.has_arcs(), + "show_contexts": self.config.json_show_contexts, + } + + measured_files = {} + for file_reporter, analysis in get_analysis_to_report(self.coverage, morfs): + measured_files[file_reporter.relative_filename()] = self.report_one_file( + coverage_data, + analysis, + file_reporter, + ) + + self.report_data["files"] = measured_files + self.report_data["totals"] = self.make_summary(self.total) + + if coverage_data.has_arcs(): + self.report_data["totals"].update(self.make_branch_summary(self.total)) + + json.dump( + self.report_data, + outfile, + indent=(4 if self.config.json_pretty_print else None), + ) + + return self.total.n_statements and self.total.pc_covered + + def report_one_file( + self, coverage_data: CoverageData, analysis: Analysis, file_reporter: FileReporter + ) -> JsonObj: + """Extract the relevant report data for a single file.""" + nums = analysis.numbers + self.total += nums + summary = self.make_summary(nums) + reported_file: JsonObj = { + "executed_lines": sorted(analysis.executed), + "summary": summary, + "missing_lines": sorted(analysis.missing), + "excluded_lines": sorted(analysis.excluded), + } + if self.config.json_show_contexts: + reported_file["contexts"] = coverage_data.contexts_by_lineno(analysis.filename) + if coverage_data.has_arcs(): + summary.update(self.make_branch_summary(nums)) + reported_file["executed_branches"] = list( + _convert_branch_arcs(analysis.executed_branch_arcs()), + ) + reported_file["missing_branches"] = list( + _convert_branch_arcs(analysis.missing_branch_arcs()), + ) + + num_lines = len(file_reporter.source().splitlines()) + regions = file_reporter.code_regions() + for noun, plural in file_reporter.code_region_kinds(): + outside_lines = set(range(1, num_lines + 1)) + for region in regions: + if region.kind != noun: + continue + outside_lines -= region.lines + + narrower = AnalysisNarrower(analysis) + narrower.add_regions(r.lines for r in regions if r.kind == noun) + narrower.add_regions([outside_lines]) + + reported_file[plural] = region_data = {} + for region in regions: + if region.kind != noun: + continue + region_data[region.name] = self.make_region_data( + coverage_data, + narrower.narrow(region.lines), + ) + + region_data[""] = self.make_region_data( + coverage_data, + narrower.narrow(outside_lines), + ) + return reported_file + + def make_region_data(self, coverage_data: CoverageData, narrowed_analysis: Analysis) -> JsonObj: + """Create the data object for one region of a file.""" + narrowed_nums = narrowed_analysis.numbers + narrowed_summary = self.make_summary(narrowed_nums) + this_region = { + "executed_lines": sorted(narrowed_analysis.executed), + "summary": narrowed_summary, + "missing_lines": sorted(narrowed_analysis.missing), + "excluded_lines": sorted(narrowed_analysis.excluded), + } + if self.config.json_show_contexts: + contexts = coverage_data.contexts_by_lineno(narrowed_analysis.filename) + this_region["contexts"] = contexts + if coverage_data.has_arcs(): + narrowed_summary.update(self.make_branch_summary(narrowed_nums)) + this_region["executed_branches"] = list( + _convert_branch_arcs(narrowed_analysis.executed_branch_arcs()), + ) + this_region["missing_branches"] = list( + _convert_branch_arcs(narrowed_analysis.missing_branch_arcs()), + ) + return this_region + + +def _convert_branch_arcs( + branch_arcs: dict[TLineNo, list[TLineNo]], +) -> Iterable[tuple[TLineNo, TLineNo]]: + """Convert branch arcs to a list of two-element tuples.""" + for source, targets in branch_arcs.items(): + for target in targets: + yield source, target diff --git a/venv/Lib/site-packages/coverage/lcovreport.py b/venv/Lib/site-packages/coverage/lcovreport.py new file mode 100644 index 0000000..c29cfe4 --- /dev/null +++ b/venv/Lib/site-packages/coverage/lcovreport.py @@ -0,0 +1,219 @@ +# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 +# For details: https://github.com/coveragepy/coveragepy/blob/main/NOTICE.txt + +"""LCOV reporting for coverage.py.""" + +from __future__ import annotations + +import base64 +import hashlib +import sys +from collections.abc import Iterable +from typing import IO, TYPE_CHECKING + +from coverage.plugin import FileReporter +from coverage.report_core import get_analysis_to_report +from coverage.results import Analysis, AnalysisNarrower, Numbers +from coverage.types import TMorf + +if TYPE_CHECKING: + from coverage import Coverage + + +def line_hash(line: str) -> str: + """Produce a hash of a source line for use in the LCOV file.""" + # The LCOV file format optionally allows each line to be MD5ed as a + # fingerprint of the file. This is not a security use. Some security + # scanners raise alarms about the use of MD5 here, but it is a false + # positive. This is not a security concern. + # The unusual encoding of the MD5 hash, as a base64 sequence with the + # trailing = signs stripped, is specified by the LCOV file format. + hashed = hashlib.md5(line.encode("utf-8"), usedforsecurity=False).digest() + return base64.b64encode(hashed).decode("ascii").rstrip("=") + + +def lcov_lines( + analysis: Analysis, + lines: list[int], + source_lines: list[str], + outfile: IO[str], +) -> None: + """Emit line coverage records for an analyzed file.""" + hash_suffix = "" + for line in lines: + if source_lines: + hash_suffix = "," + line_hash(source_lines[line - 1]) + # Q: can we get info about the number of times a statement is + # executed? If so, that should be recorded here. + hit = int(line not in analysis.missing) + outfile.write(f"DA:{line},{hit}{hash_suffix}\n") + + if analysis.numbers.n_statements > 0: + outfile.write(f"LF:{analysis.numbers.n_statements}\n") + outfile.write(f"LH:{analysis.numbers.n_executed}\n") + + +def lcov_functions( + fr: FileReporter, + file_analysis: Analysis, + outfile: IO[str], +) -> None: + """Emit function coverage records for an analyzed file.""" + # lcov 2.2 introduces a new format for function coverage records. + # We continue to generate the old format because we don't know what + # version of the lcov tools will be used to read this report. + + # "and region.lines" below avoids a crash due to a bug in PyPy 3.8 + # where, for whatever reason, when collecting data in --branch mode, + # top-level functions have an empty lines array. Instead we just don't + # emit function records for those. + + # suppressions because of https://github.com/pylint-dev/pylint/issues/9923 + functions = [ + ( + min(region.start, min(region.lines)), # pylint: disable=nested-min-max + max(region.start, max(region.lines)), # pylint: disable=nested-min-max + region, + ) + for region in fr.code_regions() + if region.kind == "function" and region.lines + ] + if not functions: + return + + narrower = AnalysisNarrower(file_analysis) + narrower.add_regions(r.lines for _, _, r in functions) + + functions.sort() + functions_hit = 0 + for first_line, last_line, region in functions: + # A function counts as having been executed if any of it has been + # executed. + analysis = narrower.narrow(region.lines) + hit = int(analysis.numbers.n_executed > 0) + functions_hit += hit + + outfile.write(f"FN:{first_line},{last_line},{region.name}\n") + outfile.write(f"FNDA:{hit},{region.name}\n") + + outfile.write(f"FNF:{len(functions)}\n") + outfile.write(f"FNH:{functions_hit}\n") + + +def lcov_arcs( + fr: FileReporter, + analysis: Analysis, + lines: list[int], + outfile: IO[str], +) -> None: + """Emit branch coverage records for an analyzed file.""" + branch_stats = analysis.branch_stats() + executed_arcs = analysis.executed_branch_arcs() + missing_arcs = analysis.missing_branch_arcs() + + for line in lines: + if line not in branch_stats: + continue + + # This is only one of several possible ways to map our sets of executed + # and not-executed arcs to BRDA codes. It seems to produce reasonable + # results when fed through genhtml. + _, taken = branch_stats[line] + + if taken == 0: + # When _none_ of the out arcs from 'line' were executed, + # it can mean the line always raised an exception. + assert len(executed_arcs[line]) == 0 + destinations = [(dst, "-") for dst in missing_arcs[line]] + else: + # Q: can we get counts of the number of times each arc was executed? + # branch_stats has "total" and "taken" counts for each branch, + # but it doesn't have "taken" broken down by destination. + destinations = [(dst, "1") for dst in executed_arcs[line]] + destinations.extend((dst, "0") for dst in missing_arcs[line]) + + # Sort exit arcs after normal arcs. Exit arcs typically come from + # an if statement, at the end of a function, with no else clause. + # This structure reads like you're jumping to the end of the function + # when the conditional expression is false, so it should be presented + # as the second alternative for the branch, after the alternative that + # enters the if clause. + destinations.sort(key=lambda d: (d[0] < 0, d)) + + for dst, hit in destinations: + branch = fr.arc_description(line, dst) + outfile.write(f"BRDA:{line},0,{branch},{hit}\n") + + # Summary of the branch coverage. + brf = sum(t for t, k in branch_stats.values()) + brh = brf - sum(t - k for t, k in branch_stats.values()) + if brf > 0: + outfile.write(f"BRF:{brf}\n") + outfile.write(f"BRH:{brh}\n") + + +class LcovReporter: + """A reporter for writing LCOV coverage reports.""" + + report_type = "LCOV report" + + def __init__(self, coverage: Coverage) -> None: + self.coverage = coverage + self.config = coverage.config + self.total = Numbers(self.coverage.config.precision) + + def report(self, morfs: Iterable[TMorf] | None, outfile: IO[str]) -> float: + """Renders the full lcov report. + + `morfs` is a list of modules or filenames + + outfile is the file object to write the file into. + """ + + self.coverage.get_data() + outfile = outfile or sys.stdout + + # ensure file records are sorted by the _relative_ filename, not the full path + to_report = [ + (fr.relative_filename(), fr, analysis) + for fr, analysis in get_analysis_to_report(self.coverage, morfs) + ] + to_report.sort() + + for fname, fr, analysis in to_report: + self.total += analysis.numbers + self.lcov_file(fname, fr, analysis, outfile) + + return self.total.n_statements and self.total.pc_covered + + def lcov_file( + self, + rel_fname: str, + fr: FileReporter, + analysis: Analysis, + outfile: IO[str], + ) -> None: + """Produces the lcov data for a single file. + + This currently supports both line and branch coverage, + however function coverage is not supported. + """ + + if analysis.numbers.n_statements == 0: + if self.config.skip_empty: + return + + outfile.write(f"SF:{rel_fname}\n") + + lines = sorted(analysis.statements) + if self.config.lcov_line_checksums: + source_lines = fr.source().splitlines() + else: + source_lines = [] + + lcov_lines(analysis, lines, source_lines, outfile) + lcov_functions(fr, analysis, outfile) + if analysis.has_arcs: + lcov_arcs(fr, analysis, lines, outfile) + + outfile.write("end_of_record\n") diff --git a/venv/Lib/site-packages/coverage/misc.py b/venv/Lib/site-packages/coverage/misc.py new file mode 100644 index 0000000..82abeab --- /dev/null +++ b/venv/Lib/site-packages/coverage/misc.py @@ -0,0 +1,373 @@ +# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 +# For details: https://github.com/coveragepy/coveragepy/blob/main/NOTICE.txt + +"""Miscellaneous stuff for coverage.py.""" + +from __future__ import annotations + +import contextlib +import datetime +import errno +import functools +import hashlib +import importlib +import importlib.util +import inspect +import os +import os.path +import re +import sys +import types +from collections.abc import Iterable, Iterator, Mapping, Sequence +from types import ModuleType +from typing import Any, NoReturn, TypeVar + +# In 6.0, the exceptions moved from misc.py to exceptions.py. But a number of +# other packages were importing the exceptions from misc, so import them here. +# pylint: disable=unused-wildcard-import +from coverage.exceptions import * # pylint: disable=wildcard-import +from coverage.exceptions import CoverageException +from coverage.types import TArc + +ISOLATED_MODULES: dict[ModuleType, ModuleType] = {} + + +def isolate_module(mod: ModuleType) -> ModuleType: + """Copy a module so that we are isolated from aggressive mocking. + + If a test suite mocks os.path.exists (for example), and then we need to use + it during the test, everything will get tangled up if we use their mock. + Making a copy of the module when we import it will isolate coverage.py from + those complications. + """ + if mod not in ISOLATED_MODULES: + new_mod = types.ModuleType(mod.__name__) + ISOLATED_MODULES[mod] = new_mod + for name in dir(mod): + value = getattr(mod, name) + if isinstance(value, types.ModuleType): + value = isolate_module(value) + setattr(new_mod, name, value) + return ISOLATED_MODULES[mod] + + +os = isolate_module(os) + + +class SysModuleSaver: + """Saves the contents of sys.modules, and removes new modules later.""" + + def __init__(self) -> None: + self.old_modules = set(sys.modules) + + def restore(self) -> None: + """Remove any modules imported since this object started.""" + new_modules = set(sys.modules) - self.old_modules + for m in new_modules: + del sys.modules[m] + + +@contextlib.contextmanager +def sys_modules_saved() -> Iterator[None]: + """A context manager to remove any modules imported during a block.""" + saver = SysModuleSaver() + try: + yield + finally: + saver.restore() + + +def import_third_party(modname: str) -> tuple[ModuleType, bool]: + """Import a third-party module we need, but might not be installed. + + This also cleans out the module after the import, so that coverage won't + appear to have imported it. This lets the third party use coverage for + their own tests. + + Arguments: + modname (str): the name of the module to import. + + Returns: + The imported module, and a boolean indicating if the module could be imported. + + If the boolean is False, the module returned is not the one you want: don't use it. + + """ + with sys_modules_saved(): + try: + return importlib.import_module(modname), True + except ImportError: + return sys, False + + +def nice_pair(pair: TArc) -> str: + """Make a nice string representation of a pair of numbers. + + If the numbers are equal, just return the number, otherwise return the pair + with a dash between them, indicating the range. + + """ + start, end = pair + if start == end: + return f"{start}" + else: + return f"{start}-{end}" + + +def bool_or_none(b: Any) -> bool | None: + """Return bool(b), but preserve None.""" + if b is None: + return None + else: + return bool(b) + + +def join_regex(regexes: Iterable[str]) -> str: + """Combine a series of regex strings into one that matches any of them.""" + regexes = list(regexes) + if len(regexes) == 1: + return regexes[0] + else: + return "|".join(f"(?:{r})" for r in regexes) + + +def file_be_gone(path: str) -> None: + """Remove a file, and don't get annoyed if it doesn't exist.""" + try: + os.remove(path) + except OSError as e: + if e.errno != errno.ENOENT: + raise + + +def ensure_dir(directory: str) -> None: + """Make sure the directory exists. + + If `directory` is None or empty, do nothing. + """ + if directory: + os.makedirs(directory, exist_ok=True) + + +def ensure_dir_for_file(path: str) -> None: + """Make sure the directory for the path exists.""" + ensure_dir(os.path.dirname(path)) + + +class Hasher: + """Hashes Python data for fingerprinting.""" + + def __init__(self) -> None: + self.hash = hashlib.new("sha3_256", usedforsecurity=False) + + def update(self, v: Any) -> None: + """Add `v` to the hash, recursively if needed.""" + self.hash.update(str(type(v)).encode("utf-8")) + match v: + case None: + pass + case str(): + self.hash.update(v.encode("utf-8")) + case bytes(): + self.hash.update(v) + case int() | float(): + self.hash.update(str(v).encode("utf-8")) + case tuple() | list(): + for e in v: + self.update(e) + case dict(): + keys = v.keys() + for k in sorted(keys): + self.update(k) + self.update(v[k]) + case _: + for k in dir(v): + if k.startswith("__"): + continue + a = getattr(v, k) + if inspect.isroutine(a): + continue + self.update(k) + self.update(a) + self.hash.update(b".") + + def hexdigest(self) -> str: + """Retrieve the hex digest of the hash.""" + return self.hash.hexdigest()[:32] + + +def _needs_to_implement(that: Any, func_name: str) -> NoReturn: + """Helper to raise NotImplementedError in interface stubs.""" + if hasattr(that, "_coverage_plugin_name"): + thing = "Plugin" + name = that._coverage_plugin_name + else: + thing = "Class" + klass = that.__class__ + name = f"{klass.__module__}.{klass.__name__}" + + raise NotImplementedError( + f"{thing} {name!r} needs to implement {func_name}()", + ) + + +class DefaultValue: + """A sentinel object to use for unusual default-value needs. + + Construct with a string that will be used as the repr, for display in help + and Sphinx output. + + """ + + def __init__(self, display_as: str) -> None: + self.display_as = display_as + + def __repr__(self) -> str: + return self.display_as + + +def substitute_variables(text: str, variables: Mapping[str, str]) -> str: + """Substitute ``${VAR}`` variables in `text` with their values. + + Variables in the text can take a number of shell-inspired forms:: + + $VAR + ${VAR} + ${VAR?} strict: an error if VAR isn't defined. + ${VAR-missing} defaulted: "missing" if VAR isn't defined. + $$ just a dollar sign. + + `variables` is a dictionary of variable values. + + Returns the resulting text with values substituted. + + """ + dollar_pattern = r"""(?x) # Use extended regex syntax + \$ # A dollar sign, + (?: # then + (?P \$ ) | # a dollar sign, or + (?P \w+ ) | # a plain word, or + \{ # a {-wrapped + (?P \w+ ) # word, + (?: # either + (?P \? ) | # with a strict marker + -(?P [^}]* ) # or a default value + )? # maybe. + } + ) + """ + + dollar_groups = ("dollar", "word1", "word2") + + def dollar_replace(match: re.Match[str]) -> str: + """Called for each $replacement.""" + # Only one of the dollar_groups will have matched, just get its text. + word = next(g for g in match.group(*dollar_groups) if g) # pragma: always breaks + if word == "$": + return "$" + elif word in variables: + return variables[word] + elif match["strict"]: + msg = f"Variable {word} is undefined: {text!r}" + raise CoverageException(msg) + else: + return match["defval"] + + text = re.sub(dollar_pattern, dollar_replace, text) + return text + + +def format_local_datetime(dt: datetime.datetime) -> str: + """Return a string with local timezone representing the date.""" + return dt.astimezone().strftime("%Y-%m-%d %H:%M %z") + + +def import_local_file(modname: str, modfile: str | None = None) -> ModuleType: + """Import a local file as a module. + + Opens a file in the current directory named `modname`.py, imports it + as `modname`, and returns the module object. `modfile` is the file to + import if it isn't in the current directory. + + """ + if modfile is None: + modfile = modname + ".py" + spec = importlib.util.spec_from_file_location(modname, modfile) + assert spec is not None + mod = importlib.util.module_from_spec(spec) + sys.modules[modname] = mod + assert spec.loader is not None + spec.loader.exec_module(mod) + + return mod + + +@functools.cache +def _human_key(s: str) -> tuple[list[str | int], str]: + """Turn a string into a list of string and number chunks. + + "z23a" -> (["z", 23, "a"], "z23a") + + The original string is appended as a last value to ensure the + key is unique enough so that "x1y" and "x001y" can be distinguished. + """ + + def tryint(s: str) -> str | int: + """If `s` is a number, return an int, else `s` unchanged.""" + try: + return int(s) + except ValueError: + return s + + return ([tryint(c) for c in re.split(r"(\d+)", s)], s) + + +def human_sorted(strings: Iterable[str]) -> list[str]: + """Sort the given iterable of strings the way that humans expect. + + Numeric components in the strings are sorted as numbers. + + Returns the sorted list. + + """ + return sorted(strings, key=_human_key) + + +SortableItem = TypeVar("SortableItem", bound=Sequence[Any]) + + +def human_sorted_items( + items: Iterable[SortableItem], + reverse: bool = False, +) -> list[SortableItem]: + """Sort (string, ...) items the way humans expect. + + The elements of `items` can be any tuple/list. They'll be sorted by the + first element (a string), with ties broken by the remaining elements. + + Returns the sorted list of items. + """ + return sorted(items, key=lambda item: (_human_key(item[0]), *item[1:]), reverse=reverse) + + +def plural(n: int, thing: str = "", things: str = "") -> str: + """Pluralize a word. + + If n is 1, return thing. Otherwise return things, or thing+s. + """ + if n == 1: + return thing + else: + return things or (thing + "s") + + +def stdout_link(text: str, url: str) -> str: + """Format text+url as a clickable link for stdout. + + If attached to a terminal, use escape sequences. Otherwise, just return + the text. + """ + if hasattr(sys.stdout, "isatty") and sys.stdout.isatty(): + return f"\033]8;;{url}\a{text}\033]8;;\a" + else: + return text diff --git a/venv/Lib/site-packages/coverage/multiproc.py b/venv/Lib/site-packages/coverage/multiproc.py new file mode 100644 index 0000000..dde2ca2 --- /dev/null +++ b/venv/Lib/site-packages/coverage/multiproc.py @@ -0,0 +1,120 @@ +# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 +# For details: https://github.com/coveragepy/coveragepy/blob/main/NOTICE.txt + +"""Monkey-patching to add multiprocessing support for coverage.py""" + +from __future__ import annotations + +import multiprocessing +import multiprocessing.process +import os +import os.path +import sys +import traceback +from typing import Any + +from coverage.debug import DebugControl + +# An attribute that will be set on the module to indicate that it has been +# monkey-patched. +PATCHED_MARKER = "_coverage$patched" + + +OriginalProcess = multiprocessing.process.BaseProcess +original_bootstrap = OriginalProcess._bootstrap # type: ignore[attr-defined] + + +class ProcessWithCoverage(OriginalProcess): # pylint: disable=abstract-method + """A replacement for multiprocess.Process that starts coverage.""" + + def _bootstrap(self, *args, **kwargs): # type: ignore[no-untyped-def] + """Wrapper around _bootstrap to start coverage.""" + debug: DebugControl | None = None + try: + from coverage import Coverage # avoid circular import + + cov = Coverage(data_suffix=True, auto_data=True) + cov._warn_preimported_source = False + cov.start() + _debug = cov._debug + assert _debug is not None + if _debug.should("multiproc"): + debug = _debug + if debug: + debug.write("Calling multiprocessing bootstrap") + except Exception: + print("Exception during multiprocessing bootstrap init:", file=sys.stderr) + traceback.print_exc(file=sys.stderr) + sys.stderr.flush() + raise + try: + return original_bootstrap(self, *args, **kwargs) + finally: + if debug: + debug.write("Finished multiprocessing bootstrap") + try: + cov.stop() + cov.save() + except Exception as exc: + if debug: + debug.write("Exception during multiprocessing bootstrap cleanup", exc=exc) + raise + if debug: + debug.write("Saved multiprocessing data") + + +class Stowaway: + """An object to pickle, so when it is unpickled, it can apply the monkey-patch.""" + + def __init__(self, rcfile: str) -> None: + self.rcfile = rcfile + + def __getstate__(self) -> dict[str, str]: + return {"rcfile": self.rcfile} + + def __setstate__(self, state: dict[str, str]) -> None: + patch_multiprocessing(state["rcfile"]) + + +def patch_multiprocessing(rcfile: str) -> None: + """Monkey-patch the multiprocessing module. + + This enables coverage measurement of processes started by multiprocessing. + This involves aggressive monkey-patching. + + `rcfile` is the path to the rcfile being used. + + """ + + if hasattr(multiprocessing, PATCHED_MARKER): + return + + OriginalProcess._bootstrap = ProcessWithCoverage._bootstrap # type: ignore[attr-defined] + + # Set the value in ProcessWithCoverage that will be pickled into the child + # process. + os.environ["COVERAGE_RCFILE"] = os.path.abspath(rcfile) + + # When spawning processes rather than forking them, we have no state in the + # new process. We sneak in there with a Stowaway: we stuff one of our own + # objects into the data that gets pickled and sent to the subprocess. When + # the Stowaway is unpickled, its __setstate__ method is called, which + # re-applies the monkey-patch. + # Windows only spawns, so this is needed to keep Windows working. + try: + from multiprocessing import spawn + + original_get_preparation_data = spawn.get_preparation_data + except (ImportError, AttributeError): + pass + else: + + def get_preparation_data_with_stowaway(name: str) -> dict[str, Any]: + """Get the original preparation data, and also insert our stowaway.""" + d = original_get_preparation_data(name) + d["stowaway"] = Stowaway(rcfile) + return d + + spawn.get_preparation_data = get_preparation_data_with_stowaway + + setattr(multiprocessing, PATCHED_MARKER, True) diff --git a/venv/Lib/site-packages/coverage/numbits.py b/venv/Lib/site-packages/coverage/numbits.py new file mode 100644 index 0000000..e813559 --- /dev/null +++ b/venv/Lib/site-packages/coverage/numbits.py @@ -0,0 +1,146 @@ +# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 +# For details: https://github.com/coveragepy/coveragepy/blob/main/NOTICE.txt + +""" +Functions to manipulate packed binary representations of number sets. + +To save space, coverage stores sets of line numbers in SQLite using a packed +binary representation called a numbits. A numbits is a set of positive +integers. + +A numbits is stored as a blob in the database. The exact meaning of the bytes +in the blobs should be considered an implementation detail that might change in +the future. Use these functions to work with those binary blobs of data. + +""" + +from __future__ import annotations + +import json +import sqlite3 +from collections.abc import Iterable +from itertools import zip_longest + + +def nums_to_numbits(nums: Iterable[int]) -> bytes: + """Convert `nums` into a numbits. + + Arguments: + nums: a reusable iterable of integers, the line numbers to store. + + Returns: + A binary blob. + """ + try: + nbytes = max(nums) // 8 + 1 + except ValueError: + # nums was empty. + return b"" + b = bytearray(nbytes) + for num in nums: + b[num // 8] |= 1 << num % 8 + return bytes(b) + + +def numbits_to_nums(numbits: bytes) -> list[int]: + """Convert a numbits into a list of numbers. + + Arguments: + numbits: a binary blob, the packed number set. + + Returns: + A list of ints. + + When registered as a SQLite function by :func:`register_sqlite_functions`, + this returns a string, a JSON-encoded list of ints. + + """ + nums = [] + for byte_i, byte in enumerate(numbits): + for bit_i in range(8): + if byte & (1 << bit_i): + nums.append(byte_i * 8 + bit_i) + return nums + + +def numbits_union(numbits1: bytes, numbits2: bytes) -> bytes: + """Compute the union of two numbits. + + Returns: + A new numbits, the union of `numbits1` and `numbits2`. + """ + byte_pairs = zip_longest(numbits1, numbits2, fillvalue=0) + return bytes(b1 | b2 for b1, b2 in byte_pairs) + + +def numbits_intersection(numbits1: bytes, numbits2: bytes) -> bytes: + """Compute the intersection of two numbits. + + Returns: + A new numbits, the intersection `numbits1` and `numbits2`. + """ + byte_pairs = zip_longest(numbits1, numbits2, fillvalue=0) + intersection_bytes = bytes(b1 & b2 for b1, b2 in byte_pairs) + return intersection_bytes.rstrip(b"\0") + + +def numbits_any_intersection(numbits1: bytes, numbits2: bytes) -> bool: + """Is there any number that appears in both numbits? + + Determine whether two number sets have a non-empty intersection. This is + faster than computing the intersection. + + Returns: + A bool, True if there is any number in both `numbits1` and `numbits2`. + """ + byte_pairs = zip_longest(numbits1, numbits2, fillvalue=0) + return any(b1 & b2 for b1, b2 in byte_pairs) + + +def num_in_numbits(num: int, numbits: bytes) -> bool: + """Does the integer `num` appear in `numbits`? + + Returns: + A bool, True if `num` is a member of `numbits`. + """ + nbyte, nbit = divmod(num, 8) + if nbyte >= len(numbits): + return False + return bool(numbits[nbyte] & (1 << nbit)) + + +def register_sqlite_functions(connection: sqlite3.Connection) -> None: + """ + Define numbits functions in a SQLite connection. + + This defines these functions for use in SQLite statements: + + * :func:`numbits_union` + * :func:`numbits_intersection` + * :func:`numbits_any_intersection` + * :func:`num_in_numbits` + * :func:`numbits_to_nums` + + `connection` is a :class:`sqlite3.Connection ` + object. After creating the connection, pass it to this function to + register the numbits functions. Then you can use numbits functions in your + queries:: + + import sqlite3 + from coverage.numbits import register_sqlite_functions + + conn = sqlite3.connect("example.db") + register_sqlite_functions(conn) + c = conn.cursor() + # Kind of a nonsense query: + # Find all the files and contexts that executed line 47 in any file: + c.execute( + "select file_id, context_id from line_bits where num_in_numbits(?, numbits)", + (47,) + ) + """ + connection.create_function("numbits_union", 2, numbits_union) + connection.create_function("numbits_intersection", 2, numbits_intersection) + connection.create_function("numbits_any_intersection", 2, numbits_any_intersection) + connection.create_function("num_in_numbits", 2, num_in_numbits) + connection.create_function("numbits_to_nums", 1, lambda b: json.dumps(numbits_to_nums(b))) diff --git a/venv/Lib/site-packages/coverage/parser.py b/venv/Lib/site-packages/coverage/parser.py new file mode 100644 index 0000000..9b13f19 --- /dev/null +++ b/venv/Lib/site-packages/coverage/parser.py @@ -0,0 +1,1215 @@ +# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 +# For details: https://github.com/coveragepy/coveragepy/blob/main/NOTICE.txt + +"""Code parsing for coverage.py.""" + +from __future__ import annotations + +import ast +import collections +import functools +import os +import re +import token +import tokenize +from collections.abc import Iterable, Sequence +from dataclasses import dataclass +from types import CodeType +from typing import Callable, Optional, Protocol, cast + +from coverage import env +from coverage.bytecode import code_objects +from coverage.debug import short_stack +from coverage.exceptions import NoSource, NotPython +from coverage.misc import isolate_module, nice_pair +from coverage.phystokens import generate_tokens +from coverage.types import TArc, TLineNo + +os = isolate_module(os) + + +class PythonParser: + """Parse code to find executable lines, excluded lines, etc. + + This information is all based on static analysis: no code execution is + involved. + + """ + + def __init__( + self, + text: str | None = None, + filename: str | None = None, + exclude: str | None = None, + ) -> None: + """ + Source can be provided as `text`, the text itself, or `filename`, from + which the text will be read. Excluded lines are those that match + `exclude`, a regex string. + + """ + assert text or filename, "PythonParser needs either text or filename" + self.filename = filename or "" + if text is not None: + self.text: str = text + else: + from coverage.python import get_python_source + + try: + self.text = get_python_source(self.filename) + except OSError as err: + raise NoSource(f"No source for code: '{self.filename}': {err}") from err + + self.exclude = exclude + + # The parsed AST of the text. + self._ast_root: ast.AST | None = None + + # The normalized line numbers of the statements in the code. Exclusions + # are taken into account, and statements are adjusted to their first + # lines. + self.statements: set[TLineNo] = set() + + # The normalized line numbers of the excluded lines in the code, + # adjusted to their first lines. + self.excluded: set[TLineNo] = set() + + # The raw_* attributes are only used in this class, and in + # lab/parser.py to show how this class is working. + + # The line numbers that start statements, as reported by the line + # number table in the bytecode. + self.raw_statements: set[TLineNo] = set() + + # The raw line numbers of excluded lines of code, as marked by pragmas. + self.raw_excluded: set[TLineNo] = set() + + # The line numbers of docstring lines. + self.raw_docstrings: set[TLineNo] = set() + + # Internal detail, used by lab/parser.py. + self.show_tokens = False + + # A dict mapping line numbers to lexical statement starts for + # multi-line statements. + self.multiline_map: dict[TLineNo, TLineNo] = {} + + # Lazily-created arc data, and missing arc descriptions. + self._all_arcs: set[TArc] | None = None + self._missing_arc_fragments: TArcFragments | None = None + self._with_jump_fixers: dict[TArc, tuple[TArc, TArc]] = {} + + def lines_matching(self, regex: str) -> set[TLineNo]: + """Find the lines matching a regex. + + Returns a set of line numbers, the lines that contain a match for + `regex`. The entire line needn't match, just a part of it. + Handles multiline regex patterns. + + """ + matches: set[TLineNo] = set() + + last_start = 0 + last_start_line = 0 + for match in re.finditer(regex, self.text, flags=re.MULTILINE): + start, end = match.span() + start_line = last_start_line + self.text.count("\n", last_start, start) + end_line = last_start_line + self.text.count("\n", last_start, end) + matches.update( + self.multiline_map.get(i, i) for i in range(start_line + 1, end_line + 2) + ) + last_start = start + last_start_line = start_line + return matches + + def _raw_parse(self) -> None: + """Parse the source to find the interesting facts about its lines. + + A handful of attributes are updated. + + """ + # Find lines which match an exclusion pattern. + if self.exclude: + self.raw_excluded = self.lines_matching(self.exclude) + self.excluded = set(self.raw_excluded) + + # The current number of indents. + indent: int = 0 + # An exclusion comment will exclude an entire clause at this indent. + exclude_indent: int = 0 + # Are we currently excluding lines? + excluding: bool = False + # The line number of the first line in a multi-line statement. + first_line: int = 0 + # Is the file empty? + empty: bool = True + # Parenthesis (and bracket) nesting level. + nesting: int = 0 + + assert self.text is not None + tokgen = generate_tokens(self.text) + for toktype, ttext, (slineno, _), (elineno, _), ltext in tokgen: + if self.show_tokens: # pragma: debugging + print( + "%10s %5s %-20r %r" + % ( + tokenize.tok_name.get(toktype, toktype), + nice_pair((slineno, elineno)), + ttext, + ltext, + ) + ) + if toktype == token.INDENT: + indent += 1 + elif toktype == token.DEDENT: + indent -= 1 + elif toktype == token.OP: + if ttext == ":" and nesting == 0: + should_exclude = self.excluded.intersection(range(first_line, elineno + 1)) + if not excluding and should_exclude: + # Start excluding a suite. We trigger off of the colon + # token so that the #pragma comment will be recognized on + # the same line as the colon. + self.excluded.add(elineno) + exclude_indent = indent + excluding = True + elif ttext in "([{": + nesting += 1 + elif ttext in ")]}": + nesting -= 1 + elif toktype == token.NEWLINE: + if first_line and elineno != first_line: + # We're at the end of a line, and we've ended on a + # different line than the first line of the statement, + # so record a multi-line range. + for l in range(first_line, elineno + 1): + self.multiline_map[l] = first_line + first_line = 0 + + if ttext.strip() and toktype != tokenize.COMMENT: + # A non-white-space token. + empty = False + if not first_line: + # The token is not white space, and is the first in a statement. + first_line = slineno + # Check whether to end an excluded suite. + if excluding and indent <= exclude_indent: + excluding = False + if excluding: + self.excluded.add(elineno) + + # Find the starts of the executable statements. + if not empty: + byte_parser = ByteParser(self.text, filename=self.filename) + self.raw_statements.update(byte_parser._find_statements()) + + self.excluded = self.first_lines(self.excluded) + + # AST lets us find classes, docstrings, and decorator-affected + # functions and classes. + assert self._ast_root is not None + for node in ast.walk(self._ast_root): + # Find docstrings. + if isinstance(node, (ast.ClassDef, ast.FunctionDef, ast.AsyncFunctionDef, ast.Module)): + if node.body: + first = node.body[0] + if ( + isinstance(first, ast.Expr) + and isinstance(first.value, ast.Constant) + and isinstance(first.value.value, str) + ): + self.raw_docstrings.update( + range(first.lineno, cast(int, first.end_lineno) + 1) + ) + # Exclusions carry from decorators and signatures to the bodies of + # functions and classes. + if isinstance(node, (ast.ClassDef, ast.FunctionDef, ast.AsyncFunctionDef)): + first_line = min((d.lineno for d in node.decorator_list), default=node.lineno) + if self.excluded.intersection(range(first_line, node.lineno + 1)): + self.excluded.update(range(first_line, cast(int, node.end_lineno) + 1)) + + @functools.lru_cache(maxsize=1000) + def first_line(self, lineno: TLineNo) -> TLineNo: + """Return the first line number of the statement including `lineno`.""" + if lineno < 0: + lineno = -self.multiline_map.get(-lineno, -lineno) + else: + lineno = self.multiline_map.get(lineno, lineno) + return lineno + + def first_lines(self, linenos: Iterable[TLineNo]) -> set[TLineNo]: + """Map the line numbers in `linenos` to the correct first line of the + statement. + + Returns a set of the first lines. + + """ + return {self.first_line(l) for l in linenos} + + def translate_lines(self, lines: Iterable[TLineNo]) -> set[TLineNo]: + """Implement `FileReporter.translate_lines`.""" + return self.first_lines(lines) + + def translate_arcs(self, arcs: Iterable[TArc]) -> set[TArc]: + """Implement `FileReporter.translate_arcs`.""" + return {(self.first_line(a), self.first_line(b)) for (a, b) in self.fix_with_jumps(arcs)} + + def parse_source(self) -> None: + """Parse source text to find executable lines, excluded lines, etc. + + Sets the .excluded and .statements attributes, normalized to the first + line of multi-line statements. + + """ + try: + self._ast_root = ast.parse(self.text) + self._raw_parse() + except (tokenize.TokenError, IndentationError, SyntaxError) as err: + if hasattr(err, "lineno"): + lineno = err.lineno # IndentationError + else: + lineno = err.args[1][0] # TokenError + raise NotPython( + f"Couldn't parse '{self.filename}' as Python source: " + + f"{err.args[0]!r} at line {lineno}", + ) from err + + ignore = self.excluded | self.raw_docstrings + starts = self.raw_statements - ignore + self.statements = self.first_lines(starts) - ignore + + def arcs(self) -> set[TArc]: + """Get information about the arcs available in the code. + + Returns a set of line number pairs. Line numbers have been normalized + to the first line of multi-line statements. + + """ + if self._all_arcs is None: + self._analyze_ast() + assert self._all_arcs is not None + return self._all_arcs + + def _analyze_ast(self) -> None: + """Run the AstArcAnalyzer and save its results. + + `_all_arcs` is the set of arcs in the code. + + """ + assert self._ast_root is not None + aaa = AstArcAnalyzer(self.filename, self._ast_root, self.raw_statements, self.multiline_map) + aaa.analyze() + arcs = aaa.arcs + self._with_jump_fixers = aaa.with_jump_fixers() + if self._with_jump_fixers: + arcs = self.fix_with_jumps(arcs) + + self._all_arcs = set() + for l1, l2 in arcs: + fl1 = self.first_line(l1) + fl2 = self.first_line(l2) + if fl1 != fl2: + self._all_arcs.add((fl1, fl2)) + + self._missing_arc_fragments = aaa.missing_arc_fragments + + def fix_with_jumps(self, arcs: Iterable[TArc]) -> set[TArc]: + """Adjust arcs to fix jumps leaving `with` statements. + + Consider this code: + + with open("/tmp/test", "w") as f1: + a = 2 + b = 3 + print(4) + + In 3.10+, we get traces for lines 1, 2, 3, 1, 4. But we want to present + it to the user as if it had been 1, 2, 3, 4. The arc 3->1 should be + replaced with 3->4, and 1->4 should be removed. + + For this code, the fixers dict is {(3, 1): ((1, 4), (3, 4))}. The key + is the actual measured arc from the end of the with block back to the + start of the with-statement. The values are start_next (the with + statement to the next statement after the with), and end_next (the end + of the with-statement to the next statement after the with). + + With nested with-statements, we have to trace through a few levels to + correct a longer chain of arcs. + + """ + to_remove = set() + to_add = set() + for arc in arcs: + if arc in self._with_jump_fixers: + end0 = arc[0] + to_remove.add(arc) + start_next, end_next = self._with_jump_fixers[arc] + while start_next in self._with_jump_fixers: + to_remove.add(start_next) + start_next, end_next = self._with_jump_fixers[start_next] + to_remove.add(end_next) + to_add.add((end0, end_next[1])) + to_remove.add(start_next) + arcs = (set(arcs) | to_add) - to_remove + return arcs + + @functools.lru_cache + def exit_counts(self) -> dict[TLineNo, int]: + """Get a count of exits from that each line. + + Excluded lines are excluded. + + """ + exit_counts: dict[TLineNo, int] = collections.defaultdict(int) + for l1, l2 in self.arcs(): + assert l1 > 0, f"{l1=} should be greater than zero in {self.filename}" + if l1 in self.excluded: + # Don't report excluded lines as line numbers. + continue + if l2 in self.excluded: + # Arcs to excluded lines shouldn't count. + continue + exit_counts[l1] += 1 + + return exit_counts + + def _finish_action_msg(self, action_msg: str | None, end: TLineNo) -> str: + """Apply some defaulting and formatting to an arc's description.""" + if action_msg is None: + if end < 0: + action_msg = "jump to the function exit" + else: + action_msg = "jump to line {lineno}" + action_msg = action_msg.format(lineno=end) + return action_msg + + def missing_arc_description(self, start: TLineNo, end: TLineNo) -> str: + """Provide an English sentence describing a missing arc.""" + if self._missing_arc_fragments is None: + self._analyze_ast() + assert self._missing_arc_fragments is not None + + fragment_pairs = self._missing_arc_fragments.get((start, end), [(None, None)]) + + msgs = [] + for missing_cause_msg, action_msg in fragment_pairs: + action_msg = self._finish_action_msg(action_msg, end) + msg = f"line {start} didn't {action_msg}" + if missing_cause_msg is not None: + msg += f" because {missing_cause_msg.format(lineno=start)}" + + msgs.append(msg) + + return " or ".join(msgs) + + def arc_description(self, start: TLineNo, end: TLineNo) -> str: + """Provide an English description of an arc's effect.""" + if self._missing_arc_fragments is None: + self._analyze_ast() + assert self._missing_arc_fragments is not None + + fragment_pairs = self._missing_arc_fragments.get((start, end), [(None, None)]) + action_msg = self._finish_action_msg(fragment_pairs[0][1], end) + return action_msg + + +class ByteParser: + """Parse bytecode to understand the structure of code.""" + + def __init__( + self, + text: str, + code: CodeType | None = None, + filename: str | None = None, + ) -> None: + self.text = text + if code is not None: + self.code = code + else: + assert filename is not None + # We only get here if earlier ast parsing succeeded, so no need to + # catch errors. + self.code = compile(text, filename, "exec", dont_inherit=True) + + def child_parsers(self) -> Iterable[ByteParser]: + """Iterate over all the code objects nested within this one. + + The iteration includes `self` as its first value. + + We skip code objects named `__annotate__` since they are deferred + annotations that usually are never run. If there are errors in the + annotations, they will be caught by type checkers or other tools that + use annotations. + + """ + return ( + ByteParser(self.text, code=c) + for c in code_objects(self.code) + if c.co_name != "__annotate__" + ) + + def _line_numbers(self) -> Iterable[TLineNo]: + """Yield the line numbers possible in this code object. + + Uses co_lines() to produce a sequence: l0, l1, ... + """ + for _, _, line in self.code.co_lines(): + if line: + yield line + + def _find_statements(self) -> Iterable[TLineNo]: + """Find the statements in `self.code`. + + Produce a sequence of line numbers that start statements. Recurses + into all code objects reachable from `self.code`. + + """ + for bp in self.child_parsers(): + # Get all of the lineno information from this code. + yield from bp._line_numbers() + + +# +# AST analysis +# + + +@dataclass(frozen=True, order=True) +class ArcStart: + """The information needed to start an arc. + + `lineno` is the line number the arc starts from. + + `cause` is an English text fragment used as the `missing_cause_msg` for + AstArcAnalyzer.missing_arc_fragments. It will be used to describe why an + arc wasn't executed, so should fit well into a sentence of the form, + "Line 17 didn't run because {cause}." The fragment can include "{lineno}" + to have `lineno` interpolated into it. + + As an example, this code:: + + if something(x): # line 1 + func(x) # line 2 + more_stuff() # line 3 + + would have two ArcStarts: + + - ArcStart(1, "the condition on line 1 was always true") + - ArcStart(1, "the condition on line 1 was never true") + + The first would be used to create an arc from 1 to 3, creating a message like + "line 1 didn't jump to line 3 because the condition on line 1 was always true." + + The second would be used for the arc from 1 to 2, creating a message like + "line 1 didn't jump to line 2 because the condition on line 1 was never true." + + """ + + lineno: TLineNo + cause: str = "" + + +class TAddArcFn(Protocol): + """The type for AstArcAnalyzer.add_arc().""" + + def __call__( + self, + start: TLineNo, + end: TLineNo, + missing_cause_msg: str | None = None, + action_msg: str | None = None, + ) -> None: + """ + Record an arc from `start` to `end`. + + `missing_cause_msg` is a description of the reason the arc wasn't + taken if it wasn't taken. For example, "the condition on line 10 was + never true." + + `action_msg` is a description of what the arc does, like "jump to line + 10" or "exit from function 'fooey'." + + """ + + +TArcFragments = dict[TArc, list[tuple[Optional[str], Optional[str]]]] + + +class Block: + """ + Blocks need to handle various exiting statements in their own ways. + + All of these methods take a list of exits, and a callable `add_arc` + function that they can use to add arcs if needed. They return True if the + exits are handled, or False if the search should continue up the block + stack. + """ + + # pylint: disable=unused-argument + def process_break_exits(self, exits: set[ArcStart], add_arc: TAddArcFn) -> bool: + """Process break exits.""" + return False + + def process_continue_exits(self, exits: set[ArcStart], add_arc: TAddArcFn) -> bool: + """Process continue exits.""" + return False + + def process_raise_exits(self, exits: set[ArcStart], add_arc: TAddArcFn) -> bool: + """Process raise exits.""" + return False + + def process_return_exits(self, exits: set[ArcStart], add_arc: TAddArcFn) -> bool: + """Process return exits.""" + return False + + +class LoopBlock(Block): + """A block on the block stack representing a `for` or `while` loop.""" + + def __init__(self, start: TLineNo) -> None: + # The line number where the loop starts. + self.start = start + # A set of ArcStarts, the arcs from break statements exiting this loop. + self.break_exits: set[ArcStart] = set() + + def process_break_exits(self, exits: set[ArcStart], add_arc: TAddArcFn) -> bool: + self.break_exits.update(exits) + return True + + def process_continue_exits(self, exits: set[ArcStart], add_arc: TAddArcFn) -> bool: + for xit in exits: + add_arc(xit.lineno, self.start, xit.cause) + return True + + +class FunctionBlock(Block): + """A block on the block stack representing a function definition.""" + + def __init__(self, start: TLineNo, name: str) -> None: + # The line number where the function starts. + self.start = start + # The name of the function. + self.name = name + + def process_raise_exits(self, exits: set[ArcStart], add_arc: TAddArcFn) -> bool: + for xit in exits: + add_arc( + xit.lineno, + -self.start, + xit.cause, + f"except from function {self.name!r}", + ) + return True + + def process_return_exits(self, exits: set[ArcStart], add_arc: TAddArcFn) -> bool: + for xit in exits: + add_arc( + xit.lineno, + -self.start, + xit.cause, + f"return from function {self.name!r}", + ) + return True + + +class TryBlock(Block): + """A block on the block stack representing a `try` block.""" + + def __init__(self, handler_start: TLineNo | None, final_start: TLineNo | None) -> None: + # The line number of the first "except" handler, if any. + self.handler_start = handler_start + # The line number of the "finally:" clause, if any. + self.final_start = final_start + + def process_raise_exits(self, exits: set[ArcStart], add_arc: TAddArcFn) -> bool: + if self.handler_start is not None: + for xit in exits: + add_arc(xit.lineno, self.handler_start, xit.cause) + return True + + +# TODO: Shouldn't the cause messages join with "and" instead of "or"? + + +def is_constant_test_expr(node: ast.AST) -> tuple[bool, bool]: + """Is this a compile-time constant test expression? + + We don't try to mimic all of CPython's optimizations. We just have to + handle the kinds of constant expressions people might actually use. + + """ + match node: + case ast.Constant(): + return True, bool(node.value) + case ast.Name(): + if node.id in ["True", "False", "None", "__debug__"]: + return True, eval(node.id) # pylint: disable=eval-used + case ast.UnaryOp(): + if isinstance(node.op, ast.Not): + is_constant, val = is_constant_test_expr(node.operand) + return is_constant, not val + case ast.BoolOp(): + rets = [is_constant_test_expr(v) for v in node.values] + is_constant = all(is_const for is_const, _ in rets) + if is_constant: + op = any if isinstance(node.op, ast.Or) else all + return True, op(v for _, v in rets) + return False, False + + +class AstArcAnalyzer: + """Analyze source text with an AST to find executable code paths. + + The .analyze() method does the work, and populates these attributes: + + `arcs`: a set of (from, to) pairs of the the arcs possible in the code. + + `missing_arc_fragments`: a dict mapping (from, to) arcs to lists of + message fragments explaining why the arc is missing from execution:: + + { (start, end): [(missing_cause_msg, action_msg), ...], } + + For an arc starting from line 17, they should be usable to form complete + sentences like: "Line 17 didn't {action_msg} because {missing_cause_msg}". + + NOTE: Starting in July 2024, I've been whittling this down to only report + arc that are part of true branches. It's not clear how far this work will + go. + + """ + + def __init__( + self, + filename: str, + root_node: ast.AST, + statements: set[TLineNo], + multiline: dict[TLineNo, TLineNo], + ) -> None: + self.filename = filename + self.root_node = root_node + self.statements = {multiline.get(l, l) for l in statements} + self.multiline = multiline + + # Turn on AST dumps with an environment variable. + # $set_env.py: COVERAGE_AST_DUMP - Dump the AST nodes when parsing code. + dump_ast = bool(int(os.getenv("COVERAGE_AST_DUMP", "0"))) + + if dump_ast: # pragma: debugging + # Dump the AST so that failing tests have helpful output. + print(f"Statements: {self.statements}") + print(f"Multiline map: {self.multiline}") + print(ast.dump(self.root_node, include_attributes=True, indent=4)) + + self.arcs: set[TArc] = set() + self.missing_arc_fragments: TArcFragments = collections.defaultdict(list) + self.block_stack: list[Block] = [] + + # If `with` clauses jump to their start on the way out, we need + # information to be able to skip over that jump. We record the arcs + # from `with` into the clause (with_entries), and the arcs from the + # clause to the `with` (with_exits). + self.current_with_starts: set[TLineNo] = set() + self.all_with_starts: set[TLineNo] = set() + self.with_entries: set[TArc] = set() + self.with_exits: set[TArc] = set() + + # $set_env.py: COVERAGE_TRACK_ARCS - Trace possible arcs added while parsing code. + self.debug = bool(int(os.getenv("COVERAGE_TRACK_ARCS", "0"))) + + def analyze(self) -> None: + """Examine the AST tree from `self.root_node` to determine possible arcs.""" + for node in ast.walk(self.root_node): + node_name = node.__class__.__name__ + code_object_handler = getattr(self, f"_code_object__{node_name}", None) + if code_object_handler is not None: + code_object_handler(node) + + def with_jump_fixers(self) -> dict[TArc, tuple[TArc, TArc]]: + """Get a dict with data for fixing jumps out of with statements. + + Returns a dict. The keys are arcs leaving a with-statement by jumping + back to its start. The values are pairs: first, the arc from the start + to the next statement, then the arc that exits the with without going + to the start. + + """ + fixers = {} + with_nexts = { + arc + for arc in self.arcs + if arc[0] in self.all_with_starts and arc not in self.with_entries + } + for start in self.all_with_starts: + nexts = {arc[1] for arc in with_nexts if arc[0] == start} + if not nexts: + continue + assert len(nexts) == 1, f"Expected one arc, got {nexts} with {start = }" + nxt = nexts.pop() + ends = {arc[0] for arc in self.with_exits if arc[1] == start} + for end in ends: + fixers[(end, start)] = ((start, nxt), (end, nxt)) + return fixers + + # Code object dispatchers: _code_object__* + # + # These methods are used by analyze() as the start of the analysis. + # There is one for each construct with a code object. + + def _code_object__Module(self, node: ast.Module) -> None: + start = self.line_for_node(node) + if node.body: + exits = self.process_body(node.body) + for xit in exits: + self.add_arc(xit.lineno, -start, xit.cause, "exit the module") + else: + # Empty module. + self.add_arc(start, -start) + + def _code_object__FunctionDef(self, node: ast.FunctionDef) -> None: + start = self.line_for_node(node) + self.block_stack.append(FunctionBlock(start=start, name=node.name)) + exits = self.process_body(node.body) + self.process_return_exits(exits) + self.block_stack.pop() + + _code_object__AsyncFunctionDef = _code_object__FunctionDef + + def _code_object__ClassDef(self, node: ast.ClassDef) -> None: + start = self.line_for_node(node) + exits = self.process_body(node.body) + for xit in exits: + self.add_arc(xit.lineno, -start, xit.cause, f"exit class {node.name!r}") + + def add_arc( + self, + start: TLineNo, + end: TLineNo, + missing_cause_msg: str | None = None, + action_msg: str | None = None, + ) -> None: + """Add an arc, including message fragments to use if it is missing.""" + if self.debug: # pragma: debugging + print(f"Adding possible arc: ({start}, {end}): {missing_cause_msg!r}, {action_msg!r}") + print(short_stack(), end="\n\n") + self.arcs.add((start, end)) + if start in self.current_with_starts: + self.with_entries.add((start, end)) + + if missing_cause_msg is not None or action_msg is not None: + self.missing_arc_fragments[(start, end)].append((missing_cause_msg, action_msg)) + + def nearest_blocks(self) -> Iterable[Block]: + """Yield the blocks in nearest-to-farthest order.""" + return reversed(self.block_stack) + + def line_for_node(self, node: ast.AST) -> TLineNo: + """What is the right line number to use for this node? + + This dispatches to _line__Node functions where needed. + + """ + node_name = node.__class__.__name__ + handler = cast( + Optional[Callable[[ast.AST], TLineNo]], + getattr(self, f"_line__{node_name}", None), + ) + if handler is not None: + line = handler(node) + else: + line = node.lineno # type: ignore[attr-defined] + return self.multiline.get(line, line) + + # First lines: _line__* + # + # Dispatched by line_for_node, each method knows how to identify the first + # line number in the node, as Python will report it. + + def _line_decorated(self, node: ast.FunctionDef) -> TLineNo: + """Compute first line number for things that can be decorated (classes and functions).""" + if node.decorator_list: + lineno = node.decorator_list[0].lineno + else: + lineno = node.lineno + return lineno + + def _line__Assign(self, node: ast.Assign) -> TLineNo: + return self.line_for_node(node.value) + + _line__ClassDef = _line_decorated + + def _line__Dict(self, node: ast.Dict) -> TLineNo: + if node.keys: + if node.keys[0] is not None: + return node.keys[0].lineno + else: + # Unpacked dict literals `{**{"a":1}}` have None as the key, + # use the value in that case. + return node.values[0].lineno + else: + return node.lineno + + _line__FunctionDef = _line_decorated + _line__AsyncFunctionDef = _line_decorated + + def _line__List(self, node: ast.List) -> TLineNo: + if node.elts: + return self.line_for_node(node.elts[0]) + else: + return node.lineno + + def _line__Module(self, node: ast.Module) -> TLineNo: # pylint: disable=unused-argument + return 1 + + # The node types that just flow to the next node with no complications. + OK_TO_DEFAULT = { + "AnnAssign", + "Assign", + "Assert", + "AugAssign", + "Delete", + "Expr", + "Global", + "Import", + "ImportFrom", + "Nonlocal", + "Pass", + } + + def node_exits(self, node: ast.AST) -> set[ArcStart]: + """Find the set of arc starts that exit this node. + + Return a set of ArcStarts, exits from this node to the next. Because a + node represents an entire sub-tree (including its children), the exits + from a node can be arbitrarily complex:: + + if something(1): + if other(2): + doit(3) + else: + doit(5) + + There are three exits from line 1: they start at lines 1, 3 and 5. + There are two exits from line 2: lines 3 and 5. + + """ + node_name = node.__class__.__name__ + handler = cast( + Optional[Callable[[ast.AST], set[ArcStart]]], + getattr(self, f"_handle__{node_name}", None), + ) + if handler is not None: + arc_starts = handler(node) + else: + # No handler: either it's something that's ok to default (a simple + # statement), or it's something we overlooked. + if env.TESTING: + if node_name not in self.OK_TO_DEFAULT: + raise RuntimeError(f"*** Unhandled: {node}") # pragma: only failure + + # Default for simple statements: one exit from this node. + arc_starts = {ArcStart(self.line_for_node(node))} + return arc_starts + + def process_body( + self, + body: Sequence[ast.AST], + from_start: ArcStart | None = None, + prev_starts: set[ArcStart] | None = None, + ) -> set[ArcStart]: + """Process the body of a compound statement. + + `body` is the body node to process. + + `from_start` is a single `ArcStart` that starts an arc into this body. + `prev_starts` is a set of ArcStarts that can all be the start of arcs + into this body. Only one of `from_start` and `prev_starts` should be + given. + + Records arcs within the body by calling `self.add_arc`. + + Returns a set of ArcStarts, the exits from this body. + + """ + if prev_starts is None: + if from_start is None: + prev_starts = set() + else: + prev_starts = {from_start} + else: + assert from_start is None + + # Loop over the nodes in the body, making arcs from each one's exits to + # the next node. + for body_node in body: + lineno = self.line_for_node(body_node) + if lineno not in self.statements: + continue + for prev_start in prev_starts: + self.add_arc(prev_start.lineno, lineno, prev_start.cause) + prev_starts = self.node_exits(body_node) + return prev_starts + + # Exit processing: process_*_exits + # + # These functions process the four kinds of jump exits: break, continue, + # raise, and return. To figure out where an exit goes, we have to look at + # the block stack context. For example, a break will jump to the nearest + # enclosing loop block, or the nearest enclosing finally block, whichever + # is nearer. + + def process_break_exits(self, exits: set[ArcStart]) -> None: + """Add arcs due to jumps from `exits` being breaks.""" + for block in self.nearest_blocks(): # pragma: always breaks + if block.process_break_exits(exits, self.add_arc): + break + + def process_continue_exits(self, exits: set[ArcStart]) -> None: + """Add arcs due to jumps from `exits` being continues.""" + for block in self.nearest_blocks(): # pragma: always breaks + if block.process_continue_exits(exits, self.add_arc): + break + + def process_raise_exits(self, exits: set[ArcStart]) -> None: + """Add arcs due to jumps from `exits` being raises.""" + for block in self.nearest_blocks(): + if block.process_raise_exits(exits, self.add_arc): + break + + def process_return_exits(self, exits: set[ArcStart]) -> None: + """Add arcs due to jumps from `exits` being returns.""" + for block in self.nearest_blocks(): # pragma: always breaks + if block.process_return_exits(exits, self.add_arc): + break + + # Node handlers: _handle__* + # + # Each handler deals with a specific AST node type, dispatched from + # node_exits. Handlers return the set of exits from that node, and can + # also call self.add_arc to record arcs they find. These functions mirror + # the Python semantics of each syntactic construct. See the docstring + # for node_exits to understand the concept of exits from a node. + # + # Every node type that represents a statement should have a handler, or it + # should be listed in OK_TO_DEFAULT. + + def _handle__Break(self, node: ast.Break) -> set[ArcStart]: + here = self.line_for_node(node) + break_start = ArcStart(here, cause="the break on line {lineno} wasn't executed") + self.process_break_exits({break_start}) + return set() + + def _handle_decorated(self, node: ast.FunctionDef) -> set[ArcStart]: + """Add arcs for things that can be decorated (classes and functions).""" + main_line: TLineNo = node.lineno + last: TLineNo | None = node.lineno + decs = node.decorator_list + if decs: + last = None + for dec_node in decs: + dec_start = self.line_for_node(dec_node) + if last is not None and dec_start != last: + self.add_arc(last, dec_start) + last = dec_start + assert last is not None + self.add_arc(last, main_line) + last = main_line + # The definition line may have been missed, but we should have it + # in `self.statements`. For some constructs, `line_for_node` is + # not what we'd think of as the first line in the statement, so map + # it to the first one. + assert node.body, f"Oops: {node.body = } in {self.filename}@{node.lineno}" + # The body is handled in collect_arcs. + assert last is not None + return {ArcStart(last)} + + _handle__ClassDef = _handle_decorated + + def _handle__Continue(self, node: ast.Continue) -> set[ArcStart]: + here = self.line_for_node(node) + continue_start = ArcStart(here, cause="the continue on line {lineno} wasn't executed") + self.process_continue_exits({continue_start}) + return set() + + def _handle__For(self, node: ast.For) -> set[ArcStart]: + start = self.line_for_node(node.iter) + self.block_stack.append(LoopBlock(start=start)) + from_start = ArcStart(start, cause="the loop on line {lineno} never started") + exits = self.process_body(node.body, from_start=from_start) + # Any exit from the body will go back to the top of the loop. + for xit in exits: + self.add_arc(xit.lineno, start, xit.cause) + my_block = self.block_stack.pop() + assert isinstance(my_block, LoopBlock) + exits = my_block.break_exits + from_start = ArcStart(start, cause="the loop on line {lineno} didn't complete") + if node.orelse: + else_exits = self.process_body(node.orelse, from_start=from_start) + exits |= else_exits + else: + # No else clause: exit from the for line. + exits.add(from_start) + return exits + + _handle__AsyncFor = _handle__For + + _handle__FunctionDef = _handle_decorated + _handle__AsyncFunctionDef = _handle_decorated + + def _handle__If(self, node: ast.If) -> set[ArcStart]: + start = self.line_for_node(node.test) + constant_test, val = is_constant_test_expr(node.test) + exits = set() + if not constant_test or val: + from_start = ArcStart(start, cause="the condition on line {lineno} was never true") + exits |= self.process_body(node.body, from_start=from_start) + if not constant_test or not val: + from_start = ArcStart(start, cause="the condition on line {lineno} was always true") + exits |= self.process_body(node.orelse, from_start=from_start) + return exits + + def _handle__Match(self, node: ast.Match) -> set[ArcStart]: + start = self.line_for_node(node) + last_start = start + exits = set() + for case in node.cases: + case_start = self.line_for_node(case.pattern) + self.add_arc(last_start, case_start, "the pattern on line {lineno} always matched") + from_start = ArcStart( + case_start, + cause="the pattern on line {lineno} never matched", + ) + exits |= self.process_body(case.body, from_start=from_start) + last_start = case_start + + # case is now the last case, check for wildcard match. + pattern = case.pattern # pylint: disable=undefined-loop-variable + while isinstance(pattern, ast.MatchOr): + pattern = pattern.patterns[-1] + while isinstance(pattern, ast.MatchAs) and pattern.pattern is not None: + pattern = pattern.pattern + had_wildcard = ( + isinstance(pattern, ast.MatchAs) and pattern.pattern is None and case.guard is None # pylint: disable=undefined-loop-variable + ) + + if not had_wildcard: + exits.add( + ArcStart(case_start, cause="the pattern on line {lineno} always matched"), + ) + return exits + + def _handle__Raise(self, node: ast.Raise) -> set[ArcStart]: + here = self.line_for_node(node) + raise_start = ArcStart(here, cause="the raise on line {lineno} wasn't executed") + self.process_raise_exits({raise_start}) + # `raise` statement jumps away, no exits from here. + return set() + + def _handle__Return(self, node: ast.Return) -> set[ArcStart]: + here = self.line_for_node(node) + return_start = ArcStart(here, cause="the return on line {lineno} wasn't executed") + self.process_return_exits({return_start}) + # `return` statement jumps away, no exits from here. + return set() + + def _handle__Try(self, node: ast.Try) -> set[ArcStart]: + if node.handlers: + handler_start = self.line_for_node(node.handlers[0]) + else: + handler_start = None + + if node.finalbody: + final_start = self.line_for_node(node.finalbody[0]) + else: + final_start = None + + # This is true by virtue of Python syntax: have to have either except + # or finally, or both. + assert handler_start is not None or final_start is not None + try_block = TryBlock(handler_start, final_start) + self.block_stack.append(try_block) + + start = self.line_for_node(node) + exits = self.process_body(node.body, from_start=ArcStart(start)) + + # We're done with the `try` body, so this block no longer handles + # exceptions. We keep the block so the `finally` clause can pick up + # flows from the handlers and `else` clause. + if node.finalbody: + try_block.handler_start = None + else: + self.block_stack.pop() + + handler_exits: set[ArcStart] = set() + + if node.handlers: + for handler_node in node.handlers: + handler_start = self.line_for_node(handler_node) + from_cause = "the exception caught by line {lineno} didn't happen" + from_start = ArcStart(handler_start, cause=from_cause) + handler_exits |= self.process_body(handler_node.body, from_start=from_start) + + if node.orelse: + exits = self.process_body(node.orelse, prev_starts=exits) + + exits |= handler_exits + + if node.finalbody: + self.block_stack.pop() + final_from = exits + + final_exits = self.process_body(node.finalbody, prev_starts=final_from) + + if exits: + # The finally clause's exits are only exits for the try block + # as a whole if the try block had some exits to begin with. + exits = final_exits + + return exits + + _handle__TryStar = _handle__Try + + def _handle__While(self, node: ast.While) -> set[ArcStart]: + start = to_top = self.line_for_node(node.test) + constant_test, _ = is_constant_test_expr(node.test) + self.block_stack.append(LoopBlock(start=to_top)) + from_start = ArcStart(start, cause="the condition on line {lineno} was never true") + exits = self.process_body(node.body, from_start=from_start) + for xit in exits: + self.add_arc(xit.lineno, to_top, xit.cause) + exits = set() + my_block = self.block_stack.pop() + assert isinstance(my_block, LoopBlock) + exits.update(my_block.break_exits) + from_start = ArcStart(start, cause="the condition on line {lineno} was always true") + if node.orelse: + else_exits = self.process_body(node.orelse, from_start=from_start) + exits |= else_exits + else: + # No `else` clause: you can exit from the start. + if not constant_test: + exits.add(from_start) + return exits + + def _handle__With(self, node: ast.With) -> set[ArcStart]: + if env.PYBEHAVIOR.exit_with_through_ctxmgr: + starts = [self.line_for_node(item.context_expr) for item in node.items] + else: + starts = [self.line_for_node(node)] + for start in starts: + self.current_with_starts.add(start) + self.all_with_starts.add(start) + + exits = self.process_body(node.body, from_start=ArcStart(starts[-1])) + + start = starts[-1] + self.current_with_starts.remove(start) + with_exit = {ArcStart(start)} + if exits: + for xit in exits: + self.add_arc(xit.lineno, start) + self.with_exits.add((xit.lineno, start)) + exits = with_exit + + return exits + + _handle__AsyncWith = _handle__With diff --git a/venv/Lib/site-packages/coverage/patch.py b/venv/Lib/site-packages/coverage/patch.py new file mode 100644 index 0000000..42ce036 --- /dev/null +++ b/venv/Lib/site-packages/coverage/patch.py @@ -0,0 +1,166 @@ +# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 +# For details: https://github.com/coveragepy/coveragepy/blob/main/NOTICE.txt + +"""Invasive patches for coverage.py.""" + +from __future__ import annotations + +import atexit +import contextlib +import os +import site +from pathlib import Path +from typing import TYPE_CHECKING, Any, NoReturn + +from coverage import env +from coverage.debug import NoDebugging, DevNullDebug +from coverage.exceptions import ConfigError, CoverageException + +if TYPE_CHECKING: + from coverage import Coverage + from coverage.config import CoverageConfig + from coverage.types import TDebugCtl + + +def apply_patches( + cov: Coverage, + config: CoverageConfig, + debug: TDebugCtl, + *, + make_pth_file: bool = True, +) -> None: + """Apply invasive patches requested by `[run] patch=`.""" + debug = debug if debug.should("patch") else DevNullDebug() + for patch in sorted(set(config.patch)): + match patch: + case "_exit": + _patch__exit(cov, debug) + + case "execv": + _patch_execv(cov, config, debug) + + case "fork": + _patch_fork(debug) + + case "subprocess": + _patch_subprocess(config, debug, make_pth_file) + + case _: + raise ConfigError(f"Unknown patch {patch!r}") + + +def _patch__exit(cov: Coverage, debug: TDebugCtl) -> None: + """Patch os._exit.""" + debug.write("Patching _exit") + + old_exit = os._exit + + def coverage_os_exit_patch(status: int) -> NoReturn: + with contextlib.suppress(Exception): + debug.write(f"Using _exit patch with {cov = }") + with contextlib.suppress(Exception): + cov.save() + old_exit(status) + + os._exit = coverage_os_exit_patch + + +def _patch_execv(cov: Coverage, config: CoverageConfig, debug: TDebugCtl) -> None: + """Patch the execv family of functions.""" + if env.WINDOWS: + raise CoverageException("patch=execv isn't supported yet on Windows.") + + debug.write("Patching execv") + + def make_execv_patch(fname: str, old_execv: Any) -> Any: + def coverage_execv_patch(*args: Any, **kwargs: Any) -> Any: + with contextlib.suppress(Exception): + debug.write(f"Using execv patch for {fname} with {cov = }") + with contextlib.suppress(Exception): + cov.save() + + if fname.endswith("e"): + # Assume the `env` argument is passed positionally. + new_env = args[-1] + # Pass our configuration in the new environment. + new_env["COVERAGE_PROCESS_CONFIG"] = config.serialize() + if env.TESTING: + # The subprocesses need to use the same core as the main process. + new_env["COVERAGE_CORE"] = os.getenv("COVERAGE_CORE") + + # When testing locally, we need to honor the pyc file location + # or they get written to the .tox directories and pollute the + # next run with a different core. + if (cache_prefix := os.getenv("PYTHONPYCACHEPREFIX")) is not None: + new_env["PYTHONPYCACHEPREFIX"] = cache_prefix + + # Without this, it fails on PyPy and Ubuntu. + new_env["PATH"] = os.getenv("PATH") + old_execv(*args, **kwargs) + + return coverage_execv_patch + + # All the exec* and spawn* functions eventually call execv or execve. + os.execv = make_execv_patch("execv", os.execv) + os.execve = make_execv_patch("execve", os.execve) + + +def _patch_fork(debug: TDebugCtl) -> None: + """Ensure Coverage is properly reset after a fork.""" + from coverage.control import _after_fork_in_child + + if env.WINDOWS: + raise CoverageException("patch=fork isn't supported yet on Windows.") + + debug.write("Patching fork") + os.register_at_fork(after_in_child=_after_fork_in_child) + + +def _patch_subprocess(config: CoverageConfig, debug: TDebugCtl, make_pth_file: bool) -> None: + """Write .pth files and set environment vars to measure subprocesses.""" + debug.write("Patching subprocess") + + if make_pth_file: + pth_files = create_pth_files(debug) + + def delete_pth_files() -> None: + for p in pth_files: + debug.write(f"Deleting subprocess .pth file: {str(p)!r}") + p.unlink(missing_ok=True) + + atexit.register(delete_pth_files) + assert config.config_file is not None + os.environ["COVERAGE_PROCESS_CONFIG"] = config.serialize() + + +# Writing .pth files is not obvious. On Windows, getsitepackages() returns two +# directories. A .pth file in the first will be run, but coverage isn't +# importable yet. We write into all the places we can, but with defensive +# import code. + +PTH_CODE = """\ +try: + import coverage +except: + pass +else: + coverage.process_startup() +""" + +PTH_TEXT = f"import sys; exec({PTH_CODE!r})\n" + + +def create_pth_files(debug: TDebugCtl = NoDebugging()) -> list[Path]: + """Create .pth files for measuring subprocesses.""" + pth_files = [] + for pth_dir in site.getsitepackages(): + pth_file = Path(pth_dir) / f"subcover_{os.getpid()}.pth" + try: + if debug.should("patch"): + debug.write(f"Writing subprocess .pth file: {str(pth_file)!r}") + pth_file.write_text(PTH_TEXT, encoding="utf-8") + except OSError: # pragma: cant happen + continue + else: + pth_files.append(pth_file) + return pth_files diff --git a/venv/Lib/site-packages/coverage/phystokens.py b/venv/Lib/site-packages/coverage/phystokens.py new file mode 100644 index 0000000..bac06c0 --- /dev/null +++ b/venv/Lib/site-packages/coverage/phystokens.py @@ -0,0 +1,197 @@ +# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 +# For details: https://github.com/coveragepy/coveragepy/blob/main/NOTICE.txt + +"""Better tokenizing for coverage.py.""" + +from __future__ import annotations + +import ast +import io +import keyword +import re +import sys +import token +import tokenize +from collections.abc import Iterable + +from coverage import env +from coverage.types import TLineNo, TSourceTokenLines + +TokenInfos = Iterable[tokenize.TokenInfo] + + +def _phys_tokens(toks: TokenInfos) -> TokenInfos: + """Return all physical tokens, even line continuations. + + tokenize.generate_tokens() doesn't return a token for the backslash that + continues lines. This wrapper provides those tokens so that we can + re-create a faithful representation of the original source. + + Returns the same values as generate_tokens() + + """ + last_line: str | None = None + last_lineno = -1 + last_ttext: str = "" + for ttype, ttext, (slineno, scol), (elineno, ecol), ltext in toks: + if last_lineno != elineno: + if last_line and last_line.endswith("\\\n"): + # We are at the beginning of a new line, and the last line + # ended with a backslash. We probably have to inject a + # backslash token into the stream. Unfortunately, there's more + # to figure out. This code:: + # + # usage = """\ + # HEY THERE + # """ + # + # triggers this condition, but the token text is:: + # + # '"""\\\nHEY THERE\n"""' + # + # so we need to figure out if the backslash is already in the + # string token or not. + inject_backslash = True + if last_ttext.endswith("\\"): + inject_backslash = False + elif ttype == token.STRING: + if ( # pylint: disable=simplifiable-if-statement + last_line.endswith("\\\n") + and last_line.rstrip(" \\\n").endswith(last_ttext) + ): + # Deal with special cases like such code:: + # + # a = ["aaa",\ # there may be zero or more blanks between "," and "\". + # "bbb \ + # ccc"] + # + inject_backslash = True + else: + # It's a multi-line string and the first line ends with + # a backslash, so we don't need to inject another. + inject_backslash = False + elif env.PYBEHAVIOR.fstring_syntax and ttype == token.FSTRING_MIDDLE: + inject_backslash = False + if inject_backslash: + # Figure out what column the backslash is in. + ccol = len(last_line.split("\n")[-2]) - 1 + # Yield the token, with a fake token type. + yield tokenize.TokenInfo( + 99999, + "\\\n", + (slineno, ccol), + (slineno, ccol + 2), + last_line, + ) + last_line = ltext + if ttype not in (tokenize.NEWLINE, tokenize.NL): + last_ttext = ttext + yield tokenize.TokenInfo(ttype, ttext, (slineno, scol), (elineno, ecol), ltext) + last_lineno = elineno + + +def find_soft_key_lines(source: str) -> set[TLineNo]: + """Helper for finding lines with soft keywords, like match/case lines.""" + soft_key_lines: set[TLineNo] = set() + + for node in ast.walk(ast.parse(source)): + if isinstance(node, ast.Match): + soft_key_lines.add(node.lineno) + for case in node.cases: + soft_key_lines.add(case.pattern.lineno) + elif sys.version_info >= (3, 12) and isinstance(node, ast.TypeAlias): + soft_key_lines.add(node.lineno) + + return soft_key_lines + + +def source_token_lines(source: str) -> TSourceTokenLines: + """Generate a series of lines, one for each line in `source`. + + Each line is a list of pairs, each pair is a token:: + + [('key', 'def'), ('ws', ' '), ('nam', 'hello'), ('op', '('), ... ] + + Each pair has a token class, and the token text. + + If you concatenate all the token texts, and then join them with newlines, + you should have your original `source` back, with two differences: + trailing white space is not preserved, and a final line with no newline + is indistinguishable from a final line with a newline. + + """ + + ws_tokens = {token.INDENT, token.DEDENT, token.NEWLINE, tokenize.NL} + line: list[tuple[str, str]] = [] + col = 0 + + source = source.expandtabs(8).replace("\r\n", "\n") + tokgen = generate_tokens(source) + + soft_key_lines = find_soft_key_lines(source) + + for ttype, ttext, (sline, scol), (_, ecol), _ in _phys_tokens(tokgen): + mark_start = True + for part in re.split("(\n)", ttext): + if part == "\n": + yield line + line = [] + col = 0 + mark_end = False + elif part == "": + mark_end = False + elif ttype in ws_tokens: + mark_end = False + else: + if env.PYBEHAVIOR.fstring_syntax and ttype == token.FSTRING_MIDDLE: + part = part.replace("{", "{{").replace("}", "}}") + ecol = scol + len(part) + if mark_start and scol > col: + line.append(("ws", " " * (scol - col))) + mark_start = False + tok_class = tokenize.tok_name.get(ttype, "xx").lower()[:3] + if ttype == token.NAME: + if keyword.iskeyword(ttext): + # Hard keywords are always keywords. + tok_class = "key" + elif keyword.issoftkeyword(ttext): + # Soft keywords appear at the start of their line. + if len(line) == 0: + is_start_of_line = True + elif (len(line) == 1) and line[0][0] == "ws": + is_start_of_line = True + else: + is_start_of_line = False + if is_start_of_line and sline in soft_key_lines: + tok_class = "key" + line.append((tok_class, part)) + mark_end = True + scol = 0 + if mark_end: + col = ecol + + if line: + yield line + + +def generate_tokens(text: str) -> TokenInfos: + """A helper around `tokenize.generate_tokens`. + + Originally this was used to cache the results, but it didn't seem to make + reporting go faster, and caused issues with using too much memory. + + """ + readline = io.StringIO(text).readline + return tokenize.generate_tokens(readline) + + +def source_encoding(source: bytes) -> str: + """Determine the encoding for `source`, according to PEP 263. + + `source` is a byte string: the text of the program. + + Returns a string, the name of the encoding. + + """ + readline = iter(source.splitlines(True)).__next__ + return tokenize.detect_encoding(readline)[0] diff --git a/venv/Lib/site-packages/coverage/plugin.py b/venv/Lib/site-packages/coverage/plugin.py new file mode 100644 index 0000000..bdfa317 --- /dev/null +++ b/venv/Lib/site-packages/coverage/plugin.py @@ -0,0 +1,617 @@ +# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 +# For details: https://github.com/coveragepy/coveragepy/blob/main/NOTICE.txt + +""" +.. versionadded:: 4.0 + +Plug-in interfaces for coverage.py. + +Coverage.py supports a few different kinds of plug-ins that change its +behavior: + +* File tracers implement tracing of non-Python file types. + +* Configurers add custom configuration, using Python code to change the + configuration. + +* Dynamic context switchers decide when the dynamic context has changed, for + example, to record what test function produced the coverage. + +To write a coverage.py plug-in, create a module with a subclass of +:class:`~coverage.CoveragePlugin`. You will override methods in your class to +participate in various aspects of coverage.py's processing. +Different types of plug-ins have to override different methods. + +Any plug-in can optionally implement :meth:`~coverage.CoveragePlugin.sys_info` +to provide debugging information about their operation. + +Your module must also contain a ``coverage_init`` function that registers an +instance of your plug-in class:: + + import coverage + + class MyPlugin(coverage.CoveragePlugin): + ... + + def coverage_init(reg, options): + reg.add_file_tracer(MyPlugin()) + +You use the `reg` parameter passed to your ``coverage_init`` function to +register your plug-in object. The registration method you call depends on +what kind of plug-in it is. + +If your plug-in takes options, the `options` parameter is a dictionary of your +plug-in's options from the coverage.py configuration file. Use them however +you want to configure your object before registering it. + +Coverage.py will store its own information on your plug-in object, using +attributes whose names start with ``_coverage_``. Don't be startled. + +.. warning:: + Plug-ins are imported by coverage.py before it begins measuring code. + If you write a plugin in your own project, it might import your product + code before coverage.py can start measuring. This can result in your + own code being reported as missing. + + One solution is to put your plugins in your project tree, but not in + your importable Python package. + + +.. _file_tracer_plugins: + +File Tracers +============ + +File tracers implement measurement support for non-Python files. File tracers +implement the :meth:`~coverage.CoveragePlugin.file_tracer` method to claim +files and the :meth:`~coverage.CoveragePlugin.file_reporter` method to report +on those files. + +In your ``coverage_init`` function, use the ``add_file_tracer`` method to +register your file tracer. + + +.. _configurer_plugins: + +Configurers +=========== + +.. versionadded:: 4.5 + +Configurers modify the configuration of coverage.py during start-up. +Configurers implement the :meth:`~coverage.CoveragePlugin.configure` method to +change the configuration. + +In your ``coverage_init`` function, use the ``add_configurer`` method to +register your configurer. + + +.. _dynamic_context_plugins: + +Dynamic Context Switchers +========================= + +.. versionadded:: 5.0 + +Dynamic context switcher plugins implement the +:meth:`~coverage.CoveragePlugin.dynamic_context` method to dynamically compute +the context label for each measured frame. + +Computed context labels are useful when you want to group measured data without +modifying the source code. + +For example, you could write a plugin that checks `frame.f_code` to inspect +the currently executed method, and set the context label to a fully qualified +method name if it's an instance method of `unittest.TestCase` and the method +name starts with 'test'. Such a plugin would provide basic coverage grouping +by test and could be used with test runners that have no built-in coveragepy +support. + +In your ``coverage_init`` function, use the ``add_dynamic_context`` method to +register your dynamic context switcher. + +""" + +from __future__ import annotations + +import dataclasses +import functools +from collections.abc import Iterable +from types import FrameType +from typing import Any + +from coverage import files +from coverage.misc import _needs_to_implement +from coverage.types import TArc, TConfigurable, TLineNo, TSourceTokenLines + + +class CoveragePlugin: + """Base class for coverage.py plug-ins.""" + + _coverage_plugin_name: str + _coverage_enabled: bool + + def file_tracer(self, filename: str) -> FileTracer | None: # pylint: disable=unused-argument + """Get a :class:`FileTracer` object for a file. + + Plug-in type: file tracer. + + Every Python source file is offered to your plug-in to give it a chance + to take responsibility for tracing the file. If your plug-in can + handle the file, it should return a :class:`FileTracer` object. + Otherwise return None. + + There is no way to register your plug-in for particular files. + Instead, this method is invoked for all files as they are executed, + and the plug-in decides whether it can trace the file or not. + Be prepared for `filename` to refer to all kinds of files that have + nothing to do with your plug-in. + + The file name will be a Python file being executed. There are two + broad categories of behavior for a plug-in, depending on the kind of + files your plug-in supports: + + * Static file names: each of your original source files has been + converted into a distinct Python file. Your plug-in is invoked with + the Python file name, and it maps it back to its original source + file. + + * Dynamic file names: all of your source files are executed by the same + Python file. In this case, your plug-in implements + :meth:`FileTracer.dynamic_source_filename` to provide the actual + source file for each execution frame. + + `filename` is a string, the path to the file being considered. This is + the absolute real path to the file. If you are comparing to other + paths, be sure to take this into account. + + Returns a :class:`FileTracer` object to use to trace `filename`, or + None if this plug-in cannot trace this file. + + """ + return None + + def file_reporter( + self, + filename: str, # pylint: disable=unused-argument + ) -> FileReporter | str: # str should be Literal["python"] + """Get the :class:`FileReporter` class to use for a file. + + Plug-in type: file tracer. + + This will only be invoked if `filename` returns non-None from + :meth:`file_tracer`. It's an error to return None from this method. + + Returns a :class:`FileReporter` object to use to report on `filename`, + or the string `"python"` to have coverage.py treat the file as Python. + + """ + _needs_to_implement(self, "file_reporter") + + def dynamic_context( + self, + frame: FrameType, # pylint: disable=unused-argument + ) -> str | None: + """Get the dynamically computed context label for `frame`. + + Plug-in type: dynamic context. + + This method is invoked for each frame when outside of a dynamic + context, to see if a new dynamic context should be started. If it + returns a string, a new context label is set for this and deeper + frames. The dynamic context ends when this frame returns. + + Returns a string to start a new dynamic context, or None if no new + context should be started. + + """ + return None + + def find_executable_files( + self, + src_dir: str, # pylint: disable=unused-argument + ) -> Iterable[str]: + """Yield all of the executable files in `src_dir`, recursively. + + Plug-in type: file tracer. + + Executability is a plug-in-specific property, but generally means files + which would have been considered for coverage analysis, had they been + included automatically. + + Returns or yields a sequence of strings, the paths to files that could + have been executed, including files that had been executed. + + """ + return [] + + def configure(self, config: TConfigurable) -> None: + """Modify the configuration of coverage.py. + + Plug-in type: configurer. + + This method is called during coverage.py start-up, to give your plug-in + a chance to change the configuration. The `config` parameter is an + object with :meth:`~coverage.Coverage.get_option` and + :meth:`~coverage.Coverage.set_option` methods. Do not call any other + methods on the `config` object. + + """ + pass + + def sys_info(self) -> Iterable[tuple[str, Any]]: + """Get a list of information useful for debugging. + + Plug-in type: any. + + This method will be invoked for ``--debug=sys``. Your + plug-in can return any information it wants to be displayed. + + Returns a list of pairs: `[(name, value), ...]`. + + """ + return [] + + +class CoveragePluginBase: + """Plugins produce specialized objects, which point back to the original plugin.""" + + _coverage_plugin: CoveragePlugin + + +class FileTracer(CoveragePluginBase): + """Support needed for files during the execution phase. + + File tracer plug-ins implement subclasses of FileTracer to return from + their :meth:`~CoveragePlugin.file_tracer` method. + + You may construct this object from :meth:`CoveragePlugin.file_tracer` any + way you like. A natural choice would be to pass the file name given to + `file_tracer`. + + `FileTracer` objects should only be created in the + :meth:`CoveragePlugin.file_tracer` method. + + See :ref:`howitworks` for details of the different coverage.py phases. + + """ + + def source_filename(self) -> str: + """The source file name for this file. + + This may be any file name you like. A key responsibility of a plug-in + is to own the mapping from Python execution back to whatever source + file name was originally the source of the code. + + See :meth:`CoveragePlugin.file_tracer` for details about static and + dynamic file names. + + Returns the file name to credit with this execution. + + """ + _needs_to_implement(self, "source_filename") + + def has_dynamic_source_filename(self) -> bool: + """Does this FileTracer have dynamic source file names? + + FileTracers can provide dynamically determined file names by + implementing :meth:`dynamic_source_filename`. Invoking that function + is expensive. To determine whether to invoke it, coverage.py uses the + result of this function to know if it needs to bother invoking + :meth:`dynamic_source_filename`. + + See :meth:`CoveragePlugin.file_tracer` for details about static and + dynamic file names. + + Returns True if :meth:`dynamic_source_filename` should be called to get + dynamic source file names. + + """ + return False + + def dynamic_source_filename( + self, + filename: str, # pylint: disable=unused-argument + frame: FrameType, # pylint: disable=unused-argument + ) -> str | None: + """Get a dynamically computed source file name. + + Some plug-ins need to compute the source file name dynamically for each + frame. + + This function will not be invoked if + :meth:`has_dynamic_source_filename` returns False. + + Returns the source file name for this frame, or None if this frame + shouldn't be measured. + + """ + return None + + def line_number_range(self, frame: FrameType) -> tuple[TLineNo, TLineNo]: + """Get the range of source line numbers for a given a call frame. + + The call frame is examined, and the source line number in the original + file is returned. The return value is a pair of numbers, the starting + line number and the ending line number, both inclusive. For example, + returning (5, 7) means that lines 5, 6, and 7 should be considered + executed. + + This function might decide that the frame doesn't indicate any lines + from the source file were executed. Return (-1, -1) in this case to + tell coverage.py that no lines should be recorded for this frame. + + """ + lineno = frame.f_lineno + return lineno, lineno + + +@dataclasses.dataclass +class CodeRegion: + """Data for a region of code found by :meth:`FileReporter.code_regions`.""" + + #: The kind of region, like `"function"` or `"class"`. Must be one of the + #: singular values returned by :meth:`FileReporter.code_region_kinds`. + kind: str + + #: The name of the region. For example, a function or class name. + name: str + + #: The line in the source file to link to when navigating to the region. + #: Can be a line not mentioned in `lines`. + start: int + + #: The lines in the region. Should be lines that could be executed in the + #: region. For example, a class region includes all of the lines in the + #: methods of the class, but not the lines defining class attributes, since + #: they are executed on import, not as part of exercising the class. The + #: set can include non-executable lines like blanks and comments. + lines: set[int] + + def __lt__(self, other: CodeRegion) -> bool: + """To support sorting to make test-writing easier.""" + if self.name == other.name: + return min(self.lines) < min(other.lines) + return self.name < other.name + + +@functools.total_ordering +class FileReporter(CoveragePluginBase): + """Support needed for files during the analysis and reporting phases. + + File tracer plug-ins implement a subclass of `FileReporter`, and return + instances from their :meth:`CoveragePlugin.file_reporter` method. + + There are many methods here, but only :meth:`lines` is required, to provide + the set of executable lines in the file. + + See :ref:`howitworks` for details of the different coverage.py phases. + + """ + + def __init__(self, filename: str) -> None: + """Simple initialization of a `FileReporter`. + + The `filename` argument is the path to the file being reported. This + will be available as the `.filename` attribute on the object. Other + method implementations on this base class rely on this attribute. + + """ + self.filename = filename + + def __repr__(self) -> str: + return f"<{self.__class__.__name__} filename={self.filename!r}>" + + def relative_filename(self) -> str: + """Get the relative file name for this file. + + This file path will be displayed in reports. The default + implementation will supply the actual project-relative file path. You + only need to supply this method if you have an unusual syntax for file + paths. + + """ + return files.relative_filename(self.filename) + + def source(self) -> str: + """Get the source for the file. + + Returns a Unicode string. + + The base implementation simply reads the `self.filename` file and + decodes it as UTF-8. Override this method if your file isn't readable + as a text file, or if you need other encoding support. + + """ + with open(self.filename, encoding="utf-8") as f: + return f.read() + + def lines(self) -> set[TLineNo]: + """Get the executable lines in this file. + + Your plug-in must determine which lines in the file were possibly + executable. This method returns a set of those line numbers. + + Returns a set of line numbers. + + """ + _needs_to_implement(self, "lines") + + def excluded_lines(self) -> set[TLineNo]: + """Get the excluded executable lines in this file. + + Your plug-in can use any method it likes to allow the user to exclude + executable lines from consideration. + + Returns a set of line numbers. + + The base implementation returns the empty set. + + """ + return set() + + def translate_lines(self, lines: Iterable[TLineNo]) -> set[TLineNo]: + """Translate recorded lines into reported lines. + + Some file formats will want to report lines slightly differently than + they are recorded. For example, Python records the last line of a + multi-line statement, but reports are nicer if they mention the first + line. + + Your plug-in can optionally define this method to perform these kinds + of adjustment. + + `lines` is a sequence of integers, the recorded line numbers. + + Returns a set of integers, the adjusted line numbers. + + The base implementation returns the numbers unchanged. + + """ + return set(lines) + + def arcs(self) -> set[TArc]: + """Get the executable arcs in this file. + + To support branch coverage, your plug-in needs to be able to indicate + possible execution paths, as a set of line number pairs. Each pair is + a `(prev, next)` pair indicating that execution can transition from the + `prev` line number to the `next` line number. + + Returns a set of pairs of line numbers. The default implementation + returns an empty set. + + """ + return set() + + def no_branch_lines(self) -> set[TLineNo]: + """Get the lines excused from branch coverage in this file. + + Your plug-in can use any method it likes to allow the user to exclude + lines from consideration of branch coverage. + + Returns a set of line numbers. + + The base implementation returns the empty set. + + """ + return set() + + def translate_arcs(self, arcs: Iterable[TArc]) -> set[TArc]: + """Translate recorded arcs into reported arcs. + + Similar to :meth:`translate_lines`, but for arcs. `arcs` is a set of + line number pairs. + + Returns a set of line number pairs. + + The default implementation returns `arcs` unchanged. + + """ + return set(arcs) + + def exit_counts(self) -> dict[TLineNo, int]: + """Get a count of exits from that each line. + + To determine which lines are branches, coverage.py looks for lines that + have more than one exit. This function creates a dict mapping each + executable line number to a count of how many exits it has. + + To be honest, this feels wrong, and should be refactored. Let me know + if you attempt to implement this method in your plug-in... + + """ + return {} + + def missing_arc_description( + self, + start: TLineNo, + end: TLineNo, + executed_arcs: Iterable[TArc] | None = None, # pylint: disable=unused-argument + ) -> str: + """Provide an English sentence describing a missing arc. + + The `start` and `end` arguments are the line numbers of the missing + arc. Negative numbers indicate entering or exiting code objects. + + The `executed_arcs` argument is a set of line number pairs, the arcs + that were executed in this file. + + By default, this simply returns the string "Line {start} didn't jump + to {end}". + + """ + return f"Line {start} didn't jump to line {end}" + + def arc_description( + self, + start: TLineNo, # pylint: disable=unused-argument + end: TLineNo, + ) -> str: + """Provide an English description of an arc's effect.""" + return f"jump to line {end}" + + def source_token_lines(self) -> TSourceTokenLines: + """Generate a series of tokenized lines, one for each line in `source`. + + These tokens are used for syntax-colored reports. + + Each line is a list of pairs, each pair is a token:: + + [("key", "def"), ("ws", " "), ("nam", "hello"), ("op", "("), ... ] + + Each pair has a token class, and the token text. The token classes + are: + + * ``"com"``: a comment + * ``"key"``: a keyword + * ``"nam"``: a name, or identifier + * ``"num"``: a number + * ``"op"``: an operator + * ``"str"``: a string literal + * ``"ws"``: some white space + * ``"txt"``: some other kind of text + + If you concatenate all the token texts, and then join them with + newlines, you should have your original source back. + + The default implementation simply returns each line tagged as + ``"txt"``. + + """ + for line in self.source().splitlines(): + yield [("txt", line)] + + def code_regions(self) -> Iterable[CodeRegion]: + """Identify regions in the source file for finer reporting than by file. + + Returns an iterable of :class:`CodeRegion` objects. The kinds reported + should be in the possibilities returned by :meth:`code_region_kinds`. + + """ + return [] + + def code_region_kinds(self) -> Iterable[tuple[str, str]]: + """Return the kinds of code regions this plugin can find. + + The returned pairs are the singular and plural forms of the kinds:: + + [ + ("function", "functions"), + ("class", "classes"), + ] + + This will usually be hard-coded, but could also differ by the specific + source file involved. + + """ + return [] + + def __eq__(self, other: Any) -> bool: + return isinstance(other, FileReporter) and self.filename == other.filename + + def __lt__(self, other: Any) -> bool: + return isinstance(other, FileReporter) and self.filename < other.filename + + # This object doesn't need to be hashed. + __hash__ = None # type: ignore[assignment] diff --git a/venv/Lib/site-packages/coverage/plugin_support.py b/venv/Lib/site-packages/coverage/plugin_support.py new file mode 100644 index 0000000..2374b18 --- /dev/null +++ b/venv/Lib/site-packages/coverage/plugin_support.py @@ -0,0 +1,299 @@ +# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 +# For details: https://github.com/coveragepy/coveragepy/blob/main/NOTICE.txt + +"""Support for plugins.""" + +from __future__ import annotations + +import os +import os.path +import sys +from collections.abc import Iterable, Iterator +from types import FrameType +from typing import Any, Callable + +from coverage.exceptions import PluginError +from coverage.misc import isolate_module +from coverage.plugin import CoveragePlugin, FileReporter, FileTracer +from coverage.types import TArc, TConfigurable, TDebugCtl, TLineNo, TPluginConfig, TSourceTokenLines + +os = isolate_module(os) + + +class Plugins: + """The currently loaded collection of coverage.py plugins.""" + + def __init__(self, debug: TDebugCtl | None = None) -> None: + self.order: list[CoveragePlugin] = [] + self.names: dict[str, CoveragePlugin] = {} + self.file_tracers: list[CoveragePlugin] = [] + self.configurers: list[CoveragePlugin] = [] + self.context_switchers: list[CoveragePlugin] = [] + + self.current_module: str | None = None + self.debug = debug + + def load_from_config( + self, + modules: Iterable[str], + config: TPluginConfig, + ) -> None: + """Load plugin modules, and read their settings from configuration.""" + + for module in modules: + self.current_module = module + __import__(module) + mod = sys.modules[module] + + coverage_init = getattr(mod, "coverage_init", None) + if not coverage_init: + raise PluginError( + f"Plugin module {module!r} didn't define a coverage_init function", + ) + + options = config.get_plugin_options(module) + coverage_init(self, options) + + self.current_module = None + + def load_from_callables( + self, + plugin_inits: Iterable[TCoverageInit], + ) -> None: + """Load plugins from callables provided.""" + for fn in plugin_inits: + fn(self) + + def add_file_tracer(self, plugin: CoveragePlugin) -> None: + """Add a file tracer plugin. + + `plugin` is an instance of a third-party plugin class. It must + implement the :meth:`CoveragePlugin.file_tracer` method. + + """ + self._add_plugin(plugin, self.file_tracers) + + def add_configurer(self, plugin: CoveragePlugin) -> None: + """Add a configuring plugin. + + `plugin` is an instance of a third-party plugin class. It must + implement the :meth:`CoveragePlugin.configure` method. + + """ + self._add_plugin(plugin, self.configurers) + + def add_dynamic_context(self, plugin: CoveragePlugin) -> None: + """Add a dynamic context plugin. + + `plugin` is an instance of a third-party plugin class. It must + implement the :meth:`CoveragePlugin.dynamic_context` method. + + """ + self._add_plugin(plugin, self.context_switchers) + + def add_noop(self, plugin: CoveragePlugin) -> None: + """Add a plugin that does nothing. + + This is only useful for testing the plugin support. + + """ + self._add_plugin(plugin, None) + + def _add_plugin( + self, + plugin: CoveragePlugin, + specialized: list[CoveragePlugin] | None, + ) -> None: + """Add a plugin object. + + `plugin` is a :class:`CoveragePlugin` instance to add. `specialized` + is a list to append the plugin to. + + """ + plugin_name = f"{self.current_module}.{plugin.__class__.__name__}" + if self.debug and self.debug.should("plugin"): + self.debug.write(f"Loaded plugin {self.current_module!r}: {plugin!r}") + labelled = LabelledDebug(f"plugin {self.current_module!r}", self.debug) + plugin = DebugPluginWrapper(plugin, labelled) + + plugin._coverage_plugin_name = plugin_name + plugin._coverage_enabled = True + self.order.append(plugin) + self.names[plugin_name] = plugin + if specialized is not None: + specialized.append(plugin) + + def __bool__(self) -> bool: + return bool(self.order) + + def __iter__(self) -> Iterator[CoveragePlugin]: + return iter(self.order) + + def get(self, plugin_name: str) -> CoveragePlugin: + """Return a plugin by name.""" + return self.names[plugin_name] + + +TCoverageInit = Callable[[Plugins], None] + + +class LabelledDebug: + """A Debug writer, but with labels for prepending to the messages.""" + + def __init__(self, label: str, debug: TDebugCtl, prev_labels: Iterable[str] = ()): + self.labels = list(prev_labels) + [label] + self.debug = debug + + def add_label(self, label: str) -> LabelledDebug: + """Add a label to the writer, and return a new `LabelledDebug`.""" + return LabelledDebug(label, self.debug, self.labels) + + def message_prefix(self) -> str: + """The prefix to use on messages, combining the labels.""" + prefixes = self.labels + [""] + return ":\n".join(" " * i + label for i, label in enumerate(prefixes)) + + def write(self, message: str) -> None: + """Write `message`, but with the labels prepended.""" + self.debug.write(f"{self.message_prefix()}{message}") + + +class DebugPluginWrapper(CoveragePlugin): + """Wrap a plugin, and use debug to report on what it's doing.""" + + def __init__(self, plugin: CoveragePlugin, debug: LabelledDebug) -> None: + super().__init__() + self.plugin = plugin + self.debug = debug + + def file_tracer(self, filename: str) -> FileTracer | None: + tracer = self.plugin.file_tracer(filename) + self.debug.write(f"file_tracer({filename!r}) --> {tracer!r}") + if tracer: + debug = self.debug.add_label(f"file {filename!r}") + tracer = DebugFileTracerWrapper(tracer, debug) + return tracer + + def file_reporter(self, filename: str) -> FileReporter | str: + reporter = self.plugin.file_reporter(filename) + assert isinstance(reporter, FileReporter) + self.debug.write(f"file_reporter({filename!r}) --> {reporter!r}") + if reporter: + debug = self.debug.add_label(f"file {filename!r}") + reporter = DebugFileReporterWrapper(filename, reporter, debug) + return reporter + + def dynamic_context(self, frame: FrameType) -> str | None: + context = self.plugin.dynamic_context(frame) + self.debug.write(f"dynamic_context({frame!r}) --> {context!r}") + return context + + def find_executable_files(self, src_dir: str) -> Iterable[str]: + executable_files = self.plugin.find_executable_files(src_dir) + self.debug.write(f"find_executable_files({src_dir!r}) --> {executable_files!r}") + return executable_files + + def configure(self, config: TConfigurable) -> None: + self.debug.write(f"configure({config!r})") + self.plugin.configure(config) + + def sys_info(self) -> Iterable[tuple[str, Any]]: + return self.plugin.sys_info() + + +class DebugFileTracerWrapper(FileTracer): + """A debugging `FileTracer`.""" + + def __init__(self, tracer: FileTracer, debug: LabelledDebug) -> None: + self.tracer = tracer + self.debug = debug + + def _show_frame(self, frame: FrameType) -> str: + """A short string identifying a frame, for debug messages.""" + filename = os.path.basename(frame.f_code.co_filename) + return f"{filename}@{frame.f_lineno}" + + def source_filename(self) -> str: + sfilename = self.tracer.source_filename() + self.debug.write(f"source_filename() --> {sfilename!r}") + return sfilename + + def has_dynamic_source_filename(self) -> bool: + has = self.tracer.has_dynamic_source_filename() + self.debug.write(f"has_dynamic_source_filename() --> {has!r}") + return has + + def dynamic_source_filename(self, filename: str, frame: FrameType) -> str | None: + dyn = self.tracer.dynamic_source_filename(filename, frame) + self.debug.write( + "dynamic_source_filename({!r}, {}) --> {!r}".format( + filename, + self._show_frame(frame), + dyn, + ) + ) + return dyn + + def line_number_range(self, frame: FrameType) -> tuple[TLineNo, TLineNo]: + pair = self.tracer.line_number_range(frame) + self.debug.write(f"line_number_range({self._show_frame(frame)}) --> {pair!r}") + return pair + + +class DebugFileReporterWrapper(FileReporter): + """A debugging `FileReporter`.""" + + def __init__(self, filename: str, reporter: FileReporter, debug: LabelledDebug) -> None: + super().__init__(filename) + self.reporter = reporter + self.debug = debug + + def relative_filename(self) -> str: + ret = self.reporter.relative_filename() + self.debug.write(f"relative_filename() --> {ret!r}") + return ret + + def lines(self) -> set[TLineNo]: + ret = self.reporter.lines() + self.debug.write(f"lines() --> {ret!r}") + return ret + + def excluded_lines(self) -> set[TLineNo]: + ret = self.reporter.excluded_lines() + self.debug.write(f"excluded_lines() --> {ret!r}") + return ret + + def translate_lines(self, lines: Iterable[TLineNo]) -> set[TLineNo]: + ret = self.reporter.translate_lines(lines) + self.debug.write(f"translate_lines({lines!r}) --> {ret!r}") + return ret + + def translate_arcs(self, arcs: Iterable[TArc]) -> set[TArc]: + ret = self.reporter.translate_arcs(arcs) + self.debug.write(f"translate_arcs({arcs!r}) --> {ret!r}") + return ret + + def no_branch_lines(self) -> set[TLineNo]: + ret = self.reporter.no_branch_lines() + self.debug.write(f"no_branch_lines() --> {ret!r}") + return ret + + def exit_counts(self) -> dict[TLineNo, int]: + ret = self.reporter.exit_counts() + self.debug.write(f"exit_counts() --> {ret!r}") + return ret + + def arcs(self) -> set[TArc]: + ret = self.reporter.arcs() + self.debug.write(f"arcs() --> {ret!r}") + return ret + + def source(self) -> str: + ret = self.reporter.source() + self.debug.write(f"source() --> {len(ret)} chars") + return ret + + def source_token_lines(self) -> TSourceTokenLines: + ret = list(self.reporter.source_token_lines()) + self.debug.write(f"source_token_lines() --> {len(ret)} tokens") + return ret diff --git a/venv/Lib/site-packages/coverage/py.typed b/venv/Lib/site-packages/coverage/py.typed new file mode 100644 index 0000000..bacd23a --- /dev/null +++ b/venv/Lib/site-packages/coverage/py.typed @@ -0,0 +1 @@ +# Marker file for PEP 561 to indicate that this package has type hints. diff --git a/venv/Lib/site-packages/coverage/python.py b/venv/Lib/site-packages/coverage/python.py new file mode 100644 index 0000000..8fa734e --- /dev/null +++ b/venv/Lib/site-packages/coverage/python.py @@ -0,0 +1,272 @@ +# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 +# For details: https://github.com/coveragepy/coveragepy/blob/main/NOTICE.txt + +"""Python source expertise for coverage.py""" + +from __future__ import annotations + +import os.path +import types +import zipimport +from collections.abc import Iterable +from typing import TYPE_CHECKING + +from coverage import env +from coverage.exceptions import CoverageException, NoSource +from coverage.files import canonical_filename, relative_filename, zip_location +from coverage.misc import isolate_module, join_regex +from coverage.parser import PythonParser +from coverage.phystokens import source_encoding, source_token_lines +from coverage.plugin import CodeRegion, FileReporter +from coverage.regions import code_regions +from coverage.types import TArc, TLineNo, TMorf, TSourceTokenLines + +if TYPE_CHECKING: + from coverage import Coverage + +# Protect ourselves against aggressive mocking. +os = isolate_module(os) +# Save the original `open` function so later mocks don't break us. +open = open # pylint: disable=redefined-builtin + + +def read_python_source(filename: str) -> bytes: + """Read the Python source text from `filename`. + + Returns bytes. + + """ + with open(filename, "rb") as f: + source = f.read() + + return source.replace(b"\r\n", b"\n").replace(b"\r", b"\n") + + +def get_python_source(filename: str) -> str: + """Return the source code, as unicode.""" + base, ext = os.path.splitext(filename) + if ext == ".py" and env.WINDOWS: + exts = [".py", ".pyw"] + else: + exts = [ext] + + source_bytes: bytes | None + for ext in exts: + try_filename = base + ext + if os.path.exists(try_filename): + # A regular text file: open it. + source_bytes = read_python_source(try_filename) + break + + # Maybe it's in a zip file? + source_bytes = get_zip_bytes(try_filename) + if source_bytes is not None: + break + else: + # Couldn't find source. + raise NoSource(f"No source for code: '{filename}'.", slug="no-source") + + # Replace \f because of http://bugs.python.org/issue19035 + source_bytes = source_bytes.replace(b"\f", b" ") + source = source_bytes.decode(source_encoding(source_bytes), "replace") + + # Python code should always end with a line with a newline. + if source and source[-1] != "\n": + source += "\n" + + return source + + +def get_zip_bytes(filename: str) -> bytes | None: + """Get data from `filename` if it is a zip file path. + + Returns the bytestring data read from the zip file, or None if no zip file + could be found or `filename` isn't in it. The data returned will be + an empty string if the file is empty. + + """ + zipfile_inner = zip_location(filename) + if zipfile_inner is not None: + zipfile, inner = zipfile_inner + try: + zi = zipimport.zipimporter(zipfile) + except zipimport.ZipImportError: + return None + try: + data = zi.get_data(inner) + except OSError: + return None + return data + return None + + +def source_for_file(filename: str) -> str: + """Return the source filename for `filename`. + + Given a file name being traced, return the best guess as to the source + file to attribute it to. + + """ + if filename.endswith(".py"): + # .py files are themselves source files. + return filename + + elif filename.endswith((".pyc", ".pyo")): + # Bytecode files probably have source files near them. + py_filename = filename[:-1] + if os.path.exists(py_filename): + # Found a .py file, use that. + return py_filename + if env.WINDOWS: + # On Windows, it could be a .pyw file. + pyw_filename = py_filename + "w" + if os.path.exists(pyw_filename): + return pyw_filename + # Didn't find source, but it's probably the .py file we want. + return py_filename + + # No idea, just use the file name as-is. + return filename + + +def source_for_morf(morf: TMorf) -> str: + """Get the source filename for the module-or-file `morf`.""" + if hasattr(morf, "__file__") and morf.__file__: + filename = morf.__file__ + elif isinstance(morf, types.ModuleType): + # A module should have had .__file__, otherwise we can't use it. + # This could be a PEP-420 namespace package. + raise CoverageException(f"Module {morf} has no file") + else: + filename = morf + + filename = source_for_file(filename) + return filename + + +class PythonFileReporter(FileReporter): + """Report support for a Python file.""" + + def __init__(self, morf: TMorf, coverage: Coverage | None = None) -> None: + self.coverage = coverage + + filename = source_for_morf(morf) + + fname = filename + canonicalize = True + if self.coverage is not None: + if self.coverage.config.relative_files: + canonicalize = False + if canonicalize: + fname = canonical_filename(filename) + super().__init__(fname) + + if hasattr(morf, "__name__"): + name = morf.__name__.replace(".", os.sep) + if os.path.basename(filename).startswith("__init__."): + name += os.sep + "__init__" + name += ".py" + else: + name = relative_filename(filename) + self.relname = name + + self._source: str | None = None + self._parser: PythonParser | None = None + self._excluded = None + + def __repr__(self) -> str: + return f"" + + def relative_filename(self) -> str: + return self.relname + + @property + def parser(self) -> PythonParser: + """Lazily create a :class:`PythonParser`.""" + assert self.coverage is not None + if self._parser is None: + self._parser = PythonParser( + filename=self.filename, + exclude=self.coverage._exclude_regex("exclude"), + ) + self._parser.parse_source() + return self._parser + + def lines(self) -> set[TLineNo]: + """Return the line numbers of statements in the file.""" + return self.parser.statements + + def multiline_map(self) -> dict[TLineNo, TLineNo]: + """A map of line numbers to first-line in a multi-line statement.""" + return self.parser.multiline_map + + def excluded_lines(self) -> set[TLineNo]: + """Return the line numbers of statements in the file.""" + return self.parser.excluded + + def translate_lines(self, lines: Iterable[TLineNo]) -> set[TLineNo]: + return self.parser.translate_lines(lines) + + def translate_arcs(self, arcs: Iterable[TArc]) -> set[TArc]: + return self.parser.translate_arcs(arcs) + + def no_branch_lines(self) -> set[TLineNo]: + assert self.coverage is not None + no_branch = self.parser.lines_matching( + join_regex(self.coverage.config.partial_list + self.coverage.config.partial_always_list) + ) + return no_branch + + def arcs(self) -> set[TArc]: + return self.parser.arcs() + + def exit_counts(self) -> dict[TLineNo, int]: + return self.parser.exit_counts() + + def missing_arc_description( + self, + start: TLineNo, + end: TLineNo, + executed_arcs: Iterable[TArc] | None = None, + ) -> str: + return self.parser.missing_arc_description(start, end) + + def arc_description(self, start: TLineNo, end: TLineNo) -> str: + return self.parser.arc_description(start, end) + + def source(self) -> str: + if self._source is None: + self._source = get_python_source(self.filename) + return self._source + + def should_be_python(self) -> bool: + """Does it seem like this file should contain Python? + + This is used to decide if a file reported as part of the execution of + a program was really likely to have contained Python in the first + place. + + """ + # Get the file extension. + _, ext = os.path.splitext(self.filename) + + # Anything named *.py* should be Python. + if ext.startswith(".py"): + return True + # A file with no extension should be Python. + if not ext: + return True + # Everything else is probably not Python. + return False + + def source_token_lines(self) -> TSourceTokenLines: + return source_token_lines(self.source()) + + def code_regions(self) -> Iterable[CodeRegion]: + return code_regions(self.source()) + + def code_region_kinds(self) -> Iterable[tuple[str, str]]: + return [ + ("function", "functions"), + ("class", "classes"), + ] diff --git a/venv/Lib/site-packages/coverage/pytracer.py b/venv/Lib/site-packages/coverage/pytracer.py new file mode 100644 index 0000000..977d8e0 --- /dev/null +++ b/venv/Lib/site-packages/coverage/pytracer.py @@ -0,0 +1,369 @@ +# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 +# For details: https://github.com/coveragepy/coveragepy/blob/main/NOTICE.txt + +"""Raw data collector for coverage.py.""" + +from __future__ import annotations + +import atexit +import dis +import itertools +import sys +import threading +from types import FrameType, ModuleType +from typing import Any, Callable, cast + +from coverage import env +from coverage.types import ( + TArc, + TFileDisposition, + TLineNo, + Tracer, + TShouldStartContextFn, + TShouldTraceFn, + TTraceData, + TTraceFileData, + TTraceFn, + TWarnFn, +) + +# I don't understand why, but if we use `cast(set[TLineNo], ...)` inside +# the _trace() function, we get some strange behavior on PyPy 3.10. +# Assigning these names here and using them below fixes the problem. +# See https://github.com/coveragepy/coveragepy/issues/1902 +set_TLineNo = set[TLineNo] +set_TArc = set[TArc] + + +# We need the YIELD_VALUE opcode below, in a comparison-friendly form. +# PYVERSIONS: RESUME is new in Python3.11 +RESUME = dis.opmap.get("RESUME") +RETURN_VALUE = dis.opmap["RETURN_VALUE"] +if RESUME is None: + YIELD_VALUE = dis.opmap["YIELD_VALUE"] + YIELD_FROM = dis.opmap["YIELD_FROM"] + YIELD_FROM_OFFSET = 0 if env.PYPY else 2 +else: + YIELD_VALUE = YIELD_FROM = YIELD_FROM_OFFSET = -1 + +# When running meta-coverage, this file can try to trace itself, which confuses +# everything. Don't trace ourselves. + +THIS_FILE = __file__.rstrip("co") + + +class PyTracer(Tracer): + """Python implementation of the raw data tracer.""" + + # Because of poor implementations of trace-function-manipulating tools, + # the Python trace function must be kept very simple. In particular, there + # must be only one function ever set as the trace function, both through + # sys.settrace, and as the return value from the trace function. Put + # another way, the trace function must always return itself. It cannot + # swap in other functions, or return None to avoid tracing a particular + # frame. + # + # The trace manipulator that introduced this restriction is DecoratorTools, + # which sets a trace function, and then later restores the pre-existing one + # by calling sys.settrace with a function it found in the current frame. + # + # Systems that use DecoratorTools (or similar trace manipulations) must use + # PyTracer to get accurate results. The command-line --timid argument is + # used to force the use of this tracer. + + tracer_ids = itertools.count() + + def __init__(self) -> None: + # Which tracer are we? + self.id = next(self.tracer_ids) + + # Attributes set from the collector: + self.data: TTraceData + self.trace_arcs = False + self.should_trace: TShouldTraceFn + self.should_trace_cache: dict[str, TFileDisposition | None] + self.should_start_context: TShouldStartContextFn | None = None + self.switch_context: Callable[[str | None], None] | None = None + self.lock_data: Callable[[], None] + self.unlock_data: Callable[[], None] + self.warn: TWarnFn + + # The threading module to use, if any. + self.threading: ModuleType | None = None + + self.cur_file_data: TTraceFileData | None = None + self.last_line: TLineNo = 0 + self.cur_file_name: str | None = None + self.context: str | None = None + self.started_context = False + + # The data_stack parallels the Python call stack. Each entry is + # information about an active frame, a four-element tuple: + # [0] The TTraceData for this frame's file. Could be None if we + # aren't tracing this frame. + # [1] The current file name for the frame. None if we aren't tracing + # this frame. + # [2] The last line number executed in this frame. + # [3] Boolean: did this frame start a new context? + self.data_stack: list[tuple[TTraceFileData | None, str | None, TLineNo, bool]] = [] + self.thread: threading.Thread | None = None + self.stopped = False + self._activity = False + + self.in_atexit = False + # On exit, self.in_atexit = True + atexit.register(setattr, self, "in_atexit", True) + + # Cache a bound method on the instance, so that we don't have to + # re-create a bound method object all the time. + self._cached_bound_method_trace: TTraceFn = self._trace + + def __repr__(self) -> str: + points = sum(len(v) for v in self.data.values()) + files = len(self.data) + return f"" + + def log(self, marker: str, *args: Any) -> None: + """For hard-core logging of what this tracer is doing.""" + with open("/tmp/debug_trace.txt", "a", encoding="utf-8") as f: + f.write(f"{marker} {self.id}[{len(self.data_stack)}]") + if 0: # if you want thread ids.. + f.write( # type: ignore[unreachable] + ".{:x}.{:x}".format( + self.thread.ident, + self.threading.current_thread().ident, + ) + ) + f.write(" {}".format(" ".join(map(str, args)))) + if 0: # if you want callers.. + f.write(" | ") # type: ignore[unreachable] + stack = " / ".join( + (fname or "???").rpartition("/")[-1] for _, fname, _, _ in self.data_stack + ) + f.write(stack) + f.write("\n") + + def _trace( + self, + frame: FrameType, + event: str, + arg: Any, # pylint: disable=unused-argument + lineno: TLineNo | None = None, # pylint: disable=unused-argument + ) -> TTraceFn | None: + """The trace function passed to sys.settrace.""" + + if THIS_FILE in frame.f_code.co_filename: + return None + + # f = frame; code = f.f_code + # self.log(":", f"{code.co_filename} {f.f_lineno} {code.co_name}()", event) + + if self.stopped and sys.gettrace() == self._cached_bound_method_trace: # pylint: disable=comparison-with-callable + # The PyTrace.stop() method has been called, possibly by another + # thread, let's deactivate ourselves now. + if 0: + f = frame # type: ignore[unreachable] + self.log("---\nX", f.f_code.co_filename, f.f_lineno) + while f: + self.log(">", f.f_code.co_filename, f.f_lineno, f.f_code.co_name, f.f_trace) + f = f.f_back + sys.settrace(None) + try: + self.cur_file_data, self.cur_file_name, self.last_line, self.started_context = ( + self.data_stack.pop() + ) + except IndexError: + self.log( + "Empty stack!", + frame.f_code.co_filename, + frame.f_lineno, + frame.f_code.co_name, + ) + return None + + # if event != "call" and frame.f_code.co_filename != self.cur_file_name: + # self.log("---\n*", frame.f_code.co_filename, self.cur_file_name, frame.f_lineno) + + if event == "call": + # Should we start a new context? + if self.should_start_context and self.context is None: + context_maybe = self.should_start_context(frame) # pylint: disable=not-callable + if context_maybe is not None: + self.context = context_maybe + started_context = True + assert self.switch_context is not None + self.switch_context(self.context) # pylint: disable=not-callable + else: + started_context = False + else: + started_context = False + self.started_context = started_context + + # Entering a new frame. Decide if we should trace in this file. + self._activity = True + self.data_stack.append( + ( + self.cur_file_data, + self.cur_file_name, + self.last_line, + started_context, + ), + ) + + # Improve tracing performance: when calling a function, both caller + # and callee are often within the same file. if that's the case, we + # don't have to re-check whether to trace the corresponding + # function (which is a little bit expensive since it involves + # dictionary lookups). This optimization is only correct if we + # didn't start a context. + filename = frame.f_code.co_filename + if filename != self.cur_file_name or started_context: + self.cur_file_name = filename + disp = self.should_trace_cache.get(filename) + if disp is None: + disp = self.should_trace(filename, frame) + self.should_trace_cache[filename] = disp + + self.cur_file_data = None + if disp.trace: + tracename = disp.source_filename + assert tracename is not None + self.lock_data() + try: + if tracename not in self.data: + self.data[tracename] = set() + finally: + self.unlock_data() + self.cur_file_data = self.data[tracename] + else: + frame.f_trace_lines = False + elif not self.cur_file_data: + frame.f_trace_lines = False + + # The call event is really a "start frame" event, and happens for + # function calls and re-entering generators. The f_lasti field is + # -1 for calls, and a real offset for generators. Use <0 as the + # line number for calls, and the real line number for generators. + if RESUME is not None: + # The current opcode is guaranteed to be RESUME. The argument + # determines what kind of resume it is. + oparg = frame.f_code.co_code[frame.f_lasti + 1] + real_call = (oparg == 0) # fmt: skip + else: + real_call = (getattr(frame, "f_lasti", -1) < 0) # fmt: skip + if real_call: + self.last_line = -frame.f_code.co_firstlineno + else: + self.last_line = frame.f_lineno + + elif event == "line": + # Record an executed line. + if self.cur_file_data is not None: + flineno: TLineNo = frame.f_lineno + + if self.trace_arcs: + cast(set_TArc, self.cur_file_data).add((self.last_line, flineno)) + else: + cast(set_TLineNo, self.cur_file_data).add(flineno) + self.last_line = flineno + + elif event == "return": + if self.trace_arcs and self.cur_file_data: + # Record an arc leaving the function, but beware that a + # "return" event might just mean yielding from a generator. + code = frame.f_code.co_code + lasti = frame.f_lasti + if RESUME is not None: + if len(code) == lasti + 2: + # A return from the end of a code object is a real return. + real_return = True + else: + # It is a real return if we aren't going to resume next. + if env.PYBEHAVIOR.lasti_is_yield: + lasti += 2 + real_return = code[lasti] != RESUME + else: + if code[lasti] == RETURN_VALUE: + real_return = True + elif code[lasti] == YIELD_VALUE: + real_return = False + elif len(code) <= lasti + YIELD_FROM_OFFSET: + real_return = True + elif code[lasti + YIELD_FROM_OFFSET] == YIELD_FROM: + real_return = False + else: + real_return = True + if real_return: + first = frame.f_code.co_firstlineno + cast(set_TArc, self.cur_file_data).add((self.last_line, -first)) + + # Leaving this function, pop the filename stack. + self.cur_file_data, self.cur_file_name, self.last_line, self.started_context = ( + self.data_stack.pop() + ) + # Leaving a context? + if self.started_context: + assert self.switch_context is not None + self.context = None + self.switch_context(None) # pylint: disable=not-callable + + return self._cached_bound_method_trace + + def start(self) -> TTraceFn: + """Start this Tracer. + + Return a Python function suitable for use with sys.settrace(). + + """ + self.stopped = False + if self.threading: + if self.thread is None: + self.thread = self.threading.current_thread() + + sys.settrace(self._cached_bound_method_trace) + return self._cached_bound_method_trace + + def stop(self) -> None: + """Stop this Tracer.""" + # Get the active tracer callback before setting the stop flag to be + # able to detect if the tracer was changed prior to stopping it. + tf = sys.gettrace() + + # Set the stop flag. The actual call to sys.settrace(None) will happen + # in the self._trace callback itself to make sure to call it from the + # right thread. + self.stopped = True + + if self.threading: + assert self.thread is not None + if self.thread.ident != self.threading.current_thread().ident: + # Called on a different thread than started us: we can't unhook + # ourselves, but we've set the flag that we should stop, so we + # won't do any more tracing. + # self.log("~", "stopping on different threads") + return + + # PyPy clears the trace function before running atexit functions, + # so don't warn if we are in atexit on PyPy and the trace function + # has changed to None. Metacoverage also messes this up, so don't + # warn if we are measuring ourselves. + suppress_warning = (env.PYPY and self.in_atexit and tf is None) or env.METACOV + if self.warn and not suppress_warning: + if tf != self._cached_bound_method_trace: # pylint: disable=comparison-with-callable + self.warn( + "Trace function changed, data is likely wrong: " + + f"{tf!r} != {self._cached_bound_method_trace!r}", + slug="trace-changed", + ) + + def activity(self) -> bool: + """Has there been any activity?""" + return self._activity + + def reset_activity(self) -> None: + """Reset the activity() flag.""" + self._activity = False + + def get_stats(self) -> dict[str, int] | None: + """Return a dictionary of statistics, or None.""" + return None diff --git a/venv/Lib/site-packages/coverage/regions.py b/venv/Lib/site-packages/coverage/regions.py new file mode 100644 index 0000000..891ae84 --- /dev/null +++ b/venv/Lib/site-packages/coverage/regions.py @@ -0,0 +1,127 @@ +# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 +# For details: https://github.com/coveragepy/coveragepy/blob/main/NOTICE.txt + +"""Find functions and classes in Python code.""" + +from __future__ import annotations + +import ast +import dataclasses +from typing import cast + +from coverage.plugin import CodeRegion + + +@dataclasses.dataclass +class Context: + """The nested named context of a function or class.""" + + name: str + kind: str + lines: set[int] + + +class RegionFinder: + """An ast visitor that will find and track regions of code. + + Functions and classes are tracked by name. Results are in the .regions + attribute. + + """ + + def __init__(self) -> None: + self.regions: list[CodeRegion] = [] + self.context: list[Context] = [] + + def parse_source(self, source: str) -> None: + """Parse `source` and walk the ast to populate the .regions attribute.""" + self.handle_node(ast.parse(source)) + + def fq_node_name(self) -> str: + """Get the current fully qualified name we're processing.""" + return ".".join(c.name for c in self.context) + + def handle_node(self, node: ast.AST) -> None: + """Recursively handle any node.""" + if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)): + self.handle_FunctionDef(node) + elif isinstance(node, ast.ClassDef): + self.handle_ClassDef(node) + else: + self.handle_node_body(node) + + def handle_node_body(self, node: ast.AST) -> None: + """Recursively handle the nodes in this node's body, if any.""" + for body_node in getattr(node, "body", ()): + self.handle_node(body_node) + + def handle_FunctionDef(self, node: ast.FunctionDef | ast.AsyncFunctionDef) -> None: + """Called for `def` or `async def`.""" + lines = set(range(node.body[0].lineno, cast(int, node.body[-1].end_lineno) + 1)) + if self.context and self.context[-1].kind == "class": + # Function bodies are part of their enclosing class. + self.context[-1].lines |= lines + # Function bodies should be excluded from the nearest enclosing function. + for ancestor in reversed(self.context): + if ancestor.kind == "function": + ancestor.lines -= lines + break + self.context.append(Context(node.name, "function", lines)) + self.regions.append( + CodeRegion( + kind="function", + name=self.fq_node_name(), + start=node.lineno, + lines=lines, + ) + ) + self.handle_node_body(node) + self.context.pop() + + def handle_ClassDef(self, node: ast.ClassDef) -> None: + """Called for `class`.""" + # The lines for a class are the lines in the methods of the class. + # We start empty, and count on visit_FunctionDef to add the lines it + # finds. + lines: set[int] = set() + self.context.append(Context(node.name, "class", lines)) + self.regions.append( + CodeRegion( + kind="class", + name=self.fq_node_name(), + start=node.lineno, + lines=lines, + ) + ) + self.handle_node_body(node) + self.context.pop() + # Class bodies should be excluded from the enclosing classes. + for ancestor in reversed(self.context): + if ancestor.kind == "class": + ancestor.lines -= lines + + +def code_regions(source: str) -> list[CodeRegion]: + """Find function and class regions in source code. + + Analyzes the code in `source`, and returns a list of :class:`CodeRegion` + objects describing functions and classes as regions of the code:: + + [ + CodeRegion(kind="function", name="func1", start=8, lines={10, 11, 12}), + CodeRegion(kind="function", name="MyClass.method", start=30, lines={34, 35, 36}), + CodeRegion(kind="class", name="MyClass", start=25, lines={34, 35, 36}), + ] + + The line numbers will include comments and blank lines. Later processing + will need to ignore those lines as needed. + + Nested functions and classes are excluded from their enclosing region. No + line should be reported as being part of more than one function, or more + than one class. Lines in methods are reported as being in a function and + in a class. + + """ + rf = RegionFinder() + rf.parse_source(source) + return rf.regions diff --git a/venv/Lib/site-packages/coverage/report.py b/venv/Lib/site-packages/coverage/report.py new file mode 100644 index 0000000..30fa406 --- /dev/null +++ b/venv/Lib/site-packages/coverage/report.py @@ -0,0 +1,298 @@ +# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 +# For details: https://github.com/coveragepy/coveragepy/blob/main/NOTICE.txt + +"""Summary reporting""" + +from __future__ import annotations + +import sys +from collections.abc import Iterable +from typing import IO, TYPE_CHECKING, Any + +from coverage.exceptions import ConfigError, NoDataError +from coverage.misc import human_sorted_items, plural +from coverage.plugin import FileReporter +from coverage.report_core import get_analysis_to_report +from coverage.results import Analysis, Numbers +from coverage.types import TMorf + +if TYPE_CHECKING: + from coverage import Coverage + + +class SummaryReporter: + """A reporter for writing the summary report.""" + + def __init__(self, coverage: Coverage) -> None: + self.coverage = coverage + self.config = self.coverage.config + self.branches = coverage.get_data().has_arcs() + self.outfile: IO[str] | None = None + self.output_format = self.config.format or "text" + if self.output_format not in {"text", "markdown", "total"}: + raise ConfigError(f"Unknown report format choice: {self.output_format!r}") + self.fr_analyses: list[tuple[FileReporter, Analysis]] = [] + self.skipped_count = 0 + self.empty_count = 0 + self.total = Numbers(precision=self.config.precision) + + def write(self, line: str) -> None: + """Write a line to the output, adding a newline.""" + assert self.outfile is not None + self.outfile.write(line.rstrip()) + self.outfile.write("\n") + + def write_items(self, items: Iterable[str]) -> None: + """Write a list of strings, joined together.""" + self.write("".join(items)) + + def report_text( + self, + header: list[str], + lines_values: list[list[Any]], + total_line: list[Any], + end_lines: list[str], + ) -> None: + """Internal method that prints report data in text format. + + `header` is a list with captions. + `lines_values` is list of lists of sortable values. + `total_line` is a list with values of the total line. + `end_lines` is a list of ending lines with information about skipped files. + + """ + # Prepare the formatting strings, header, and column sorting. + max_name = max([len(line[0]) for line in lines_values] + [5]) + 1 + max_n = max(len(total_line[header.index("Cover")]) + 2, len(" Cover")) + 1 + max_n = max([max_n] + [len(line[header.index("Cover")]) + 2 for line in lines_values]) + formats = dict( + Name="{:{name_len}}", + Stmts="{:>7}", + Miss="{:>7}", + Branch="{:>7}", + BrPart="{:>7}", + Cover="{:>{n}}", + Missing="{:>10}", + ) + header_items = [formats[item].format(item, name_len=max_name, n=max_n) for item in header] + header_str = "".join(header_items) + rule = "-" * len(header_str) + + # Write the header + self.write(header_str) + self.write(rule) + + # Write the data lines + formats.update( + dict( + Cover="{:>{n}}%", + Missing=" {:9}", + ) + ) + for values in lines_values: + self.write_items( + ( + formats[item].format(str(value), name_len=max_name, n=max_n - 1) + for item, value in zip(header, values) + ) + ) + + # Write a TOTAL line + if lines_values: + self.write(rule) + + self.write_items( + ( + formats[item].format(str(value), name_len=max_name, n=max_n - 1) + for item, value in zip(header, total_line) + ) + ) + + for end_line in end_lines: + self.write(end_line) + + def report_markdown( + self, + header: list[str], + lines_values: list[list[Any]], + total_line: list[Any], + end_lines: list[str], + ) -> None: + """Internal method that prints report data in markdown format. + + `header` is a list with captions. + `lines_values` is a sorted list of lists containing coverage information. + `total_line` is a list with values of the total line. + `end_lines` is a list of ending lines with information about skipped files. + + """ + # Prepare the formatting strings, header, and column sorting. + max_name = max((len(line[0].replace("_", "\\_")) for line in lines_values), default=0) + max_name = max(max_name, len("**TOTAL**")) + 1 + formats = dict( + Name="| {:{name_len}}|", + Stmts="{:>9} |", + Miss="{:>9} |", + Branch="{:>9} |", + BrPart="{:>9} |", + Cover="{:>{n}} |", + Missing="{:>10} |", + ) + max_n = max(len(total_line[header.index("Cover")]) + 6, len(" Cover ")) + header_items = [formats[item].format(item, name_len=max_name, n=max_n) for item in header] + header_str = "".join(header_items) + rule_str = "|" + " ".join( + ["- |".rjust(len(header_items[0]) - 1, "-")] + + ["-: |".rjust(len(item) - 1, "-") for item in header_items[1:]], + ) + + # Write the header + self.write(header_str) + self.write(rule_str) + + # Write the data lines + for values in lines_values: + formats.update( + dict( + Cover="{:>{n}}% |", + ) + ) + self.write_items( + ( + formats[item].format( + str(value).replace("_", "\\_"), name_len=max_name, n=max_n - 1 + ) + for item, value in zip(header, values) + ) + ) + + # Write the TOTAL line + formats.update( + dict( + Name="|{:>{name_len}} |", + Cover="{:>{n}} |", + ), + ) + total_line_items: list[str] = [] + for item, value in zip(header, total_line): + if value == "": + insert = value + elif item == "Cover": + insert = f" **{value}%**" + else: + insert = f" **{value}**" + total_line_items += formats[item].format(insert, name_len=max_name, n=max_n) + self.write_items(total_line_items) + + for end_line in end_lines: + self.write(end_line) + + def report(self, morfs: Iterable[TMorf] | None, outfile: IO[str] | None = None) -> float: + """Writes a report summarizing coverage statistics per module. + + `outfile` is a text-mode file object to write the summary to. + + """ + self.outfile = outfile or sys.stdout + + self.coverage.get_data().set_query_contexts(self.config.report_contexts) + for fr, analysis in get_analysis_to_report(self.coverage, morfs): + self.report_one_file(fr, analysis) + + if not self.total.n_files and not self.skipped_count: + raise NoDataError("No data to report.") + + if self.output_format == "total": + self.write(self.total.pc_covered_str) + else: + self.tabular_report() + + return self.total.pc_covered + + def tabular_report(self) -> None: + """Writes tabular report formats.""" + # Prepare the header line and column sorting. + header = ["Name", "Stmts", "Miss"] + if self.branches: + header += ["Branch", "BrPart"] + header += ["Cover"] + if self.config.show_missing: + header += ["Missing"] + + column_order = dict(name=0, stmts=1, miss=2, cover=-1) + if self.branches: + column_order.update(dict(branch=3, brpart=4)) + + # `lines_values` is list of lists of sortable values. + lines_values = [] + + for fr, analysis in self.fr_analyses: + nums = analysis.numbers + args = [fr.relative_filename(), nums.n_statements, nums.n_missing] + if self.branches: + args += [nums.n_branches, nums.n_partial_branches] + args += [nums.pc_covered_str] + if self.config.show_missing: + args += [analysis.missing_formatted(branches=True)] + args += [nums.pc_covered] + lines_values.append(args) + + # Line sorting. + sort_option = (self.config.sort or "name").lower() + reverse = False + if sort_option[0] == "-": + reverse = True + sort_option = sort_option[1:] + elif sort_option[0] == "+": + sort_option = sort_option[1:] + sort_idx = column_order.get(sort_option) + if sort_idx is None: + raise ConfigError(f"Invalid sorting option: {self.config.sort!r}") + if sort_option == "name": + lines_values = human_sorted_items(lines_values, reverse=reverse) + else: + lines_values.sort( + key=lambda line: (line[sort_idx], line[0]), + reverse=reverse, + ) + + # Calculate total if we had at least one file. + total_line = ["TOTAL", self.total.n_statements, self.total.n_missing] + if self.branches: + total_line += [self.total.n_branches, self.total.n_partial_branches] + total_line += [self.total.pc_covered_str] + if self.config.show_missing: + total_line += [""] + + # Create other final lines. + end_lines = [] + if self.config.skip_covered and self.skipped_count: + files = plural(self.skipped_count, "file") + end_lines.append( + f"\n{self.skipped_count} {files} skipped due to complete coverage.", + ) + if self.config.skip_empty and self.empty_count: + files = plural(self.empty_count, "file") + end_lines.append(f"\n{self.empty_count} empty {files} skipped.") + + if self.output_format == "markdown": + formatter = self.report_markdown + else: + formatter = self.report_text + formatter(header, lines_values, total_line, end_lines) + + def report_one_file(self, fr: FileReporter, analysis: Analysis) -> None: + """Report on just one file, the callback from report().""" + nums = analysis.numbers + self.total += nums + + no_missing_lines = (nums.n_missing == 0) # fmt: skip + no_missing_branches = (nums.n_partial_branches == 0) # fmt: skip + if self.config.skip_covered and no_missing_lines and no_missing_branches: + # Don't report on 100% files. + self.skipped_count += 1 + elif self.config.skip_empty and nums.n_statements == 0: + # Don't report on empty files. + self.empty_count += 1 + else: + self.fr_analyses.append((fr, analysis)) diff --git a/venv/Lib/site-packages/coverage/report_core.py b/venv/Lib/site-packages/coverage/report_core.py new file mode 100644 index 0000000..6a672a1 --- /dev/null +++ b/venv/Lib/site-packages/coverage/report_core.py @@ -0,0 +1,117 @@ +# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 +# For details: https://github.com/coveragepy/coveragepy/blob/main/NOTICE.txt + +"""Reporter foundation for coverage.py.""" + +from __future__ import annotations + +import sys +from collections.abc import Iterable +from typing import IO, TYPE_CHECKING, Callable, Protocol + +from coverage.exceptions import NoDataError, NotPython +from coverage.files import GlobMatcher, prep_patterns +from coverage.misc import ensure_dir_for_file, file_be_gone +from coverage.plugin import FileReporter +from coverage.results import Analysis +from coverage.types import TMorf + +if TYPE_CHECKING: + from coverage import Coverage + + +class Reporter(Protocol): + """What we expect of reporters.""" + + report_type: str + + def report(self, morfs: Iterable[TMorf] | None, outfile: IO[str]) -> float: + """Generate a report of `morfs`, written to `outfile`.""" + + +def render_report( + output_path: str, + reporter: Reporter, + morfs: Iterable[TMorf] | None, + msgfn: Callable[[str], None], +) -> float: + """Run a one-file report generator, managing the output file. + + This function ensures the output file is ready to be written to. Then writes + the report to it. Then closes the file and cleans up. + + """ + file_to_close = None + delete_file = False + + if output_path == "-": + outfile = sys.stdout + else: + # Ensure that the output directory is created; done here because this + # report pre-opens the output file. HtmlReporter does this on its own + # because its task is more complex, being multiple files. + ensure_dir_for_file(output_path) + outfile = open(output_path, "w", encoding="utf-8") + file_to_close = outfile + delete_file = True + + try: + ret = reporter.report(morfs, outfile=outfile) + if file_to_close is not None: + msgfn(f"Wrote {reporter.report_type} to {output_path}") + delete_file = False + return ret + finally: + if file_to_close is not None: + file_to_close.close() + if delete_file: + file_be_gone(output_path) # pragma: part covered (doesn't return) + + +def get_analysis_to_report( + coverage: Coverage, + morfs: Iterable[TMorf] | None, +) -> Iterable[tuple[FileReporter, Analysis]]: + """Get the files to report on. + + For each morf in `morfs`, if it should be reported on (based on the omit + and include configuration options), yield a pair, the `FileReporter` and + `Analysis` for the morf. + + """ + fr_morfs = coverage._get_file_reporters(morfs) + config = coverage.config + + if config.report_include: + matcher = GlobMatcher(prep_patterns(config.report_include), "report_include") + fr_morfs = [(fr, morf) for (fr, morf) in fr_morfs if matcher.match(fr.filename)] + + if config.report_omit: + matcher = GlobMatcher(prep_patterns(config.report_omit), "report_omit") + fr_morfs = [(fr, morf) for (fr, morf) in fr_morfs if not matcher.match(fr.filename)] + + if not fr_morfs: + raise NoDataError("No data to report.") + + for fr, morf in sorted(fr_morfs): + try: + analysis = coverage._analyze(morf) + except NotPython: + # Only report errors for .py files, and only if we didn't + # explicitly suppress those errors. + # NotPython is only raised by PythonFileReporter, which has a + # should_be_python() method. + if fr.should_be_python(): # type: ignore[attr-defined] + if config.ignore_errors: + msg = f"Couldn't parse Python file '{fr.filename}'" + coverage._warn(msg, slug="couldnt-parse") + else: + raise + except Exception as exc: + if config.ignore_errors: + msg = f"Couldn't parse '{fr.filename}': {exc}".rstrip() + coverage._warn(msg, slug="couldnt-parse") + else: + raise + else: + yield (fr, analysis) diff --git a/venv/Lib/site-packages/coverage/results.py b/venv/Lib/site-packages/coverage/results.py new file mode 100644 index 0000000..4487bae --- /dev/null +++ b/venv/Lib/site-packages/coverage/results.py @@ -0,0 +1,502 @@ +# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 +# For details: https://github.com/coveragepy/coveragepy/blob/main/NOTICE.txt + +"""Results of coverage measurement.""" + +from __future__ import annotations + +import collections +import dataclasses +from collections.abc import Iterable +from typing import TYPE_CHECKING + +from coverage.exceptions import ConfigError +from coverage.misc import nice_pair +from coverage.types import TArc, TLineNo + +if TYPE_CHECKING: + from coverage.data import CoverageData + from coverage.plugin import FileReporter + + +def analysis_from_file_reporter( + data: CoverageData, + precision: int, + file_reporter: FileReporter, + filename: str, +) -> Analysis: + """Create an Analysis from a FileReporter.""" + has_arcs = data.has_arcs() + statements = file_reporter.lines() + excluded = file_reporter.excluded_lines() + executed = file_reporter.translate_lines(data.lines(filename) or []) + + if has_arcs: + arc_possibilities_set = file_reporter.arcs() + arcs: Iterable[TArc] = data.arcs(filename) or [] + arcs = file_reporter.translate_arcs(arcs) + + # Reduce the set of arcs to the ones that could be branches. + dests = collections.defaultdict(set) + for fromno, tono in arc_possibilities_set: + dests[fromno].add(tono) + single_dests = { + fromno: list(tonos)[0] for fromno, tonos in dests.items() if len(tonos) == 1 + } + new_arcs = set() + for fromno, tono in arcs: + if fromno != tono: + new_arcs.add((fromno, tono)) + else: + if fromno in single_dests: + new_arcs.add((fromno, single_dests[fromno])) + + arcs_executed_set = file_reporter.translate_arcs(new_arcs) + exit_counts = file_reporter.exit_counts() + no_branch = file_reporter.no_branch_lines() + else: + arc_possibilities_set = set() + arcs_executed_set = set() + exit_counts = {} + no_branch = set() + + return Analysis( + precision=precision, + filename=filename, + has_arcs=has_arcs, + statements=statements, + excluded=excluded, + executed=executed, + arc_possibilities_set=arc_possibilities_set, + arcs_executed_set=arcs_executed_set, + exit_counts=exit_counts, + no_branch=no_branch, + ) + + +@dataclasses.dataclass +class Analysis: + """The results of analyzing a FileReporter.""" + + precision: int + filename: str + has_arcs: bool + statements: set[TLineNo] + excluded: set[TLineNo] + executed: set[TLineNo] + arc_possibilities_set: set[TArc] + arcs_executed_set: set[TArc] + exit_counts: dict[TLineNo, int] + no_branch: set[TLineNo] + + def __post_init__(self) -> None: + self.arc_possibilities = sorted(self.arc_possibilities_set) + self.arcs_executed = sorted(self.arcs_executed_set) + self.missing = self.statements - self.executed + + if self.has_arcs: + n_branches = self._total_branches() + mba = self.missing_branch_arcs() + n_partial_branches = sum(len(v) for k, v in mba.items() if k not in self.missing) + n_missing_branches = sum(len(v) for k, v in mba.items()) + else: + n_branches = n_partial_branches = n_missing_branches = 0 + + self.numbers = Numbers( + precision=self.precision, + n_files=1, + n_statements=len(self.statements), + n_excluded=len(self.excluded), + n_missing=len(self.missing), + n_branches=n_branches, + n_partial_branches=n_partial_branches, + n_missing_branches=n_missing_branches, + ) + + def missing_formatted(self, branches: bool = False) -> str: + """The missing line numbers, formatted nicely. + + Returns a string like "1-2, 5-11, 13-14". + + If `branches` is true, includes the missing branch arcs also. + + """ + if branches and self.has_arcs: + arcs = self.missing_branch_arcs().items() + else: + arcs = None + + return format_lines(self.statements, self.missing, arcs=arcs) + + def arcs_missing(self) -> list[TArc]: + """Returns a sorted list of the un-executed arcs in the code.""" + missing = ( + p + for p in self.arc_possibilities + if p not in self.arcs_executed_set + and p[0] not in self.no_branch + and p[1] not in self.excluded + ) + return sorted(missing) + + def _branch_lines(self) -> list[TLineNo]: + """Returns a list of line numbers that have more than one exit.""" + return [l1 for l1, count in self.exit_counts.items() if count > 1] + + def _total_branches(self) -> int: + """How many total branches are there?""" + return sum(count for count in self.exit_counts.values() if count > 1) + + def missing_branch_arcs(self) -> dict[TLineNo, list[TLineNo]]: + """Return arcs that weren't executed from branch lines. + + Returns {l1:[l2a,l2b,...], ...} + + """ + missing = self.arcs_missing() + branch_lines = set(self._branch_lines()) + mba = collections.defaultdict(list) + for l1, l2 in missing: + assert l1 != l2, f"In {self.filename}, didn't expect {l1} == {l2}" + if l1 in branch_lines: + mba[l1].append(l2) + return mba + + def executed_branch_arcs(self) -> dict[TLineNo, list[TLineNo]]: + """Return arcs that were executed from branch lines. + + Only include ones that we considered possible. + + Returns {l1:[l2a,l2b,...], ...} + + """ + branch_lines = set(self._branch_lines()) + eba = collections.defaultdict(list) + for l1, l2 in self.arcs_executed: + assert l1 != l2, f"Oops: Didn't think this could happen: {l1 = }, {l2 = }" + if (l1, l2) not in self.arc_possibilities_set: + continue + if l1 in branch_lines: + eba[l1].append(l2) + return eba + + def branch_stats(self) -> dict[TLineNo, tuple[int, int]]: + """Get stats about branches. + + Returns a dict mapping line numbers to a tuple: + (total_exits, taken_exits). + + """ + + missing_arcs = self.missing_branch_arcs() + stats = {} + for lnum in self._branch_lines(): + exits = self.exit_counts[lnum] + missing = len(missing_arcs[lnum]) + stats[lnum] = (exits, exits - missing) + return stats + + +TRegionLines = frozenset[TLineNo] + + +class AnalysisNarrower: + """ + For reducing an `Analysis` to a subset of its lines. + + Originally this was a simpler method on Analysis, but that led to quadratic + behavior. This class does the bulk of the work up-front to provide the + same results in linear time. + + Create an AnalysisNarrower from an Analysis, bulk-add region lines to it + with `add_regions`, then individually request new narrowed Analysis objects + for each region with `narrow`. Doing most of the work in limited calls to + `add_regions` lets us avoid poor performance. + """ + + # In this class, regions are represented by a frozenset of their lines. + + def __init__(self, analysis: Analysis) -> None: + self.analysis = analysis + self.region2arc_possibilities: dict[TRegionLines, set[TArc]] = collections.defaultdict(set) + self.region2arc_executed: dict[TRegionLines, set[TArc]] = collections.defaultdict(set) + self.region2exit_counts: dict[TRegionLines, dict[TLineNo, int]] = collections.defaultdict( + dict + ) + + def add_regions(self, liness: Iterable[set[TLineNo]]) -> None: + """ + Pre-process a number of sets of line numbers. Later calls to `narrow` + with one of these sets will provide a narrowed Analysis. + """ + if self.analysis.has_arcs: + line2region: dict[TLineNo, TRegionLines] = {} + + for lines in liness: + fzlines = frozenset(lines) + for line in lines: + line2region[line] = fzlines + + def collect_arcs( + arc_set: set[TArc], + region2arcs: dict[TRegionLines, set[TArc]], + ) -> None: + for a, b in arc_set: + if r := line2region.get(a): + region2arcs[r].add((a, b)) + if r := line2region.get(b): + region2arcs[r].add((a, b)) + + collect_arcs(self.analysis.arc_possibilities_set, self.region2arc_possibilities) + collect_arcs(self.analysis.arcs_executed_set, self.region2arc_executed) + + for lno, num in self.analysis.exit_counts.items(): + if r := line2region.get(lno): + self.region2exit_counts[r][lno] = num + + def narrow(self, lines: set[TLineNo]) -> Analysis: + """Create a narrowed Analysis. + + The current analysis is copied to make a new one that only considers + the lines in `lines`. + """ + + # Technically, the set intersections in this method are still O(N**2) + # since this method is called N times, but they're very fast and moving + # them to `add_regions` won't avoid the quadratic time. + + statements = self.analysis.statements & lines + excluded = self.analysis.excluded & lines + executed = self.analysis.executed & lines + + if self.analysis.has_arcs: + fzlines = frozenset(lines) + arc_possibilities_set = self.region2arc_possibilities[fzlines] + arcs_executed_set = self.region2arc_executed[fzlines] + exit_counts = self.region2exit_counts[fzlines] + no_branch = self.analysis.no_branch & lines + else: + arc_possibilities_set = set() + arcs_executed_set = set() + exit_counts = {} + no_branch = set() + + return Analysis( + precision=self.analysis.precision, + filename=self.analysis.filename, + has_arcs=self.analysis.has_arcs, + statements=statements, + excluded=excluded, + executed=executed, + arc_possibilities_set=arc_possibilities_set, + arcs_executed_set=arcs_executed_set, + exit_counts=exit_counts, + no_branch=no_branch, + ) + + +@dataclasses.dataclass +class Numbers: + """The numerical results of measuring coverage. + + This holds the basic statistics from `Analysis`, and is used to roll + up statistics across files. + + """ + + precision: int = 0 + n_files: int = 0 + n_statements: int = 0 + n_excluded: int = 0 + n_missing: int = 0 + n_branches: int = 0 + n_partial_branches: int = 0 + n_missing_branches: int = 0 + + @property + def n_executed(self) -> int: + """Returns the number of executed statements.""" + return self.n_statements - self.n_missing + + @property + def n_executed_branches(self) -> int: + """Returns the number of executed branches.""" + return self.n_branches - self.n_missing_branches + + @property + def ratio_statements(self) -> tuple[int, int]: + """Return numerator/denominator for statement coverage.""" + return self.n_executed, self.n_statements + + @property + def ratio_branches(self) -> tuple[int, int]: + """Return numerator/denominator for branch coverage.""" + return self.n_executed_branches, self.n_branches + + def _percent(self, numerator: int, denominator: int) -> float: + """Helper for pc_* properties.""" + if denominator > 0: + return (100.0 * numerator) / denominator + return 100.0 + + @property + def pc_covered(self) -> float: + """Returns a single percentage value for coverage.""" + return self._percent(*self.ratio_covered) + + @property + def pc_statements(self) -> float: + """Returns the percentage covered for statements.""" + return self._percent(*self.ratio_statements) + + @property + def pc_branches(self) -> float: + """Returns the percentage covered for branches.""" + return self._percent(*self.ratio_branches) + + @property + def pc_covered_str(self) -> str: + """Returns the percent covered, as a string, without a percent sign. + + Note that "0" is only returned when the value is truly zero, and "100" + is only returned when the value is truly 100. Rounding can never + result in either "0" or "100". + + """ + return display_covered(self.pc_covered, self.precision) + + @property + def pc_statements_str(self) -> str: + """Returns the statement percent covered without a percent sign.""" + return display_covered(self.pc_statements, self.precision) + + @property + def pc_branches_str(self) -> str: + """Returns the branch percent covered without a percent sign.""" + return display_covered(self.pc_branches, self.precision) + + @property + def ratio_covered(self) -> tuple[int, int]: + """Return a numerator and denominator for the coverage ratio.""" + numerator = self.n_executed + self.n_executed_branches + denominator = self.n_statements + self.n_branches + return numerator, denominator + + def __add__(self, other: Numbers) -> Numbers: + return Numbers( + self.precision, + self.n_files + other.n_files, + self.n_statements + other.n_statements, + self.n_excluded + other.n_excluded, + self.n_missing + other.n_missing, + self.n_branches + other.n_branches, + self.n_partial_branches + other.n_partial_branches, + self.n_missing_branches + other.n_missing_branches, + ) + + def __radd__(self, other: int) -> Numbers: + # Implementing 0+Numbers allows us to sum() a list of Numbers. + assert other == 0 # we only ever call it this way. + return self + + +def display_covered(pc: float, precision: int) -> str: + """Return a displayable total percentage, as a string. + + Note that "0" is only returned when the value is truly zero, and "100" + is only returned when the value is truly 100. Rounding can never + result in either "0" or "100". + + """ + near0 = 1.0 / 10**precision + if 0 < pc < near0: + pc = near0 + elif (100.0 - near0) < pc < 100: + pc = 100.0 - near0 + else: + pc = round(pc, precision) + return f"{pc:.{precision}f}" + + +def _line_ranges( + statements: Iterable[TLineNo], + lines: Iterable[TLineNo], +) -> list[tuple[TLineNo, TLineNo]]: + """Produce a list of ranges for `format_lines`.""" + statements = sorted(statements) + lines = sorted(lines) + + pairs = [] + start: TLineNo | None = None + lidx = 0 + for stmt in statements: + if lidx >= len(lines): + break + if stmt == lines[lidx]: + lidx += 1 + if not start: + start = stmt + end = stmt + elif start: + pairs.append((start, end)) + start = None + if start: + pairs.append((start, end)) + return pairs + + +def format_lines( + statements: Iterable[TLineNo], + lines: Iterable[TLineNo], + arcs: Iterable[tuple[TLineNo, list[TLineNo]]] | None = None, +) -> str: + """Nicely format a list of line numbers. + + Format a list of line numbers for printing by coalescing groups of lines as + long as the lines represent consecutive statements. This will coalesce + even if there are gaps between statements. + + For example, if `statements` is [1,2,3,4,5,10,11,12,13,14] and + `lines` is [1,2,5,10,11,13,14] then the result will be "1-2, 5-11, 13-14". + + Both `lines` and `statements` can be any iterable. All of the elements of + `lines` must be in `statements`, and all of the values must be positive + integers. + + If `arcs` is provided, they are (start,[end,end,end]) pairs that will be + included in the output as long as start isn't in `lines`. + + """ + line_items = [(pair[0], nice_pair(pair)) for pair in _line_ranges(statements, lines)] + if arcs is not None: + line_exits = sorted(arcs) + for line, exits in line_exits: + for ex in sorted(exits): + if line not in lines and ex not in lines: + dest = ex if ex > 0 else "exit" + line_items.append((line, f"{line}->{dest}")) + + ret = ", ".join(t[-1] for t in sorted(line_items)) + return ret + + +def should_fail_under(total: float, fail_under: float, precision: int) -> bool: + """Determine if a total should fail due to fail-under. + + `total` is a float, the coverage measurement total. `fail_under` is the + fail_under setting to compare with. `precision` is the number of digits + to consider after the decimal point. + + Returns True if the total should fail. + + """ + # We can never achieve higher than 100% coverage, or less than zero. + if not (0 <= fail_under <= 100.0): + msg = f"fail_under={fail_under} is invalid. Must be between 0 and 100." + raise ConfigError(msg) + + # Special case for fail_under=100, it must really be 100. + if fail_under == 100.0 and total != 100.0: + return True + + return round(total, precision) < fail_under diff --git a/venv/Lib/site-packages/coverage/sqldata.py b/venv/Lib/site-packages/coverage/sqldata.py new file mode 100644 index 0000000..7741e47 --- /dev/null +++ b/venv/Lib/site-packages/coverage/sqldata.py @@ -0,0 +1,1153 @@ +# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 +# For details: https://github.com/coveragepy/coveragepy/blob/main/NOTICE.txt + +"""SQLite coverage data.""" + +from __future__ import annotations + +import collections +import datetime +import functools +import glob +import itertools +import os +import random +import socket +import sqlite3 +import string +import sys +import textwrap +import threading +import uuid +import zlib +from collections.abc import Collection, Mapping, Sequence +from typing import Any, Callable, cast + +from coverage.debug import NoDebugging, auto_repr, file_summary +from coverage.exceptions import CoverageException, DataError +from coverage.misc import file_be_gone, isolate_module +from coverage.numbits import numbits_to_nums, numbits_union, nums_to_numbits +from coverage.sqlitedb import SqliteDb +from coverage.types import AnyCallable, FilePath, TArc, TDebugCtl, TLineNo, TWarnFn +from coverage.version import __version__ + +os = isolate_module(os) + +# If you change the schema: increment the SCHEMA_VERSION and update the +# docs in docs/dbschema.rst by running "make cogdoc". + +SCHEMA_VERSION = 7 + +# Schema versions: +# 1: Released in 5.0a2 +# 2: Added contexts in 5.0a3. +# 3: Replaced line table with line_map table. +# 4: Changed line_map.bitmap to line_map.numbits. +# 5: Added foreign key declarations. +# 6: Key-value in meta. +# 7: line_map -> line_bits + +SCHEMA = """\ +CREATE TABLE coverage_schema ( + -- One row, to record the version of the schema in this db. + version integer +); + +CREATE TABLE meta ( + -- Key-value pairs, to record metadata about the data + key text, + value text, + unique (key) + -- Possible keys: + -- 'has_arcs' boolean -- Is this data recording branches? + -- 'sys_argv' text -- The coverage command line that recorded the data. + -- 'version' text -- The version of coverage.py that made the file. + -- 'when' text -- Datetime when the file was created. +); + +CREATE TABLE file ( + -- A row per file measured. + id integer primary key, + path text, + unique (path) +); + +CREATE TABLE context ( + -- A row per context measured. + id integer primary key, + context text, + unique (context) +); + +CREATE TABLE line_bits ( + -- If recording lines, a row per context per file executed. + -- All of the line numbers for that file/context are in one numbits. + file_id integer, -- foreign key to `file`. + context_id integer, -- foreign key to `context`. + numbits blob, -- see the numbits functions in coverage.numbits + foreign key (file_id) references file (id), + foreign key (context_id) references context (id), + unique (file_id, context_id) +); + +CREATE TABLE arc ( + -- If recording branches, a row per context per from/to line transition executed. + file_id integer, -- foreign key to `file`. + context_id integer, -- foreign key to `context`. + fromno integer, -- line number jumped from. + tono integer, -- line number jumped to. + foreign key (file_id) references file (id), + foreign key (context_id) references context (id), + unique (file_id, context_id, fromno, tono) +); + +CREATE TABLE tracer ( + -- A row per file indicating the tracer used for that file. + file_id integer primary key, + tracer text, + foreign key (file_id) references file (id) +); +""" + + +def _locked(method: AnyCallable) -> AnyCallable: + """A decorator for methods that should hold self._lock.""" + + @functools.wraps(method) + def _wrapped(self: CoverageData, *args: Any, **kwargs: Any) -> Any: + if self._debug.should("lock"): + self._debug.write(f"Locking {self._lock!r} for {method.__name__}") + with self._lock: + if self._debug.should("lock"): + self._debug.write(f"Locked {self._lock!r} for {method.__name__}") + return method(self, *args, **kwargs) + + return _wrapped + + +class NumbitsUnionAgg: + """SQLite aggregate function for computing union of numbits.""" + + def __init__(self) -> None: + self.result = b"" + + def step(self, value: bytes) -> None: + """Process one value in the aggregation.""" + self.result = numbits_union(self.result, value) + + def finalize(self) -> bytes: + """Return the final aggregated result.""" + return self.result + + +class CoverageData: + """Manages collected coverage data, including file storage. + + This class is the public supported API to the data that coverage.py + collects during program execution. It includes information about what code + was executed. It does not include information from the analysis phase, to + determine what lines could have been executed, or what lines were not + executed. + + .. note:: + + The data file is currently a SQLite database file, with a + :ref:`documented schema `. The schema is subject to change + though, so be careful about querying it directly. Use this API if you + can to isolate yourself from changes. + + There are a number of kinds of data that can be collected: + + * **lines**: the line numbers of source lines that were executed. + These are always available. + + * **arcs**: pairs of source and destination line numbers for transitions + between source lines. These are only available if branch coverage was + used. + + * **file tracer names**: the module names of the file tracer plugins that + handled each file in the data. + + Lines, arcs, and file tracer names are stored for each source file. File + names in this API are case-sensitive, even on platforms with + case-insensitive file systems. + + A data file either stores lines, or arcs, but not both. + + A data file is associated with the data when the :class:`CoverageData` + is created, using the parameters `basename`, `suffix`, and `no_disk`. The + base name can be queried with :meth:`base_filename`, and the actual file + name being used is available from :meth:`data_filename`. + + To read an existing coverage.py data file, use :meth:`read`. You can then + access the line, arc, or file tracer data with :meth:`lines`, :meth:`arcs`, + or :meth:`file_tracer`. + + The :meth:`has_arcs` method indicates whether arc data is available. You + can get a set of the files in the data with :meth:`measured_files`. As + with most Python containers, you can determine if there is any data at all + by using this object as a boolean value. + + The contexts for each line in a file can be read with + :meth:`contexts_by_lineno`. + + To limit querying to certain contexts, use :meth:`set_query_context` or + :meth:`set_query_contexts`. These will narrow the focus of subsequent + :meth:`lines`, :meth:`arcs`, and :meth:`contexts_by_lineno` calls. The set + of all measured context names can be retrieved with + :meth:`measured_contexts`. + + Most data files will be created by coverage.py itself, but you can use + methods here to create data files if you like. The :meth:`add_lines`, + :meth:`add_arcs`, and :meth:`add_file_tracers` methods add data, in ways + that are convenient for coverage.py. + + To record data for contexts, use :meth:`set_context` to set a context to + be used for subsequent :meth:`add_lines` and :meth:`add_arcs` calls. + + To add a source file without any measured data, use :meth:`touch_file`, + or :meth:`touch_files` for a list of such files. + + Write the data to its file with :meth:`write`. + + You can clear the data in memory with :meth:`erase`. Data for specific + files can be removed from the database with :meth:`purge_files`. + + Two data collections can be combined by using :meth:`update` on one + :class:`CoverageData`, passing it the other. + + Data in a :class:`CoverageData` can be serialized and deserialized with + :meth:`dumps` and :meth:`loads`. + + The methods used during the coverage.py collection phase + (:meth:`add_lines`, :meth:`add_arcs`, :meth:`set_context`, and + :meth:`add_file_tracers`) are thread-safe. Other methods may not be. + + """ + + def __init__( + self, + basename: FilePath | None = None, + suffix: str | bool | None = None, + no_disk: bool = False, + warn: TWarnFn | None = None, + debug: TDebugCtl | None = None, + ) -> None: + """Create a :class:`CoverageData` object to hold coverage-measured data. + + Arguments: + basename (str): the base name of the data file, defaulting to + ".coverage". This can be a path to a file in another directory. + suffix (str or bool): has the same meaning as the `data_suffix` + argument to :class:`coverage.Coverage`. + no_disk (bool): if True, keep all data in memory, and don't + write any disk file. + warn: a warning callback function, accepting a warning message + argument. + debug: a `DebugControl` object (optional) + + """ + self._no_disk = no_disk + self._basename = os.path.abspath(basename or ".coverage") + self._suffix = suffix + self._warn = warn + self._debug = debug or NoDebugging() + + self._choose_filename() + # Maps filenames to row ids. + self._file_map: dict[str, int] = {} + # Maps thread ids to SqliteDb objects. + self._dbs: dict[int, SqliteDb] = {} + self._pid = os.getpid() + # Synchronize the operations used during collection. + self._lock = threading.RLock() + + # Are we in sync with the data file? + self._have_used = False + + self._has_lines = False + self._has_arcs = False + + self._current_context: str | None = None + self._current_context_id: int | None = None + self._query_context_ids: list[int] | None = None + + __repr__ = auto_repr + + def _debug_dataio(self, msg: str, filename: str) -> None: + """A helper for debug messages which are all similar.""" + if self._debug.should("dataio"): + self._debug.write(f"{msg} {filename!r} ({file_summary(filename)})") + + def _choose_filename(self) -> None: + """Set self._filename based on inited attributes.""" + if self._no_disk: + self._filename = f"file:coverage-{uuid.uuid4()}?mode=memory&cache=shared" + else: + self._filename = self._basename + suffix = filename_suffix(self._suffix) + if suffix: + self._filename += f".{suffix}" + + def _reset(self) -> None: + """Reset our attributes.""" + if not self._no_disk: + self.close() + self._file_map = {} + self._have_used = False + self._current_context_id = None + + def close(self, force: bool = False) -> None: + """Really close all the database objects.""" + if self._debug.should("dataio"): + self._debug.write(f"Closing dbs, force={force}: {self._dbs}") + for db in self._dbs.values(): + db.close(force=force) + self._dbs = {} + + def _open_db(self) -> None: + """Open an existing db file, and read its metadata.""" + self._debug_dataio("Opening data file", self._filename) + self._dbs[threading.get_ident()] = SqliteDb(self._filename, self._debug, self._no_disk) + self._read_db() + + def _read_db(self) -> None: + """Read the metadata from a database so that we are ready to use it.""" + with self._dbs[threading.get_ident()] as db: + try: + row = db.execute_one("select version from coverage_schema") + assert row is not None + except Exception as exc: + if "no such table: coverage_schema" in str(exc): + self._init_db(db) + else: + raise DataError( + "Data file {!r} doesn't seem to be a coverage data file: {}".format( + self._filename, + exc, + ), + ) from exc + else: + schema_version = row[0] + if schema_version != SCHEMA_VERSION: + raise DataError( + "Couldn't use data file {!r}: wrong schema: {} instead of {}".format( + self._filename, + schema_version, + SCHEMA_VERSION, + ), + ) + + row = db.execute_one("select value from meta where key = 'has_arcs'") + if row is not None: + self._has_arcs = bool(int(row[0])) + self._has_lines = not self._has_arcs + + with db.execute("select id, path from file") as cur: + for file_id, path in cur: + self._file_map[path] = file_id + + def _init_db(self, db: SqliteDb) -> None: + """Write the initial contents of the database.""" + self._debug_dataio("Initing data file", self._filename) + db.executescript(SCHEMA) + db.execute_void("INSERT INTO coverage_schema (version) VALUES (?)", (SCHEMA_VERSION,)) + + # When writing metadata, avoid information that will needlessly change + # the hash of the data file, unless we're debugging processes. + meta_data = [ + ("version", __version__), + ] + if self._debug.should("process"): + meta_data.extend( + [ + ("sys_argv", str(getattr(sys, "argv", None))), + ("when", datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")), + ] + ) + db.executemany_void("INSERT OR IGNORE INTO meta (key, value) VALUES (?, ?)", meta_data) + + def _connect(self) -> SqliteDb: + """Get the SqliteDb object to use.""" + if threading.get_ident() not in self._dbs: + self._open_db() + return self._dbs[threading.get_ident()] + + def __bool__(self) -> bool: + if threading.get_ident() not in self._dbs and not os.path.exists(self._filename): + return False + try: + with self._connect() as con: + with con.execute("SELECT * FROM file LIMIT 1") as cur: + return bool(list(cur)) + except CoverageException: + return False + + def dumps(self) -> bytes: + """Serialize the current data to a byte string. + + The format of the serialized data is not documented. It is only + suitable for use with :meth:`loads` in the same version of + coverage.py. + + Note that this serialization is not what gets stored in coverage data + files. This method is meant to produce bytes that can be transmitted + elsewhere and then deserialized with :meth:`loads`. + + Returns: + A byte string of serialized data. + + .. versionadded:: 5.0 + + """ + self._debug_dataio("Dumping data from data file", self._filename) + with self._connect() as con: + script = con.dump() + return b"z" + zlib.compress(script.encode("utf-8")) + + def loads(self, data: bytes) -> None: + """Deserialize data from :meth:`dumps`. + + Use with a newly-created empty :class:`CoverageData` object. It's + undefined what happens if the object already has data in it. + + Note that this is not for reading data from a coverage data file. It + is only for use on data you produced with :meth:`dumps`. + + Arguments: + data: A byte string of serialized data produced by :meth:`dumps`. + + .. versionadded:: 5.0 + + """ + self._debug_dataio("Loading data into data file", self._filename) + if data[:1] != b"z": + raise DataError( + f"Unrecognized serialization: {data[:40]!r} (head of {len(data)} bytes)", + ) + script = zlib.decompress(data[1:]).decode("utf-8") + self._dbs[threading.get_ident()] = db = SqliteDb(self._filename, self._debug, self._no_disk) + with db: + db.executescript(script) + self._read_db() + self._have_used = True + + def _file_id(self, filename: str, add: bool = False) -> int | None: + """Get the file id for `filename`. + + If filename is not in the database yet, add it if `add` is True. + If `add` is not True, return None. + """ + if filename not in self._file_map: + if add: + with self._connect() as con: + self._file_map[filename] = con.execute_for_rowid( + "INSERT OR REPLACE INTO file (path) VALUES (?)", + (filename,), + ) + return self._file_map.get(filename) + + def _context_id(self, context: str) -> int | None: + """Get the id for a context.""" + assert context is not None + self._start_using() + with self._connect() as con: + row = con.execute_one("SELECT id FROM context WHERE context = ?", (context,)) + if row is not None: + return cast(int, row[0]) + else: + return None + + @_locked + def set_context(self, context: str | None) -> None: + """Set the current context for future :meth:`add_lines` etc. + + `context` is a str, the name of the context to use for the next data + additions. The context persists until the next :meth:`set_context`. + + .. versionadded:: 5.0 + + """ + if self._debug.should("dataop"): + self._debug.write(f"Setting coverage context: {context!r}") + self._current_context = context + self._current_context_id = None + + def _set_context_id(self) -> None: + """Use the _current_context to set _current_context_id.""" + context = self._current_context or "" + context_id = self._context_id(context) + if context_id is not None: + self._current_context_id = context_id + else: + with self._connect() as con: + self._current_context_id = con.execute_for_rowid( + "INSERT INTO context (context) VALUES (?)", + (context,), + ) + + def base_filename(self) -> str: + """The base filename for storing data. + + .. versionadded:: 5.0 + + """ + return self._basename + + def data_filename(self) -> str: + """Where is the data stored? + + .. versionadded:: 5.0 + + """ + return self._filename + + @_locked + def add_lines(self, line_data: Mapping[str, Collection[TLineNo]]) -> None: + """Add measured line data. + + `line_data` is a dictionary mapping file names to iterables of ints:: + + { filename: { line1, line2, ... }, ...} + + """ + if self._debug.should("dataop"): + self._debug.write( + "Adding lines: %d files, %d lines total" + % ( + len(line_data), + sum(len(lines) for lines in line_data.values()), + ) + ) + if self._debug.should("dataop2"): + for filename, linenos in sorted(line_data.items()): + self._debug.write(f" {filename}: {linenos}") + self._start_using() + self._choose_lines_or_arcs(lines=True) + if not line_data: + return + with self._connect() as con: + self._set_context_id() + for filename, linenos in line_data.items(): + line_bits = nums_to_numbits(linenos) + file_id = self._file_id(filename, add=True) + query = "SELECT numbits FROM line_bits WHERE file_id = ? AND context_id = ?" + with con.execute(query, (file_id, self._current_context_id)) as cur: + existing = list(cur) + if existing: + line_bits = numbits_union(line_bits, existing[0][0]) + + con.execute_void( + """ + INSERT OR REPLACE INTO line_bits + (file_id, context_id, numbits) VALUES (?, ?, ?) + """, + (file_id, self._current_context_id, line_bits), + ) + + @_locked + def add_arcs(self, arc_data: Mapping[str, Collection[TArc]]) -> None: + """Add measured arc data. + + `arc_data` is a dictionary mapping file names to iterables of pairs of + ints:: + + { filename: { (l1,l2), (l1,l2), ... }, ...} + + """ + if self._debug.should("dataop"): + self._debug.write( + "Adding arcs: %d files, %d arcs total" + % ( + len(arc_data), + sum(len(arcs) for arcs in arc_data.values()), + ) + ) + if self._debug.should("dataop2"): + for filename, arcs in sorted(arc_data.items()): + self._debug.write(f" {filename}: {arcs}") + self._start_using() + self._choose_lines_or_arcs(arcs=True) + if not arc_data: + return + with self._connect() as con: + self._set_context_id() + for filename, arcs in arc_data.items(): + if not arcs: + continue + file_id = self._file_id(filename, add=True) + data = [(file_id, self._current_context_id, fromno, tono) for fromno, tono in arcs] + con.executemany_void( + """ + INSERT OR IGNORE INTO arc + (file_id, context_id, fromno, tono) VALUES (?, ?, ?, ?) + """, + data, + ) + + def _choose_lines_or_arcs(self, lines: bool = False, arcs: bool = False) -> None: + """Force the data file to choose between lines and arcs.""" + assert lines or arcs + assert not (lines and arcs) + if lines and self._has_arcs: + if self._debug.should("dataop"): + self._debug.write("Error: Can't add line measurements to existing branch data") + raise DataError("Can't add line measurements to existing branch data") + if arcs and self._has_lines: + if self._debug.should("dataop"): + self._debug.write("Error: Can't add branch measurements to existing line data") + raise DataError("Can't add branch measurements to existing line data") + if not self._has_arcs and not self._has_lines: + self._has_lines = lines + self._has_arcs = arcs + with self._connect() as con: + con.execute_void( + "INSERT OR IGNORE INTO meta (key, value) VALUES (?, ?)", + ("has_arcs", str(int(arcs))), + ) + + @_locked + def add_file_tracers(self, file_tracers: Mapping[str, str]) -> None: + """Add per-file plugin information. + + `file_tracers` is { filename: plugin_name, ... } + + """ + if self._debug.should("dataop"): + self._debug.write(f"Adding file tracers: {len(file_tracers)} files") + if not file_tracers: + return + self._start_using() + with self._connect() as con: + for filename, plugin_name in file_tracers.items(): + file_id = self._file_id(filename, add=True) + existing_plugin = self.file_tracer(filename) + if existing_plugin: + if existing_plugin != plugin_name: + raise DataError( + "Conflicting file tracer name for '{}': {!r} vs {!r}".format( + filename, + existing_plugin, + plugin_name, + ), + ) + elif plugin_name: + con.execute_void( + "INSERT INTO TRACER (file_id, tracer) VALUES (?, ?)", + (file_id, plugin_name), + ) + + def touch_file(self, filename: str, plugin_name: str = "") -> None: + """Ensure that `filename` appears in the data, empty if needed. + + `plugin_name` is the name of the plugin responsible for this file. + It is used to associate the right filereporter, etc. + """ + self.touch_files([filename], plugin_name) + + def touch_files(self, filenames: Collection[str], plugin_name: str | None = None) -> None: + """Ensure that `filenames` appear in the data, empty if needed. + + `plugin_name` is the name of the plugin responsible for these files. + It is used to associate the right filereporter, etc. + """ + if self._debug.should("dataop"): + self._debug.write(f"Touching {filenames!r}") + self._start_using() + with self._connect(): # Use this to get one transaction. + if not self._has_arcs and not self._has_lines: + raise DataError("Can't touch files in an empty CoverageData") + + for filename in filenames: + self._file_id(filename, add=True) + if plugin_name: + # Set the tracer for this file + self.add_file_tracers({filename: plugin_name}) + + def purge_files(self, filenames: Collection[str]) -> None: + """Purge any existing coverage data for the given `filenames`. + + .. versionadded:: 7.2 + + """ + if self._debug.should("dataop"): + self._debug.write(f"Purging data for {filenames!r}") + self._start_using() + with self._connect() as con: + if self._has_lines: + sql = "DELETE FROM line_bits WHERE file_id=?" + elif self._has_arcs: + sql = "DELETE FROM arc WHERE file_id=?" + else: + raise DataError("Can't purge files in an empty CoverageData") + + for filename in filenames: + file_id = self._file_id(filename, add=False) + if file_id is None: + continue + con.execute_void(sql, (file_id,)) + + def update( + self, + other_data: CoverageData, + map_path: Callable[[str], str] | None = None, + ) -> None: + """Update this data with data from another :class:`CoverageData`. + + If `map_path` is provided, it's a function that re-map paths to match + the local machine's. Note: `map_path` is None only when called + directly from the test suite. + + """ + if self._debug.should("dataop"): + self._debug.write( + "Updating with data from {!r}".format( + getattr(other_data, "_filename", "???"), + ) + ) + if self._has_lines and other_data._has_arcs: + raise DataError( + "Can't combine branch coverage data with statement data", slug="cant-combine" + ) + if self._has_arcs and other_data._has_lines: + raise DataError( + "Can't combine statement coverage data with branch data", slug="cant-combine" + ) + + map_path = map_path or (lambda p: p) + + # Force the database we're writing to to exist before we start nesting contexts. + self._start_using() + other_data.read() + + # Ensure other_data has a properly initialized database + with other_data._connect(): + pass + + with self._connect() as con: + assert con.con is not None + con.con.isolation_level = "IMMEDIATE" + + # Register functions for SQLite + con.con.create_function("numbits_union", 2, numbits_union) + con.con.create_function("map_path", 1, map_path) + con.con.create_aggregate( + "numbits_union_agg", + 1, + NumbitsUnionAgg, # type: ignore[arg-type] + ) + + # Attach the other database + con.execute_void("ATTACH DATABASE ? AS other_db", (other_data.data_filename(),)) + + # Create temporary table with mapped file paths to avoid repeated map_path() calls + con.execute_void(""" + CREATE TEMP TABLE other_file_mapped AS + SELECT + other_file.id as other_file_id, + map_path(other_file.path) as mapped_path + FROM other_db.file AS other_file + """) + + # Check for tracer conflicts before proceeding + with con.execute(""" + SELECT other_file_mapped.mapped_path, + COALESCE(main.tracer.tracer, ''), + COALESCE(other_db.tracer.tracer, '') + FROM main.file + LEFT JOIN main.tracer ON main.file.id = main.tracer.file_id + INNER JOIN other_file_mapped ON main.file.path = other_file_mapped.mapped_path + LEFT JOIN other_db.tracer ON other_file_mapped.other_file_id = other_db.tracer.file_id + WHERE COALESCE(main.tracer.tracer, '') != COALESCE(other_db.tracer.tracer, '') + """) as cur: + conflicts = list(cur) + if conflicts: + path, this_tracer, other_tracer = conflicts[0] + raise DataError( + "Conflicting file tracer name for '{}': {!r} vs {!r}".format( + path, + this_tracer, + other_tracer, + ), + ) + + # Insert missing files from other_db (with map_path applied) + con.execute_void(""" + INSERT OR IGNORE INTO main.file (path) + SELECT DISTINCT mapped_path FROM other_file_mapped + """) + + # Insert missing contexts from other_db + con.execute_void(""" + INSERT OR IGNORE INTO main.context (context) + SELECT context FROM other_db.context + """) + + # Update file_map with any new files + with con.execute("SELECT id, path FROM file") as cur: + self._file_map.update({path: id for id, path in cur}) + + with con.execute(""" + SELECT + EXISTS(SELECT 1 FROM other_db.arc), + EXISTS(SELECT 1 FROM other_db.line_bits) + """) as cur: + has_arcs, has_lines = cur.fetchone() + + # Handle arcs if present in other_db + if has_arcs: + self._choose_lines_or_arcs(arcs=True) + + # Create context mapping table for faster lookups + con.execute_void(""" + CREATE TEMP TABLE context_mapping AS + SELECT + other_context.id as other_id, + main_context.id as main_id + FROM other_db.context AS other_context + INNER JOIN main.context AS main_context ON other_context.context = main_context.context + """) + + con.execute_void(""" + INSERT OR IGNORE INTO main.arc (file_id, context_id, fromno, tono) + SELECT + main_file.id, + context_mapping.main_id, + other_arc.fromno, + other_arc.tono + FROM other_db.arc AS other_arc + INNER JOIN other_file_mapped ON other_arc.file_id = other_file_mapped.other_file_id + INNER JOIN context_mapping ON other_arc.context_id = context_mapping.other_id + INNER JOIN main.file AS main_file ON other_file_mapped.mapped_path = main_file.path + """) + + # Handle line_bits if present in other_db + if has_lines: + self._choose_lines_or_arcs(lines=True) + + # Handle line_bits by aggregating other_db data by mapped target, + # then inserting/updating + con.execute_void(""" + INSERT OR REPLACE INTO main.line_bits (file_id, context_id, numbits) + SELECT + main_file.id, + main_context.id, + numbits_union( + COALESCE(( + SELECT numbits FROM main.line_bits + WHERE file_id = main_file.id AND context_id = main_context.id + ), X''), + aggregated.combined_numbits + ) + FROM ( + SELECT + other_file_mapped.mapped_path, + other_context.context, + numbits_union_agg(other_line_bits.numbits) as combined_numbits + FROM other_db.line_bits AS other_line_bits + INNER JOIN other_file_mapped ON other_line_bits.file_id = other_file_mapped.other_file_id + INNER JOIN other_db.context AS other_context ON other_line_bits.context_id = other_context.id + GROUP BY other_file_mapped.mapped_path, other_context.context + ) AS aggregated + INNER JOIN main.file AS main_file ON aggregated.mapped_path = main_file.path + INNER JOIN main.context AS main_context ON aggregated.context = main_context.context + """) + + # Insert tracers from other_db (avoiding conflicts we already checked) + con.execute_void(""" + INSERT OR IGNORE INTO main.tracer (file_id, tracer) + SELECT + main_file.id, + other_tracer.tracer + FROM other_db.tracer AS other_tracer + INNER JOIN other_file_mapped ON other_tracer.file_id = other_file_mapped.other_file_id + INNER JOIN main.file AS main_file ON other_file_mapped.mapped_path = main_file.path + """) + + if not self._no_disk: + # Update all internal cache data. + self._reset() + self.read() + + def erase(self, parallel: bool = False) -> None: + """Erase the data in this object. + + If `parallel` is true, then also deletes data files created from the + basename by parallel-mode. + + """ + self._reset() + if self._no_disk: + return + self._debug_dataio("Erasing data file", self._filename) + file_be_gone(self._filename) + if parallel: + data_dir, local = os.path.split(self._filename) + local_abs_path = os.path.join(os.path.abspath(data_dir), local) + pattern = glob.escape(local_abs_path) + ".*" + for filename in glob.glob(pattern): + self._debug_dataio("Erasing parallel data file", filename) + file_be_gone(filename) + + def read(self) -> None: + """Start using an existing data file.""" + if os.path.exists(self._filename): + with self._connect(): + self._have_used = True + + def write(self) -> None: + """Ensure the data is written to the data file.""" + self._debug_dataio("Writing (no-op) data file", self._filename) + + def _start_using(self) -> None: + """Call this before using the database at all.""" + if self._pid != os.getpid(): + # Looks like we forked! Have to start a new data file. + self._reset() + self._choose_filename() + self._pid = os.getpid() + if not self._have_used: + self.erase() + self._have_used = True + + def has_arcs(self) -> bool: + """Does the database have arcs (True) or lines (False).""" + return bool(self._has_arcs) + + def measured_files(self) -> set[str]: + """A set of all files that have been measured. + + Note that a file may be mentioned as measured even though no lines or + arcs for that file are present in the data. + + """ + return set(self._file_map) + + def measured_contexts(self) -> set[str]: + """A set of all contexts that have been measured. + + .. versionadded:: 5.0 + + """ + self._start_using() + with self._connect() as con: + with con.execute("SELECT DISTINCT(context) FROM context") as cur: + contexts = {row[0] for row in cur} + return contexts + + def file_tracer(self, filename: str) -> str | None: + """Get the plugin name of the file tracer for a file. + + Returns the name of the plugin that handles this file. If the file was + measured, but didn't use a plugin, then "" is returned. If the file + was not measured, then None is returned. + + """ + self._start_using() + with self._connect() as con: + file_id = self._file_id(filename) + if file_id is None: + return None + row = con.execute_one("SELECT tracer FROM tracer WHERE file_id = ?", (file_id,)) + if row is not None: + return row[0] or "" + return "" # File was measured, but no tracer associated. + + def set_query_context(self, context: str) -> None: + """Set a context for subsequent querying. + + The next :meth:`lines`, :meth:`arcs`, or :meth:`contexts_by_lineno` + calls will be limited to only one context. `context` is a string which + must match a context exactly. If it does not, no exception is raised, + but queries will return no data. + + .. versionadded:: 5.0 + + """ + self._start_using() + with self._connect() as con: + with con.execute("SELECT id FROM context WHERE context = ?", (context,)) as cur: + self._query_context_ids = [row[0] for row in cur.fetchall()] + + def set_query_contexts(self, contexts: Sequence[str] | None) -> None: + """Set a number of contexts for subsequent querying. + + The next :meth:`lines`, :meth:`arcs`, or :meth:`contexts_by_lineno` + calls will be limited to the specified contexts. `contexts` is a list + of Python regular expressions. Contexts will be matched using + :func:`re.search `. Data will be included in query + results if they are part of any of the contexts matched. + + .. versionadded:: 5.0 + + """ + self._start_using() + if contexts: + with self._connect() as con: + context_clause = " or ".join(["context REGEXP ?"] * len(contexts)) + with con.execute("SELECT id FROM context WHERE " + context_clause, contexts) as cur: + self._query_context_ids = [row[0] for row in cur.fetchall()] + else: + self._query_context_ids = None + + def lines(self, filename: str) -> list[TLineNo] | None: + """Get the list of lines executed for a source file. + + If the file was not measured, returns None. A file might be measured, + and have no lines executed, in which case an empty list is returned. + + If the file was executed, returns a list of integers, the line numbers + executed in the file. The list is in no particular order. + + """ + self._start_using() + if self.has_arcs(): + arcs = self.arcs(filename) + if arcs is not None: + all_lines = itertools.chain.from_iterable(arcs) + return list({l for l in all_lines if l > 0}) + + with self._connect() as con: + file_id = self._file_id(filename) + if file_id is None: + return None + else: + query = "SELECT numbits FROM line_bits WHERE file_id = ?" + data = [file_id] + if self._query_context_ids is not None: + ids_array = ", ".join("?" * len(self._query_context_ids)) + query += " AND context_id IN (" + ids_array + ")" + data += self._query_context_ids + with con.execute(query, data) as cur: + bitmaps = list(cur) + nums = set() + for row in bitmaps: + nums.update(numbits_to_nums(row[0])) + return list(nums) + + def arcs(self, filename: str) -> list[TArc] | None: + """Get the list of arcs executed for a file. + + If the file was not measured, returns None. A file might be measured, + and have no arcs executed, in which case an empty list is returned. + + If the file was executed, returns a list of 2-tuples of integers. Each + pair is a starting line number and an ending line number for a + transition from one line to another. The list is in no particular + order. + + Negative numbers have special meaning. If the starting line number is + -N, it represents an entry to the code object that starts at line N. + If the ending ling number is -N, it's an exit from the code object that + starts at line N. + + """ + self._start_using() + with self._connect() as con: + file_id = self._file_id(filename) + if file_id is None: + return None + else: + query = "SELECT DISTINCT fromno, tono FROM arc WHERE file_id = ?" + data = [file_id] + if self._query_context_ids is not None: + ids_array = ", ".join("?" * len(self._query_context_ids)) + query += " AND context_id IN (" + ids_array + ")" + data += self._query_context_ids + with con.execute(query, data) as cur: + return list(cur) + + def contexts_by_lineno(self, filename: str) -> dict[TLineNo, list[str]]: + """Get the contexts for each line in a file. + + Returns: + A dict mapping line numbers to a list of context names. + + .. versionadded:: 5.0 + + """ + self._start_using() + with self._connect() as con: + file_id = self._file_id(filename) + if file_id is None: + return {} + + lineno_contexts_map = collections.defaultdict(set) + if self.has_arcs(): + query = """ + SELECT arc.fromno, arc.tono, context.context + FROM arc, context + WHERE arc.file_id = ? AND arc.context_id = context.id + """ + data = [file_id] + if self._query_context_ids is not None: + ids_array = ", ".join("?" * len(self._query_context_ids)) + query += " AND arc.context_id IN (" + ids_array + ")" + data += self._query_context_ids + with con.execute(query, data) as cur: + for fromno, tono, context in cur: + if fromno > 0: + lineno_contexts_map[fromno].add(context) + if tono > 0: + lineno_contexts_map[tono].add(context) + else: + query = """ + SELECT l.numbits, c.context FROM line_bits l, context c + WHERE l.context_id = c.id + AND file_id = ? + """ + data = [file_id] + if self._query_context_ids is not None: + ids_array = ", ".join("?" * len(self._query_context_ids)) + query += " AND l.context_id IN (" + ids_array + ")" + data += self._query_context_ids + with con.execute(query, data) as cur: + for numbits, context in cur: + for lineno in numbits_to_nums(numbits): + lineno_contexts_map[lineno].add(context) + + return {lineno: list(contexts) for lineno, contexts in lineno_contexts_map.items()} + + @classmethod + def sys_info(cls) -> list[tuple[str, Any]]: + """Our information for `Coverage.sys_info`. + + Returns a list of (key, value) pairs. + + """ + with SqliteDb(":memory:", debug=NoDebugging()) as db: + with db.execute("PRAGMA temp_store") as cur: + temp_store = [row[0] for row in cur] + with db.execute("PRAGMA compile_options") as cur: + copts = [row[0] for row in cur] + copts = textwrap.wrap(", ".join(copts), width=75) + + return [ + ("sqlite3_sqlite_version", sqlite3.sqlite_version), + ("sqlite3_temp_store", temp_store), + ("sqlite3_compile_options", copts), + ] + + +def filename_suffix(suffix: str | bool | None) -> str | None: + """Compute a filename suffix for a data file. + + If `suffix` is a string or None, simply return it. If `suffix` is True, + then build a suffix incorporating the hostname, process id, and a random + number. + + Returns a string or None. + + """ + if suffix is True: + # If data_suffix was a simple true value, then make a suffix with + # plenty of distinguishing information. We do this here in + # `save()` at the last minute so that the pid will be correct even + # if the process forks. + die = random.Random(os.urandom(8)) + letters = string.ascii_uppercase + string.ascii_lowercase + rolls = "".join(die.choice(letters) for _ in range(6)) + suffix = f"{socket.gethostname()}.{os.getpid()}.X{rolls}x" + elif suffix is False: + suffix = None + return suffix diff --git a/venv/Lib/site-packages/coverage/sqlitedb.py b/venv/Lib/site-packages/coverage/sqlitedb.py new file mode 100644 index 0000000..b05b7f1 --- /dev/null +++ b/venv/Lib/site-packages/coverage/sqlitedb.py @@ -0,0 +1,239 @@ +# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 +# For details: https://github.com/coveragepy/coveragepy/blob/main/NOTICE.txt + +"""SQLite abstraction for coverage.py""" + +from __future__ import annotations + +import contextlib +import re +import sqlite3 +from collections.abc import Iterable, Iterator +from typing import Any, cast + +from coverage.debug import auto_repr, clipped_repr, exc_one_line +from coverage.exceptions import DataError +from coverage.types import TDebugCtl + + +class SqliteDb: + """A simple abstraction over a SQLite database. + + Use as a context manager, then you can use it like a + :class:`python:sqlite3.Connection` object:: + + with SqliteDb(filename, debug_control) as db: + with db.execute("select a, b from some_table") as cur: + for a, b in cur: + etc(a, b) + + """ + + def __init__(self, filename: str, debug: TDebugCtl, no_disk: bool = False) -> None: + self.debug = debug + self.filename = filename + self.no_disk = no_disk + self.nest = 0 + self.con: sqlite3.Connection | None = None + + __repr__ = auto_repr + + def _connect(self) -> None: + """Connect to the db and do universal initialization.""" + if self.con is not None: + return + + # It can happen that Python switches threads while the tracer writes + # data. The second thread will also try to write to the data, + # effectively causing a nested context. However, given the idempotent + # nature of the tracer operations, sharing a connection among threads + # is not a problem. + if self.debug.should("sql"): + self.debug.write(f"Connecting to {self.filename!r}") + try: + # Use uri=True when connecting to memory URIs + if self.filename.startswith("file:"): + self.con = sqlite3.connect(self.filename, check_same_thread=False, uri=True) + else: + self.con = sqlite3.connect(self.filename, check_same_thread=False) + except sqlite3.Error as exc: + raise DataError(f"Couldn't use data file {self.filename!r}: {exc}") from exc + + if self.debug.should("sql"): + self.debug.write(f"Connected to {self.filename!r} as {self.con!r}") + + self.con.create_function("REGEXP", 2, lambda txt, pat: re.search(txt, pat) is not None) + + # Turning off journal_mode can speed up writing. It can't always be + # disabled, so we have to be prepared for *-journal files elsewhere. + # In Python 3.12+, we can change the config to allow journal_mode=off. + if hasattr(sqlite3, "SQLITE_DBCONFIG_DEFENSIVE"): + # Turn off defensive mode, so that journal_mode=off can succeed. + self.con.setconfig( # type: ignore[attr-defined, unused-ignore] + sqlite3.SQLITE_DBCONFIG_DEFENSIVE, + False, + ) + + # This pragma makes writing faster. It disables rollbacks, but we never need them. + self.execute_void("pragma journal_mode=off") + + # This pragma makes writing faster. It can fail in unusual situations + # (https://github.com/coveragepy/coveragepy/issues/1646), so use fail_ok=True + # to keep things going. + self.execute_void("pragma synchronous=off", fail_ok=True) + + def close(self, force: bool = False) -> None: + """If needed, close the connection.""" + if self.con is not None: + if force or not self.no_disk: + if self.debug.should("sql"): + self.debug.write(f"Closing {self.con!r} on {self.filename!r}") + self.con.close() + self.con = None + + def __enter__(self) -> SqliteDb: + if self.nest == 0: + self._connect() + assert self.con is not None + self.con.__enter__() + self.nest += 1 + return self + + def __exit__(self, exc_type, exc_value, traceback) -> None: # type: ignore[no-untyped-def] + self.nest -= 1 + if self.nest == 0: + try: + assert self.con is not None + self.con.__exit__(exc_type, exc_value, traceback) + self.close() + except Exception as exc: + if self.debug.should("sql"): + self.debug.write(f"EXCEPTION from __exit__: {exc_one_line(exc)}") + raise DataError(f"Couldn't end data file {self.filename!r}: {exc}") from exc + + def _execute(self, sql: str, parameters: Iterable[Any]) -> sqlite3.Cursor: + """Same as :meth:`python:sqlite3.Connection.execute`.""" + if self.debug.should("sql"): + tail = f" with {parameters!r}" if parameters else "" + self.debug.write(f"Executing {sql!r}{tail}") + try: + assert self.con is not None + try: + return self.con.execute(sql, parameters) # type: ignore[arg-type] + except Exception: + # In some cases, an error might happen that isn't really an + # error. Try again immediately. + # https://github.com/coveragepy/coveragepy/issues/1010 + return self.con.execute(sql, parameters) # type: ignore[arg-type] + except sqlite3.Error as exc: + msg = str(exc) + if not self.no_disk: + try: + # `execute` is the first thing we do with the database, so try + # hard to provide useful hints if something goes wrong now. + with open(self.filename, "rb") as bad_file: + cov4_sig = b"!coverage.py: This is a private format" + if bad_file.read(len(cov4_sig)) == cov4_sig: + msg = ( + "Looks like a coverage 4.x data file. " + + "Are you mixing versions of coverage?" + ) + except Exception: + pass + if self.debug.should("sql"): + self.debug.write(f"EXCEPTION from execute: {exc_one_line(exc)}") + raise DataError(f"Couldn't use data file {self.filename!r}: {msg}") from exc + + @contextlib.contextmanager + def execute( + self, + sql: str, + parameters: Iterable[Any] = (), + ) -> Iterator[sqlite3.Cursor]: + """Context managed :meth:`python:sqlite3.Connection.execute`. + + Use with a ``with`` statement to auto-close the returned cursor. + """ + cur = self._execute(sql, parameters) + try: + yield cur + finally: + cur.close() + + def execute_void(self, sql: str, parameters: Iterable[Any] = (), fail_ok: bool = False) -> None: + """Same as :meth:`python:sqlite3.Connection.execute` when you don't need the cursor. + + If `fail_ok` is True, then SQLite errors are ignored. + """ + try: + # PyPy needs the .close() calls here, or sqlite gets twisted up: + # https://bitbucket.org/pypy/pypy/issues/2872/default-isolation-mode-is-different-on + self._execute(sql, parameters).close() + except DataError: + if not fail_ok: + raise + + def execute_for_rowid(self, sql: str, parameters: Iterable[Any] = ()) -> int: + """Like execute, but returns the lastrowid.""" + with self.execute(sql, parameters) as cur: + assert cur.lastrowid is not None + rowid: int = cur.lastrowid + if self.debug.should("sqldata"): + self.debug.write(f"Row id result: {rowid!r}") + return rowid + + def execute_one(self, sql: str, parameters: Iterable[Any] = ()) -> tuple[Any, ...] | None: + """Execute a statement and return the one row that results. + + This is like execute(sql, parameters).fetchone(), except it is + correct in reading the entire result set. This will raise an + exception if more than one row results. + + Returns a row, or None if there were no rows. + """ + with self.execute(sql, parameters) as cur: + rows = list(cur) + if len(rows) == 0: + return None + elif len(rows) == 1: + return cast(tuple[Any, ...], rows[0]) + else: + raise AssertionError(f"SQL {sql!r} shouldn't return {len(rows)} rows") + + def _executemany(self, sql: str, data: list[Any]) -> sqlite3.Cursor: + """Same as :meth:`python:sqlite3.Connection.executemany`.""" + if self.debug.should("sql"): + final = ":" if self.debug.should("sqldata") else "" + self.debug.write(f"Executing many {sql!r} with {len(data)} rows{final}") + if self.debug.should("sqldata"): + for i, row in enumerate(data): + self.debug.write(f"{i:4d}: {row!r}") + assert self.con is not None + try: + return self.con.executemany(sql, data) + except Exception: + # In some cases, an error might happen that isn't really an + # error. Try again immediately. + # https://github.com/coveragepy/coveragepy/issues/1010 + return self.con.executemany(sql, data) + + def executemany_void(self, sql: str, data: list[Any]) -> None: + """Same as :meth:`python:sqlite3.Connection.executemany` when you don't need the cursor.""" + self._executemany(sql, data).close() + + def executescript(self, script: str) -> None: + """Same as :meth:`python:sqlite3.Connection.executescript`.""" + if self.debug.should("sql"): + self.debug.write( + "Executing script with {} chars: {}".format( + len(script), + clipped_repr(script, 100), + ) + ) + assert self.con is not None + self.con.executescript(script).close() + + def dump(self) -> str: + """Return a multi-line string, the SQL dump of the database.""" + assert self.con is not None + return "\n".join(self.con.iterdump()) diff --git a/venv/Lib/site-packages/coverage/sysmon.py b/venv/Lib/site-packages/coverage/sysmon.py new file mode 100644 index 0000000..294cda7 --- /dev/null +++ b/venv/Lib/site-packages/coverage/sysmon.py @@ -0,0 +1,513 @@ +# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 +# For details: https://github.com/coveragepy/coveragepy/blob/main/NOTICE.txt + +"""Callback functions and support for sys.monitoring data collection.""" + +from __future__ import annotations + +import collections +import functools +import inspect +import os +import os.path +import sys +import threading +import traceback +from dataclasses import dataclass +from types import CodeType +from typing import Any, Callable, NewType, Optional, cast + +from coverage import env +from coverage.bytecode import TBranchTrails, always_jumps, branch_trails +from coverage.debug import short_filename, short_stack +from coverage.exceptions import NoSource, NotPython +from coverage.misc import isolate_module +from coverage.parser import PythonParser +from coverage.types import ( + AnyCallable, + TFileDisposition, + TLineNo, + TOffset, + Tracer, + TShouldStartContextFn, + TShouldTraceFn, + TTraceData, + TTraceFileData, + TWarnFn, +) + +# Only needed for some of the commented-out logging: +# from coverage.debug import ppformat + +os = isolate_module(os) + +# pylint: disable=unused-argument + +# $set_env.py: COVERAGE_SYSMON_LOG - Log sys.monitoring activity +LOG = bool(int(os.getenv("COVERAGE_SYSMON_LOG", 0))) + +# $set_env.py: COVERAGE_SYSMON_STATS - Collect sys.monitoring stats +COLLECT_STATS = bool(int(os.getenv("COVERAGE_SYSMON_STATS", 0))) + +# This module will be imported in all versions of Python, but only used in 3.12+ +# It will be type-checked for 3.12, but not for earlier versions. +sys_monitoring = getattr(sys, "monitoring", None) + +DISABLE_TYPE = NewType("DISABLE_TYPE", object) +MonitorReturn = Optional[DISABLE_TYPE] +DISABLE = cast(MonitorReturn, getattr(sys_monitoring, "DISABLE", None)) + + +if LOG: # pragma: debugging + + class LoggingWrapper: + """Wrap a namespace to log all its functions.""" + + def __init__(self, wrapped: Any, namespace: str) -> None: + self.wrapped = wrapped + self.namespace = namespace + + def __getattr__(self, name: str) -> Callable[..., Any]: + def _wrapped(*args: Any, **kwargs: Any) -> Any: + log(f"{self.namespace}.{name}{args}{kwargs}") + return getattr(self.wrapped, name)(*args, **kwargs) + + return _wrapped + + sys_monitoring = LoggingWrapper(sys_monitoring, "sys.monitoring") + assert sys_monitoring is not None + + short_stack = functools.partial( + short_stack, + full=True, + short_filenames=True, + frame_ids=True, + ) + seen_threads: set[int] = set() + + def log(msg: str) -> None: + """Write a message to our detailed debugging log(s).""" + # Thread ids are reused across processes? + # Make a shorter number more likely to be unique. + pid = os.getpid() + tid = cast(int, threading.current_thread().ident) + tslug = f"{(pid * tid) % 9_999_991:07d}" + if tid not in seen_threads: + seen_threads.add(tid) + log(f"New thread {tid} {tslug}:\n{short_stack()}") + # log_seq = int(os.getenv("PANSEQ", "0")) + # root = f"/tmp/pan.{log_seq:03d}" + for filename in [ + "/tmp/foo.out", + # f"{root}.out", + # f"{root}-{pid}.out", + # f"{root}-{pid}-{tslug}.out", + ]: + with open(filename, "a", encoding="utf-8") as f: + try: + print(f"{pid}:{tslug}: {msg}", file=f, flush=True) + except UnicodeError: + print(f"{pid}:{tslug}: {ascii(msg)}", file=f, flush=True) + + def arg_repr(arg: Any) -> str: + """Make a customized repr for logged values.""" + if isinstance(arg, CodeType): + return ( + f"" + ) + return repr(arg) + + def panopticon(*names: str | None) -> AnyCallable: + """Decorate a function to log its calls.""" + + def _decorator(method: AnyCallable) -> AnyCallable: + @functools.wraps(method) + def _wrapped(self: Any, *args: Any) -> Any: + try: + # log(f"{method.__name__}() stack:\n{short_stack()}") + args_reprs = [] + for name, arg in zip(names, args): + if name is None: + continue + args_reprs.append(f"{name}={arg_repr(arg)}") + log(f"{id(self):#x}:{method.__name__}({', '.join(args_reprs)})") + ret = method(self, *args) + # log(f" end {id(self):#x}:{method.__name__}({', '.join(args_reprs)})") + return ret + except Exception as exc: + log(f"!!{exc.__class__.__name__}: {exc}") + if 1: + log("".join(traceback.format_exception(exc))) + try: + assert sys_monitoring is not None + sys_monitoring.set_events(sys.monitoring.COVERAGE_ID, 0) + except ValueError: + # We might have already shut off monitoring. + log("oops, shutting off events with disabled tool id") + raise + + return _wrapped + + return _decorator + +else: + + def log(msg: str) -> None: + """Write a message to our detailed debugging log(s), but not really.""" + + def panopticon(*names: str | None) -> AnyCallable: + """Decorate a function to log its calls, but not really.""" + + def _decorator(meth: AnyCallable) -> AnyCallable: + return meth + + return _decorator + + +@dataclass +class CodeInfo: + """The information we want about each code object.""" + + tracing: bool + file_data: TTraceFileData | None + byte_to_line: dict[TOffset, TLineNo] | None + + # Keys are start instruction offsets for branches. + # Values are dicts: + # { + # (from_line, to_line): {offset, offset, ...}, + # (from_line, to_line): {offset, offset, ...}, + # } + branch_trails: TBranchTrails + + # Always-jumps are bytecode offsets that do no work but move + # to another offset. + always_jumps: dict[TOffset, TOffset] + + +def bytes_to_lines(code: CodeType) -> dict[TOffset, TLineNo]: + """Make a dict mapping byte code offsets to line numbers.""" + b2l = {} + for bstart, bend, lineno in code.co_lines(): + if lineno is not None: + for boffset in range(bstart, bend, 2): + b2l[boffset] = lineno + return b2l + + +class SysMonitor(Tracer): + """Python implementation of the raw data tracer for PEP669 implementations.""" + + # One of these will be used across threads. Be careful. + + def __init__(self, tool_id: int) -> None: + # Attributes set from the collector: + self.data: TTraceData + self.trace_arcs = False + self.should_trace: TShouldTraceFn + self.should_trace_cache: dict[str, TFileDisposition | None] + # TODO: should_start_context and switch_context are unused! + # Change tests/testenv.py:DYN_CONTEXTS when this is updated. + self.should_start_context: TShouldStartContextFn | None = None + self.switch_context: Callable[[str | None], None] | None = None + self.lock_data: Callable[[], None] + self.unlock_data: Callable[[], None] + # TODO: warn is unused. + self.warn: TWarnFn + + self.myid = tool_id + + # Map id(code_object) -> CodeInfo + self.code_infos: dict[int, CodeInfo] = {} + # A list of code_objects, just to keep them alive so that id's are + # useful as identity. + self.code_objects: list[CodeType] = [] + + # Map filename:__name__ -> set(id(code_object)) + self.filename_code_ids: dict[str, set[int]] = collections.defaultdict(set) + + self.sysmon_on = False + self.lock = threading.Lock() + + self.stats: dict[str, int] | None = None + if COLLECT_STATS: + self.stats = dict.fromkeys( + "starts start_tracing returns line_lines line_arcs branches branch_trails".split(), + 0, + ) + + self._activity = False + + def __repr__(self) -> str: + points = sum(len(v) for v in self.data.values()) + files = len(self.data) + return f"" + + @panopticon() + def start(self) -> None: + """Start this Tracer.""" + with self.lock: + assert sys_monitoring is not None + sys_monitoring.use_tool_id(self.myid, "coverage.py") + register = functools.partial(sys_monitoring.register_callback, self.myid) + events = sys.monitoring.events + + sys_monitoring.set_events(self.myid, events.PY_START) + register(events.PY_START, self.sysmon_py_start) + if self.trace_arcs: + register(events.PY_RETURN, self.sysmon_py_return) + register(events.LINE, self.sysmon_line_arcs) + if env.PYBEHAVIOR.branch_right_left: + register( + events.BRANCH_RIGHT, # type:ignore[attr-defined] + self.sysmon_branch_either, + ) + register( + events.BRANCH_LEFT, # type:ignore[attr-defined] + self.sysmon_branch_either, + ) + else: + register(events.LINE, self.sysmon_line_lines) + sys_monitoring.restart_events() + self.sysmon_on = True + + @panopticon() + def stop(self) -> None: + """Stop this Tracer.""" + with self.lock: + if not self.sysmon_on: + # In forking situations, we might try to stop when we are not + # started. Do nothing in that case. + return + assert sys_monitoring is not None + sys_monitoring.set_events(self.myid, 0) + self.sysmon_on = False + sys_monitoring.free_tool_id(self.myid) + + if LOG: # pragma: debugging + items = sorted( + self.filename_code_ids.items(), + key=lambda item: len(item[1]), + reverse=True, + ) + code_objs = sum(len(code_ids) for _, code_ids in items) + dupes = code_objs - len(items) + if dupes: + log(f"==== Duplicate code objects: {dupes} duplicates, {code_objs} total") + for filename, code_ids in items: + if len(code_ids) > 1: + log(f"{len(code_ids):>5} objects: {filename}") + else: + log("==== Duplicate code objects: none") + + @panopticon() + def post_fork(self) -> None: + """The process has forked, clean up as needed.""" + self.stop() + + def activity(self) -> bool: + """Has there been any activity?""" + return self._activity + + def reset_activity(self) -> None: + """Reset the activity() flag.""" + self._activity = False + + def get_stats(self) -> dict[str, int] | None: + """Return a dictionary of statistics, or None.""" + return self.stats + + @panopticon("code", "@") + def sysmon_py_start(self, code: CodeType, instruction_offset: TOffset) -> MonitorReturn: + """Handle sys.monitoring.events.PY_START events.""" + self._activity = True + if self.stats is not None: + self.stats["starts"] += 1 + + # Entering a new frame. Decide if we should trace in this file. + code_info = self.code_infos.get(id(code)) + tracing_code: bool | None = None + file_data: TTraceFileData | None = None + if code_info is not None: + tracing_code = code_info.tracing + file_data = code_info.file_data + + if tracing_code is None: + filename = code.co_filename + disp = self.should_trace_cache.get(filename) + if disp is None: + frame = inspect.currentframe() + if frame is not None: + frame = inspect.currentframe().f_back # type: ignore[union-attr] + if LOG: # pragma: debugging + # @panopticon adds a frame. + frame = frame.f_back # type: ignore[union-attr] + disp = self.should_trace(filename, frame) # type: ignore[arg-type] + self.should_trace_cache[filename] = disp + + tracing_code = disp.trace + if tracing_code: + tracename = disp.source_filename + assert tracename is not None + self.lock_data() + try: + if tracename not in self.data: + self.data[tracename] = set() + finally: + self.unlock_data() + file_data = self.data[tracename] + b2l = bytes_to_lines(code) + else: + file_data = None + b2l = None + + code_info = CodeInfo( + tracing=tracing_code, + file_data=file_data, + byte_to_line=b2l, + branch_trails={}, + always_jumps={}, + ) + self.code_infos[id(code)] = code_info + self.code_objects.append(code) + + if tracing_code: + if self.stats is not None: + self.stats["start_tracing"] += 1 + events = sys.monitoring.events + with self.lock: + if self.sysmon_on: + assert sys_monitoring is not None + local_events = events.PY_RETURN | events.PY_RESUME | events.LINE + if self.trace_arcs: + assert env.PYBEHAVIOR.branch_right_left + local_events |= ( + events.BRANCH_RIGHT # type:ignore[attr-defined] + | events.BRANCH_LEFT # type:ignore[attr-defined] + ) + sys_monitoring.set_local_events(self.myid, code, local_events) + + if LOG: # pragma: debugging + if code.co_filename not in {""}: + self.filename_code_ids[f"{code.co_filename}:{code.co_name}"].add( + id(code) + ) + + return DISABLE + + @panopticon("code", "@", None) + def sysmon_py_return( + self, + code: CodeType, + instruction_offset: TOffset, + retval: object, + ) -> MonitorReturn: + """Handle sys.monitoring.events.PY_RETURN events for branch coverage.""" + if self.stats is not None: + self.stats["returns"] += 1 + code_info = self.code_infos.get(id(code)) + # code_info is not None and code_info.file_data is not None, since we + # wouldn't have enabled this event if they were. + last_line = code_info.byte_to_line.get(instruction_offset) # type: ignore + if last_line is not None: + arc = (last_line, -code.co_firstlineno) + code_info.file_data.add(arc) # type: ignore + # log(f"adding {arc=}") + return DISABLE + + @panopticon("code", "line") + def sysmon_line_lines(self, code: CodeType, line_number: TLineNo) -> MonitorReturn: + """Handle sys.monitoring.events.LINE events for line coverage.""" + if self.stats is not None: + self.stats["line_lines"] += 1 + code_info = self.code_infos.get(id(code)) + # It should be true that code_info is not None and code_info.file_data + # is not None, since we wouldn't have enabled this event if they were. + # But somehow code_info can be None here, so we have to check. + if code_info is not None and code_info.file_data is not None: + code_info.file_data.add(line_number) # type: ignore + # log(f"adding {line_number=}") + return DISABLE + + @panopticon("code", "line") + def sysmon_line_arcs(self, code: CodeType, line_number: TLineNo) -> MonitorReturn: + """Handle sys.monitoring.events.LINE events for branch coverage.""" + if self.stats is not None: + self.stats["line_arcs"] += 1 + code_info = self.code_infos[id(code)] + # code_info is not None and code_info.file_data is not None, since we + # wouldn't have enabled this event if they were. + arc = (line_number, line_number) + code_info.file_data.add(arc) # type: ignore + # log(f"adding {arc=}") + return DISABLE + + @panopticon("code", "@", "@") + def sysmon_branch_either( + self, code: CodeType, instruction_offset: TOffset, destination_offset: TOffset + ) -> MonitorReturn: + """Handle BRANCH_RIGHT and BRANCH_LEFT events.""" + if self.stats is not None: + self.stats["branches"] += 1 + code_info = self.code_infos[id(code)] + # code_info is not None and code_info.file_data is not None, since we + # wouldn't have enabled this event if they were. + if not code_info.branch_trails: + if self.stats is not None: + self.stats["branch_trails"] += 1 + multiline_map = get_multiline_map(code.co_filename) + code_info.branch_trails = branch_trails(code, multiline_map=multiline_map) + code_info.always_jumps = always_jumps(code) + # log(f"branch_trails for {code}:\n{ppformat(code_info.branch_trails)}") + added_arc = False + dest_info = code_info.branch_trails.get(instruction_offset) + + # Re-map the destination offset through always-jumps to deal with NOP etc. + dests = {destination_offset} + while (dest := code_info.always_jumps.get(destination_offset)) is not None: + destination_offset = dest + dests.add(destination_offset) + + # log(f"dest_info = {ppformat(dest_info)}") + if dest_info is not None: + for arc, offsets in dest_info.items(): + if arc is None: + continue + if dests & offsets: + code_info.file_data.add(arc) # type: ignore + # log(f"adding {arc=}") + added_arc = True + break + + if not added_arc: + # This could be an exception jumping from line to line. + assert code_info.byte_to_line is not None + l1 = code_info.byte_to_line.get(instruction_offset) + if l1 is not None: + l2 = code_info.byte_to_line.get(destination_offset) + if l2 is not None and l1 != l2: + arc = (l1, l2) + code_info.file_data.add(arc) # type: ignore + # log(f"adding unforeseen {arc=}") + + return DISABLE + + +@functools.lru_cache(maxsize=5) +def get_multiline_map(filename: str) -> dict[TLineNo, TLineNo]: + """Get a PythonParser for the given filename, cached.""" + try: + parser = PythonParser(filename=filename) + parser.parse_source() + except NotPython: + # The file was not Python. This can happen when the code object refers + # to an original non-Python source file, like a Jinja template. + # In that case, just return an empty map, which might lead to slightly + # wrong branch coverage, but we don't have any better option. + return {} + except NoSource: + # This can happen if open() in python.py fails. + return {} + return parser.multiline_map diff --git a/venv/Lib/site-packages/coverage/templite.py b/venv/Lib/site-packages/coverage/templite.py new file mode 100644 index 0000000..7c0d083 --- /dev/null +++ b/venv/Lib/site-packages/coverage/templite.py @@ -0,0 +1,318 @@ +# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 +# For details: https://github.com/coveragepy/coveragepy/blob/main/NOTICE.txt + +"""A simple Python template renderer, for a nano-subset of Django syntax. + +For a detailed discussion of this code, see this chapter from 500 Lines: +http://aosabook.org/en/500L/a-template-engine.html + +""" + +# Coincidentally named the same as http://code.activestate.com/recipes/496702/ + +from __future__ import annotations + +import re +from typing import Any, Callable, NoReturn, cast + + +class TempliteSyntaxError(ValueError): + """Raised when a template has a syntax error.""" + + pass + + +class TempliteValueError(ValueError): + """Raised when an expression won't evaluate in a template.""" + + pass + + +class CodeBuilder: + """Build source code conveniently.""" + + def __init__(self, indent: int = 0) -> None: + self.code: list[str | CodeBuilder] = [] + self.indent_level = indent + + def __str__(self) -> str: + return "".join(str(c) for c in self.code) + + def add_line(self, line: str) -> None: + """Add a line of source to the code. + + Indentation and newline will be added for you, don't provide them. + + """ + self.code.extend([" " * self.indent_level, line, "\n"]) + + def add_section(self) -> CodeBuilder: + """Add a section, a sub-CodeBuilder.""" + section = CodeBuilder(self.indent_level) + self.code.append(section) + return section + + INDENT_STEP = 4 # PEP8 says so! + + def indent(self) -> None: + """Increase the current indent for following lines.""" + self.indent_level += self.INDENT_STEP + + def dedent(self) -> None: + """Decrease the current indent for following lines.""" + self.indent_level -= self.INDENT_STEP + + def get_globals(self) -> dict[str, Any]: + """Execute the code, and return a dict of globals it defines.""" + # A check that the caller really finished all the blocks they started. + assert self.indent_level == 0 + # Get the Python source as a single string. + python_source = str(self) + # Execute the source, defining globals, and return them. + global_namespace: dict[str, Any] = {} + exec(python_source, global_namespace) + return global_namespace + + +class Templite: + """A simple template renderer, for a nano-subset of Django syntax. + + Supported constructs are extended variable access:: + + {{var.modifier.modifier|filter|filter}} + + loops:: + + {% for var in list %}...{% endfor %} + + and ifs:: + + {% if var %}...{% endif %} + + if-else:: + + {% if var %}...{% else %}...{% endif %} + + Comments are within curly-hash markers:: + + {# This will be ignored #} + + Lines between `{% joined %}` and `{% endjoined %}` will have lines stripped + and joined. Be careful, this could join words together! + + Any of these constructs can have a hyphen at the end (`-}}`, `-%}`, `-#}`), + which will collapse the white space following the tag. + + Construct a Templite with the template text, then use `render` against a + dictionary context to create a finished string:: + + templite = Templite(''' +

Hello {{name|upper}}!

+ {% for topic in topics %} +

You are interested in {{topic}}.

+ {% endif %} + ''', + {"upper": str.upper}, + ) + text = templite.render({ + "name": "Ned", + "topics": ["Python", "Geometry", "Juggling"], + }) + + """ + + def __init__(self, text: str, *contexts: dict[str, Any]) -> None: + """Construct a Templite with the given `text`. + + `contexts` are dictionaries of values to use for future renderings. + These are good for filters and global values. + + """ + self.context = {} + for context in contexts: + self.context.update(context) + + self.all_vars: set[str] = set() + self.loop_vars: set[str] = set() + + # We construct a function in source form, then compile it and hold onto + # it, and execute it to render the template. + code = CodeBuilder() + + code.add_line("def render_function(context, do_dots):") + code.indent() + vars_code = code.add_section() + code.add_line("result = []") + code.add_line("append_result = result.append") + code.add_line("extend_result = result.extend") + code.add_line("to_str = str") + + buffered: list[str] = [] + + def flush_output() -> None: + """Force `buffered` to the code builder.""" + if len(buffered) == 1: + code.add_line("append_result(%s)" % buffered[0]) + elif len(buffered) > 1: + code.add_line("extend_result([%s])" % ", ".join(buffered)) + del buffered[:] + + ops_stack = [] + + # Split the text to form a list of tokens. + tokens = re.split(r"(?s)({{.*?}}|{%.*?%}|{#.*?#})", text) + + squash = in_joined = False + + for token in tokens: + if token.startswith("{"): + start, end = 2, -2 + squash = (token[-3] == "-") # fmt: skip + if squash: + end = -3 + + if token.startswith("{#"): + # Comment: ignore it and move on. + continue + elif token.startswith("{{"): + # An expression to evaluate. + expr = self._expr_code(token[start:end].strip()) + buffered.append("to_str(%s)" % expr) + else: + # token.startswith("{%") + # Action tag: split into words and parse further. + flush_output() + + words = token[start:end].strip().split() + if words[0] == "if": + # An if statement: evaluate the expression to determine if. + if len(words) != 2: + self._syntax_error("Don't understand if", token) + ops_stack.append("if") + code.add_line("if %s:" % self._expr_code(words[1])) + code.indent() + elif words[0] == "else": + if len(words) != 1: + self._syntax_error("Don't understand else", token) + if not ops_stack or ops_stack[-1] != "if": + self._syntax_error("Mismatched else", token) + code.dedent() + code.add_line("else:") + code.indent() + elif words[0] == "for": + # A loop: iterate over expression result. + if len(words) != 4 or words[2] != "in": + self._syntax_error("Don't understand for", token) + ops_stack.append("for") + self._variable(words[1], self.loop_vars) + code.add_line( + f"for c_{words[1]} in {self._expr_code(words[3])}:", + ) + code.indent() + elif words[0] == "joined": + ops_stack.append("joined") + in_joined = True + elif words[0].startswith("end"): + # Endsomething. Pop the ops stack. + if len(words) != 1: + self._syntax_error("Don't understand end", token) + end_what = words[0][3:] + if not ops_stack: + self._syntax_error("Too many ends", token) + start_what = ops_stack.pop() + if start_what != end_what: + self._syntax_error("Mismatched end tag", end_what) + if end_what == "joined": + in_joined = False + else: + code.dedent() + else: + self._syntax_error("Don't understand tag", words[0]) + else: + # Literal content. If it isn't empty, output it. + if in_joined: + token = re.sub(r"\s*\n\s*", "", token.strip()) + elif squash: + token = token.lstrip() + if token: + buffered.append(repr(token)) + + if ops_stack: + self._syntax_error("Unmatched action tag", ops_stack[-1]) + + flush_output() + + for var_name in self.all_vars - self.loop_vars: + vars_code.add_line(f"c_{var_name} = context[{var_name!r}]") + + code.add_line("return ''.join(result)") + code.dedent() + self._render_function = cast( + Callable[ + [dict[str, Any], Callable[..., Any]], + str, + ], + code.get_globals()["render_function"], + ) + + def _expr_code(self, expr: str) -> str: + """Generate a Python expression for `expr`.""" + if "|" in expr: + pipes = expr.split("|") + code = self._expr_code(pipes[0]) + for func in pipes[1:]: + self._variable(func, self.all_vars) + code = f"c_{func}({code})" + elif "." in expr: + dots = expr.split(".") + code = self._expr_code(dots[0]) + args = ", ".join(repr(d) for d in dots[1:]) + code = f"do_dots({code}, {args})" + else: + self._variable(expr, self.all_vars) + code = "c_%s" % expr + return code + + def _syntax_error(self, msg: str, thing: Any) -> NoReturn: + """Raise a syntax error using `msg`, and showing `thing`.""" + raise TempliteSyntaxError(f"{msg}: {thing!r}") + + def _variable(self, name: str, vars_set: set[str]) -> None: + """Track that `name` is used as a variable. + + Adds the name to `vars_set`, a set of variable names. + + Raises an syntax error if `name` is not a valid name. + + """ + if not re.match(r"[_a-zA-Z][_a-zA-Z0-9]*$", name): + self._syntax_error("Not a valid name", name) + vars_set.add(name) + + def render(self, context: dict[str, Any] | None = None) -> str: + """Render this template by applying it to `context`. + + `context` is a dictionary of values to use in this rendering. + + """ + # Make the complete context we'll use. + render_context = dict(self.context) + if context: + render_context.update(context) + return self._render_function(render_context, self._do_dots) + + def _do_dots(self, value: Any, *dots: str) -> Any: + """Evaluate dotted expressions at run-time.""" + for dot in dots: + try: + value = getattr(value, dot) + except AttributeError: + try: + value = value[dot] + except (TypeError, KeyError) as exc: + raise TempliteValueError( + f"Couldn't evaluate {value!r}.{dot}", + ) from exc + if callable(value): + value = value() + return value diff --git a/venv/Lib/site-packages/coverage/tomlconfig.py b/venv/Lib/site-packages/coverage/tomlconfig.py new file mode 100644 index 0000000..2a1f7df --- /dev/null +++ b/venv/Lib/site-packages/coverage/tomlconfig.py @@ -0,0 +1,210 @@ +# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 +# For details: https://github.com/coveragepy/coveragepy/blob/main/NOTICE.txt + +"""TOML configuration support for coverage.py""" + +from __future__ import annotations + +import os +import re +from collections.abc import Iterable +from typing import Any, Callable, TypeVar + +from coverage import config, env +from coverage.exceptions import ConfigError +from coverage.misc import import_third_party, isolate_module, substitute_variables +from coverage.types import TConfigSectionOut, TConfigValueOut + +os = isolate_module(os) + +if env.PYVERSION >= (3, 11, 0, "alpha", 7): + import tomllib # pylint: disable=import-error + + has_tomllib = True +else: + # TOML support on Python 3.10 and below is an install-time extra option. + tomllib, has_tomllib = import_third_party("tomli") + + +class TomlDecodeError(Exception): + """An exception class that exists even when toml isn't installed.""" + + pass + + +TWant = TypeVar("TWant") + + +class TomlConfigParser: + """TOML file reading with the interface of HandyConfigParser.""" + + # This class has the same interface as config.HandyConfigParser, no + # need for docstrings. + # pylint: disable=missing-function-docstring + + def __init__(self, our_file: bool) -> None: + self.our_file = our_file + self.data: dict[str, Any] = {} + + def read(self, filenames: Iterable[str]) -> list[str]: + # RawConfigParser takes a filename or list of filenames, but we only + # ever call this with a single filename. + assert isinstance(filenames, (bytes, str, os.PathLike)) + filename = os.fspath(filenames) + + try: + with open(filename, encoding="utf-8") as fp: + toml_text = fp.read() + except OSError: + return [] + if has_tomllib: + try: + self.data = tomllib.loads(toml_text) + except tomllib.TOMLDecodeError as err: + raise TomlDecodeError(str(err)) from err + return [filename] + else: + has_toml = re.search(r"^\[tool\.coverage(\.|])", toml_text, flags=re.MULTILINE) + if self.our_file or has_toml: + # Looks like they meant to read TOML, but we can't read it. + msg = "Can't read {!r} without TOML support. Install with [toml] extra" + raise ConfigError(msg.format(filename)) + return [] + + def _get_section(self, section: str) -> tuple[str | None, TConfigSectionOut | None]: + """Get a section from the data. + + Arguments: + section (str): A section name, which can be dotted. + + Returns: + name (str): the actual name of the section that was found, if any, + or None. + data (str): the dict of data in the section, or None if not found. + + """ + prefixes = ["tool.coverage."] + for prefix in prefixes: + real_section = prefix + section + parts = real_section.split(".") + try: + data = self.data[parts[0]] + for part in parts[1:]: + data = data[part] + except KeyError: + continue + break + else: + return None, None + return real_section, data + + def _get(self, section: str, option: str) -> tuple[str, TConfigValueOut]: + """Like .get, but returns the real section name and the value.""" + name, data = self._get_section(section) + if data is None: + raise ConfigError(f"No section: {section!r}") + assert name is not None + try: + value = data[option] + except KeyError: + raise ConfigError(f"No option {option!r} in section: {name!r}") from None + return name, value + + def _get_single(self, section: str, option: str) -> Any: + """Get a single-valued option. + + Performs environment substitution if the value is a string. Other types + will be converted later as needed. + """ + name, value = self._get(section, option) + if isinstance(value, str): + value = substitute_variables(value, os.environ) + return name, value + + def has_option(self, section: str, option: str) -> bool: + _, data = self._get_section(section) + if data is None: + return False + return option in data + + def real_section(self, section: str) -> str | None: + name, _ = self._get_section(section) + return name + + def has_section(self, section: str) -> bool: + name, _ = self._get_section(section) + return bool(name) + + def options(self, section: str) -> list[str]: + _, data = self._get_section(section) + if data is None: + raise ConfigError(f"No section: {section!r}") + return list(data.keys()) + + def get_section(self, section: str) -> TConfigSectionOut: + _, data = self._get_section(section) + return data or {} + + def get(self, section: str, option: str) -> Any: + _, value = self._get_single(section, option) + return value + + def _check_type( + self, + section: str, + option: str, + value: Any, + type_: type[TWant], + converter: Callable[[Any], TWant] | None, + type_desc: str, + ) -> TWant: + """Check that `value` has the type we want, converting if needed. + + Returns the resulting value of the desired type. + """ + if isinstance(value, type_): + return value + if isinstance(value, str) and converter is not None: + try: + return converter(value) + except Exception as e: + raise ValueError( + f"Option [{section}]{option} couldn't convert to {type_desc}: {value!r}", + ) from e + raise ValueError( + f"Option [{section}]{option} is not {type_desc}: {value!r}", + ) + + def getboolean(self, section: str, option: str) -> bool: + name, value = self._get_single(section, option) + bool_strings = {"true": True, "false": False} + return self._check_type(name, option, value, bool, bool_strings.__getitem__, "a boolean") + + def getfile(self, section: str, option: str) -> str: + _, value = self._get_single(section, option) + return config.process_file_value(value) + + def _get_list(self, section: str, option: str) -> tuple[str, list[str]]: + """Get a list of strings, substituting environment variables in the elements.""" + name, values = self._get(section, option) + values = self._check_type(name, option, values, list, None, "a list") + values = [substitute_variables(value, os.environ) for value in values] + return name, values + + def getlist(self, section: str, option: str) -> list[str]: + _, values = self._get_list(section, option) + return values + + def getregexlist(self, section: str, option: str) -> list[str]: + name, values = self._get_list(section, option) + return config.process_regexlist(name, option, values) + + def getint(self, section: str, option: str) -> int: + name, value = self._get_single(section, option) + return self._check_type(name, option, value, int, int, "an integer") + + def getfloat(self, section: str, option: str) -> float: + name, value = self._get_single(section, option) + if isinstance(value, int): + value = float(value) + return self._check_type(name, option, value, float, float, "a float") diff --git a/venv/Lib/site-packages/coverage/tracer.cp312-win_amd64.pyd b/venv/Lib/site-packages/coverage/tracer.cp312-win_amd64.pyd new file mode 100644 index 0000000000000000000000000000000000000000..f8103b899322378e3e4d286121f6545ccf2c354b GIT binary patch literal 22528 zcmeHveRx#WwfCMRlMEkm0%kB;l|jNa7KnjCO^lHlN$`ve5FsFKfypqLkkMpjI&+2u zn`&?pWjN#&t+x1RD-A+`*V{{5s}XyzgCGgw2jaD&+^3?~+QIk{t%YFK^ZwR8`y`VA zdhPqXecnIX;W@MRUSE6dwb%YyXTtpNZep2?u`E2Q%2*E|eeC@EuYctk7&~v`p7Yof zqh6ZSV{pARsiwgnv4+CI)nQMQ)$0iag0gj$&l-*ftp0$ta^YfYQ?SlgoSQqkP^cdN zXsB!H*Oz{(KkmCbKV5|StIZP!Y5=diQ-iNIUoxFyKRsjBD?L>}W`t`(5 zb5MW2dBOn6Ki_=mz%3lU^Qi_e=JiE>Zv(Y;uAWs_F;;hXHj8}!V!zHaz%H|n$vAHs z`xjtlY1mm;0Os*>8{D9kGZ@R}sA0&q071^hCxg|3RTI*|nRN|gznQV+ka+~Kg@oG} z8zdNFtbhhg|7>FHKT$q@6Jr z{9|RTwm4kpkv-tM2q-%qGUD5KQa*N2iZwQN^gIx1@r=eZ7f;H^&RC*2q%rb7qOZKo z>J%mv!DlZHN5Wo|xlh_qpuW`LN%`10pRX|pM)DDZuQ=aQstkYZjNLF?`^PHT7fZ1Z zt&;Mlq&V}Wj)U^JSQ%>_E5)ubM6*>(#Z1N|WnRIVl*t;!dM0I*MkzXz^2rRYt>jF~ z>l&r>Ov*DF#ZHt9$hPs^Hdwgd`aMaV@-We|23xM7PWx8xZwUXRghL z1sxZ}nHph_JHbNf5Q)tOt&i6)JBpHAGb5wGCH{t_Ex}xGJ`}e)YQ(4RRo6A)(xrs9#=aBJh6)FA<0^|pxId{8KOT%vr|!0Eyj_} z_8LpDn9=fMOQZ}I#tRY;EkXP0l>4t_jOxZRuxf)z^mt8So-1BiSm27g3ayKm-hy~7 zk(4i_PPwoa1Dt#TovX4Pl;U!s)ur@G%4?EhQZ3&qhSu2r68B0G85A9)f88cB<47;I zJ9s5tZHiZCN%3{G@kxs1MG}kOw;nYmgZz{c&#)zi%+cbjL~*pxl!><%F5Bcn!$lnl zd9>|_?VxIzN(z*DXPHZ%YLv6gr8hLnS?1EeYm~FhrL7v}EORNYQO+`#f*OU-rIGV| ziAJeDbN-cUlx1g9CTWz~GbuS5rQuAiqR) zNS8hH0KV^-Zae8zvL$6~_L)NKAf)z6u>;m}&fNOCQ#ntye3x2@J+lMi%-kNwca`mA zW!bNvAblYz-dHHPFtFAtn`)j2WB)gC+Ei!eCuKj(x?`&Cq@+}t&fcPVxWyl4wZ0)K z^AU(2Ok>QU9&pBb^D4`EE3La+3L20UGzDjne$}!MY^ZEO#ibykB(N-#P&&()baUQ0 zu;P%UY@%(QA|0#YyO=M_3r#dfp#HSW`+{@t>Ddy+HlKd3$$btbaq6hUEF~mwpYoiv z_nrJxN6pf6(jLx(T~yjEW%fChz0#QHsyY%*vlFF0)pBSm*>@DHrd%c|_a?wDDTgIx zlxkTIk}|kgofs=;t!61U)es#8(^QK}1kA|gg(WoW-TQcPXgP9YT9LhZ_E!{NK z3XETa6NwbRhlI*(BTQUQf@Mz;#oWG>bSn#sY)4efWD-#Z_%3lh$KOj#=(B1ua~k== z-0>SAu}=cy@;*f|hT-v21B;|Li<@X>%cs&h_dJjn{_w2@Z885fm z;9!c1%FdS2PVXDi-gmQ9%l)LXBN0VN-HT%Xo#+r}ia$%DlEQ0FDi)LYqS z0-gc#bDPt9K-zmeOUmSY{WG~^9>g)WQW+?JJ~~FV45C6UOi5$S5sM}oB!g`qNgXHU z{Mhw;u#5R%qs!yDeaApZi-Oan7BL_x&r`@(Q&2CXcyPEKD=7w2KidASTH1?9=xD5z zZ}>%q=vZ#=zj9ssNLpLg`TnqYsiVfRM1+oHJK5V_NQ*oLO{Ma2rSg8I^0iC(jCPBS zU#Tj2K52x0Omn-WJVTw5;*S@$kxY(qV^&#C>rIFQQ)ffI#HNdCL?J(OlJ% zi<)Xa@gQmOF3hqW!9+4TdfI6IWWJnSh10MUZ#LOZ#)Das65}l>^Qt9p9%+*@t4&h; zM+vltt^GUH)$%@b`!1@N#nuCuJAR69M6r{?<<#ZGI7soUWz|M`XuFzUi-xeW0dACLws_LeO%*VghyqWwRMIRgP*6W76}^qCld;j z87NHVS5(W)VZHIt#r>J_@Wlu=)$$f@1~6^}JEmMFXKxOnJv)#2cQrn8prl$hk^wY- z7NK?8FR_KsR}OGaQ%ZMj8riPToZmtVjo&M{1NDzm_)#V!o&z_IC#mv53Y6U6gUhLW z-k(RaSyE16b1=6*0uk)+56_=*+Hp>s&S}OUP;-jqktrml>6VlQ zc5cyVVj)vL;R9ot2tD(amtD&9d0|K0{go&w&|`O1FY*b0qzoTh!W7GIf; zIF1%|w#xXACi9$X* z<5FI~eTa=~8F@bYO^TXcG(}341P8_oi57Rj(^@m}7L$^x9bGe>$^xr1zS4T;>BtKy z%-^K)1x~@VN8U7x^Fdqdj7h{4osg^pn<7<;Hcq`6mkMZXA0``A%g?|c zpHm>#U7+BjvuqKcfNg$mhwXm zt}@SpK4Q+CRnSM{m;;#b7}lF|AM4&410V440mE0lCvBp%#2NL~X;94qLQP(*vr z(AnZi5UM&((!tnjUS9+=75GGP71s8jFVUiW2^r9bYho>)>e!%aZnuD|e6RUl>T>MP zRwiF4d{Ie6Zs10O4gpF*b~Q=LzRlX5>9lSI-0x(6Mz=}PR3140K{4@ z9zLIpNGW~58_e|*i1VAEV$fDA1C-Np$Y&m%Qq)VqgCHwSw|+ar6ik}PPPC~~GnXX`sV(MaIYUfYf@1H@~sGG2Hj@Rz>i(v~NOdLFW z;COb=x;C;E=3V4~v*cVyY|s$BAo=aDRkeR?a`Kk|#^mxplWSTsY{!(8Q|s`F*N$T= zZfh;`ahJk8StcQL?5@t-4=ZU}za@3H7t(wrbSZK*+YofvQGam>Q9mVYiBma3lGRRJ z22n4<@mw)}1qP~FF@I6S;Ix0i)jYwsAmz_e*;O8_UFPObp8mr zIx8x`<5b?RuQyL@2Z0*j$PH30cX1ms=@J-ASY1;S=83S`{A9rm<|oIpsc%MeBsI}- zF#1v1w<+?z&CijTsvnR&$7Hjl_TpAtHcfSQUaUsDB}HpnQZ!rWiX3t%bCGDNDXgY} z7^@A$3&=3{-^_?3jqwzK}Xsq_qw-InnVC7Ys<_IjuCamUH%^Ei6re%X%;gKAlVM$e*3yT9u9ZRAj2 zg~ZcbVltPQCp}BCLfh|o*f+jKLm_7=z5Pfk#pkr%^MjV5ty)5W4BwRX2(cyZpDk4rEZNCfb73R$R48PD?E(`C3y<6g2ium03t zRgD_uLi%ytf>U_lBgL`?k0Im@0olNT;av(hTt&;K?qS>&Zfk)&k0l-yz{bEvPX#w8A|AE(H{m{j zx^ff=o|2AZ=JsEJ84gq4pjo#Ka|9$TU$iv+D3wH)B8?PN4biKiZzjZSM@Z~rcpE26 z1XS`6mRYnpO>0v=Pv=oBJ1>OqRM{9)Go#lHwP;Iek340tFY6W%r|?S{~umzCEL|7|kXAienSuko`7cC`zBc0M0^w1UM`uIs8QW z(iI5w`)6b9cY-M43{r}h9Bb_;V`spzG=VMAIEw0uaTS5M@wMwUPCLqPK(yERufR~V zy~b{oD0q8~zZ9hxZmOVEtkNs_%;28#S)ypsFNtALNYvaC81`+4qv=|~8!FMx$A ze6R^UE8Pb_@N7Op=S|h}0}G^;Uk^g54f*_p)LBFMeH=CYL_uG#qn)6Zj+6~ZZWWe- z6Q1rP&)}|ifCwR~6yYe0VZ+vU4J6gxn3~c03mQCn6+n~%9PmD*sRnO+ISb04nc4OB z^oJ-DSiUdXEOpM$lX|Q2NWW@1mQQw7VLDXdVl)LBaeSOX<#AfMbAEwVKMhW$ssQ2x zNVHic8_Kq-0m-&kdYTRs^m<*IjEZfzHOXAL7Xz?{tU$0-OQ#N_+D%nV`4#(KgqcMY zSU8YX%%-8ID|k0r8eW0v9g6P;NT^eO3BZHWQN{L5S`6h%Ltsjcz54gIHR z-6FkmZhs39-KzqT~2_ zAt^ZcB6G2ZLJZNh3|9&q=J~9pLm{TF9VTPyc0rc+bv0Ka=fTNVEYj4W6F&tmxl7Y- zOg_k2;aJhnPEkM6&I{O24@Pqi7~iyGk|kTP+9huTLG(F4xl*f)f`=BP*#E&$2eoyh ztzru#net|IHBLLtwNplHgjE|rO&TGIu9B7aQ~l2xPcgh|t2YOY7V2=U zPyLhmPUCmLfS_9iKvjaM-Ue{Mcon`4+yESVpN0~a{2E_;+_C-b?V0~w#_G$Azj+l0 zhZf13n?wPd>ox+JFRUx0-%~{0J!3M6%~24oaZP>B{M60)QtY_`Df3nHQ;uw@b8MTd zbLZe*FM1h{!lA_@| zJC76j%$0AS)Y+LN60LoF^+CZ(d&@Okdk5wJwVQangIXe6IMUHWsZMQs=vYst?u5aT zqDf$PuBFriLd6bPLpuWQlhCOg#S%+*4!UC4rA{nzC`hX1cR2A;OLn}^LJ<*|i^(RN zI;G6y9=>5eQg|57lGg1fpV9h8zT`7i>b%5Fx0cR|WdQx-aiXIBW4Svz4(QlElT`k8 z_=Gk)X-;bUur6!UWT&OcK5`D(=McoyHrDl_cTbqFdA5Y@pt)<^rvcg5xb8E5z~>zE zs*>c3?YckR=nF*0yw)kH<}T%Qbmf$R*{WIUHRfLl&$PkDUgJ0vX{E17WE+@VR8gCq z!Q@F5Ap#c}cc1srT624_EV54N3v*Yc za-^pamGWdO7phe7I?Z@XF*)@3*S*G@Q6e*Xjq^~#B&DfeilTYXAM+ygsNiv z!*s@ebv)K*>NOrgk)}x8Nbi0fC-++ARk1#6ukmSA5g;Huh0@dDIt;rXMY(eFL0I(_ z%)F0kjnIofG^`*k@~qy)ZZ8M&je%wKVHCT0xl5nRsnTg zdxYGJy1XFl7X80pz~2ejCSX9o z@KynD60lUjLIKAMIJiV>=Y0X+67U59dj#Af;6?!h0xlQuCIM#(m@gm`aG*x?N5BID zJ}Ka10{&RQ=Y${Di@I<#8!TaMwBhK`I!mkD)1c1(7n?@c>;JD{qCwMJ>mR9~Ht*C= zFzjFL4|p2g_5Ma*z|-Ut2H?ld!GPa;1|brRhP}RFZaM7nl1$j=i39^U1VfFRBg0%% zgD2vyTMNa0@30Uk-ryQv*t6PKJZCN>D*cgAFyfc}!9dO05a~-tTZ5}^^?Bt8S4#96 zYB+obpH3grFKn6XiO5zRY1NR}<(XT@xkHUnctV#?$Ge4Z^uDC~p%F(ZcOcrd$`^Kr zJ%QD_ADexi+rqwjb_?J<-`YyQmpbVQuN4hdg~P$HHR$z5!(m^YwYdS_u-5q_o>kDe z+Db-E_Xn;Wt=n}*JJzNsdKUJ{(Qv>zbGjT2HB#Fcxki%lwRn9YY7%9VC7i|#9=Fa# zs%O9{MwxaeQSIH8PF46%@$6=zgGXX+As_&iZleHjdfZCP+N1O0p4T$KE4j& zBvYrF8+>wuFKm^A+CW%6Rtg&$4(4s}d2e(31K!4H9kuyC$P30{4b7e~AHyNJdbrmg zi9~tBAOc3vk~bKD%iaDucRegjX@~ghe6laxaAR^I5nR=>yc*MIsVXv4$i}^TNo6#gqBr8#-S<&}fDV!)$BK$rmltyGv zSjLi-D;a;k6^IS9>0n*Fl3VOU}$KPQp^`s=XN03BIl%8W^czguF)ls~WT^=o?xUM#vA(PmdTb-Yj>6@b_YJm=$3z z)*3=bJijN_Brk_Io2%&#GW_SRE|a^>`3=Yy7gloG{4) ze_(R?@OLS`fYlRN3omIbd^`~sXh}ygwutK75FKZ&Z}hCj{Lt5DZHDXZiFJ2qIUcMO z`%9Gu#|oG);Kc$K30NwiuFp=J<=^8|J96LCn+V;Y;oAh%@k8+MWzSFpX@z-bx3fjq zw_dp$a{$KWThlmQsS-0tk{9NiRyC}A>BF0fg;;I630)>JtJ^V&X*X8 z<9vN1aMhge@JJlzJ30ba!}*Q_hpq}+YB=8j;b5TtcSVfGig@_n85OPxHp)$PsM8qI zSPJ~@x8Gj3iY9$>zUW_A_7*xFyq$p&xbO0I22*i08XsGbB3q;JnSd+JL_00oVypjM zR04z$MZsl^K;nw zcbNxrkB>P#Dv?v0T>`!NSuDRM54M?D0#5%D&*P%K)ViiWN7HZ0Vx~n|q0CzJjoN*W z>@~8siAd>KF(0C9hvmce{F$)Pl*vr*W*yI{hHa({W~w`% zwP6kU7*F*=;4a8!7kqvm%aik&Y1Vj-H)5_YCmMW2e5v&jvXr1p(`SIpIE#r{dx}-d54)Ym`S74b!PU8g-p+$TG038^Ci>HoIt+nOzi~a6CVBel6)IyB*-Y z6l>@P=p!iNt=eGDW@hxojJ}v>jtLp?Gtp72K&NpFYvX3f&0@K;Mzh>S+0~<{oy)L} z3B9w689Rt40lfqb83snDEOoprM)+(Gvlx0<_7D%ovj*ci%gFM_=Y^=RWG{Vm zsMWM@x-U{GjJ1I1`qO}VBc2eRw(-cw0^$d!{9;z@4cTVQoZjpYxIImESI;UAt*v9# zYn=hVJaj#@E+&5M^B(TgCWp3;=(uH+!?0tnV{OqtJAi8g&Xgj%5i8yRaETPY$AQ}c z+_^sg%nNCr`Db28^TW&~8vJ|Zg|5hUSFd$f`fx1|df8tL)obVbn%r~4J|Et=VPYT; z;9d{}E=Z|?j0}ywNbuj1S-qALBW}E*@rC`~1-@oBfp~&-QRG`3b#)7QdIa}cofD#U z&@_ICtI}8Rrs>C6A9$7^xr0Ad2L@N&z+Buy7t3LOuh08ez{1D;J7f z1!MmOPFFCn8r=vsQGxw;DrQL_;$I!`)e(hL71*NLiGIiqSq@|EfW%zNFA&OwYv%%< zLt)FfzZL`oKG+rY!s<0coLY+L574$4cNuh^9T&EtF1is*>7yg!SUkt+6qGWqd9J51 zqN@%LX(k(xDDvZd2leCLHy###G;OgT6n^LoI1`4|#DF9~~0a$9?B8|ByEhz%H6G)d*Rc zh1R0$0c6G^CGPRY0bhb^%eA`onV6)3_vn z?Fxt8FN{k94W2+*lC0ytdjGMk=bw6Y!!JQJSU9ai(@>9ji*g)-)gw zTL&b&FcX$6UK>H=*7%!zjtHe$sl*%xWLuG5^l8j4|Ee(3ku($$_E09R5PnZ1qHwVb zxd#dA*uTNrO5dvJ>earmZsokHMGLB2HqGU@ak<@d+>wya>#s+mS92b`H3j&HjC3Ha zUH(9Q&|T+?$l>5xcO#TDc0FtIHAQ?VEuFJy$$}c^{HhtVO878hId&sYfmO&KT%{%H zGBs`fAks=rO~HVBO?`+0pdL3zX!px-QWLvoxa{_^*~}gBHBjmqL^s7p0p)xH_5RiH zIkLLQzcwQ;wt5X-@@kS=_jQslu*M$_2AX*4bJhqBEKV(SoFQj2b{Rto)#r`MKKBU1 z1hhwd$eV`z)0-l6CYp|ZO^0Lr!Rd`Nrq7tJbvccW|5ZfBfTeO=NB;7L5V!yQd@~Q| zzkK^<+-GvVw8DC%{y()hCPdZWozNLLgR`6Xw_YWD>LBl1@;;J|&|%)$^FccK z?*{2YginvKORN3u-_}+CpX~qJP300+(O;w>_$N<59n2K;(lkEGf24kNzUnkSYbq1U zI$v!XAFhfcK004R8eho>u3>$lG``Yv@U^A!+0Vh3hCXLin*%ugh`2>6>92myH6Kcy zTHG8?lSU4u?OZZxtly}Le8V@>C+0)7uWbIpe~LEoLmmp&g7fyQO`i`%g1#(`FMU4L3i|pqzV!L9 zSrc(+Q=Y#KPc`@n?!eQ4GQn5yw2*$lJ0{@0 z2k`*jg+=iJls5tX77y8wnzN@|{(F}K2Ry+q@Ek>%Ax|I0lSDZ+KX0%y_Ank?@;U55 zxd3H?&660LhH@L=dC0L}hjJcZ7!S3ZnrBbV!BZ}t-v5QbPcVsRBgzE-gy#{I30^$~ zenFYw^>_%6InOrZq5jhUpLZ#q1n~6kFN9}#wFd7--h4ms>jB4LVK)Ik7Lfjr!8()) zejm>RD9eC5@Q}>}e}H$7j{)BbIDqGIlnFjM75zqe3*bIH)K(HO6YnUgoCR1R%633m zlnKT~c_ZKxqD=6hDAR9Jr%h+V6Q@S0-HX8LXMVLbc6Pw;br zCwR$~8qX}iJ4@g*%9{iJ77zK5AbvN-%U=TCXTunR2fra>m*FWw8NVar??p>crgy6R z-9@zq`FjbJ3DP?UhB85VPp}bXf=BVZhw?w>$AH^E<_z3Evj2F00z}DK@T0Pj3k0ne z68DW1;GzYhp?wRE+0t?$~@wWVvYi*2pl*|0OT zvt?)7&h?bhw~?b7y!?V;_Pwr}3vvpuo>`1a&>Q+Hl>Nq22`Lw8GeTlf0z&D~qN pcXao3C%XH(k9HsLPIeD=OFOD}H0)TvW7Ce!JN}_Z^Kb6_{{d~0dN}|9 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/coverage/tracer.pyi b/venv/Lib/site-packages/coverage/tracer.pyi new file mode 100644 index 0000000..c45bf39 --- /dev/null +++ b/venv/Lib/site-packages/coverage/tracer.pyi @@ -0,0 +1,43 @@ +# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 +# For details: https://github.com/coveragepy/coveragepy/blob/main/NOTICE.txt + +"""Typing information for the constructs from our .c files.""" + +from typing import Any, Dict + +from coverage.types import TFileDisposition, TTraceData, TTraceFn, Tracer + +class CFileDisposition(TFileDisposition): + """CFileDisposition is in ctracer/filedisp.c""" + + canonical_filename: Any + file_tracer: Any + has_dynamic_filename: Any + original_filename: Any + reason: Any + source_filename: Any + trace: Any + def __init__(self) -> None: ... + +class CTracer(Tracer): + """CTracer is in ctracer/tracer.c""" + + check_include: Any + concur_id_func: Any + data: TTraceData + disable_plugin: Any + file_tracers: Any + should_start_context: Any + should_trace: Any + should_trace_cache: Any + switch_context: Any + lock_data: Any + unlock_data: Any + trace_arcs: Any + warn: Any + def __init__(self) -> None: ... + def activity(self) -> bool: ... + def get_stats(self) -> Dict[str, int]: ... + def reset_activity(self) -> Any: ... + def start(self) -> TTraceFn: ... + def stop(self) -> None: ... diff --git a/venv/Lib/site-packages/coverage/types.py b/venv/Lib/site-packages/coverage/types.py new file mode 100644 index 0000000..8298e89 --- /dev/null +++ b/venv/Lib/site-packages/coverage/types.py @@ -0,0 +1,206 @@ +# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 +# For details: https://github.com/coveragepy/coveragepy/blob/main/NOTICE.txt + +""" +Types for use throughout coverage.py. +""" + +from __future__ import annotations + +import os +import pathlib +from collections.abc import Iterable, Mapping +from types import FrameType, ModuleType +from typing import TYPE_CHECKING, Any, Callable, Optional, Protocol + +if TYPE_CHECKING: + from coverage.plugin import FileTracer + + +AnyCallable = Callable[..., Any] + +## File paths + +# For arguments that are file paths: +FilePath = str | os.PathLike[str] +# For testing FilePath arguments +FilePathClasses = [str, pathlib.Path] +FilePathType = type[str] | type[pathlib.Path] + +## Python tracing + + +class TTraceFn(Protocol): + """A Python trace function.""" + + def __call__( + self, + frame: FrameType, + event: str, + arg: Any, + lineno: TLineNo | None = None, # Our own twist, see collector.py + ) -> TTraceFn | None: ... + + +## Coverage.py tracing + +# Line numbers are pervasive enough that they deserve their own type. +TLineNo = int + +# Bytecode offsets are pervasive enough that they deserve their own type. +TOffset = int + +TArc = tuple[TLineNo, TLineNo] + + +class TFileDisposition(Protocol): + """A simple value type for recording what to do with a file.""" + + original_filename: str + canonical_filename: str + source_filename: str | None + trace: bool + reason: str + file_tracer: FileTracer | None + has_dynamic_filename: bool + + +# When collecting data, we use a dictionary with a few possible shapes. The +# keys are always file names. +# - If measuring line coverage, the values are sets of line numbers. +# - If measuring arcs in the Python tracer, the values are sets of arcs (pairs +# of line numbers). +# - If measuring arcs in the C tracer, the values are sets of packed arcs (two +# line numbers combined into one integer). + +TTraceFileData = set[TLineNo] | set[TArc] | set[int] + +TTraceData = dict[str, TTraceFileData] + +# Functions passed into collectors. +TShouldTraceFn = Callable[[str, FrameType], TFileDisposition] +TCheckIncludeFn = Callable[[str, FrameType], bool] +TShouldStartContextFn = Callable[[FrameType], str | None] + + +class Tracer(Protocol): + """Anything that can report on Python execution.""" + + data: TTraceData + trace_arcs: bool + should_trace: TShouldTraceFn + should_trace_cache: Mapping[str, TFileDisposition | None] + should_start_context: TShouldStartContextFn | None + switch_context: Callable[[str | None], None] | None + lock_data: Callable[[], None] + unlock_data: Callable[[], None] + warn: TWarnFn + + def __init__(self) -> None: ... + + def start(self) -> TTraceFn | None: + """Start this tracer, return a trace function if based on sys.settrace.""" + + def stop(self) -> None: + """Stop this tracer.""" + + def activity(self) -> bool: + """Has there been any activity?""" + + def reset_activity(self) -> None: + """Reset the activity() flag.""" + + def get_stats(self) -> dict[str, int] | None: + """Return a dictionary of statistics, or None.""" + + +## Coverage + +# Many places use kwargs as Coverage kwargs. +TCovKwargs = Any + + +## Configuration + +# One value read from a config file. +TConfigValueIn = Optional[bool | int | float | str | Iterable[str] | Mapping[str, Iterable[str]]] +TConfigValueOut = Optional[bool | int | float | str | list[str] | dict[str, list[str]]] +# An entire config section, mapping option names to values. +TConfigSectionIn = Mapping[str, TConfigValueIn] +TConfigSectionOut = Mapping[str, TConfigValueOut] + + +class TConfigurable(Protocol): + """Something that can proxy to the coverage configuration settings.""" + + def get_option(self, option_name: str) -> TConfigValueOut | None: + """Get an option from the configuration. + + `option_name` is a colon-separated string indicating the section and + option name. For example, the ``branch`` option in the ``[run]`` + section of the config file would be indicated with `"run:branch"`. + + Returns the value of the option. + + """ + + def set_option(self, option_name: str, value: TConfigValueIn | TConfigSectionIn) -> None: + """Set an option in the configuration. + + `option_name` is a colon-separated string indicating the section and + option name. For example, the ``branch`` option in the ``[run]`` + section of the config file would be indicated with `"run:branch"`. + + `value` is the new value for the option. + + """ + + +class TPluginConfig(Protocol): + """Something that can provide options to a plugin.""" + + def get_plugin_options(self, plugin: str) -> TConfigSectionOut: + """Get the options for a plugin.""" + + +## Parsing + +TMorf = ModuleType | str + +TSourceTokenLines = Iterable[list[tuple[str, str]]] + + +## Plugins + + +class TPlugin(Protocol): + """What all plugins have in common.""" + + _coverage_plugin_name: str + _coverage_enabled: bool + + +## Debugging + + +class TWarnFn(Protocol): + """A callable warn() function.""" + + def __call__(self, msg: str, slug: str | None = None, once: bool = False) -> None: ... + + +class TDebugCtl(Protocol): + """A DebugControl object, or something like it.""" + + def should(self, option: str) -> bool: + """Decide whether to output debug information in category `option`.""" + + def write(self, msg: str) -> None: + """Write a line of debug output.""" + + +class TWritable(Protocol): + """Anything that can be written to.""" + + def write(self, msg: str) -> None: + """Write a message.""" diff --git a/venv/Lib/site-packages/coverage/version.py b/venv/Lib/site-packages/coverage/version.py new file mode 100644 index 0000000..112f95b --- /dev/null +++ b/venv/Lib/site-packages/coverage/version.py @@ -0,0 +1,35 @@ +# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 +# For details: https://github.com/coveragepy/coveragepy/blob/main/NOTICE.txt + +"""The version and URL for coverage.py""" +# This file is exec'ed in setup.py, don't import anything! + +from __future__ import annotations + +# version_info: same semantics as sys.version_info. +# _dev: the .devN suffix if any. +version_info = (7, 12, 0, "final", 0) +_dev = 0 + + +def _make_version( + major: int, + minor: int, + micro: int, + releaselevel: str = "final", + serial: int = 0, + dev: int = 0, +) -> str: + """Create a readable version string from version_info tuple components.""" + assert releaselevel in ["alpha", "beta", "candidate", "final"] + version = f"{major}.{minor}.{micro}" + if releaselevel != "final": + short = {"alpha": "a", "beta": "b", "candidate": "rc"}[releaselevel] + version += f"{short}{serial}" + if dev != 0: + version += f".dev{dev}" + return version + + +__version__ = _make_version(*version_info, _dev) +__url__ = f"https://coverage.readthedocs.io/en/{__version__}" diff --git a/venv/Lib/site-packages/coverage/xmlreport.py b/venv/Lib/site-packages/coverage/xmlreport.py new file mode 100644 index 0000000..46dedf2 --- /dev/null +++ b/venv/Lib/site-packages/coverage/xmlreport.py @@ -0,0 +1,264 @@ +# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 +# For details: https://github.com/coveragepy/coveragepy/blob/main/NOTICE.txt + +"""XML reporting for coverage.py""" + +from __future__ import annotations + +import os +import os.path +import sys +import time +import xml.dom.minidom +from collections.abc import Iterable +from dataclasses import dataclass +from typing import IO, TYPE_CHECKING, Any + +from coverage import __version__, files +from coverage.misc import human_sorted, human_sorted_items, isolate_module +from coverage.plugin import FileReporter +from coverage.report_core import get_analysis_to_report +from coverage.results import Analysis +from coverage.types import TMorf +from coverage.version import __url__ + +if TYPE_CHECKING: + from coverage import Coverage + +os = isolate_module(os) + + +DTD_URL = "https://raw.githubusercontent.com/cobertura/web/master/htdocs/xml/coverage-04.dtd" + + +def rate(hit: int, num: int) -> str: + """Return the fraction of `hit`/`num`, as a string.""" + if num == 0: + return "1" + else: + return f"{hit / num:.4g}" + + +@dataclass +class PackageData: + """Data we keep about each "package" (in Java terms).""" + + elements: dict[str, xml.dom.minidom.Element] + hits: int + lines: int + br_hits: int + branches: int + + +def appendChild(parent: Any, child: Any) -> None: + """Append a child to a parent, in a way mypy will shut up about.""" + parent.appendChild(child) + + +class XmlReporter: + """A reporter for writing Cobertura-style XML coverage results.""" + + report_type = "XML report" + + def __init__(self, coverage: Coverage) -> None: + self.coverage = coverage + self.config = self.coverage.config + + self.source_paths = set() + if self.config.source: + for src in self.config.source: + if os.path.exists(src): + if self.config.relative_files: + src = src.rstrip(r"\/") + else: + src = files.canonical_filename(src) + self.source_paths.add(src) + self.packages: dict[str, PackageData] = {} + self.xml_out: xml.dom.minidom.Document + + def report(self, morfs: Iterable[TMorf] | None, outfile: IO[str] | None = None) -> float: + """Generate a Cobertura-compatible XML report for `morfs`. + + `morfs` is a list of modules or file names. + + `outfile` is a file object to write the XML to. + + """ + # Initial setup. + outfile = outfile or sys.stdout + has_arcs = self.coverage.get_data().has_arcs() + + # Create the DOM that will store the data. + impl = xml.dom.minidom.getDOMImplementation() + assert impl is not None + self.xml_out = impl.createDocument(None, "coverage", None) + + # Write header stuff. + xcoverage = self.xml_out.documentElement + assert xcoverage is not None + xcoverage.setAttribute("version", __version__) + xcoverage.setAttribute("timestamp", str(int(time.time() * 1000))) + xcoverage.appendChild( + self.xml_out.createComment( + f" Generated by coverage.py: {__url__} ", + ) + ) + xcoverage.appendChild(self.xml_out.createComment(f" Based on {DTD_URL} ")) + + # Call xml_file for each file in the data. + for fr, analysis in get_analysis_to_report(self.coverage, morfs): + self.xml_file(fr, analysis, has_arcs) + + xsources = self.xml_out.createElement("sources") + xcoverage.appendChild(xsources) + + # Populate the XML DOM with the source info. + for path in human_sorted(self.source_paths): + xsource = self.xml_out.createElement("source") + appendChild(xsources, xsource) + txt = self.xml_out.createTextNode(path) + appendChild(xsource, txt) + + lnum_tot, lhits_tot = 0, 0 + bnum_tot, bhits_tot = 0, 0 + + xpackages = self.xml_out.createElement("packages") + xcoverage.appendChild(xpackages) + + # Populate the XML DOM with the package info. + for pkg_name, pkg_data in human_sorted_items(self.packages.items()): + xpackage = self.xml_out.createElement("package") + appendChild(xpackages, xpackage) + xclasses = self.xml_out.createElement("classes") + appendChild(xpackage, xclasses) + for _, class_elt in human_sorted_items(pkg_data.elements.items()): + appendChild(xclasses, class_elt) + xpackage.setAttribute("name", pkg_name.replace(os.sep, ".")) + xpackage.setAttribute("line-rate", rate(pkg_data.hits, pkg_data.lines)) + if has_arcs: + branch_rate = rate(pkg_data.br_hits, pkg_data.branches) + else: + branch_rate = "0" + xpackage.setAttribute("branch-rate", branch_rate) + xpackage.setAttribute("complexity", "0") + + lhits_tot += pkg_data.hits + lnum_tot += pkg_data.lines + bhits_tot += pkg_data.br_hits + bnum_tot += pkg_data.branches + + xcoverage.setAttribute("lines-valid", str(lnum_tot)) + xcoverage.setAttribute("lines-covered", str(lhits_tot)) + xcoverage.setAttribute("line-rate", rate(lhits_tot, lnum_tot)) + if has_arcs: + xcoverage.setAttribute("branches-valid", str(bnum_tot)) + xcoverage.setAttribute("branches-covered", str(bhits_tot)) + xcoverage.setAttribute("branch-rate", rate(bhits_tot, bnum_tot)) + else: + xcoverage.setAttribute("branches-covered", "0") + xcoverage.setAttribute("branches-valid", "0") + xcoverage.setAttribute("branch-rate", "0") + xcoverage.setAttribute("complexity", "0") + + # Write the output file. + outfile.write(serialize_xml(self.xml_out)) + + # Return the total percentage. + denom = lnum_tot + bnum_tot + if denom == 0: + pct = 0.0 + else: + pct = 100.0 * (lhits_tot + bhits_tot) / denom + return pct + + def xml_file(self, fr: FileReporter, analysis: Analysis, has_arcs: bool) -> None: + """Add to the XML report for a single file.""" + + if self.config.skip_empty: + if analysis.numbers.n_statements == 0: + return + + # Create the "lines" and "package" XML elements, which + # are populated later. Note that a package == a directory. + filename = fr.filename.replace("\\", "/") + for source_path in self.source_paths: + if not self.config.relative_files: + source_path = files.canonical_filename(source_path) + if filename.startswith(source_path.replace("\\", "/") + "/"): + rel_name = filename[len(source_path) + 1 :] + break + else: + rel_name = fr.relative_filename().replace("\\", "/") + self.source_paths.add(fr.filename[: -len(rel_name)].rstrip(r"\/")) + + dirname = os.path.dirname(rel_name) or "." + dirname = "/".join(dirname.split("/")[: self.config.xml_package_depth]) + package_name = dirname.replace("/", ".") + + package = self.packages.setdefault(package_name, PackageData({}, 0, 0, 0, 0)) + + xclass: xml.dom.minidom.Element = self.xml_out.createElement("class") + + appendChild(xclass, self.xml_out.createElement("methods")) + + xlines = self.xml_out.createElement("lines") + appendChild(xclass, xlines) + + xclass.setAttribute("name", os.path.relpath(rel_name, dirname)) + xclass.setAttribute("filename", rel_name.replace("\\", "/")) + xclass.setAttribute("complexity", "0") + + branch_stats = analysis.branch_stats() + missing_branch_arcs = analysis.missing_branch_arcs() + + # For each statement, create an XML "line" element. + for line in sorted(analysis.statements): + xline = self.xml_out.createElement("line") + xline.setAttribute("number", str(line)) + + # Q: can we get info about the number of times a statement is + # executed? If so, that should be recorded here. + xline.setAttribute("hits", str(int(line not in analysis.missing))) + + if has_arcs: + if line in branch_stats: + total, taken = branch_stats[line] + xline.setAttribute("branch", "true") + xline.setAttribute( + "condition-coverage", + f"{100 * taken // total}% ({taken}/{total})", + ) + if line in missing_branch_arcs: + annlines = ["exit" if b < 0 else str(b) for b in missing_branch_arcs[line]] + xline.setAttribute("missing-branches", ",".join(annlines)) + appendChild(xlines, xline) + + class_lines = len(analysis.statements) + class_hits = class_lines - len(analysis.missing) + + if has_arcs: + class_branches = sum(t for t, k in branch_stats.values()) + missing_branches = sum(t - k for t, k in branch_stats.values()) + class_br_hits = class_branches - missing_branches + else: + class_branches = 0 + class_br_hits = 0 + + # Finalize the statistics that are collected in the XML DOM. + xclass.setAttribute("line-rate", rate(class_hits, class_lines)) + if has_arcs: + branch_rate = rate(class_br_hits, class_branches) + else: + branch_rate = "0" + xclass.setAttribute("branch-rate", branch_rate) + + package.elements[rel_name] = xclass + package.hits += class_hits + package.lines += class_lines + package.br_hits += class_br_hits + package.branches += class_branches + + +def serialize_xml(dom: xml.dom.minidom.Document) -> str: + """Serialize a minidom node to XML.""" + return dom.toprettyxml() diff --git a/venv/Lib/site-packages/iniconfig-2.3.0.dist-info/INSTALLER b/venv/Lib/site-packages/iniconfig-2.3.0.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/venv/Lib/site-packages/iniconfig-2.3.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/Lib/site-packages/iniconfig-2.3.0.dist-info/METADATA b/venv/Lib/site-packages/iniconfig-2.3.0.dist-info/METADATA new file mode 100644 index 0000000..fc3c00d --- /dev/null +++ b/venv/Lib/site-packages/iniconfig-2.3.0.dist-info/METADATA @@ -0,0 +1,79 @@ +Metadata-Version: 2.4 +Name: iniconfig +Version: 2.3.0 +Summary: brain-dead simple config-ini parsing +Author-email: Ronny Pfannschmidt , Holger Krekel +License-Expression: MIT +Project-URL: Homepage, https://github.com/pytest-dev/iniconfig +Classifier: Development Status :: 4 - Beta +Classifier: Intended Audience :: Developers +Classifier: Operating System :: MacOS :: MacOS X +Classifier: Operating System :: Microsoft :: Windows +Classifier: Operating System :: POSIX +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 +Classifier: Topic :: Utilities +Requires-Python: >=3.10 +Description-Content-Type: text/x-rst +License-File: LICENSE +Dynamic: license-file + +iniconfig: brain-dead simple parsing of ini files +======================================================= + +iniconfig is a small and simple INI-file parser module +having a unique set of features: + +* maintains order of sections and entries +* supports multi-line values with or without line-continuations +* supports "#" comments everywhere +* raises errors with proper line-numbers +* no bells and whistles like automatic substitutions +* iniconfig raises an Error if two sections have the same name. + +If you encounter issues or have feature wishes please report them to: + + https://github.com/RonnyPfannschmidt/iniconfig/issues + +Basic Example +=================================== + +If you have an ini file like this: + +.. code-block:: ini + + # content of example.ini + [section1] # comment + name1=value1 # comment + name1b=value1,value2 # comment + + [section2] + name2= + line1 + line2 + +then you can do: + +.. code-block:: pycon + + >>> import iniconfig + >>> ini = iniconfig.IniConfig("example.ini") + >>> ini['section1']['name1'] # raises KeyError if not exists + 'value1' + >>> ini.get('section1', 'name1b', [], lambda x: x.split(",")) + ['value1', 'value2'] + >>> ini.get('section1', 'notexist', [], lambda x: x.split(",")) + [] + >>> [x.name for x in list(ini)] + ['section1', 'section2'] + >>> list(list(ini)[0].items()) + [('name1', 'value1'), ('name1b', 'value1,value2')] + >>> 'section1' in ini + True + >>> 'inexistendsection' in ini + False diff --git a/venv/Lib/site-packages/iniconfig-2.3.0.dist-info/RECORD b/venv/Lib/site-packages/iniconfig-2.3.0.dist-info/RECORD new file mode 100644 index 0000000..ca14c63 --- /dev/null +++ b/venv/Lib/site-packages/iniconfig-2.3.0.dist-info/RECORD @@ -0,0 +1,15 @@ +iniconfig-2.3.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +iniconfig-2.3.0.dist-info/METADATA,sha256=QNdz-E5OES9JW79PG-nL0tRWwK6271MR910b8yLyFls,2526 +iniconfig-2.3.0.dist-info/RECORD,, +iniconfig-2.3.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91 +iniconfig-2.3.0.dist-info/licenses/LICENSE,sha256=NAn6kfes5VeJRjJnZlbjImT-XvdYFTVyXcmiN3RVG9Q,1098 +iniconfig-2.3.0.dist-info/top_level.txt,sha256=7KfM0fugdlToj9UW7enKXk2HYALQD8qHiyKtjhSzgN8,10 +iniconfig/__init__.py,sha256=XL5eqUYj4mskAOorZ5jfRAinJvJzTI-fJxpP4xfXtaw,7497 +iniconfig/__pycache__/__init__.cpython-312.pyc,, +iniconfig/__pycache__/_parse.cpython-312.pyc,, +iniconfig/__pycache__/_version.cpython-312.pyc,, +iniconfig/__pycache__/exceptions.cpython-312.pyc,, +iniconfig/_parse.py,sha256=5ncBl7MAQiaPNnpRrs9FR4t6G6DkgOUs458OY_1CR28,5223 +iniconfig/_version.py,sha256=KNFYe-Vtdt7Z-oHyl8jmDAQ9qXoCNMAEXigj6BR1QUI,704 +iniconfig/exceptions.py,sha256=mipQ_aMxD9CvSvFWN1oTXY4QuRnKAMZ1f3sCdmjDTU0,399 +iniconfig/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 diff --git a/venv/Lib/site-packages/iniconfig-2.3.0.dist-info/WHEEL b/venv/Lib/site-packages/iniconfig-2.3.0.dist-info/WHEEL new file mode 100644 index 0000000..e7fa31b --- /dev/null +++ b/venv/Lib/site-packages/iniconfig-2.3.0.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: setuptools (80.9.0) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/venv/Lib/site-packages/iniconfig-2.3.0.dist-info/licenses/LICENSE b/venv/Lib/site-packages/iniconfig-2.3.0.dist-info/licenses/LICENSE new file mode 100644 index 0000000..46f4b28 --- /dev/null +++ b/venv/Lib/site-packages/iniconfig-2.3.0.dist-info/licenses/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2010 - 2023 Holger Krekel and others + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/venv/Lib/site-packages/iniconfig-2.3.0.dist-info/top_level.txt b/venv/Lib/site-packages/iniconfig-2.3.0.dist-info/top_level.txt new file mode 100644 index 0000000..9dda536 --- /dev/null +++ b/venv/Lib/site-packages/iniconfig-2.3.0.dist-info/top_level.txt @@ -0,0 +1 @@ +iniconfig diff --git a/venv/Lib/site-packages/iniconfig/__init__.py b/venv/Lib/site-packages/iniconfig/__init__.py new file mode 100644 index 0000000..b84809f --- /dev/null +++ b/venv/Lib/site-packages/iniconfig/__init__.py @@ -0,0 +1,249 @@ +"""brain-dead simple parser for ini-style files. +(C) Ronny Pfannschmidt, Holger Krekel -- MIT licensed +""" + +import os +from collections.abc import Callable +from collections.abc import Iterator +from collections.abc import Mapping +from typing import Final +from typing import TypeVar +from typing import overload + +__all__ = ["IniConfig", "ParseError", "COMMENTCHARS", "iscommentline"] + +from . import _parse +from ._parse import COMMENTCHARS +from ._parse import iscommentline +from .exceptions import ParseError + +_D = TypeVar("_D") +_T = TypeVar("_T") + + +class SectionWrapper: + config: Final["IniConfig"] + name: Final[str] + + def __init__(self, config: "IniConfig", name: str) -> None: + self.config = config + self.name = name + + def lineof(self, name: str) -> int | None: + return self.config.lineof(self.name, name) + + @overload + def get(self, key: str) -> str | None: ... + + @overload + def get( + self, + key: str, + convert: Callable[[str], _T], + ) -> _T | None: ... + + @overload + def get( + self, + key: str, + default: None, + convert: Callable[[str], _T], + ) -> _T | None: ... + + @overload + def get(self, key: str, default: _D, convert: None = None) -> str | _D: ... + + @overload + def get( + self, + key: str, + default: _D, + convert: Callable[[str], _T], + ) -> _T | _D: ... + + # TODO: investigate possible mypy bug wrt matching the passed over data + def get( # type: ignore [misc] + self, + key: str, + default: _D | None = None, + convert: Callable[[str], _T] | None = None, + ) -> _D | _T | str | None: + return self.config.get(self.name, key, convert=convert, default=default) + + def __getitem__(self, key: str) -> str: + return self.config.sections[self.name][key] + + def __iter__(self) -> Iterator[str]: + section: Mapping[str, str] = self.config.sections.get(self.name, {}) + + def lineof(key: str) -> int: + return self.config.lineof(self.name, key) # type: ignore[return-value] + + yield from sorted(section, key=lineof) + + def items(self) -> Iterator[tuple[str, str]]: + for name in self: + yield name, self[name] + + +class IniConfig: + path: Final[str] + sections: Final[Mapping[str, Mapping[str, str]]] + _sources: Final[Mapping[tuple[str, str | None], int]] + + def __init__( + self, + path: str | os.PathLike[str], + data: str | None = None, + encoding: str = "utf-8", + *, + _sections: Mapping[str, Mapping[str, str]] | None = None, + _sources: Mapping[tuple[str, str | None], int] | None = None, + ) -> None: + self.path = os.fspath(path) + + # Determine sections and sources + if _sections is not None and _sources is not None: + # Use provided pre-parsed data (called from parse()) + sections_data = _sections + sources = _sources + else: + # Parse the data (backward compatible path) + if data is None: + with open(self.path, encoding=encoding) as fp: + data = fp.read() + + # Use old behavior (no stripping) for backward compatibility + sections_data, sources = _parse.parse_ini_data( + self.path, data, strip_inline_comments=False + ) + + # Assign once to Final attributes + self._sources = sources + self.sections = sections_data + + @classmethod + def parse( + cls, + path: str | os.PathLike[str], + data: str | None = None, + encoding: str = "utf-8", + *, + strip_inline_comments: bool = True, + strip_section_whitespace: bool = False, + ) -> "IniConfig": + """Parse an INI file. + + Args: + path: Path to the INI file (used for error messages) + data: Optional INI content as string. If None, reads from path. + encoding: Encoding to use when reading the file (default: utf-8) + strip_inline_comments: Whether to strip inline comments from values + (default: True). When True, comments starting with # or ; are + removed from values, matching the behavior for section comments. + strip_section_whitespace: Whether to strip whitespace from section and key names + (default: False). When True, strips Unicode whitespace from section and key names, + addressing issue #4. When False, preserves existing behavior for backward compatibility. + + Returns: + IniConfig instance with parsed configuration + + Example: + # With comment stripping (default): + config = IniConfig.parse("setup.cfg") + # value = "foo" instead of "foo # comment" + + # Without comment stripping (old behavior): + config = IniConfig.parse("setup.cfg", strip_inline_comments=False) + # value = "foo # comment" + + # With section name stripping (opt-in for issue #4): + config = IniConfig.parse("setup.cfg", strip_section_whitespace=True) + # section names and keys have Unicode whitespace stripped + """ + fspath = os.fspath(path) + + if data is None: + with open(fspath, encoding=encoding) as fp: + data = fp.read() + + sections_data, sources = _parse.parse_ini_data( + fspath, + data, + strip_inline_comments=strip_inline_comments, + strip_section_whitespace=strip_section_whitespace, + ) + + # Call constructor with pre-parsed sections and sources + return cls(path=fspath, _sections=sections_data, _sources=sources) + + def lineof(self, section: str, name: str | None = None) -> int | None: + lineno = self._sources.get((section, name)) + return None if lineno is None else lineno + 1 + + @overload + def get( + self, + section: str, + name: str, + ) -> str | None: ... + + @overload + def get( + self, + section: str, + name: str, + convert: Callable[[str], _T], + ) -> _T | None: ... + + @overload + def get( + self, + section: str, + name: str, + default: None, + convert: Callable[[str], _T], + ) -> _T | None: ... + + @overload + def get( + self, section: str, name: str, default: _D, convert: None = None + ) -> str | _D: ... + + @overload + def get( + self, + section: str, + name: str, + default: _D, + convert: Callable[[str], _T], + ) -> _T | _D: ... + + def get( # type: ignore + self, + section: str, + name: str, + default: _D | None = None, + convert: Callable[[str], _T] | None = None, + ) -> _D | _T | str | None: + try: + value: str = self.sections[section][name] + except KeyError: + return default + else: + if convert is not None: + return convert(value) + else: + return value + + def __getitem__(self, name: str) -> SectionWrapper: + if name not in self.sections: + raise KeyError(name) + return SectionWrapper(self, name) + + def __iter__(self) -> Iterator[SectionWrapper]: + for name in sorted(self.sections, key=self.lineof): # type: ignore + yield SectionWrapper(self, name) + + def __contains__(self, arg: str) -> bool: + return arg in self.sections diff --git a/venv/Lib/site-packages/iniconfig/__pycache__/__init__.cpython-312.pyc b/venv/Lib/site-packages/iniconfig/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f33902140957fc889fccb178e448be88e85f99d7 GIT binary patch literal 10874 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 G9Qhypdr`Ik literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/iniconfig/__pycache__/_parse.cpython-312.pyc b/venv/Lib/site-packages/iniconfig/__pycache__/_parse.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a892250bd82ebb82f959b829672066d7afbfc57b GIT binary patch literal 5574 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/iniconfig/__pycache__/_version.cpython-312.pyc b/venv/Lib/site-packages/iniconfig/__pycache__/_version.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ba3c00ccf4a1eba476db41d3a3e8698364c33e6d GIT binary patch literal 789 zcmYL{&rcIU6vt<~+xBki`?U5NEm zD9l$yO)w))=5m7c;ZIn#zo&OP$skQedarF-+VK>KTUH2!T9)Zf&GXf z^hYeNhjIny-vK_ME!0E=eUU6gBuk=_mnShIw_3$ER>8 zDo8wD7cB&AxzN6}=860A;1ZE1ix3w<6G4UWI=)a^ER_rLsi-)ODs-SihHDr*trnbO z*0(n*c=>5%`B`=C30H$&m$^;P5Iw|a;xcmsF8hJU%@>vDo7MF-T;JMQt#D&`y;iH% zadm~Oot>Ao5%3grb=^+j1mMoc_|jSA@y4ET7J2-J&H>RyedVEDE|)j9?Y0xpqSJ5) zZTIYMuNXM~Ua`5)2yHQ!+6UAxyg}&78 zko^{Y=;Y^k!=}gz8*k7b2VtwuG7RpTZ&0li*w~~W7%FZ0!KbDU-kM+;< zUl#|nrIYM@zZRzN95#*~9I}&i{!Mk1ofXnlzdACKZ!9-?%3J3mDEFqIpkT$soX pQ8r9X57G2!dTxN``iXbV@Lq9%U}g?$qg;M~@_qBY;oSvW{{d{w)<^&V literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/iniconfig/__pycache__/exceptions.cpython-312.pyc b/venv/Lib/site-packages/iniconfig/__pycache__/exceptions.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5d43755950b89fb25cd0dfe761a407366f81b43c GIT binary patch literal 1193 zcmZ8fO=}ZD7@pakO*S#HYD0}*SSSdqv=)!Gh+gG^64!1vab{CY$)RAy z1{CW-8WGWB#Y;WufACV#Ls<0W$y=coOE3D)=A-Bw-mmAGkN27PQ#zeQu%7=p;XcI( z{ZL7F8J%GL2n0)rA%;Cv!a0nQhAtqc%_F9B?VaA{<@6e{jGrp8aA=%&ecN-|P}`cF zWZj405uC_T4l|U~ZXm96vJa}iTX#sffXD5m!bj1`Hy(SNN|SCMb10TS`i$dgdUkxviTMWBJk1(W|%fB|G55 zwqrA1s^u%S;lP&D!^Nu0xavAT!~L23C3h+>UEo!0XWA}unTHoRWxwDSkMsKuuLN${ zmm`&$NWl%W{o4wTbRU}lKw4ln=g>DZ-H3lSv&~F%=8gPn9&2T?3*>R4HE`hR*_Sfl}yiZCepz=m;**KM%V%DcR}YOWPpxD=WZ!33lB<0uSIv`R7tpPw`e@%SaZ1A^rKK{fdMZv)dar7G zZA7S%33a$B?4#87{c>POe*r|@^@Hd?L`IR)oiT8@=TQn>?1XC6#p$REq126xl15$z zv-uFUl6{MMJj<-8MB@RQXM3<_aH63I8 S9c6#TQh0oE-v$B~?fMJ#Xb?UC literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/iniconfig/_parse.py b/venv/Lib/site-packages/iniconfig/_parse.py new file mode 100644 index 0000000..57b9b44 --- /dev/null +++ b/venv/Lib/site-packages/iniconfig/_parse.py @@ -0,0 +1,163 @@ +from collections.abc import Mapping +from typing import NamedTuple + +from .exceptions import ParseError + +COMMENTCHARS = "#;" + + +class ParsedLine(NamedTuple): + lineno: int + section: str | None + name: str | None + value: str | None + + +def parse_ini_data( + path: str, + data: str, + *, + strip_inline_comments: bool, + strip_section_whitespace: bool = False, +) -> tuple[Mapping[str, Mapping[str, str]], Mapping[tuple[str, str | None], int]]: + """Parse INI data and return sections and sources mappings. + + Args: + path: Path for error messages + data: INI content as string + strip_inline_comments: Whether to strip inline comments from values + strip_section_whitespace: Whether to strip whitespace from section and key names + (default: False). When True, addresses issue #4 by stripping Unicode whitespace. + + Returns: + Tuple of (sections_data, sources) where: + - sections_data: mapping of section -> {name -> value} + - sources: mapping of (section, name) -> line number + """ + tokens = parse_lines( + path, + data.splitlines(True), + strip_inline_comments=strip_inline_comments, + strip_section_whitespace=strip_section_whitespace, + ) + + sources: dict[tuple[str, str | None], int] = {} + sections_data: dict[str, dict[str, str]] = {} + + for lineno, section, name, value in tokens: + if section is None: + raise ParseError(path, lineno, "no section header defined") + sources[section, name] = lineno + if name is None: + if section in sections_data: + raise ParseError(path, lineno, f"duplicate section {section!r}") + sections_data[section] = {} + else: + if name in sections_data[section]: + raise ParseError(path, lineno, f"duplicate name {name!r}") + assert value is not None + sections_data[section][name] = value + + return sections_data, sources + + +def parse_lines( + path: str, + line_iter: list[str], + *, + strip_inline_comments: bool = False, + strip_section_whitespace: bool = False, +) -> list[ParsedLine]: + result: list[ParsedLine] = [] + section = None + for lineno, line in enumerate(line_iter): + name, data = _parseline( + path, line, lineno, strip_inline_comments, strip_section_whitespace + ) + # new value + if name is not None and data is not None: + result.append(ParsedLine(lineno, section, name, data)) + # new section + elif name is not None and data is None: + if not name: + raise ParseError(path, lineno, "empty section name") + section = name + result.append(ParsedLine(lineno, section, None, None)) + # continuation + elif name is None and data is not None: + if not result: + raise ParseError(path, lineno, "unexpected value continuation") + last = result.pop() + if last.name is None: + raise ParseError(path, lineno, "unexpected value continuation") + + if last.value: + last = last._replace(value=f"{last.value}\n{data}") + else: + last = last._replace(value=data) + result.append(last) + return result + + +def _parseline( + path: str, + line: str, + lineno: int, + strip_inline_comments: bool, + strip_section_whitespace: bool, +) -> tuple[str | None, str | None]: + # blank lines + if iscommentline(line): + line = "" + else: + line = line.rstrip() + if not line: + return None, None + # section + if line[0] == "[": + realline = line + for c in COMMENTCHARS: + line = line.split(c)[0].rstrip() + if line[-1] == "]": + section_name = line[1:-1] + # Optionally strip whitespace from section name (issue #4) + if strip_section_whitespace: + section_name = section_name.strip() + return section_name, None + return None, realline.strip() + # value + elif not line[0].isspace(): + try: + name, value = line.split("=", 1) + if ":" in name: + raise ValueError() + except ValueError: + try: + name, value = line.split(":", 1) + except ValueError: + raise ParseError(path, lineno, f"unexpected line: {line!r}") from None + + # Strip key name (always for backward compatibility, optionally with unicode awareness) + key_name = name.strip() + + # Strip value + value = value.strip() + # Strip inline comments from values if requested (issue #55) + if strip_inline_comments: + for c in COMMENTCHARS: + value = value.split(c)[0].rstrip() + + return key_name, value + # continuation + else: + line = line.strip() + # Strip inline comments from continuations if requested (issue #55) + if strip_inline_comments: + for c in COMMENTCHARS: + line = line.split(c)[0].rstrip() + return None, line + + +def iscommentline(line: str) -> bool: + c = line.lstrip()[:1] + return c in COMMENTCHARS diff --git a/venv/Lib/site-packages/iniconfig/_version.py b/venv/Lib/site-packages/iniconfig/_version.py new file mode 100644 index 0000000..b982b02 --- /dev/null +++ b/venv/Lib/site-packages/iniconfig/_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 = '2.3.0' +__version_tuple__ = version_tuple = (2, 3, 0) + +__commit_id__ = commit_id = None diff --git a/venv/Lib/site-packages/iniconfig/exceptions.py b/venv/Lib/site-packages/iniconfig/exceptions.py new file mode 100644 index 0000000..d078bc6 --- /dev/null +++ b/venv/Lib/site-packages/iniconfig/exceptions.py @@ -0,0 +1,16 @@ +from typing import Final + + +class ParseError(Exception): + path: Final[str] + lineno: Final[int] + msg: Final[str] + + def __init__(self, path: str, lineno: int, msg: str) -> None: + super().__init__(path, lineno, msg) + self.path = path + self.lineno = lineno + self.msg = msg + + def __str__(self) -> str: + return f"{self.path}:{self.lineno + 1}: {self.msg}" diff --git a/venv/Lib/site-packages/iniconfig/py.typed b/venv/Lib/site-packages/iniconfig/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/venv/Lib/site-packages/jinja2-3.1.6.dist-info/INSTALLER b/venv/Lib/site-packages/jinja2-3.1.6.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/venv/Lib/site-packages/jinja2-3.1.6.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/Lib/site-packages/jinja2-3.1.6.dist-info/METADATA b/venv/Lib/site-packages/jinja2-3.1.6.dist-info/METADATA new file mode 100644 index 0000000..ffef2ff --- /dev/null +++ b/venv/Lib/site-packages/jinja2-3.1.6.dist-info/METADATA @@ -0,0 +1,84 @@ +Metadata-Version: 2.4 +Name: Jinja2 +Version: 3.1.6 +Summary: A very fast and expressive template engine. +Maintainer-email: Pallets +Requires-Python: >=3.7 +Description-Content-Type: text/markdown +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Web Environment +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content +Classifier: Topic :: Text Processing :: Markup :: HTML +Classifier: Typing :: Typed +License-File: LICENSE.txt +Requires-Dist: MarkupSafe>=2.0 +Requires-Dist: Babel>=2.7 ; extra == "i18n" +Project-URL: Changes, https://jinja.palletsprojects.com/changes/ +Project-URL: Chat, https://discord.gg/pallets +Project-URL: Documentation, https://jinja.palletsprojects.com/ +Project-URL: Donate, https://palletsprojects.com/donate +Project-URL: Source, https://github.com/pallets/jinja/ +Provides-Extra: i18n + +# Jinja + +Jinja is a fast, expressive, extensible templating engine. Special +placeholders in the template allow writing code similar to Python +syntax. Then the template is passed data to render the final document. + +It includes: + +- Template inheritance and inclusion. +- Define and import macros within templates. +- HTML templates can use autoescaping to prevent XSS from untrusted + user input. +- A sandboxed environment can safely render untrusted templates. +- AsyncIO support for generating templates and calling async + functions. +- I18N support with Babel. +- Templates are compiled to optimized Python code just-in-time and + cached, or can be compiled ahead-of-time. +- Exceptions point to the correct line in templates to make debugging + easier. +- Extensible filters, tests, functions, and even syntax. + +Jinja's philosophy is that while application logic belongs in Python if +possible, it shouldn't make the template designer's job difficult by +restricting functionality too much. + + +## In A Nutshell + +```jinja +{% extends "base.html" %} +{% block title %}Members{% endblock %} +{% block content %} + +{% endblock %} +``` + +## Donate + +The Pallets organization develops and supports Jinja and other popular +packages. In order to grow the community of contributors and users, and +allow the maintainers to devote more time to the projects, [please +donate today][]. + +[please donate today]: https://palletsprojects.com/donate + +## Contributing + +See our [detailed contributing documentation][contrib] for many ways to +contribute, including reporting issues, requesting features, asking or answering +questions, and making PRs. + +[contrib]: https://palletsprojects.com/contributing/ + diff --git a/venv/Lib/site-packages/jinja2-3.1.6.dist-info/RECORD b/venv/Lib/site-packages/jinja2-3.1.6.dist-info/RECORD new file mode 100644 index 0000000..0ca35a7 --- /dev/null +++ b/venv/Lib/site-packages/jinja2-3.1.6.dist-info/RECORD @@ -0,0 +1,57 @@ +jinja2-3.1.6.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +jinja2-3.1.6.dist-info/METADATA,sha256=aMVUj7Z8QTKhOJjZsx7FDGvqKr3ZFdkh8hQ1XDpkmcg,2871 +jinja2-3.1.6.dist-info/RECORD,, +jinja2-3.1.6.dist-info/WHEEL,sha256=_2ozNFCLWc93bK4WKHCO-eDUENDlo-dgc9cU3qokYO4,82 +jinja2-3.1.6.dist-info/entry_points.txt,sha256=OL85gYU1eD8cuPlikifFngXpeBjaxl6rIJ8KkC_3r-I,58 +jinja2-3.1.6.dist-info/licenses/LICENSE.txt,sha256=O0nc7kEF6ze6wQ-vG-JgQI_oXSUrjp3y4JefweCUQ3s,1475 +jinja2/__init__.py,sha256=xxepO9i7DHsqkQrgBEduLtfoz2QCuT6_gbL4XSN1hbU,1928 +jinja2/__pycache__/__init__.cpython-312.pyc,, +jinja2/__pycache__/_identifier.cpython-312.pyc,, +jinja2/__pycache__/async_utils.cpython-312.pyc,, +jinja2/__pycache__/bccache.cpython-312.pyc,, +jinja2/__pycache__/compiler.cpython-312.pyc,, +jinja2/__pycache__/constants.cpython-312.pyc,, +jinja2/__pycache__/debug.cpython-312.pyc,, +jinja2/__pycache__/defaults.cpython-312.pyc,, +jinja2/__pycache__/environment.cpython-312.pyc,, +jinja2/__pycache__/exceptions.cpython-312.pyc,, +jinja2/__pycache__/ext.cpython-312.pyc,, +jinja2/__pycache__/filters.cpython-312.pyc,, +jinja2/__pycache__/idtracking.cpython-312.pyc,, +jinja2/__pycache__/lexer.cpython-312.pyc,, +jinja2/__pycache__/loaders.cpython-312.pyc,, +jinja2/__pycache__/meta.cpython-312.pyc,, +jinja2/__pycache__/nativetypes.cpython-312.pyc,, +jinja2/__pycache__/nodes.cpython-312.pyc,, +jinja2/__pycache__/optimizer.cpython-312.pyc,, +jinja2/__pycache__/parser.cpython-312.pyc,, +jinja2/__pycache__/runtime.cpython-312.pyc,, +jinja2/__pycache__/sandbox.cpython-312.pyc,, +jinja2/__pycache__/tests.cpython-312.pyc,, +jinja2/__pycache__/utils.cpython-312.pyc,, +jinja2/__pycache__/visitor.cpython-312.pyc,, +jinja2/_identifier.py,sha256=_zYctNKzRqlk_murTNlzrju1FFJL7Va_Ijqqd7ii2lU,1958 +jinja2/async_utils.py,sha256=vK-PdsuorOMnWSnEkT3iUJRIkTnYgO2T6MnGxDgHI5o,2834 +jinja2/bccache.py,sha256=gh0qs9rulnXo0PhX5jTJy2UHzI8wFnQ63o_vw7nhzRg,14061 +jinja2/compiler.py,sha256=9RpCQl5X88BHllJiPsHPh295Hh0uApvwFJNQuutULeM,74131 +jinja2/constants.py,sha256=GMoFydBF_kdpaRKPoM5cl5MviquVRLVyZtfp5-16jg0,1433 +jinja2/debug.py,sha256=CnHqCDHd-BVGvti_8ZsTolnXNhA3ECsY-6n_2pwU8Hw,6297 +jinja2/defaults.py,sha256=boBcSw78h-lp20YbaXSJsqkAI2uN_mD_TtCydpeq5wU,1267 +jinja2/environment.py,sha256=9nhrP7Ch-NbGX00wvyr4yy-uhNHq2OCc60ggGrni_fk,61513 +jinja2/exceptions.py,sha256=ioHeHrWwCWNaXX1inHmHVblvc4haO7AXsjCp3GfWvx0,5071 +jinja2/ext.py,sha256=5PF5eHfh8mXAIxXHHRB2xXbXohi8pE3nHSOxa66uS7E,31875 +jinja2/filters.py,sha256=PQ_Egd9n9jSgtnGQYyF4K5j2nYwhUIulhPnyimkdr-k,55212 +jinja2/idtracking.py,sha256=-ll5lIp73pML3ErUYiIJj7tdmWxcH_IlDv3yA_hiZYo,10555 +jinja2/lexer.py,sha256=LYiYio6br-Tep9nPcupWXsPEtjluw3p1mU-lNBVRUfk,29786 +jinja2/loaders.py,sha256=wIrnxjvcbqh5VwW28NSkfotiDq8qNCxIOSFbGUiSLB4,24055 +jinja2/meta.py,sha256=OTDPkaFvU2Hgvx-6akz7154F8BIWaRmvJcBFvwopHww,4397 +jinja2/nativetypes.py,sha256=7GIGALVJgdyL80oZJdQUaUfwSt5q2lSSZbXt0dNf_M4,4210 +jinja2/nodes.py,sha256=m1Duzcr6qhZI8JQ6VyJgUNinjAf5bQzijSmDnMsvUx8,34579 +jinja2/optimizer.py,sha256=rJnCRlQ7pZsEEmMhsQDgC_pKyDHxP5TPS6zVPGsgcu8,1651 +jinja2/parser.py,sha256=lLOFy3sEmHc5IaEHRiH1sQVnId2moUQzhyeJZTtdY30,40383 +jinja2/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +jinja2/runtime.py,sha256=gDk-GvdriJXqgsGbHgrcKTP0Yp6zPXzhzrIpCFH3jAU,34249 +jinja2/sandbox.py,sha256=Mw2aitlY2I8la7FYhcX2YG9BtUYcLnD0Gh3d29cDWrY,15009 +jinja2/tests.py,sha256=VLsBhVFnWg-PxSBz1MhRnNWgP1ovXk3neO1FLQMeC9Q,5926 +jinja2/utils.py,sha256=rRp3o9e7ZKS4fyrWRbELyLcpuGVTFcnooaOa1qx_FIk,24129 +jinja2/visitor.py,sha256=EcnL1PIwf_4RVCOMxsRNuR8AXHbS1qfAdMOE2ngKJz4,3557 diff --git a/venv/Lib/site-packages/jinja2-3.1.6.dist-info/WHEEL b/venv/Lib/site-packages/jinja2-3.1.6.dist-info/WHEEL new file mode 100644 index 0000000..23d2d7e --- /dev/null +++ b/venv/Lib/site-packages/jinja2-3.1.6.dist-info/WHEEL @@ -0,0 +1,4 @@ +Wheel-Version: 1.0 +Generator: flit 3.11.0 +Root-Is-Purelib: true +Tag: py3-none-any diff --git a/venv/Lib/site-packages/jinja2-3.1.6.dist-info/entry_points.txt b/venv/Lib/site-packages/jinja2-3.1.6.dist-info/entry_points.txt new file mode 100644 index 0000000..abc3eae --- /dev/null +++ b/venv/Lib/site-packages/jinja2-3.1.6.dist-info/entry_points.txt @@ -0,0 +1,3 @@ +[babel.extractors] +jinja2=jinja2.ext:babel_extract[i18n] + diff --git a/venv/Lib/site-packages/jinja2-3.1.6.dist-info/licenses/LICENSE.txt b/venv/Lib/site-packages/jinja2-3.1.6.dist-info/licenses/LICENSE.txt new file mode 100644 index 0000000..c37cae4 --- /dev/null +++ b/venv/Lib/site-packages/jinja2-3.1.6.dist-info/licenses/LICENSE.txt @@ -0,0 +1,28 @@ +Copyright 2007 Pallets + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/venv/Lib/site-packages/jinja2/__init__.py b/venv/Lib/site-packages/jinja2/__init__.py new file mode 100644 index 0000000..1a423a3 --- /dev/null +++ b/venv/Lib/site-packages/jinja2/__init__.py @@ -0,0 +1,38 @@ +"""Jinja is a template engine written in pure Python. It provides a +non-XML syntax that supports inline expressions and an optional +sandboxed environment. +""" + +from .bccache import BytecodeCache as BytecodeCache +from .bccache import FileSystemBytecodeCache as FileSystemBytecodeCache +from .bccache import MemcachedBytecodeCache as MemcachedBytecodeCache +from .environment import Environment as Environment +from .environment import Template as Template +from .exceptions import TemplateAssertionError as TemplateAssertionError +from .exceptions import TemplateError as TemplateError +from .exceptions import TemplateNotFound as TemplateNotFound +from .exceptions import TemplateRuntimeError as TemplateRuntimeError +from .exceptions import TemplatesNotFound as TemplatesNotFound +from .exceptions import TemplateSyntaxError as TemplateSyntaxError +from .exceptions import UndefinedError as UndefinedError +from .loaders import BaseLoader as BaseLoader +from .loaders import ChoiceLoader as ChoiceLoader +from .loaders import DictLoader as DictLoader +from .loaders import FileSystemLoader as FileSystemLoader +from .loaders import FunctionLoader as FunctionLoader +from .loaders import ModuleLoader as ModuleLoader +from .loaders import PackageLoader as PackageLoader +from .loaders import PrefixLoader as PrefixLoader +from .runtime import ChainableUndefined as ChainableUndefined +from .runtime import DebugUndefined as DebugUndefined +from .runtime import make_logging_undefined as make_logging_undefined +from .runtime import StrictUndefined as StrictUndefined +from .runtime import Undefined as Undefined +from .utils import clear_caches as clear_caches +from .utils import is_undefined as is_undefined +from .utils import pass_context as pass_context +from .utils import pass_environment as pass_environment +from .utils import pass_eval_context as pass_eval_context +from .utils import select_autoescape as select_autoescape + +__version__ = "3.1.6" diff --git a/venv/Lib/site-packages/jinja2/__pycache__/__init__.cpython-312.pyc b/venv/Lib/site-packages/jinja2/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4a66ebc6f119db28b70082754b70a353d142e570 GIT binary patch literal 1647 zcmZ9M+in|07{|wU96RfC;zQ2aBrPba?4~Ugib^GN;-I8T8<1dRX*Jm$oN0DvS2JT{ z_X_TK19%0VfQR4>EUkpZ6?dSDo8G`Tldf&Lvj6&h|M}*)-|TOtl7-;;_22vK#~ea` z=pcF0Z{XGUDTIDP9>T~=d1*ftq;Wc>IRl)*8N+kHb9l~h*3Sm>c;4_la1Q4T=lp!I zfENtsfh}wqUH~rOf?*4|h>L~`z$IKVTm&xTvf&bN1y>B0{c2FdHNzF)MZ9RZ3cQ4u z4A+3`xNdk6xPcpnmw=nNX}AvD!Y#uM;AOmQxaqG1t9aFL3wRB$8D0in$LoeyfH&}l z;Z@*GylHsN-wJGO8(s(A#@mKBfOqhY;Z5LOylZ$1_!hop*ap6hZv$`RcSbvVyDxwK zg7Fu`X3{3MqCw;nMQzG^j8pqN!IYxhX55bYg4*XJbrtfC{kgIu5e}F~p_j!&-v0XR z)RrTz$k0|-MA@<*MWIjrbc;w3ix}4n~T)p+^sidqu%$oiLwXj3lh~?WX1#4P2e(z1>nco{kM;u5mC#!>{l#l3Tzc=eN19D9rKkPxx^c*;po1H*z3@_+q{5s}hyL$2shL3`E2GgSg!?Zz7reC zW*>C!bsj$5dv~0394~YoXPmoq&D0_Tl_uu2WI@J-nHj{qw&5d&#ugpA)D*20dF?J} z(O)`I&{EV=(ozN)=X~?_q^M}Cs->o7QOlB+x|W8PCdfD^ZVE`Ww6&~dMa!y|HIQ+( zub3~zx;8enY--ukVr$t385bO9K!v_)j`It81r~b!?C7KJ{{H^?H{AfPcbm9SSHY+o zjoOOH>vpdXS6ScF?f|ylJ!O|&$sjMfsSw$Hp}!IL?>Y|SOgT;`8okax4op2g5p9@3 z-&M&${>r3MslU^i)Z9N7O1&4OPh)f(qX#i+$LO;dJ&e)g7#+pvNsJC+^r7+Z8GRI^ j_hSSM$)F#b5c+-)qdPIWoBSYz%tybM8$T4p9hmYzUFPKC literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/jinja2/__pycache__/_identifier.cpython-312.pyc b/venv/Lib/site-packages/jinja2/__pycache__/_identifier.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a1e7df44f769f614c141277c4c129b325f922028 GIT binary patch literal 2128 zcmYL~O>E=F8HQyW?KTZ^?5&3)x1^YMkxPN1=&8q|=%JUSE(&+82HxyChS!^7Po@54 z%Zx>PSF&tNrbYdc6irE_er!<^&})AVIjpmBn*ByTbuK30TQ2Q8XoCuP_`dVatC?>| z3H<9@Z+$m{&+y;xtu^jNA~$mI&1dO<=I%aBF|rz2U3e7vAhH;VMjkGH6Z*fdF4_zK z9euF)Y~h`yf62kGp8f>O{&x9Ke;NKwVSji3k@+maKFhq+*vrYw*;VW+ewDmRU1dJc zv(JxS<=Cs!SDotuyFR%-W!JUq=5_1ZV%N@>+Ls;n&z-Lt?5oaeo4xj4Pe?B?(o3Wt zLk!aEkS6IS#G$hiqqAc&4j36{WEhNU394N}0;)|J)%U2bL5ftbQr)0Nj8P*&jer`M zZ6>Lip=OqvISf>47O7dKra{dqwUBHjs1;BPNi$AnhRiIPNSZ2{8krq3yJYssLeff* z6_ACbogzB}*(MuNTP3?nwm~*}vS-vrww<7MKy755Bsm#!vg9D@sN@vLDUwqq$Ds2# zog?DL$xTDD3){_NcUCh7pY&RzCnXH4H7im zq2V5*;d2@mXm|*BO2ZxvJsNds%te^L`CQzZvEyX_!eq)D(r`5(mg@u{4;$ z#8HWNq`3^Wq|78yNj9bNLoJm8$fmqdOUv0bX44)_MgiN>?1oyl4U-*$X{hB+G00h= zwkyA|n*d`FfGG-U5@bLYXvkt4$i7r`k?+H3XhW-n+P=8> zwF6_7LaigW>kNS0t}_d@-absP2~24)<#%CxX|RKj6Z;b&?{gnFvX6BKa=F2$0MBHg zfdg5<@yD`{@gOYN=)Pe4VWBNe8v@?d2(`uN%M#xu6H^5i4-;nTI%=%cmu z=<+Ag^-q?6x%P+UCu_)UtUUN=<-_Qc<%etQ4_Ds%@$V5^-&*_QT6FVA8_&M}&d(ly zy!!M}^yiyzU=#UDfARxd!li|Uh3m*0;kP4ielPp>-n-lHhKrGJ{rUTMON(zV#_nz6 HJsrgqy??FwCP49b# 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 NX1@7*fpWIY{~IP{pW*-j literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/jinja2/__pycache__/compiler.cpython-312.pyc b/venv/Lib/site-packages/jinja2/__pycache__/compiler.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9ae6deb600d695dd81b31112c74f0debd5ccb7fa GIT binary patch literal 104054 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/jinja2/__pycache__/debug.cpython-312.pyc b/venv/Lib/site-packages/jinja2/__pycache__/debug.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3449a79e16c96dedfb824d93f338f2398ea31793 GIT binary patch literal 6575 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/jinja2/__pycache__/defaults.cpython-312.pyc b/venv/Lib/site-packages/jinja2/__pycache__/defaults.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6db3f4a8780e681eae76c7e68c8bafcfe3e3e51d GIT binary patch literal 1600 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 L-CxrSzxMwFws4=` literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/jinja2/__pycache__/ext.cpython-312.pyc b/venv/Lib/site-packages/jinja2/__pycache__/ext.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6a737a8a200a01f421d768b7e85f436de1fd714c GIT binary patch literal 41913 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/jinja2/__pycache__/filters.cpython-312.pyc b/venv/Lib/site-packages/jinja2/__pycache__/filters.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3b1eea369c49688479128fccda098cf864a25ef8 GIT binary patch literal 72324 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/jinja2/__pycache__/lexer.cpython-312.pyc b/venv/Lib/site-packages/jinja2/__pycache__/lexer.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..462f996dfb2ce5a3b84baf78768efc5e7676e6d5 GIT binary patch literal 32070 zcmcJ&3wT?}l^%F6-cJG~!S@RkDU#qz)LWt`sVRw4>qR}t?sl`OrXb!+2^2}FFF;9z zL65raiK%frqNY6~XxzOG#qF=D?M_T*Cb9f&HX|qFN$*aQ1q68rVK|BMrRV$N>~21& zsa@Mz@6P_GF75?Lp=BrY*zFb$&yW6glW!=@U$-Qr&11a62EctJD45Ap1+8PejdozP9{hx9zf=_U-5#vvo~>nBW;<{>lu2EjOC znY0esnBO#EpDY+EV1Dz2W70X~}juacZ9RUz%spe{-Jekf&KFR$JV_V3!Iv zGPp#A*E6_Og_{^$rozn(E?40e23M%ChryL9+{)l86>ej2wFC>s5FogV(9>CI&aC@MZ=#s_+&DuUFx%3~o~4Z47Q!;q44=QQ;j7_Neeq z2DhqkH-p<$co&1)Rk(-29V)z=!JR6+hrt_EcrSyyRQM$ZZ&cx425(Z~eGJ~L!uuJ# zMTHMAc&iHcF?gE_A7t=$6+Xn^9V+}XgLkU%VFq`r@DT>@QsJWv?or`m4BoB6#~Hjw zh5H%2SA|b7_$3uS$>3fUKE>dDDtwy3`&D?r#|a0-hWB`(PuvCfpx6xekk|_MWpOjy z!{R2mN5pM#kBU8TkBOadkBd9t_KO?fo)CA!Jt=m;Jtc03ds_6s9T2y`9TeN%x=U3O)>qGt15L zVLr^gr+L?+<3_c!{46*0Dq8lMIH{tmt4hA_@W9AVGy8?t#(${P zBl{M<@(rT1uZx4i>)+r$9a8Aj0Zz$NDeb$QaN+~40u2oz)f?hr@u+x2+%5KtC&ZKI zjcP0!gxKBUDa4;VuT#U%2ycjQ3d7=?V>;o?H*`ZI!kfZK20jb;tl(AS&jI!dqYOR= zd;##NAPQr^*N%UdZG+;=zp99DqBORy(At80&LbaD!yV#;3*X=bpVH!?F%~wCuo2|+ zZz*HMNh0O(JYc4I3Ax1wTr=$l@*`?k#ab&1I5>N(c@7hVrHu zW)PS*HE;inW>mvx#>o}Vb2;?v@Z_ZlaZ;QL2Halv5#Q9f*S*1gawd4eKjj|vPoDEl zd4oRqC&a6w)af3)AiB?DmCQE&(SZo%&k_$Nj8B}u#_`A5Y- zz&CZi(|tJT9`j8EMJeF+PY2z;smtC8pWyai5+!fYkI)IyMrPs?G1_nmUBS7BIDU_>^tJhpKreMnqH#uwD?~nikboO02(iOg9O7{JeaA%z|2(8 zd$muJ{1Spq$4(bw6^T+9v<$@0G?J)b9f!1*5yVq08#5ap+B{_@w70P zd|l^SM)tnZ)v@u6r_J4fzMb+)Gb}co#qrBH+hrX8XwW;c7Ue3T3=7_lF&XKCcy$fR z8yh=zo&nad;Aurkf;j4%d_Er_bmhjoL!#toIeC~H`ZasC?Cq%^?)11RBfl7+Pag<+ zrJ(zYFL(i?v{S|!7TQKUt#BEl?>t6MyZe$KpZDCvjC;hbj0-_VXD;iX;m#H{t!FQ# z_Ftkg?wxRtN#4H=B-9VKn)t@O9lPYPMT*%OG3{L&_&%lH@Bv zZhcDY6@-)lGs<^65FX4NRX)>4UxrK}=b$T)>7sEs$6GSXx@FG?xhly_2083I8Gb3bKL zCV9$wZo)r$apatM-Zzyp%YZl~qzW#3C7<`)gqVr4sW8QwCGV9?gps@yW(Q%SX_X1L zDj>xfGeD|%!Z#&i+<+)#Q43cGQUVsFLrAk>LyB*G#;*@)TJieYGyaWd{F!Hb%QN2djQ{F0em11py*s3--~Ei= z<1vRcd*JB_X?pO&|ITN8_cK2HjBkI&?+O(veHU;GlK;{rQIJl8kc?&oC3rbbhrb5= ztsI1Nl;a`Tg}F0WwPyLCiZ`ASPJp65Vg8d${2%hSc%FN=O2^G`|4@5{zsvV~G%1aQ zk0=I4y_ZCSI=fQ*)d1z=c0cPs&^_GM)phdq;Ysvwhj-K~h?6tJmu5PG-oV9<^V2>- zq%Ir2EKXe>KIS_&9Kdktz;LUWfb4)HC6oJSTYfNN)mURk6Wyjr-Ty&wBsDaR16R zZUr#Q;i5Q`vIPAX#i^0t3?`F75`)vy)T&xhJ6Nrf@X*JBSe|(?Ly4P}p@2^v(tHVFPtegtihN&Ex}x*%1RJ%T=1p!y9#r|LHbi&VcU=u-XWV5#c2 z1gX~S5v)Gjc$ETW-(j>w;VT9>kF7uCWRu73A0xhj=3J-%b0-Iuwjp__Tt!nSr<)Oj zP-zBzZFn}tC6E-h7|4@aO|dq-Of7>(A*~*n1SOdw)&#o{%{U4-&22D1LGfxZk7n|5$cf}2%EcuIyxM#EaPPijeHpZ(HKBxAA%{@`1B1PTeL1x&%NIpy zEONww-of6KS@!pxIGEC&7lR&N^5XO5%#@UCL;!t|eCNcG>>Q9JV11u3FT=UUC5?p- zOPb=wre%jaS{Zk=$4u={4V<|;R6Qarm|SY#${7=tait; zsWMi*W!Y38Yv@@v)y3)&QWSHwE}IHt#f|9-yUp;6O3r9`dvvaKemr5QPgmLX>!cAB zjirhvW=cOuuT@i0B^2YpqbW)6wNG{$lE@_4fTd2xEH0)i6gBdbKOcqkV3P2mhSQ8d z5U(xw9b-Ibju7+9D%|POrF4v%WWGj!@A1Br9<1rK$T}BaB7g#OzeK_64OyV z4Cfm6*l7D^=2BP!CA6!1xKFlB$n-&@v~y{ncb?<*o6Xb-E$TL2~m2Ea(Xm;KruG|Dtz6 z`ot+FBLUs3NNyOhNGN(IW$qh&I*eMSESYjfz%3ZZd{Y91Gz?JI%h^RWRWM3b9LXRD zq_>flgyjG&)z`SECeG$~H?-uak2~t4>l2Qagw+$%dE_x^1rwo2QW7n8tNM~wLe`gm zh;Tr;COyTWQX(y+V;cTAfzlDTnpJx;|%eeF5&8 zT-DV)Pp<0fp2_tExMSM$TFgmqT;&3?`Mtd-tQjxK!isNI>++;i`z*^rXfic<0XEIa zF;zFj6B$;y4y9y{`>pDfC!Ja+8e!}aw3#}+0DEnnf;CF&utv~*s#jZT@Ri(6&AVpk;WV?_0l3h#zz02xG1R-?KY7VFA%gNxOt5JvO%pUG`6n5e zGsdB*fGWicrAPpJkTEyI?CcMj-+aS892~xUrZrRs#lV|k2+F&M2RxoP-W(nv1;Uxu zLGZQ4438p{Lx=PVnjpPO4x6zqMX&)^Bn3xgY9NV-PZh~R*{TR1MUi!q%}GYh&EGF=5-ZY;$L@HpB}y zBy3#_D_!Qa8?N=G8#z<)lCdRjY>64SBu(}kw(GXfoyGGU$>8!r} zR0o#<1LrdO<51#`BOIQAYxepcC|C2Ep(R z{g6p8V#~<{Y*p+~%i%a{;HqA1sOVU*aEeAgS=66{Mk+GEPEE z2uU@i9i0eB=TOQ1QCee^ha@zDSKM&GKw^C$mQ#MhpspjCRfK~`Ez6r4>Gu(xva)hU znBo+yiY_2djIkK$Jw&f&OiUvqq+}Qw2{|*J(5ZxIk$<2VDXwv816N)*r+e3ytg4;U z{mfJ@FPa9LmQ#M{iZDA!BNMU)#m;`e%z&2tJgeexkk}V zzr!mn{R3dJMuAKCL-I$+xee#J2GVz0-9R}6yUbjG#7;;M1w~5*wef=5Nbtk(?eM~@ zi(g3;?1`E7u!eeUDfAOHaAYK985x0!3xpMZ+sMdU)82_pjB#W{@Q;p+NW?Crw4iz^ z9%`{cNbS-s76hW3GM&gM0;TUF>@Fv>Z?D5I-Bg_a51znlaK6Dk(P;Fhw1s<#AK;&8 zb^5wwS!J5N>(_l;dZ#qjvMXNS!$KaL?JIggKD-^c*SKXzO+-jI8e*mfxl?F$9?czv)G4eU@4)kAf?Swq0vtaB zVio2gyR?P*aZ0u35j28US>G|**TfQtP~|h^#Gcns{sY{M&Z7_A8-&i$?ahcN-t%7H zR1Qp{g}ncuu-~IzkRGVD!L*;lmIWg}M1q(o=q54H4VL`u*=82Nv`pQymkET;XV2#5 zddio86Jo=*C3x z`h~_HccM5G9L!I@SqPHV0lE!=Stj6CB6jb4|rA23TAnhzplwA3TH*y zjDEFD(XVC{kkY5f3tdgLL>5rl6rqvnc3)%t%e0}P`fT@(c&BDMd!R@6P6YOJs#?<~ z>UCN^e<^EBi*`hV^ILB1yty+ncx!LGXosvZ{d!d`Pl``u;>mo`e~3ivi=NFeR3`e0 zR7|K~b+L_92Cdt_1YhMSN)+u_xcabYN5ZiqX4=8NR=>w0y$=NG19JWca=r&AWImSJbsauXL?Ozcbp9=I~l*d`d5t|0wcOABCi_ z@sy>l#N!A?K?mBX6HJVT>cQ_AKu-+_GtOu|W~l#2y)?i?VwDk1*Mds>d&LOp!|>Cn?o z*DdQ!NKOrLXTzNSvDJB_`+E2L6>;mjB*^>G>qnPtHE~u)?XRY2J|XJexS!W?UdHD_eWgBT<697>Y@2^^1UEdlwEqHdA%V{#ZlxKF+{l!8*r zSy0tUYR|A|D<)oFvtr=&uC&1f>ahMPhnGxaDA6eX)JGL|+L6l|LZA_K`7}GCf#@zo zAYEP&$i!lGFN%e@L+qIu?3o3mg;+By{IF{jOz_*qf(!&EO&M214#6UVp%biN=4`-n z0*45b9sWY0R45ROGO-Q_Ud4h_Gyx-j9a#&IPa)E}GMF~NMSx2(`4+=pDwGK>yvs5; z2H=zcE}yY_%B7#68$y58n;}>-m4S8W1ILs2tS(-&m=U=kOQ*`ye}(0!Ih?^)EqeO@*z!1jEm zKHdwWSCETvXQq;yw)!tC2&icQ#2S*K7umtkL+U=8K|Gt03eB`h$q2mEhQ>01W>lvR z9*$E5segpPRcx`k*P1IDX4PuSXGJWLFZM7oQq4WAMI7bN9#X=OsLIQrLk-vDZ1iar z!3?-6i3);ak)JH9&O=D`==u*bg;9!w(5)kKoYFI@EKwa&#*s_Y0W3e+mZuDi{s?SN zD!W^U`wtHu?mZ^`Pe>#Eb2ymy#MMauk{q?y*#0eXo$@kI^E>3qp$zI;)0ZVgP|lQA zMm+D0K-RS^w{(1BWJ0g294~XS+>Mt#Sz3jcb-AoMa_S@df|MxRL`cg-lD=(`Cp{6H zRlFVbIQ^~sHo^>}Gi%l_Mw*_hKU=Z}sQ!vHvo5*?n(dgW<@xnet-}RWWYx&YD|Raj z#Z^8~%@n8VI6?BvlTTrtRccSG3yfa+W2BHiBZqx9#`@C=BduvC3EIT3Vf9$^vEiVl7}d}nxR{kHh}ZU4*{TYo5KI`n)eAi4Y@Oz_a^ zZ3EkYO*40|B)VY*hP?>dzfg8%<*f`bRD1Jt6)?;QgdDk}3IVTE9j&a6P@G`!T%B{s zi|m9i$IAbK628Z&G83CC3Jp$wr#RB~w-0R2Av_nMvLR%|Flu)}DO!5#WRJ z4mh@BppqXX&pgon&{xAlJPQa-27;`NJwAN9vSWV#Mdiy8d6wyMC*&R?5AukM!-oJO z6e+WRLXfwU$(kYn`E0e$NC(rWi>teWLe5cFkT?k8#8n<~y?12JoGdK4ReiI1&X7ds zKn*WMJESFRcHPQ0 z8|n{g@>?fG344h0mIiA$Q%5j`gKQhaM!g&ZH7yo#SP*698d?)hU|vX9px|YwnN&=1 zLF~lYek|&l6`8R6F>n~!B57J4Q?#0{nm$Ix)%wOlxx!j!CeTma*?=U_SeQs-(R00L z$yyz^R!0UOTGuTXmqm2%b;oQq*xQw97BSSyqjqQuLRg2YGeAMXTu?`jz!Tv`N9|xc z+&OH^h;K~Z;AT1;xz%XxpfoL>Wh$V95c$z?7_3Um$Xa^7l{7ez&FWTW0W>bxvbu9Lr$!ZymXwI#Uzsfx z_+8jl0vVG*fJy0SaAHPgP$?geS)L8FoWo$*58c*Sj?gGikbi|p>3=5YpTWV>!ziyt zx|};}r2B|lyU?>aGZLo3^bVpiWh`7l(T(Bj!*d!KLfq*2y`E%M-G@E5dzPv;#H%(W zsy5CYc=s4ofzF~ibK0IkUb5H5?X@5I;`R+`jn-bXT-Oji@bQs5M-p`#=5^n(VImU# zoHS?tF_rLLF)vF|Fw&NRh^`$>FfA3ehaNIymW z7%wx}?=i} z?@DFrg2i$ik+FPbD`Vojnh$jOG4c_r|Hj(o5%I9*=tHx8SFz*tz5lw#}@FP*Lu5k;C4j`SMSZfUyKl7cG}d4tE?DjIM`{rsd+MXfRRS zK6fCql*(}*9gaIXmPdC!M)(0hJPGRbRCOrI1zK6B(i6_X_GZ@1ekA{>1DrZ zM;^rm$OD!Fps=p_1K$e8OciV~2b)0jCZk>J_f+K7tla@h%EYvX(9g&u=OctkQF55x zjY+&eCXmUA{|3I435R>IaT=VF{x^ys{$2VRIo~5EMGoUp{vG-5lS2!Q^f@`-Cnu}o z{|SNQ?4?fsH9Vxa)6%}GO<(-D*v0mW#5k-y^$38_=5XE^zdjx*zV1&ODa6D9sr6=S zWZ>q86*GlY^LkILvV-;nJZY%@Ugjf>1iZ{6xuGD9ZQJts!7s2GP`;v70@5apz9F)n zb_%c(Cr+)tY<@J&;T1)0c%>~yy(_Xm&EXa8rR8g3I^MW-h2V6N zM&C(sc*(hSrcDlgQDprJhu6wFoxUnhs1)@v|~)BvXfrvA~EA$oI9hF395xH z?d>t+S=!&f;Jqxbo$?2C!!mBdr|#qtJzO=}X}Xd|ccl@|AMjvU@s^+KU~Rswr^yGA zGSe<)qX>G1z%+<5RJHW!M@FD}eGkdVn2C&n11ZDBD_FS$Df5VIe*}vQDhpCWX1DqO z0u(A;-EgR4S#JM^#sSUDUy^pub^Xfq--!gGhZ4-7UtV#H`IZ*v6bPD0!@9; zL+n|(hgbG{ldViX0w-IZVLOFQe&FO`31M5K&BqkNf<}|MfFggqnHn&rB^M=Vd+4pr zN8;34=m-b4Zw4sfPs5f;G6L>Q85k!NkPou)A>RY{xu#-Zzn z=J!VmKPw%rNcZb~_HeJG;6KJrjkEf+(X zuliET&c(`qMv>Vs#dh8unVok%^)alNbZpn%VCLwb;Djxq9u`c-{9lFoTQI*c*7Cww zjc6XT2v%&KSy*|6f(`h279oVHOArdpJfa;VMlXMNR+qm^O*JT^TFE*479DJCnXu`n zA97}D`QN)&^4H|I>I3v=0cu(ZyNM#U7J136Bgd@MmwOgnUU7-aD)LH>iFBvCcRJ{Y z>d;5~fAX2;QCLWjY>~0_%)yq=skh^h0}KMOXwUekGe^paG?OYG=hlf_XB0C1euaFK z=p$DIi#}~ZdUK0)pTnsJcJ7_5xUz>7WOdAO#kMFXCIE(E${oO&jEv+Du_|-4o=I1E z>^jQu!L|5X`7a=Axr;$QZf_Qbx%tlw<|BMrOg4~og7zD_S#4Ij&7LOAD#Zj&(SPLN zsnT-J*}{ByDrc8-)-WHQI-!D<%5cwHt~x1#wJ*eTE3v_KiZ7erdBdzOxLyg*>S|~8 zI4=N;!Exdn);x+lKWoU~w5&k@sNxtiIISR(nBoQftOfC1 zYa#(c^Q87CFgE4IpaH-hU~EzR**EgYehEX^fO|cRVIy1>59W9l<#7$ ziQKRSu=xX%n%^EZ7bG_TMv969rbnaWhHJ+t`%W3T4o~m1NByxAn^z6$&=GDC2mblm2Bix<*lXp z1B=>odA?+j^g`@WzvzEWPpbDyt?9{=O0H{as@^*lJgs2lY3HC4w`)P7Md|QOia(y>2c$E|C)ccx9}QI;W7ozg#}=}ttE2ysc+5Ja zfH+ZH!jwGTkBUBa-En=Y`GM}P?oL&rxFcqhS$y1*GPRef$3q1zdr$VLcS7t5m9`9b zgooS0p5Z_%g5lDgDIFmleBqGB<6&1Ogmf&~QIr=lD=9oFo#giiL#CF_*1aAilV}D! zGc}x>IWPIAFNHLnZ6T{1Ntw2VwBg~7XSxP#tAw;Iot{wTaUZ+kM$u zlW0&EvzkY!QY{?ygUp|;a$Bf+O>T(G?C?Nc8!}VVa7j#~C*+XxLzQ>408eMmA+IRf zAl)J7Z^w*|0L(Xz)9)GA!0LGPx4csNX~h3#>lxzj*pyi zaxRiXlOvTmLVvpVwaj7pOhYIDXWrQXHED{_zeUakN>d_NH0N+WL;d@RgQn*X36CjJ zzD+(8MsmuGPcEN9OBK+d8p(lkPt{QeWpzZZa_A_~YPpB}@pI`)NF{xk*9 z`+rH*<)xIJ1=2+{0R#s~u7X^j`{bBf6|A0>|DLAeC{{;o57`QC9KL>dj)z&bx$`?P zrN-<1!VfEMSA4Yjb}f{s=FX=U&R+DLj)(Od7q=vAJC<#Q-)VXO=z=R@>m+~c`;!ZU z>{wjkcbebthNj%sPT`*ShZlg~MgF$;zqWwzEor;4(K2U#TFX^7N57h=*gRM8A1W#% zyOyeU#;bNFD!S(i9+j0xs+KCY#w)fa%C^nf9<7b9tco06s@@f^-j%5AnR7r%wZ3bq zao2;!UALR=7tJ{y*R(9vY>U@yo6|isRX%pNExCK*?jC|_l0aR5FuwjELFe)`<+YGr+(acum0Yt zs4jXcxSWaoedCt){{Xhy4H>S6F(}3$ugs?}yN5&aQN!xuoD}DQ9;tvmBCDP0_E# znzk*D-s_C*JdEld;cJpLZL#Y1g^^hM?t7*D6K1qt(%Zh0(?K#aa~f5*6f$ z^e^mVl6jRcu6p+@M-34hE=O}+tVsV#Ke)V#Gk)p_nSXKMNfyKI5S8uFz zU&6J2+0{e|sqJa4p`ifV#YpHdHk3at;_TH*MH-`T#u~Sv4qNM!^&MYYr%h?Sreg1k zR)?lrxRQpCx<8#wxOP(s4inm5w5shdC)OQ~)gS$AFyTC%F0s}YJgwyHO(+r{!1kX1 zVe_8*hJ^D_TB~U(zyLwLjM|oRngfk;y<(*;372QNr0HSH?wD&&PD7ds(!awWCjl$(qi%Db~=#v zv|*jH(>trrweZSkyb{(^3WYPz^)u_Y?)~U@Y@m__7@5?%+cN$Tz6dl2S0wKxT*1H; z$BK0zI-hP?z~NA{nvQLdrF4YuG05vrGl+J!$x;h}KSJBs0Fyr)qxdP4oJ&XyWyrn) zlMEIhSr3|hCs$N@tM+DX)UcpixNuLODBLq=SawuKwtTqr_Ri?53xkV05>-9-g!?Zi z9LHm(h;w=iCRbGr~SQ+~fTcE_U{EIM$ z3YC!TJ8Vz{ky+!^zYgmK`2e97b<|J&8FDF7a9J*HR(E5*ixWP{AO`LR%ak-Ygoq1J zFlaKf_Z^7G*-b2#<5n|mujQ?opcQnLs#u(F%S+G;21P^~r-Kk{9znfu6RkUgvT5qF zhm~Cu^eAxHFy5y4vu^=fQ6>NE)so0-JMwB*@K|2e!3_%hoAb))^CIM=Dl%A3=Af#! z2(#k~n3`MFymgp{Mr9Jpr|stXcXz=I(+qnqRW$^AuD!FSV7;1771hUS%&vI^HCABf z*vC~BQ5I48zO&}+_YIp@f8Vg_9gSd~HGR!=g;%V^=$18)J@l>24J?_{>RGxh(*QEB zkc~2At>#%}=AO+G50ysKNeP_&BuYah+`Hy{1xo>P53Gzu2eYWm67rN0mziC`t%9Mt z!!t!14SGxb~Bln^3{@6mAU^ z#18oaId-L|+` zHeVp8QbY&HT#tic#}pNvGT;_UdYk*Mk{t_nK%^)kh2hW=fn7bRkaGJT1>n{UR&~ii zL7-HeR7$y-6#*u|tRry5k+J_o`Etl_gunphV`lwId%SE3lt@Itor{6<0isOGimIG^ zXhRVpi_DG_bw5uu`MU^Y=Qz_voM*!d*JkdF+ZyK%&i6%j;zZT^1#w4H(pmnvs&TFl zCuJX&Z+zg`xLoe}eSNa9^kL=32Zex|Z|WZxmcGCD)8nzq?s#E05+L^d)(3@+$@1!@ z@~(J!*J4GYeAi7%74bYMY)F>ZGa!RhKd*mWR=rf_iI;gEmbEQcb|lMp-rIJ6%V)5f z&{UPewxOgHEKyO}`|S@3o6L{Y~>TgQ{vcy!?}pT5L% z<*+2wpqk5-H4*Q3n?7v6-5w1jD%%$J#Va=~Rc?(}ZcS8fpWlzhq7K{cZMk2bs5}xo zc`D{Q_5XmcMABaeh$z0Zuj-(k{};8J`!t$=S=hS~z)v;igBJZyt=$Lp`ajp};s0}s z7U8S-5OsmzH+h3&u&* zUGv~1g1Q9G7KRJ2@$c4zt&%J3$R3Z%F5=lc5XZEPQP`YA%pKXKSy}YvdFlOdVZNnT z>6_W2RXM1;6iN=#XJMSqV=XTZk`9bL*v8`!jvXB3D<637KJ8go*d-LOFS{*VJYKJ) z&A!z-l!S|OdbLFP(sWBv-sLVk7Hr`X=!A=K^clGSfS)azx`a|4Y%E;FxNFRorud{0 z^hmP;W#39|##N>w8y9R<{7T&dJ!n&h5}JJrYTX1Qjjh?zaH(LpcmT9g6ShnB!A=Et ze52yazQe`gQnlx;;bPc{mR54BTNvD|;D^=UfWRq_?`L)@%4W-RzG!)nxMudK`I@mO z0qr~3x(o}8`B5%7*_Rsl4Sj<-w)Ro_4Rt_ov)z)4*~)MwMzR^DRD>(Xv@)XusPe_% zGlaP{i(l|~_kxbg?vY^kL?aY_pjs=VzD40OHYTgWRRL4DJi8xqnU}EJgZ5SBlv?%T zQp0TjMlPG?@Vg)DxT&9_Cw8xGD|UG@L9$`7;tArxvD+*il(VOfqSnwRi zX_er5A+|aXfc-kKOJ9hs?!N$gAF#_oANGW;fudlq(h8v>T!r0{N?08m*Rb{0@%;)` z_N}(HI$RyDVsonGx!kJ4)hti2wQKUEFXMRb%Q*gPzKr}$Ui>8>W?=3Mdg`57J8=85 zEl_-FOKQT-x97r6p_=t&A=ZT#v>QD+0$h1!9R54ajLPR`M*bB^{S6YHL+S44WL4mGv{hol~yxrK%HuJT-Xq{TpY$~i+gHv#zP&gWi%@> zTOnxD4&2%?F|U31?=yj--izPt;h0Q8M%jL{Od!QW2v=p1wudgY%{hsZ36xK#k;q_C zxKs6mJ^v$+)!a;v_v^4TRJ=45IX2SNyrm8 z_a=0AyEk`ssfAIYC+OaqvuB5=&Yo?jH}0}KD+@UUkeBQwGWH#iis){uRT-Q^+{K*R zZ2EsrWLVmO{$K*fGu|Sx5RzRax?Bq9@Jx!TJ*8(l_K;oqVFIQNPL_4uvxtJo%Ov&%h#o|I|;;d|Ds(4E!oUtmQk#LjL#gC%ptf+DncFjScN) z9}8k6J{PtJ{1asMLPLVdi^2>xb7V^zcBXy`2k2=7OgWn;9j1(qkaLKfm&qZDARUAQ zi&7TFLMb3*%dzxte#SS4?9DkA{>_q=s?_@>Rf3ih3P>`b{7uygwYu3|0%#V7U2rC& zw`5{8{mcY%!9iPj)P$iZsP4&+S$sjRFlkW4!#V%}9D`jR_f^Re^+vQ#) zM7>1Ur%L!qEeMggi*e!5fEplPilG7b`x}&8DXUpBo&|An-(BCmP5+}m=`5K)H*bE7Q^<66{nkWbJ&qz5IOep;!Xo$z%jQk& zLY0`cI$E;O^bhKAg>bBTW4w4{%(icFZ^E`OR=hE3E4*>+`msm}8CrTC+FBoH?jSCS zN_Q&arEPOo5}Xfy*E(lUHhbnA$?6)M0fCG^XMbE!^tgTd3Tz7OMR8}#yng<2WG3mV zxOMdAQD}O#30M0(9f@$2%8g#4eI)*@xINjq;RhG)Uif5u{@~|j_0gtJJ0F(qL{9FesQ30vL5Q2d~({c&^K$6vkk)$h$lUQTvx`ICWr<$ru;aU|B& zx72ks-gWe|1BtFvvFeUwQ)g^_&tmC4{+?#B65iVfQBY0uLc@dVj>qoCCHJNW?oE#y z*MEHU&e883UuxWnGZ+uI9eLPz6nN`B>|91L(YR&tt$5=OSc%s)M&J6QnY+i)p2m$! z4c+mEZpis14;v08J2x$ETkKnyh;_Vt@74Rg_xIf!f;W2b^QH}phD6i$FSyE*O}8x( zZRC~DtJX)wPhWdjwe``u<^{u|DY0&6ta>MQ2WuOm;}2@OsEaP%z4*!GQfF_xvp3PX z{|nAsv+j;1s*S#qY-ou$Y)Q6uEjBDp$69+HH1;GLTNgx1aj!Aaxi{XpH+t|gs{Bz6X15&FgR)rG6cD7Ll~!&aTLT|A2Fp(|>Xz-n>8A)Q*uj z7;D=3z`gS^F8>rh9=|ib=t?wgi|srTZ#ojMJ`y>Y?Ai!z%ag5WRl0|(uK95L?d{*) zIe!2hS6=&3?SrxnIL#T|{71Wz)on}Fo8r}*7B~N_dV8{_K05G6Z~T^lBk`Ioj~X^D zH|lm>El?y;D>P_@iEHa#YTX@g-JNLN8#(&8u603pP?wv` ze*fbAGqKYHvBp7kRVzrwdly7?M zs*DI9j^7@~7IkfB+|@aM@Ugr8W8)p;_sk3H78;@ji;Z#jws||MP+oCs`sVa|SLSu- z0#{9}xaDz~J9_XzS!=Sq=0o#s^9R<)73=VQE4q@cZ9izc+xAI^TInx1Q$_9VBa!{l zl4MeRdW3(3vz%VbPOAdd^Y&E5>cS^K$vg_55d52W@!$+m_zlcx5ic3Rhzz_2ht3P=^`@1AXb z|JYfa){_U{J@O&SIzPPz^#Y#az9>k5+ z|BzN+#OhC;HQBMC)%mfY)%9mVsJ~oB@2x{O4`CF;x<7I}?rM#j+n(A9g>Cvh_vPhd zMVBP@m0zI`XTaT(Sm>D*5^fvB>aC?<{7w%pIG6p(Kf(gtP%lAgtl2 ziGe-|KXJ-Q_{~$WFtV{w?|}n~?Njf-0Fo%CRQpdHJCOq%Kd%0^#(~4HDya{g7)+Vu zjQUO~hEg!XI&qqiGBJl>ktO3C8U$9x((2g0(`3SxF|K0J(Y`@7_GItrzJ4W#V`>h^ zD2GhdkDoZ8dS5xF;P&?^rJX!{vQI%cdEzxC>(i?WW>8MSr{#h(=2ruK#}CULktuWF zm3>MF4IVyrpiiQ+0P^Mw+myLNpM>^vWG2=};5a$NEX$mDc4tVWurYGZlQT&U+d=vk z`6?-w3*=)vNdGJOE|Ig7B2JJa5D4j;liB}w$wyb2f)khgAza@g2Bl4ec#51TMN8z& zkTXjTQ7q|gI4RvR+}keOw%sJy(hnnlK|Xs!x_)*|olN!81RkO+zaZbQ$ss}p;gr9a z;^q4oy1E#HiXSGD?>62eeFrJ=Gj<@_3SM=9h^f-zQyE6lqR z++I+T!m4=_}AajByo@D&4XAEs4slF|KTtzq~e9 z*Oe&W7~^2_maUIk?`&>L)6<43yC$hK&Q+wf@I7WeJ$x8ZdJ{s8_S?x{KscVXgZxekuSyq^zli*`>0&Wkx{q^KuM>yr_2J$y@){>;TFllim&F~Z@&F~a6xBQE(z`#uD=9;@c z_TTYCvRHEMxGhAy(Zcyt5&q_2!nK3G9)5LZ=hwe>yEsrt2pBG}@iZ3%>a7{)f#c9y(7ZOebT;Q?XO0W4Zy%PKV~26_dZH z>anRRX(~xVN;H)x=@4!W{Aq_tZ&=Yo|JOM)k%HHd?>m*wrPJ%N0Yl)bIp;q z6!Pn$18EMgg@F}%rCaoTd!!jWINu%}U7=UnY2lk9I4h4A1>u#h)bXv62Jm2fYqSA8 z7~h&MXySK7X44#Ai-kWb{UJoWKQ;c?cwc*e;Jzuo<8XZAkrj&k#cAHg?|s6-d0Jb+ z@0{x;iTEi8?=N-~^CeF>IKOBg=lRMf_*Ejla%D(c!WTa6vhW@AuRKNZ9ZxH@e2tP0 f-e1^`8~CCxIC6irath+@PG&gu7d7@1J%;}u=3uwa literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/jinja2/__pycache__/loaders.cpython-312.pyc b/venv/Lib/site-packages/jinja2/__pycache__/loaders.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7d15e64576f558a1023c4dd185259cc166b3128e GIT binary patch literal 32349 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 AR{#J2 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/jinja2/__pycache__/meta.cpython-312.pyc b/venv/Lib/site-packages/jinja2/__pycache__/meta.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6ef010eb54b5dd1b271fd7e1d704cb34b94ec38e GIT binary patch literal 5484 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 BP)`5= literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/jinja2/__pycache__/optimizer.cpython-312.pyc b/venv/Lib/site-packages/jinja2/__pycache__/optimizer.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..462d3309bfab7939972f80e1faa4fcdc6c09b88f GIT binary patch literal 2683 zcmaJ@Uu+b|8K2qP+xzFU4L)N7X)_W|oTK0v8c>TPQXnRU#ttfe;o@|)?9SPH?Co4; z_H3WIRH#CVd1!;BRtBjmG^$i0T9vmXPf_xk7hGClt%8IU<$<@OrV8RE`DXUkHpuNr zH}lQRx8I-N_kHu-m-&1a!T9*Q{jM+&`dV({0edx8{{+hta!?65*wH*((wu~&dx@%E z(lG)$=_RX1$-szemCp888y0Q=kKv`OnNkM$Q&g`MHe9z!C+%dM?B;i6!^vILOF0d_ zft-$u$jMXVW+M8q)X~t2oz_1vLKD-Juv*|&-FeDMz+Eaxz=+L!5m-1;=lrdmglKUVY8a+PZ8HIGhVdrmh3EeIYf>#4qjT-nn(8#kFgr5`^4tHA9I-0Gu-A6ws5_(;*^37R;+F>mybG#v+1(yABZz z@Ocivr!=d~7XjlSTH+h~&+8r)L)iy1fa4D!aHxpGq|Y2Gfan?rVuD+~kSI|ON4Zgd z#^ubff*qkQ8N(c1KjJb~LxaGidVV(B;A)|>_x=YJ)!lSx760y6CarVvw^R**e_5F<6p zrO%KQ1Tr>o?KquPh_eL7SxfRl;z&SaSyS!|^@oZ^n0YHs{}2OGHl!<;K83mjHB8R9 z!VQWEo`N$j-3c>m1tTO3pyA0cUq3WCJUo2r%p~L}-D}yFL#vI+T4QfuiF13W>aIf} zndl@aotb>oJv#|P>E4=UpR=Z@n5?*d#oGV!n&6>YBTTRDwt^N}{$dYI7tkXEZP{_< zM|Zkkyl~>HT*n6oFCF~-3ro8fcVBU@v8(KcbLWRYzLR_9w*Ja0@FQDJ@rdKGx&SBd zLwqSJVR)xBC*^4HUL>3ZyeH~~i$950N2kVSFO`%0ALn>;Ipy>MDr3jE*iiy%4wo_z z=QNiY!80&Dm5VYHR4z&cmQWKnkqJ~Z8jv63MXX{{)IwcQudFsgentr20!x@SP1kn= z)12SB7U-e)>^a~N(%B1WW#@|v`oGitya%=_*r9ImDG?gyX65>nT{6GB;I}#q74ZkW zu}UuKP4rvHpC+EdP3=Sccf&=^#9#35@OwB&!Z&I(@&B)pxCZ!G25sQnBrcVFEOUKr z;)=BHnbFjEvZ*bvn{=oF#G&7v zQM|KgE^!9U;$mW)%LqZt3|SLCi$a(SlcEmg9OkE}Pq}NGs`SDv6rk!xHf(OG8Z-cil$b$YTjXJhncK$ee?c~*y|H=)vv{b(LtG?%!&M%(7+qZAIZ{JFxe`#!S?4yb6 z1J~dEaPU@er|{~^&gZXrG8t~AQ2+i_r1x)aA$`l%RyXP{EaVdv^T?;e78(cj10l&qA)1g3S{lgLvym?#N;E`Qe_Uy@@Xh80Mm`keTlbhNNrE zx4gGEG>mstDUKJjvV452N=-A&nkMvpXhT4EnC826%ZqM!AE;1~;qsph?|>;x9Pt|p zyD;B|)~23knozF;a4%#h`B{XpDt$T0fQh3pJ-&!mHJlu58C#NtmBRL=lZz)?2}uvW z^w;{I>k<8vpMIA7G^yzOna&S(T-xz%5_ldWn4&6{LW}t@WttAN!E7UF$UhU2g^|f& zN+lWNT*f`jPn#GHXd%i(E_t3!og#P|(zj^JR{!+`(@qx<+mJTf*f^gykH^$WR3H_|{ WX$7K;4?ODZ#f8gV|3$DUb>9PL1BCSe literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/jinja2/__pycache__/parser.cpython-312.pyc b/venv/Lib/site-packages/jinja2/__pycache__/parser.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1be32c672c1c520ab1aff5bfccaccd1a1228245e GIT binary patch literal 61197 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 Qb`Lp?MV}hz%s}G*17qTGQvd(} literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/jinja2/__pycache__/runtime.cpython-312.pyc b/venv/Lib/site-packages/jinja2/__pycache__/runtime.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b725d08bb51a740ba1ad229914dde027852169b2 GIT binary patch literal 48885 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/jinja2/__pycache__/sandbox.cpython-312.pyc b/venv/Lib/site-packages/jinja2/__pycache__/sandbox.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..34fe1237bdd5ea850d57a66b65b233483dd3fa20 GIT binary patch literal 18101 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 wx&JOl@4JVc9=ljjrKNUv zsGeC#q-5Z7F=7ffd=!j?lwm+jkb@=ML+&Af{hGG`e%U)c_&mu02RM1Sr;Lw&2=tPC z)xER3{8{cD5M+SquI{R?uIjF?s_x&`)R?Jshh{=5;j~m$Iy@5w95Sm?k#zM;wSdE^XgW3%Qxz6mZ1UL` zYhDHVNUAnnH&X}tYIeYkvib$hjQvQPIat!JLAyb;Yx6W!W*zztp)a`DU*zaRc|Hd~ z(^!F~9yCW-%sgn)3`fPtD)Z2fw2$)4W}0?k50_w@EAVdw|CS0gM?lkBf#xV^+A7dA zf#z5Rnr6_nSDQecpL*wX9b#e&~#OxIS!ic3N$)sddkvt z>|-NUW+%q>mK`hYu?sYP6==Fa(_faR2Qqxmx6Wd{w`9#A$lGBrxDF;2vyUD8xoY;a z29yKr5XwQ;h;oP>LHQDELOINuQN~#d%9mLy$`h;&p?ln`cR%{Lntq>UX&MEKgvsN5anx3UHprpCO!nr>t!)- zl*POW%#6>YScBVGS}Vy=|Myamh;d>+Lb+}`Sy zA6KlwVUJ$(>Muhdsxv2-0|Vb$Is0ZdnR14c8Qo#&rIg_?0JdZ6S(};q-K29X!`Lo@JRk zNp5A*EaRXrHePJHp*uz@%iJo?oGi~Ih=>0L00i-GHK15m9w1DtRKctRx8SZPm4V<& z*Z0II-QiiLCl`dYcMP61=2K|fx@DU2FNqCD7*d`o&s1Iwz@}OB7ZaC9ub((^;yc%_ zr=j|=kuXe_UcSDxJnR_ut>GKlq{&jy@9c9SJ;(ZU;g z>9U*2J4ri9doaHY6d1!i;zh{@0%99@Bc`%x8eNwX)(Z&>!{?Vd5(|ZVnN5Pql|1$E z{6@zovDTc{DkR^wlp4r{&2$4;SDb=9Jy4v2av$ZP8nPnQI1i!<>zg;|(P&i17mw?< zmE{SRuuL{OstZGqC_>-wp(WY2Ao4uuJwFuViHJU7`npx59o6sOFAn@+ks2&n%4WM6 z6m30YIeK2q!Xz{-V?r2AEIKC{jyR6==t4G=7@Y&Xkxe;s`U3m_!>jtsrT~Z<7hWr~Ii+ zsc+fn`N!tx2ZnO7p`13f&%SnINTq$HU0oUecJfxKqU4a~^!cnK7Qn(PaG@f%pk17k zk?lG$bwccd_WT%#UdC_Jd3sOzk67ax`{lLgvA&$vmp?^t6+8m$-babFh9^@WD2sr0 zes`ojHp3oEPF8+z*u5mVW}K*uk!Epy+GY#clstTSk~DT~o{3GdmYk%OF;XcUz&l1V zMIJF;uwJs;;rW!6xK+-2`_cx#Wmy>S`Flq@*d!>5I%1Uz0wx0KDwHV9m{FQcKe-zEzn(8<{()*rGb@7Avt4ZSbT!- z6N!m}_kk~Epf&|>8Q?CjUd(At`^a*nM3#H1t*Mk71%zC|m5DtXT}F0kwIEeyv8Am(!xTIPh*1{zubLN>ChrcfJq%ew4Hn&>-aP_vB^F@o$GxC&~L{7 zwJ~=A1~##K#|n!;L7>tih^*nt{>thLDa&vwYwGJ667;(?&6^VWOF?bBP9Gr@(YHwW9iRUcCA1veLrXualVJkRJYnD#7#|3@L3mC# zoV10DFQbdZy@E-9f`5xLd&-aMROe~W)x zMoMQSr*-Tj@L3GmZac->(ITCi9iM+=64o(HGS-q>=3tm(1s(42EK}8j9c00>(yNm0Sc?hWlXN9|}EjH1)X*i-A z&m>&lI4I-0GBpr%gDIAAW3#h2m;-EjcGj&Pzn5U*nzC!PYDI|lC*^NJsbAr@DJCFF zN3>Amzei%L<3Ejl7ODMRr1{fGvlmfn>i_#sVWp<=D@6@8{{FyWFNpeuNUYvJ+M@iT zx;OZt+8cC3;_|om1;okz{pNS%$JRPQ_ zuUzs(7AV4&%ty*uv9Q*H9q(zaBZVDFI(@djWZCkq3ELs;4y$D$1Htm=_bUzq47eI0 z-Ri03B{t5v#mg9-Z@PQ}0{;tXM;SVdP79LJ)vQkcG*-dZVs*;Ts#}7O)t2Di;et}2 z?cUc^OFV2~rM;jcZJvB4!cHzDpnJEc0VHIgj6x zI=;_oSUa;Z_&nB=(|Ux@L6~Q^D;)~?La{Phkgp&c9+5GAA3r}L@9*2}Ymx*iwXd)^ z_{j{P1|~|_TM1mc zI1qZFUT^~`$Bm@mV+gepxPW0PHgL)f&(4}w0&X?pztg)_&N9VeR|UU>l}h0XhzAv$ z%NQ$#w>ce5H_WpcCz+P7cTm_#rfeQ2OzqUQ?~Komy*)nm&ee&x+*rv|zIf;2^d9DF zw3_1CTRbg`XInS;MrK*O^@(>gnap&8T3o}$#aG-L#U)H!ZbZ&65@Jr7B&YOCWH=(! zi?AtTf$%)yfC{sAtH-R2W8i_03%lWTqvL5REGp>E#BD0FRNO`3hNi|RrzYiDO&y|+{RN6mRy1oc%KYs1~ z*FM+!y{drPvNq-^s5UNdQRPLcYf^Si6}s9s1XbH+f-r4QO~SN!(K_{|4fRRramZ5u zZgy``<#h(s{*8_&J&${cx_?u!?#Bf?(BK8?RDJC)iClkrN>m@8{)$>#K_BgP3aCMC zU%TijsGc4Y)yIdwqE?;|kT)1mo7cX(KK*E#CTiYrNUUZr8d8URu>g`-sJ!|RR5RAu zBj%yqvZ+yb3w0Nm03;@;yoQkau3F^st}5BQs}iQW$fsNK>GtXa&`hk4zG0FeIxXfN zU7c&U*RzkZBy#8GVWR9Lkq3*D4a&I&iDyrdx<^v?c+sd@zj5$Mzt^N}=*v!o`6AX&@@^a6s+#X9q~LqavZ2)<)LPJUT-Q*0gc^ z$^FOopN{;~nP+D{IX)u#K8B`1dG-C(fzPz+)pK4D&=*>C^{N*FYb5xH zq&Z4(lcZ@TxJA;m65J-?V+6NL_&CA3oVSDEPBHe6T?BW_{vLvRmC%8;TCb1behCi{ zJVQdmI8IFNcp`E=iJ*+Ugu}p6ttN|9Bbuy_3ysc25GDoQTG1_Q{D~PW<-d z1v0(yj5d2VzgyMa07y`t*-g&2#O^|Mecrlt>)utR%E|TXHD#BK5J%A>8u4$Q^-0{bhb#Ov}S~DXKgGj zh3o^4vkn%vAnZKrWMON_HQ+w$X5pex@qp*7hlOpSl7Z5*r7SFl%FdPrEd3kw24Wf%Yn0+WNMjd}y3RPv3A|l;A+~pE9H3gTRB8|X^;wlhl)mK&J?=o8*Q2dlza~u+(rm+MoBJ;< zZ-ue}@%_u=5gS?_8!r+B1K)P7SZ4G4BF}sFDLXKY{war*7C@+V1qeOO#$n?MZ#&rA zwkN)AU*T2)5<1{`er=0;E&!rl&yHbTRFNSq_?4O z+ZnEO^x2dhNWCXe)E{3C!T!YZI5r=4Vm|EBVQb9GXAJlqQ=UQ(N0;XgaOgb!#J8Pj z+ine)3(%bvV8=?nC}M6g`U5!ZLC?AXr{gO^V($uNtwirSc-d?$_91Tp>W48X>wjZ; z&jIOY(4KAvzh?ozE**Yn53soXi0d}u4zjodh&yP+9U2x}Pkc^ApJ)}+X5R%r9u_5V zWhj7v)qg1x^i#;L`oaqSF47O_Sj2xBPg{3H37j4tq_~nRLy=gZr%#Ox^jsK@1)^#3 zkQecm3z4C);?HfoM#F{0^@zPG1O!^Uux^O~bDv2uVYW(I*o?4cSZcMTT|LKx(P%Jy zk*Z5u)j(`W4g0lOW)x0zb5UZY4vTk9C(=a&Jl9>}E}9(;g!-bCM}BT;s zTjvIRu|S8 z*B=b``?hWxiUmVa@8EFS*3+W~2GyRPcx7%&y?S&twakLssIcIvSwuj1a_iL+BvH%g zR*oBQwpxi`+ChaXL;e72t6o+ce^E;rsZzBDw-c>aYGK$nfIO6cAfgO~DD3L#xiaJn zX>ZKYn5tG$E|#CwPWf5ADsdp}Mk%6{;te5V5-qEejf(;v87qamzoam$mggu%!n8XW zjs?`PFVy1)&ZLF3*ryibMII0cG1w5&9JndOaOYRQjxu@#o(ZAHh)oEmgjUh_`xx7A z_@Yrcb}1lt`a=kI_I{fWl~)}K$ASX^Z+8SOxgP74eLP)XP>sf9pL%g+OTkb;Rs&%rpaL;vpBxJe42F=w>tMxn z`@-^tfIJjMV&v5eiMGpq!4RN<045c3MPpGd%i%tx3=PY@y;?K9fvXH=yv@CGFe--w z0VSZcQ@N~~PAa&wcM!ep$xH2(3ltZ=8dM`;>S`~Q#!AR3QO+wZC$ZK*uUEc`mID?( z1+D1pl(%^|dpEOU2y`a{fPg;`MGyQO(0aE~9rZRF&tTBo>t%Uc%|K8ZyDs%&x=72K zE@orb!@7nMx+|*n82Q|};9~Ez^lCHS53}$ z!_5DsX!rJ$&@K#gk9rSc6txlk--~!O2B!kRel^h7*-Plqp#cYgW|00{VP#G~ydaEu zsPtsS%t`#~qZ0K)>&7y4%6+#4rPM~CLE1$rKz7&^QkxK^G8|zG0jH>?%O;Py5$Sf} zFCUfKXRF))ccN15lThhk4h#!ZFHkNv#zg=A4yf0!K^$_BXZeU``^gDqmPqgsb>`$Q zJfJ0fK(hs@9T>~!bH=lbk%qR}>b9lhiA2T)QJY1uT5e7 z+7v|r8`vPbVhT9=X~kV~DO^mF6fj9fy)I&wIFQVd3;leFHI_+~K+F+aVz(oJC6*=u zyAq-z>Rh4fN{EG)+*87^&}urtK;CLf7hUp2eKBwhG^g;}><>lNO$bM+Mp@=_%f?tj zG$vlE6L_y4b(RvZ!EIFd%vmUxT8r5h<5(oVMa4+LVDB8o~Xm9!X3+g=!?HQX0cw^70+%ngwW z2Dmc_L{H-OhOkgvJ*M0Ujt9SYdGxu3@--9NZ$3Tw^xL~fk1dp~o>+ggb+YyCw$Y;x zEhQO~WT{AQ$q0B%mA+Sdr*`_tZ2hh;==Y*ou$Dfe1e__Nn)22k8V@!m2N1|(dnk6v zl@R)A43}K*YvU+_Eh1JZ-gMljUe*{Kop}MnqjOiC;R86`s^~xupzIn)OOZas^ca!SkO%g z1LBA%3Nd1j*_99y1}>sEX5a6jd)=1;{>yT(kFjPzr+_~u(<}#b1*QlxMxYP;y5g1P z)0YsTM8JfEBe6@wnhiwM0P^|5j5-bm)V_#1z<9PWKoX7l!v28lXBGG^`Y6U=AJ6Co z_sSQBVsa=F0a(Kjid4?Hu*&>7oF0mn{X@}MWI$*0WW+EgkD3sZuld3;VlmZ#4_(kG zw$Xn1s!t92E`-1|$nqiN3|#kts|?6Ky;OA6Hx!C#rGpy{vsTHW;APGbqR-LJPQKa` zKY$GMN10ksdb|E(Z*y~NCxZy3q6^RsMth95Q0gTqC>0mJ^iuK)&|V=Qi~gL@5<;Ne zfFk(3B8?MQpTWztBM7OEk3g%5;R}n;BO$KbMydOejTAZ23fxA8zo-!`jh{IyC;Z8$ z=bTL`sfkUqv<>o_N`TD6KcY+w|7iN(#B4broJZV<_?9FIkcCb0?cmd*kOiV(S7p5n z`C1UP1mmwnINA(z#G+^<@}6Mi1Rdx`v1zd zo9mGoAf0}J%^~A`656ED?*oR8Zgt`0adUYd5nO*Ea1J^?=+%~K=wT?8$KDM zb%f+v`EnpIm?aiKG0csqOqiXs@)Cd)ITW}Kd4SEjKonT2avWqrPn6^+U_39BW@NZI zCSUSh#bhM>_hSmg0xOj%L(F1WMBEG$-DLR$<}IR!<>AN>#v->kKdP2}&^BOBQKd9n zfzOmsZ`44*7ad}<+CTs#VOZt^!aKJZgOizC(00f}AdH4BrS4l|>j9z^)5bKE! zf~oX{1Cb#}URhoPuU$P1EzK5z((-!ss4pB1pJ`amr z>@_bjsvbw0@8B=mfEy-(ORzlsbE)y6wC=vtHY>HwOC1YR$$hD6R;v1$RKur)GdM69 zQDaOGkhVr4zJ_Cd!Z7_JmWIua&k(ptT1zn@Tou$R;Fq?-=t=2G)LZ8n&e1F~jhMqt zirL6RWi7cB^ZQy!=kOLeB;;}C(tiE@DIo@JE5CByn-ocr66TBI`?QweN|^OFoDv#^ z81;c&FLoBbP?b8K&Zm%wdeBDRv13MuFYRAc#gRO3OerLQDyfkZ5oq81kto zwhLs2)z9(}J!fS>a}XU2_=9~xtZ)I)nShG53P>L#jRaOUAJa&*^!8pK2zjHv>-5L< zQGux6HyG&cZDj?aT1KX@fJ8#jL!eDsCrQOMKqO8sP)es!N>(>Pv!R#c4O2y&R)=U^ zB`pZ)Asj(n+pLAKgVCb@nFeiy0O*;=Wcpg;Mh?*|$LR1jcAi#cu zJw5@XP+!0;ZRL6}zU&-COq6&KTH`e4)vXoIaX=-0^({*FCT^obrpjR{TW}Unw9Pu} z7HXUB*S5{pwoP}>l+M*|8$UXBXtI03T{3ZW*1c|_uH}B+w%NLEGu?A_PmP}#J2cTX z8U3uhE_r#bd}GSB@qwr62l1&5)9YuXndnSlu6oZs|D0!k%C`R(4_wvntW7yvz7R~7 zvQIpf3$D^fW}&1iW5(~tOhA2oUstX0kyu0b+On=q=8wE0?r9qYr~oPV(h|BF$eZjm zeb{8DUgfc!xg-Ja=;}9^Gya?I3E_QhDo4d(F*{k8uy3aLxsz;+F&&6$A<;DISA&D# z+u52z2&A`@V++VPOBlEwgl?>zqcP2ZYeCGgUXECr=IBJLlr}}8X^CcB+8T^f&a@O6 z41_^TUqL%nT08S-X)(R+3HJ5y3e*co@Gd17!N3HOh_Rg3dF$PFev0c`fB>PT86t7MI=#kB3w4y5x(==Dsky(w0}wU2Ez%--c9et0D0EDq@A|phlzvAnNpMPy@`_646lpG6o&@abA95E?DA}{&%a7PE#L0NV@&9VfQ zFm(1gIrw^+&8IbrS3)hzRVTg9Ec?V_yqQ55*H&^6A|?ye)mNZSm{~c^V^tH zj{&G05E-q{0+f2yf<`IlU)$2YZKSp3oHzU9HAGPR4u9k7b|Y`L4wm=aE0o*U(Rco} z&F$MqI$K}cI+7prOe>1sIdWbvYNt^Y2M)itJk!~!)x2Yb4;L|(EG^!>vgw(|^XE=& z)ZTzDx69a2REPP7N1y7uXzaDDXPM#^ubw;A_UyT97$R@S`Hkni=dQJ_$2`VPO}ZHC z_dw5(8tP%}O4_UjLTPIZ8v=n?+Km*duZN>m)StUd5&y>~)gL1EP4T8ME`GOMF=e-v z6mvG*Cyj}Hq9VQF8nJ{+R!*f@-jGHtSu$RT_$t<{UM;&U_8Xij+NE9DiT%E|nCQIh zh&5qSS1Cnbli#H01Hrfb$Q%|G`$$p3jGT_I$!YN6IXP`HLrP3o^^y+=6Q=Xnkz*Xb zeZ-;I6Zji(kGMvhIdE|4eab!+XToLlr#MlpHYSSuiH5+mq43}Jb3Pr3H zMqCM3KkbPwxfGY)Vs&>;yGTp09P-~^p@}?N6FWFVNk4hE6#7OVfT3HP|VsBWhENInrr6%Emgne*@I#=msM7OVy zqQ*$ksMMk?QC;_StdHM8zmv(#5ke0g!_lWkIV^c!cO|+3DaBKBtfzFw`yR`d1NUOn4dMi z-5}=OkuY|lWAd4k$4E^D?do+Xd%{pE`9nciXh7=+b!hmqE@Uz#Eh%>(HfMQQo`mu< z-WCQPLrkWIHtS3jMiC)eubA%GL8~X0_(3%Q1Au4{3M|fCL3VDJqeK2nBvt!lQo1XV zYf#7rFQ`6R@4)~1APYlg=NvCLsL^EKmm{R3he+PrOB?_grQY6l?MKx31wP5kezT@QeXjO~W9E>QiJwSg3eDn-O zheQK%+-iiX$J_z1nJR{NjD&l> zJ#irbMQ<>IZ4!oA*}Y*u_F*pSrX%bfm2{f*H?K|T0)uaWG*s7Uf|1F|uUOu5fiTqn z&@S`2#ix@t;|X)vVCq_ZpVqNov21E9TE=896?RmZZvi0a(4a!rLf#xuvMHK_=2W54 zWs-rkZ3RBs#{AZsBfsPe1*uSQ_1RKj*K;w99WA3Ev_+q_Y|?`E_%8>bam4VjtuH^c znV^LP=M!o1`f|%FVv~ubTHKD?KLV?qWni-`>bQ9M0GJV50%qjnSl+N?h)e@PTaKm^ zakJM;7Jp`7He^Z_C8-0rahY;d+YQ!v`FIT&dMFaSdqNQyR7LlB^*6=f52CaV{9;si z;3=Ef^1Z9c;*UKW7HU@~Ur4^1s%=lL>X<&2s@{?++48WwVyu8k53v}mKcqMzZOkbc z0BV~wB5PWX5s^BCL@T$airPb(XqfI9wZ$zh=b~+`aVwUS(B6i}VuLDEDR!Ap*}V;M z_p2?hwCp*b*bs{ChEQvLEVe!t_c1-iW3m0Q=y)t`xER|VFKv0Hb)UEWTo_65yR~h7 ztGU(mSZq;guZTi>RN}p`HziS7>IhY2V=^NaiFlE=cEs&_eDWpO{p@XE>EqI#O}>5c zQX@8-LqoLBsaJ7R!@kGjvvG;m8TCbqDKWBW30o^wQcR>xYCt_j0mlGVx}m|SuMftU zTv431(|$agb7>3o4*pAN%SAOZG?+F+1)jFy7A17Y#shXc=q;lz*!F;dXe@1E#-(X9 zwq?_nAU0jEt0bIZSDKm)cB!l@Jmh3e!nQej zpdO@j0D{uPnvZ@mRY#O;HsxNLdrK}pBdmNLsCSU;KjSaD4U-Y5pE)K9wyIQ7eMXY( zmG78gTVStz*Zkk*6dv!IVSH(?{L(78JvW{oe}3Yd??m72yx+KewsHH+>AA+v zxplkeTzf_jezN1KiK=%T_iI{bYg(rp=W4vE>P^#EW<6W(T^N1-lYIv!4!_fXf9YlnM&X#rlv|;qXXeUb=lP7KZM)=hb) zn%-->)Ar+z)T+%B){ISPIxc=8xa=*nE_v)o(zZ}5-)_IvJ{6y<-8y#kp{F+4cRO?| zH2u`v+U*~Ewm&SX0f?$rzq2j*+Vp`Bj@&&mbA7J;*}2tSbCvsHr{Qjy5Es_Sw~yR9 zGIf1!&F0_RHgo-7?44cn?1cScNmX)vGCpNb)oz?E**IZcs9v2soIH}MZl07L*^xC< zigtXtMsQUyjq!$gXFXJg_dW8gN1m#k_q5Mg?{Ds!-Q4wI>-^?p53ANqS(DeNu1wcV zJ7#vzblki0QO!>`%yqq(diG@Mq61reV5sx_w4TRqh@?lmX_HOza##zEDwnzoKcjqG_sczM|uy@ykD7v2mfK zJh^kWr2aw4s+*ok&y;n(r1@cWGi#kXvtg#@UPY>M-}s@2?%I?aq`DcK74DZsz^Z&z z@+Dx-wDo?=)3Ys4-`nuBmac_$Ez?!gwi$7zWv2Gt(R+J;D*klCPis;yo=MfcGeUl3B@ZQ^nmRDGGgZEE%rfTto-Oko5lEQXZ_K>w=dtPRSTE?M9HB0Ozl!UQe+omhB%$D z=xLxbfzpHoG}WANLZB4isz}(>vV^n$iHa7*I$}!{XWO9^DK=eM5QpL{VP|VS)G;GW z-NS@^N5ZK%Sjd@|*9p}N>6me`ybhKZX*e{H%gu8UjG%fbpx{Y3^&W}BTM3AQ#f4Nn z3AdhV#FOy!6J=d;DGoh_YEO6+i{h!p5izE^@Pw;Y$YYcmwIB~~t0!SC*iJ(^fp-2C z#91*~ieq8KYb?2P#nzaiIO->J`X!fAS}4C#_J)wK9uRa4)cd7B!E=?z+YC0BiLfDX zg#$9SPKIE71oo2HO2a5!H(e+__QJ`7$9oQc^Pa(d#MBJ#I|2Uk@1Y9y4czXU z(kAa_&eTz}wY}TN_B|IN16&_e_r;qjMAASHgHR8HP?qyF68->9h?2tTzt9T&acTF= zm5FX+$*iBNX-`#mOrM@F+d5m?{mUp(me(7b#8!9O4mlCb0Jizne}-7bC8^)1(BG#U zv7stKC+)!2d@u&KJ`T>wHwZ~uQQxMw<8-4=rLCk!7#eIXQGbi#eoQwaXX?9jW5xdf zAvy&Ug-sV{JW9~j7_39|!j-fG=N}M5QhwUa!mwHh!7y!5O;UD=;?g4cv!HT4Z5iyr zCU=xhoh)Hfw5{-pLNHYS71H|%YGectI~1E>>3Ha>yYFh8bv4epnnn*UxILqXK64Wv z|IIIiCQBX6omQ_)*=iP=Hl%DD#vBvpvAa4Z@tdo7OvE}}Ex&7dH#lRSIW@Q8={dP` zu4?xeLb-kQ_@S}xiPD9#st46|$pB*w%Bz#xe{&CtoH(#hu_|L1%Bvqagc2Ex^v&-~ zerK+_1^h!}OR7YE=qdZbw~|+;>fi2}^{k(Yf9%<^P$y3~7E0=-N@q(NCfo}p6-o1? zb3)=t)v2v>l^bR~8>Z_%_Uv5f*qkcaJmE;5HfnnY= zA|Xu~)@f(91U;mf6-jRc($W65#kT`E`63-llYN>M52K3AN(YJyrj_A4r$i_(Lam3x zJseW`iXm(hL#qP;6OM~C~kIB4blP>I8VStmdG0|T2)_^tkg zZXePciHV6O_3BdV8awUjF&L_UgoI>?6+MRlEHFw0SJj07ogLt$A9yfl-igk88XuIw z%Jc1(v2NO2?;r0^R?ijJkC{I$s~O*xR7MXxu+!{{&DqzF?*FvN4a#%l?D*NFB^jM_ zH;$PeI^Cm(`QAG!F)kf17yl5mAZz2Vh&V!T3c$Wzk#Nu$+kQAozgDs0X;sWP+)AW! z$rZ38p4Q4Gmv(&Aq!b~yQ?dO`a%=6dqXZYLzjjF__*uAi#bMO%MDAi`9S+V*XFZCn zxL3$gyh1$nyHNkEiU$W#Oa7OZSb`Eu0r66_wQR+fngit+oie1U08}I#Af+Stl?dAr zrsMZjPsm%iHzHS*T1n3rP{9p+!fMsa(=QT&z1ws1?bM~VT1 zRj6zA6Y453p0sO;oa9bm&EF{JYUHeAIrkLo-^z886M}VrqnzZnKn{4$*29>&K5aX8 z@=Q0`Eyo+W$k>uh=n*BeI5IX2!3+heeX=6-rfp!HeEwi;n5jlj*7P8a3(Te^L|a+Y)u;*&5D|vM<ekunRz$cSRMox>g&l@D|IPPZ zd}TCn8O0Oq8Rj2BV5QLp3=J+3$+Qcx#>nY$WHA1Yne7$aH8ZSL{~g{jD5?)Av;+Wj zIvqAXkgFiM;beUl5;@WTnt)M{+#DG0TkZwGgdSD3H!n|KepDotI7oTw;JmP?KG^D&;P&xb$LHJ|$4m=0#|`JW^S-Ti)>fO`etYk&z4NwKiiLoA-&Qkg zt4UVeUVUrzysc@$Q#xknbIN=N91we51R3Mh^pnjYJ5GUgj zGK#=1FUhYQA~^BL&Ey><{m}&z4%n-}1cdLq)AlqwtU-DBfemK$jlxhS-z27N>VHLZ z)c=NCKG+H-PnPN^6|qQnifRmK(nOQd`qk~ z=TRzJ&l#oq9RyaQR6C$>EKC)iq}gYA41NK{YuA(W;OJA%foV{9u~FE9#rv8`!~r7_ z|As~Fh>f!VG7=>s)FJ1psdJ``;fXq&S{W-yRA71eJ`^xw$ zj|7t)N~n?>$H$M~cdeUst($k%)9<6>NAJ7pW?gmju617l%JNr~k|lo}b)tyx@91)x zKXO{SDvI(JMT5bo zI*n!>YRZ`*{vnl&D>x3faD&O=b7wQgl=pZSDSg;?O6!VWP=%#c+{SHc1f17S` z+;ABDB86AejWK3RI>kH#<%tyB<5Xm$Tx7 zy1R8LZ2gs{JiAl2-MpWc5eReqpF9T=&yO1~UE;j*aYzmP zd<~B34Zz9>=8B*tFwY9oVOsXeAoiDn7h%1JlkqU}Qz6e1(FtqHOrq*1eRP7#C->@$ zBs-sng7ow9eD>xn1OFUgpcAZFdc{c90;1Cn*e4C~b9HJO5kR5rip#hW!d_I%S6nCK z6sZ@O7!^4a18Xmj{U3x%wC#k&!zC1#-C*Rc*j}|zT6wc>vMw2&Eo~gz53dkR6TWr) zJ5!AxdzvyPYw4;5NIu6Vk4=@&S2Sgc1!Qndx{{~nOX?q%SAJE8LLLdY{o+fPP}6`N z|I$^DY(gb=3=n$!l^d@z^!_nhyuRPi)oz|C?W!|6NOtu3b&dm z+-~XGQIxi@g*1-=FmU==^y!H>P1H4aHlX`-!78{6Z-@pgxpFZvyf?e8T(}&aPT;M9 zo!J5}x6qU?oD;DH=E3_~=DjqN+}V=!P;Tppu_@Fy&g}eJi|8e=L3|o;riJ!5wJG!D zIg*_le6GkV%n_O%be(7|%0YI*b57^~ghKOlTZZa?h1h&938)U1r~s-Pv9MDYqQl5Y ze3F6cMfTqVF5ON@e|+-z)cW^2 z?sWWkQ>tR?LoEEq#*Zb-=UsIR^&7?x5L=MMcHG*pJShDu0k=nV<1E+ryY}s`F#lf_ zmi=}4Jb;0}zlAnF5r0=0;k}I8|2q6Fgaa{y9W$tC#fnrk%VwQF$uulFRuqyIvUKg& zCWQGs8$*k~BxLvt8he_)IaH1gRxhV=M(jEo`j?O;Z|JS|N=+_K`cowhkU-olLZ@P1 z*^we6;P#6z9YV!g$RqB`Pf98vjkqfxf92HQK4v1!_lvtM=66bWn*OD|%Vhb93BNuP zMFc-GnJH|sbQKpB=COYhE&J6urUSwa(Qmpa_)X`*FOb}gDKDXjB^O_$`%IIjw<;w% z_%9|*g%)C+ypU9M3Z;tp8xgM&qgkyBgRtYp8V9k3pd9t$1h7OgoQWpZg?6LJPhB8y zMf#U`lRr%@tbiOAoBeL1@##Whc(>WL``&?{9Q}i%bGx4Z52c4^%oE#^5^0yF{IfM1 zKQ3!al^!0m; z=ne_MoTC9C?Ggh{9+*t=HOs&RC4**u?$xY7NZdBmPGqW^hr(7yVxw`7u}1TVEd&@D zslkA+ZMfZktN(|g|KMp$mQIKh^>0fbd)iXAwqN4GOe5ZQHRN~O;K^%HL_UV;ixlI( zcNOdaL<{nvfKvh3IlV9p%VyvJP8$$+J`{ylPLeUeb1f62(h_COQG?LW9n=*U>&j=I z@`(fAi>GYdhOF>Nk;IkT7>qPQY{;rJT(L< zX4>UQ2uJ;iPsJ=XmSI?=&^|izyP2iOa110NovONM1Pe*Sqz%?Vw?stDM(-ie5PGpw z{3R7XIG9|2yX{unT*Zbtb}pL%2U2J;(vTSuJ>y02*?9`iVY!yJVX3Dv2y;rr51p+U zXN=TYyC0SNYm6PMLRnrxV^jLIREgLf>7K5h|d(8;OKSvOR_jwbx&HPN5&>vdJdVcX;rg_!G+?Sg5YmkBV)U)u=0QE07To zoc4VXxXXk0uKZ-^4~7;gcCkXT$QhT(vL01h?pUZL>!;UKOV(#>CQC!IoWc#%sy*CY z?VgOa!cvvIa(n0&Dn)R*^n=Q~m5UV2v^y=$`YQxE05SrCGqFW_WNICjN<9UF1Uo!3 zHIk(YeTq-Ur|REpzSBHi|3TB;rkT>;ul(K0>Gs)%U9;6s5tOT_vF%Cmw&Rv#YRh{& z?(Be3HZ`%Gnp?|#slk(6<(K;wT?S_$z$68ZFQ7OkH?GliIgTkUi!9Srd_811!cAA0 z3N_D~)>y`V3wZUW=}q&SmN%`rL{br9!w45!`ckG7!{5j`APtAlP%srpW5Mxk%w~QS z;wZ9gA}N#=DeL+Nhq=vTCYTFbndOE{vk0>K=zRELI0A)#37E6R;7AJmxF%d<$25pj zUtGEXZ$XX)+0h1~R)#+y$kU9H84gP{ml+^&-1o2Me-4T-Qn1o#xB+Z5leNz^96*G< z4)j@l!HYxM`JKF4g4|z~5@C8>-BH{%2=XMe>t5w_e|~Un1r8_Fr-}E6d1gHqA8FDN zFi8m0H*XUr@Hx^Dy2H=t{)?@TLdyEM#wB)_8ZZh1;wqz6SU6w81Ey8lV-rLelEpBd? zo6*|lR(?j11U$NyqxW8NW|U3T+RD$Z9n{?K>7&NIE@2p%4js@Xk4jM95X9hK8vdTH z`vZem^SCD!9p((QKm+Y)X0JB}7u0|EU@I{70XMX*x&7A#SPoQI0Oe`Shu-A_#XL!I zNuK%ZBfg%26UnnE#x!0GiUv~9yo|ytUkL0Vd68p8jIbRZYmUJgB6k*4po>%^FUk-k z@-ZI6nk`r_Kr0#3EbHl8FDMkjX;p1L`SMKthzMxN#;2RURh2_o83bU9hqH|YMbv<8 z9qzc2&QWpgAK&C5BaW4+6o?M!84($sOc~TnuU2;heF)P$8i012l!PD$QSQVCCFuTm zWgPP*Hjgy;%lS>dK(DzVlQrnQTPUK6pp=B zup&z@#W({|Xy;vDO>ltRq?w7gkn=_|69;E^5~ZDhQMeVh&hRn?hkFqm&s0A^bgq!2 z{w^Zo>zC1$0%qX@#8kE-IX4SmbLQTQFe5H~ujWq8bmNcf?$>Xht=~RZzcVH8ny-6m zcFmbzE=oxKdb?NryMu%(1F&j6-ToF`59fEi?&W*FDG{tq%bk|#Z9nd~->_r0VaHs< zu2lU~^J|};t$X>G>Uva}cI$+XIU8hLb{;556r}Bi#p}5JBZ>4&qo#2P{7;sJnISRd zU2=^}?`voTCtIX|D3r$VctVc2#$ndbYCduL5M+Qx0@65&gz41=3jaC6Y8~~XRG=?VCiO*TkSK7>ktfh`ngP0A@RFz?3;HBH>I zXRfAW%#Anqo%OTM`c%W#dFM9lPUpmIn|E%CVGWqncGkPx30;SQ7k5;E5}M4_e* zGa$A9^!%DLbJb^3QpE$r)V-YQerdk$<++-dQ&MF{vRSI%t$%OhosGYJ>dq!&?5lrI z`HCJaoT-5MFVeA&;BzEH1B21D^)j97XJ)4Adxs)5<`ezg%r;tV%A2l zkXJSCZL3d&U7zV)@gItX+^RxlX}G;C_&&!Lz2JL`V%4{e3ck-di>0GAkk#%>0jp2J zeS?oqkC2*E^WzA5@6#6@ES^VBy>Nmb+Sfkj&=2D7WkBrZ3=UR@Fo-_;RD=Bd>Ep*b zhQef|%}mGPl^B{cUcec6I)D4sb}F4f+dbT8#yj>b9JzsJ^ftC??ush~w zp9f(OVEIu2cVdVNaOV3WTyId3ocn`ZMTP0P4d)HiHDVSu-d~9fk*5-70k7=VoqUo} zK6k9g-wd$txe=o*mE${*?jZReHhDUG)w|;C3Y#h`!R0MYSG1f5Ul#J-(I#FDV)) zlvl)ZB+Y9ZpY=HPKz_aeI{uv&DgiziqU_n*tF;*$K>>ILqtC7UYHj3&Qwx{zG@7{# z|H26aHT9RE%Bu^g82GGz3Jf=J20j$osg7)mIJT|KDzQ!Yxb@u7=FQvu{MZU7I+8&jOMv-* zq0}&B$FU2YX*_FxLOL5tas(V_oiHJHq^;e<^`=U9T&(N8dHrhw2dhb_`IPh_gWJZYShRzZVL$=0f$cu zE4*FiCAZ2=BgQn{1OLpdro9apkP+0cVW0651(HgH_lZEP_b#UZS>6*0>P2WNUwch9 z8sVkmmMyLF$VkIJ#A_)=vMoR*7-Xs^r&PUWa)L|sGDzmU&##qIJ*iH5|8+I)*)$Z| z$6=|_dZvsAIR)m)Feqofd!Y8x2`kops4{>-46}hmgo3n`(8h}aLVi|`dPf+{$72}< z%}Rw^SVpeDW{P(|sE%}hT(n`0s%8(p&hb3 zfsqvdaSx*es&Ss;Cqz5f;mxSH9Xu%I0+& z8M$#6Y3j)r0lI?_zlKdX@VxOP9C2ytN!<>O^$ByI#)N;mjAFQt*Av(P+l5vywIhUc z6)Gt1(%Q~`{cCEt9kpRE?`xa59qCrG0f4kcU0HIGN|tTrUDr1AsKL3NfDf=MLqX^# zX#Y;@5bqqZNE#Lz2xqWrH18ONl{?9mdS@?1Fa-+#EL+Zwl8GZ+L5LJ-O^Fw4FwuVh z5kZAc>tiXzDdLJ7@$oozkXmlFz#5~Vp&_kbgu#m{ZY#kEyS4?Pq=;mBmla8n%CLRF z3+>WpKpgLYB*xrHkPK^ze{%&0Yip2Cxyrt8(n)M+XRp>#Fs)fF5s_I2ScXekpwo6C zOjUOiPBAc&aK)keUl7Wj9chUQh&wfK(vK9u(u;JunZvp9b1*T7#me)O&nK@;9(%WY zTKd3o*YV>QX3Mu>uGq3BMtR>WlN(!;9xTg_H+c3GIuEGH>*4Vu) z4qrS(GnVg#ZKu3+`=`{xZ=tw1gh!@oi!HNN%I@kQ$alml7qM4U2_qeAF@5J}eX{g+ zIJWH6KqP$b57Mwce z=NgI4v4SzNrL9Nc@+hFj*LBI{{D>CYOMwu&&(<|Vl=7y{QGAn<6WrV>N@0ewGOIoR zL>r?+XfMCm2cJksvL8`tiP#FAK2l6B2C&hHdwAOn`uJxA;t~`F%|>|@8{~>rAc*|r zKw*NoAIVpmNl|^SGqsk}>4L*RjvMwwmjr7$cD1Wh`oNYOe_>a_bIvInO z!`^WCH->SrXzajSuv)LAi0k9m$2?rsBF0VaGHF$iI{Sqxj;&1V!VvWkHDbxOY!}7R z?N6z;^~nE*@QKv=NWz9+vYWOsYp1ts+kcX!CfWEU(2gI>W7+bsN+%tQ@1mR~ScQ|! zg_z@&m|=Wm!-7vRGn`Y30!4~cI6wB+h-tR5H1@)2w6YvXXGb|jV94nlvSZlN>Pp*B zKxSfR&*R_jVovDU4?}dN#ejI)OW;7kq+AF>3dEONvLZE<{fA>vN(^I(XOv4*kwZmd zDCEf+0Wvvd1k1wo6fjvJk6#Ag6ZPU#VmQF0lH8=t89>pvXvTW*w@9iqLQc)B7>3r0 z0O!9DqOwd&LV*5AB>hpslqJ`UMPbYbH^MQcC2Cl@w8wx!5A#PBg&=pBn!(ya%t)Rf z$0u5uk;#~A|CMlP1Cj${D_14!Ctpq~Q(LCCCNGUC@X-aYRi{(c?Ncw|BZioP{JFe_^+XUUaw_D4rnVLd)7i?4qXE5fd< z{iq_mZqn`k;9zhcx+kdrnQq4b9Z14fuHZH0Zfw9lPcda>)i36eAWH(jkDz&r?<~k%$4s44RSEQMjM5a4t7@6+K*orJ$rcPx9Jh<7& zA_X&LqNQ;PPW1-GeMRqmTthBYin}6VY}&SXWr>oCgvQ!KbE4y?_H7xLT#p zi4vVwNuTk#f*Tk>%TTPZks4smLAYNd_vvqa<6uws^9Q?+9zOA0x|CO(Yl>ou;h^+s_)&R@+$?ru zT_*i7=k-Vrq>{P0T1u68Kq|x7CgmQR$uoq&6-?yge@(?u^GJh}gcQwP-v%KDphE2o0*Q0QOL?T>Iv7i-Pc?8wMI z@FH|7*s}a%dhO^ohJ6>(BhD)bLPpk9NIz_`8S4 z{9u1;Z`Dq9bKIXgGP`QiytH{#{K{$(S3MHwRw9(GeS7U_(OBt?%JIrMVcmjo;M112 z_bsFDu`4$cy=%cpTT_->UL^)I85qH3XtLau#MnG_?dyyWQ^^#aK zQF^m-66W0qCSfFp2hOm#s>Zh7=p66Nm}*Ek5iCV-9{=v~iTyW^Odgq))-LW??J|wp zptE&XkL|y4WE?Ijy>qTjqjt!y&28|BwXn8fVatw3&g-IByWntV%oKQ7QAq_PqmwUW zEEE9`kPhdK+VR?n?(ub*A{J#6+{IMlM0EUxjGaZn=)Ce~=VYg5YF2(LGF$G=u(Dxk zX0{(UIpDk_txvP^sA6jgS7NmO=7AwT!i7hvGOzwa! zo>)A!jXmzIXOEu(Ac)L(L1*ua<+Eb>eR0jKxaKRsT!OkvYM#RHawYS&YS_3=oRU0!FL;wn_>M}(XqVpIT z$^|%Di{3o@-Ln{;+DUNalj~9?^;6qZCC#&zmPIEeQAJ_*Gmp$o=&s#C_?}_m>Ke@o z5SyISr)CCC7pD< zlkbj_sZxMa1gHWSjoKG8Xp1ndfyi--1Vw>3Pl-_!agc{l{(;!59}F~k$QvEGD3F(S zW{;#ON-FZu1+=%^-J6@4o%!ZlebU$%BGBIZ&k^W0FsZhAU3 zt+5o-ZKlvO8NQk{Qq!u&Oe*V2D8+1zQ9ipd?7#|6C7~>Bqd#K z+&)nLv6g_r1;WUP0Iu;U0`n>!CT`^n#k4#YpO)-Pe zS-6Qp`)aW7#OUF}hX-FAP02Rvl9RH+Qkl`|OqVTNQ(fa}RbiT{v(Xu*&x}5&o*4z@ zSl6_ioRY_xH9D#4lk$;AXHaLu?4Hgzfp|O#%E#k#J2zO;U73ftBNhhR0x1j*t-*i{ zfZgz(E~i*L?u6p;l%b?G9EaoavuRm#S4>pf5p2iXt7lA%Efa;AP1vFFrmxb4VPJJY z_X;TnhVs!uU}#x>hh1VYxN`P`^!sTTTxHfx#)9-p+SJPEwtNn9jWJ9eif2m8$4tl4I?o=9=Ocq`%!DwUFjiqItt;J>4nuBk zejz1is2jd?M%L2IqO%ifa)QpPApZ>GVj#&O(3SNhPH;I13i&%>DBU5xYY>uJVPkSy zv$^VlT} zK}buCGdo_kCsF+t$T~d+N^J5fW#?4Ob`L}5T5?H`)cTnw1`M4gV%$QIuQO9ky5C4t z1%je!3$#*o%Vsh-8V}XQ4#fPq-FEl)rx7+~?I$nP!qiI}un55hQGV@d=q?Z&v6Z(h zR1_Om++V`w^2#J8c3t}o`JG4cRBFHwiFxPV{vpS`El=C63=?1h4?^&?YcN%7UB(SU zqiQ^6S6)K@>L6UU;gl@KKT~#;>!IpNP}QnR+Uzit@A%wM7>IctKb)p)+omJhnQ3M= zgP}2xi9o?!Yaxt-n-RdAhMJR~+cmJBr`RG`=xn-;8G%sewm%|>C z6|=&)koEjt_>)g1S?@*Rm%oGp{FbK6yL$^TggmbCK#cjPM)_YbU=l6&tBg8y&$B`Fcg7Tnd=cY9^soK^2 zt>AKPc$fV<5c4=Ypmdw@bTmmh8s%F zUa2P!o8hs1_MP-4*bKuDg^#4Guo;He+CT01xZ@rU-5f+!lJHk;$i=X&y$#(~1cOPF z$Yko8B5pG;F%igv4+_1XlLzlM`?h~y+KRF>aW`lHlKq@Q1Y$uaf~P-h%DM$bHpNW& z69A5{K_ei1jaiEVzf;H5q>Q!1!n(`Dv0T`ZH~@HWke_e~!YmLKj>|~;k(L1Z^4}Xu zomsScYApt{mhl6@pMN6v{!R z+Td$wONGyrKj1QiG1Ex#PfeQ)$Uxenn&J9@8t|eytAaJEeZZ<{sDK!IqQ<~=T{y9* zsOVBDirfmg1?pg!>NwICR~c0=vn*cMbc2g2djYRY)Rw1!A#tzN7X50`FtPk^z&xz+ z9P|DR2mduN@2`Z6LT#8A=cO73AlcQ*U%3OCQ{!abmz4k~e+v8kHM{*bA}w##-H&k` zMMdjc)?bgK^Zu-VK{$Wte@nEQv%blOI$`iL>%R<^Vgo!LfIWEisB0VcBt()0I~2G{ zx03TO*L+plY=ABf{ ztk|}HwXJKVt!tUxZ0mugV)PrU(T{2tK2bY4cxBn`9yQwvI`l5ZS z=}@t?BiHlBiPul$)k16UyZf%U_Fmb)(%QQeD7Lf~S{}X~ZO!%NJ8nfE`OF8zms4duij4gNIta0CCz3Sj==NAb`a(MF~_!?8AR< zxRB<85xy4lx^?~_SY>uXw+(Iam^1at< z1q0dD?j+)`S+ja!w=7XG;?+4nsPOJ4>!5=1iu*zbCn$*Tvgyk%2}2p z@ziwx`_ef4;$VOL)YJW^o;^MAl+)zyi*t0au$ns_n>m3&)3A-Cp}GH&LE^(rs_qvEkJ~p&&jSkUhx-C{QQZ3h!5Qp1!-8wdERJv fz2P3g@xKEP3O#oTbocs%gF^Gt)V~NkxKaNF+NdlK literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/jinja2/_identifier.py b/venv/Lib/site-packages/jinja2/_identifier.py new file mode 100644 index 0000000..928c150 --- /dev/null +++ b/venv/Lib/site-packages/jinja2/_identifier.py @@ -0,0 +1,6 @@ +import re + +# generated by scripts/generate_identifier_pattern.py +pattern = re.compile( + r"[\w·̀-ͯ·҃-֑҇-ׇֽֿׁׂׅׄؐ-ًؚ-ٰٟۖ-ۜ۟-۪ۤۧۨ-ܑۭܰ-݊ަ-ް߫-߽߳ࠖ-࠙ࠛ-ࠣࠥ-ࠧࠩ-࡙࠭-࡛࣓-ࣣ࣡-ःऺ-़ा-ॏ॑-ॗॢॣঁ-ঃ়া-ৄেৈো-্ৗৢৣ৾ਁ-ਃ਼ਾ-ੂੇੈੋ-੍ੑੰੱੵઁ-ઃ઼ા-ૅે-ૉો-્ૢૣૺ-૿ଁ-ଃ଼ା-ୄେୈୋ-୍ୖୗୢୣஂா-ூெ-ைொ-்ௗఀ-ఄా-ౄె-ైొ-్ౕౖౢౣಁ-ಃ಼ಾ-ೄೆ-ೈೊ-್ೕೖೢೣഀ-ഃ഻഼ാ-ൄെ-ൈൊ-്ൗൢൣංඃ්ා-ුූෘ-ෟෲෳัิ-ฺ็-๎ັິ-ູົຼ່-ໍ༹༘༙༵༷༾༿ཱ-྄྆྇ྍ-ྗྙ-ྼ࿆ါ-ှၖ-ၙၞ-ၠၢ-ၤၧ-ၭၱ-ၴႂ-ႍႏႚ-ႝ፝-፟ᜒ-᜔ᜲ-᜴ᝒᝓᝲᝳ឴-៓៝᠋-᠍ᢅᢆᢩᤠ-ᤫᤰ-᤻ᨗ-ᨛᩕ-ᩞ᩠-᩿᩼᪰-᪽ᬀ-ᬄ᬴-᭄᭫-᭳ᮀ-ᮂᮡ-ᮭ᯦-᯳ᰤ-᰷᳐-᳔᳒-᳨᳭ᳲ-᳴᳷-᳹᷀-᷹᷻-᷿‿⁀⁔⃐-⃥⃜⃡-⃰℘℮⳯-⵿⳱ⷠ-〪ⷿ-゙゚〯꙯ꙴ-꙽ꚞꚟ꛰꛱ꠂ꠆ꠋꠣ-ꠧꢀꢁꢴ-ꣅ꣠-꣱ꣿꤦ-꤭ꥇ-꥓ꦀ-ꦃ꦳-꧀ꧥꨩ-ꨶꩃꩌꩍꩻ-ꩽꪰꪲ-ꪴꪷꪸꪾ꪿꫁ꫫ-ꫯꫵ꫶ꯣ-ꯪ꯬꯭ﬞ︀-️︠-︯︳︴﹍-﹏_𐇽𐋠𐍶-𐍺𐨁-𐨃𐨅𐨆𐨌-𐨏𐨸-𐨿𐨺𐫦𐫥𐴤-𐽆𐴧-𐽐𑀀-𑀂𑀸-𑁆𑁿-𑂂𑂰-𑂺𑄀-𑄂𑄧-𑄴𑅅𑅆𑅳𑆀-𑆂𑆳-𑇀𑇉-𑇌𑈬-𑈷𑈾𑋟-𑋪𑌀-𑌃𑌻𑌼𑌾-𑍄𑍇𑍈𑍋-𑍍𑍗𑍢𑍣𑍦-𑍬𑍰-𑍴𑐵-𑑆𑑞𑒰-𑓃𑖯-𑖵𑖸-𑗀𑗜𑗝𑘰-𑙀𑚫-𑚷𑜝-𑜫𑠬-𑠺𑨁-𑨊𑨳-𑨹𑨻-𑨾𑩇𑩑-𑩛𑪊-𑪙𑰯-𑰶𑰸-𑰿𑲒-𑲧𑲩-𑲶𑴱-𑴶𑴺𑴼𑴽𑴿-𑵅𑵇𑶊-𑶎𑶐𑶑𑶓-𑶗𑻳-𑻶𖫰-𖫴𖬰-𖬶𖽑-𖽾𖾏-𖾒𛲝𛲞𝅥-𝅩𝅭-𝅲𝅻-𝆂𝆅-𝆋𝆪-𝆭𝉂-𝉄𝨀-𝨶𝨻-𝩬𝩵𝪄𝪛-𝪟𝪡-𝪯𞀀-𞀆𞀈-𞀘𞀛-𞀡𞀣𞀤𞀦-𞣐𞀪-𞣖𞥄-𞥊󠄀-󠇯]+" # noqa: B950 +) diff --git a/venv/Lib/site-packages/jinja2/async_utils.py b/venv/Lib/site-packages/jinja2/async_utils.py new file mode 100644 index 0000000..f0c1402 --- /dev/null +++ b/venv/Lib/site-packages/jinja2/async_utils.py @@ -0,0 +1,99 @@ +import inspect +import typing as t +from functools import WRAPPER_ASSIGNMENTS +from functools import wraps + +from .utils import _PassArg +from .utils import pass_eval_context + +if t.TYPE_CHECKING: + import typing_extensions as te + +V = t.TypeVar("V") + + +def async_variant(normal_func): # type: ignore + def decorator(async_func): # type: ignore + pass_arg = _PassArg.from_obj(normal_func) + need_eval_context = pass_arg is None + + if pass_arg is _PassArg.environment: + + def is_async(args: t.Any) -> bool: + return t.cast(bool, args[0].is_async) + + else: + + def is_async(args: t.Any) -> bool: + return t.cast(bool, args[0].environment.is_async) + + # Take the doc and annotations from the sync function, but the + # name from the async function. Pallets-Sphinx-Themes + # build_function_directive expects __wrapped__ to point to the + # sync function. + async_func_attrs = ("__module__", "__name__", "__qualname__") + normal_func_attrs = tuple(set(WRAPPER_ASSIGNMENTS).difference(async_func_attrs)) + + @wraps(normal_func, assigned=normal_func_attrs) + @wraps(async_func, assigned=async_func_attrs, updated=()) + def wrapper(*args, **kwargs): # type: ignore + b = is_async(args) + + if need_eval_context: + args = args[1:] + + if b: + return async_func(*args, **kwargs) + + return normal_func(*args, **kwargs) + + if need_eval_context: + wrapper = pass_eval_context(wrapper) + + wrapper.jinja_async_variant = True # type: ignore[attr-defined] + return wrapper + + return decorator + + +_common_primitives = {int, float, bool, str, list, dict, tuple, type(None)} + + +async def auto_await(value: t.Union[t.Awaitable["V"], "V"]) -> "V": + # Avoid a costly call to isawaitable + if type(value) in _common_primitives: + return t.cast("V", value) + + if inspect.isawaitable(value): + return await t.cast("t.Awaitable[V]", value) + + return value + + +class _IteratorToAsyncIterator(t.Generic[V]): + def __init__(self, iterator: "t.Iterator[V]"): + self._iterator = iterator + + def __aiter__(self) -> "te.Self": + return self + + async def __anext__(self) -> V: + try: + return next(self._iterator) + except StopIteration as e: + raise StopAsyncIteration(e.value) from e + + +def auto_aiter( + iterable: "t.Union[t.AsyncIterable[V], t.Iterable[V]]", +) -> "t.AsyncIterator[V]": + if hasattr(iterable, "__aiter__"): + return iterable.__aiter__() + else: + return _IteratorToAsyncIterator(iter(iterable)) + + +async def auto_to_list( + value: "t.Union[t.AsyncIterable[V], t.Iterable[V]]", +) -> t.List["V"]: + return [x async for x in auto_aiter(value)] diff --git a/venv/Lib/site-packages/jinja2/bccache.py b/venv/Lib/site-packages/jinja2/bccache.py new file mode 100644 index 0000000..ada8b09 --- /dev/null +++ b/venv/Lib/site-packages/jinja2/bccache.py @@ -0,0 +1,408 @@ +"""The optional bytecode cache system. This is useful if you have very +complex template situations and the compilation of all those templates +slows down your application too much. + +Situations where this is useful are often forking web applications that +are initialized on the first request. +""" + +import errno +import fnmatch +import marshal +import os +import pickle +import stat +import sys +import tempfile +import typing as t +from hashlib import sha1 +from io import BytesIO +from types import CodeType + +if t.TYPE_CHECKING: + import typing_extensions as te + + from .environment import Environment + + class _MemcachedClient(te.Protocol): + def get(self, key: str) -> bytes: ... + + def set( + self, key: str, value: bytes, timeout: t.Optional[int] = None + ) -> None: ... + + +bc_version = 5 +# Magic bytes to identify Jinja bytecode cache files. Contains the +# Python major and minor version to avoid loading incompatible bytecode +# if a project upgrades its Python version. +bc_magic = ( + b"j2" + + pickle.dumps(bc_version, 2) + + pickle.dumps((sys.version_info[0] << 24) | sys.version_info[1], 2) +) + + +class Bucket: + """Buckets are used to store the bytecode for one template. It's created + and initialized by the bytecode cache and passed to the loading functions. + + The buckets get an internal checksum from the cache assigned and use this + to automatically reject outdated cache material. Individual bytecode + cache subclasses don't have to care about cache invalidation. + """ + + def __init__(self, environment: "Environment", key: str, checksum: str) -> None: + self.environment = environment + self.key = key + self.checksum = checksum + self.reset() + + def reset(self) -> None: + """Resets the bucket (unloads the bytecode).""" + self.code: t.Optional[CodeType] = None + + def load_bytecode(self, f: t.BinaryIO) -> None: + """Loads bytecode from a file or file like object.""" + # make sure the magic header is correct + magic = f.read(len(bc_magic)) + if magic != bc_magic: + self.reset() + return + # the source code of the file changed, we need to reload + checksum = pickle.load(f) + if self.checksum != checksum: + self.reset() + return + # if marshal_load fails then we need to reload + try: + self.code = marshal.load(f) + except (EOFError, ValueError, TypeError): + self.reset() + return + + def write_bytecode(self, f: t.IO[bytes]) -> None: + """Dump the bytecode into the file or file like object passed.""" + if self.code is None: + raise TypeError("can't write empty bucket") + f.write(bc_magic) + pickle.dump(self.checksum, f, 2) + marshal.dump(self.code, f) + + def bytecode_from_string(self, string: bytes) -> None: + """Load bytecode from bytes.""" + self.load_bytecode(BytesIO(string)) + + def bytecode_to_string(self) -> bytes: + """Return the bytecode as bytes.""" + out = BytesIO() + self.write_bytecode(out) + return out.getvalue() + + +class BytecodeCache: + """To implement your own bytecode cache you have to subclass this class + and override :meth:`load_bytecode` and :meth:`dump_bytecode`. Both of + these methods are passed a :class:`~jinja2.bccache.Bucket`. + + A very basic bytecode cache that saves the bytecode on the file system:: + + from os import path + + class MyCache(BytecodeCache): + + def __init__(self, directory): + self.directory = directory + + def load_bytecode(self, bucket): + filename = path.join(self.directory, bucket.key) + if path.exists(filename): + with open(filename, 'rb') as f: + bucket.load_bytecode(f) + + def dump_bytecode(self, bucket): + filename = path.join(self.directory, bucket.key) + with open(filename, 'wb') as f: + bucket.write_bytecode(f) + + A more advanced version of a filesystem based bytecode cache is part of + Jinja. + """ + + def load_bytecode(self, bucket: Bucket) -> None: + """Subclasses have to override this method to load bytecode into a + bucket. If they are not able to find code in the cache for the + bucket, it must not do anything. + """ + raise NotImplementedError() + + def dump_bytecode(self, bucket: Bucket) -> None: + """Subclasses have to override this method to write the bytecode + from a bucket back to the cache. If it unable to do so it must not + fail silently but raise an exception. + """ + raise NotImplementedError() + + def clear(self) -> None: + """Clears the cache. This method is not used by Jinja but should be + implemented to allow applications to clear the bytecode cache used + by a particular environment. + """ + + def get_cache_key( + self, name: str, filename: t.Optional[t.Union[str]] = None + ) -> str: + """Returns the unique hash key for this template name.""" + hash = sha1(name.encode("utf-8")) + + if filename is not None: + hash.update(f"|{filename}".encode()) + + return hash.hexdigest() + + def get_source_checksum(self, source: str) -> str: + """Returns a checksum for the source.""" + return sha1(source.encode("utf-8")).hexdigest() + + def get_bucket( + self, + environment: "Environment", + name: str, + filename: t.Optional[str], + source: str, + ) -> Bucket: + """Return a cache bucket for the given template. All arguments are + mandatory but filename may be `None`. + """ + key = self.get_cache_key(name, filename) + checksum = self.get_source_checksum(source) + bucket = Bucket(environment, key, checksum) + self.load_bytecode(bucket) + return bucket + + def set_bucket(self, bucket: Bucket) -> None: + """Put the bucket into the cache.""" + self.dump_bytecode(bucket) + + +class FileSystemBytecodeCache(BytecodeCache): + """A bytecode cache that stores bytecode on the filesystem. It accepts + two arguments: The directory where the cache items are stored and a + pattern string that is used to build the filename. + + If no directory is specified a default cache directory is selected. On + Windows the user's temp directory is used, on UNIX systems a directory + is created for the user in the system temp directory. + + The pattern can be used to have multiple separate caches operate on the + same directory. The default pattern is ``'__jinja2_%s.cache'``. ``%s`` + is replaced with the cache key. + + >>> bcc = FileSystemBytecodeCache('/tmp/jinja_cache', '%s.cache') + + This bytecode cache supports clearing of the cache using the clear method. + """ + + def __init__( + self, directory: t.Optional[str] = None, pattern: str = "__jinja2_%s.cache" + ) -> None: + if directory is None: + directory = self._get_default_cache_dir() + self.directory = directory + self.pattern = pattern + + def _get_default_cache_dir(self) -> str: + def _unsafe_dir() -> "te.NoReturn": + raise RuntimeError( + "Cannot determine safe temp directory. You " + "need to explicitly provide one." + ) + + tmpdir = tempfile.gettempdir() + + # On windows the temporary directory is used specific unless + # explicitly forced otherwise. We can just use that. + if os.name == "nt": + return tmpdir + if not hasattr(os, "getuid"): + _unsafe_dir() + + dirname = f"_jinja2-cache-{os.getuid()}" + actual_dir = os.path.join(tmpdir, dirname) + + try: + os.mkdir(actual_dir, stat.S_IRWXU) + except OSError as e: + if e.errno != errno.EEXIST: + raise + try: + os.chmod(actual_dir, stat.S_IRWXU) + actual_dir_stat = os.lstat(actual_dir) + if ( + actual_dir_stat.st_uid != os.getuid() + or not stat.S_ISDIR(actual_dir_stat.st_mode) + or stat.S_IMODE(actual_dir_stat.st_mode) != stat.S_IRWXU + ): + _unsafe_dir() + except OSError as e: + if e.errno != errno.EEXIST: + raise + + actual_dir_stat = os.lstat(actual_dir) + if ( + actual_dir_stat.st_uid != os.getuid() + or not stat.S_ISDIR(actual_dir_stat.st_mode) + or stat.S_IMODE(actual_dir_stat.st_mode) != stat.S_IRWXU + ): + _unsafe_dir() + + return actual_dir + + def _get_cache_filename(self, bucket: Bucket) -> str: + return os.path.join(self.directory, self.pattern % (bucket.key,)) + + def load_bytecode(self, bucket: Bucket) -> None: + filename = self._get_cache_filename(bucket) + + # Don't test for existence before opening the file, since the + # file could disappear after the test before the open. + try: + f = open(filename, "rb") + except (FileNotFoundError, IsADirectoryError, PermissionError): + # PermissionError can occur on Windows when an operation is + # in progress, such as calling clear(). + return + + with f: + bucket.load_bytecode(f) + + def dump_bytecode(self, bucket: Bucket) -> None: + # Write to a temporary file, then rename to the real name after + # writing. This avoids another process reading the file before + # it is fully written. + name = self._get_cache_filename(bucket) + f = tempfile.NamedTemporaryFile( + mode="wb", + dir=os.path.dirname(name), + prefix=os.path.basename(name), + suffix=".tmp", + delete=False, + ) + + def remove_silent() -> None: + try: + os.remove(f.name) + except OSError: + # Another process may have called clear(). On Windows, + # another program may be holding the file open. + pass + + try: + with f: + bucket.write_bytecode(f) + except BaseException: + remove_silent() + raise + + try: + os.replace(f.name, name) + except OSError: + # Another process may have called clear(). On Windows, + # another program may be holding the file open. + remove_silent() + except BaseException: + remove_silent() + raise + + def clear(self) -> None: + # imported lazily here because google app-engine doesn't support + # write access on the file system and the function does not exist + # normally. + from os import remove + + files = fnmatch.filter(os.listdir(self.directory), self.pattern % ("*",)) + for filename in files: + try: + remove(os.path.join(self.directory, filename)) + except OSError: + pass + + +class MemcachedBytecodeCache(BytecodeCache): + """This class implements a bytecode cache that uses a memcache cache for + storing the information. It does not enforce a specific memcache library + (tummy's memcache or cmemcache) but will accept any class that provides + the minimal interface required. + + Libraries compatible with this class: + + - `cachelib `_ + - `python-memcached `_ + + (Unfortunately the django cache interface is not compatible because it + does not support storing binary data, only text. You can however pass + the underlying cache client to the bytecode cache which is available + as `django.core.cache.cache._client`.) + + The minimal interface for the client passed to the constructor is this: + + .. class:: MinimalClientInterface + + .. method:: set(key, value[, timeout]) + + Stores the bytecode in the cache. `value` is a string and + `timeout` the timeout of the key. If timeout is not provided + a default timeout or no timeout should be assumed, if it's + provided it's an integer with the number of seconds the cache + item should exist. + + .. method:: get(key) + + Returns the value for the cache key. If the item does not + exist in the cache the return value must be `None`. + + The other arguments to the constructor are the prefix for all keys that + is added before the actual cache key and the timeout for the bytecode in + the cache system. We recommend a high (or no) timeout. + + This bytecode cache does not support clearing of used items in the cache. + The clear method is a no-operation function. + + .. versionadded:: 2.7 + Added support for ignoring memcache errors through the + `ignore_memcache_errors` parameter. + """ + + def __init__( + self, + client: "_MemcachedClient", + prefix: str = "jinja2/bytecode/", + timeout: t.Optional[int] = None, + ignore_memcache_errors: bool = True, + ): + self.client = client + self.prefix = prefix + self.timeout = timeout + self.ignore_memcache_errors = ignore_memcache_errors + + def load_bytecode(self, bucket: Bucket) -> None: + try: + code = self.client.get(self.prefix + bucket.key) + except Exception: + if not self.ignore_memcache_errors: + raise + else: + bucket.bytecode_from_string(code) + + def dump_bytecode(self, bucket: Bucket) -> None: + key = self.prefix + bucket.key + value = bucket.bytecode_to_string() + + try: + if self.timeout is not None: + self.client.set(key, value, self.timeout) + else: + self.client.set(key, value) + except Exception: + if not self.ignore_memcache_errors: + raise diff --git a/venv/Lib/site-packages/jinja2/compiler.py b/venv/Lib/site-packages/jinja2/compiler.py new file mode 100644 index 0000000..a4ff6a1 --- /dev/null +++ b/venv/Lib/site-packages/jinja2/compiler.py @@ -0,0 +1,1998 @@ +"""Compiles nodes from the parser into Python code.""" + +import typing as t +from contextlib import contextmanager +from functools import update_wrapper +from io import StringIO +from itertools import chain +from keyword import iskeyword as is_python_keyword + +from markupsafe import escape +from markupsafe import Markup + +from . import nodes +from .exceptions import TemplateAssertionError +from .idtracking import Symbols +from .idtracking import VAR_LOAD_ALIAS +from .idtracking import VAR_LOAD_PARAMETER +from .idtracking import VAR_LOAD_RESOLVE +from .idtracking import VAR_LOAD_UNDEFINED +from .nodes import EvalContext +from .optimizer import Optimizer +from .utils import _PassArg +from .utils import concat +from .visitor import NodeVisitor + +if t.TYPE_CHECKING: + import typing_extensions as te + + from .environment import Environment + +F = t.TypeVar("F", bound=t.Callable[..., t.Any]) + +operators = { + "eq": "==", + "ne": "!=", + "gt": ">", + "gteq": ">=", + "lt": "<", + "lteq": "<=", + "in": "in", + "notin": "not in", +} + + +def optimizeconst(f: F) -> F: + def new_func( + self: "CodeGenerator", node: nodes.Expr, frame: "Frame", **kwargs: t.Any + ) -> t.Any: + # Only optimize if the frame is not volatile + if self.optimizer is not None and not frame.eval_ctx.volatile: + new_node = self.optimizer.visit(node, frame.eval_ctx) + + if new_node != node: + return self.visit(new_node, frame) + + return f(self, node, frame, **kwargs) + + return update_wrapper(new_func, f) # type: ignore[return-value] + + +def _make_binop(op: str) -> t.Callable[["CodeGenerator", nodes.BinExpr, "Frame"], None]: + @optimizeconst + def visitor(self: "CodeGenerator", node: nodes.BinExpr, frame: Frame) -> None: + if ( + self.environment.sandboxed and op in self.environment.intercepted_binops # type: ignore + ): + self.write(f"environment.call_binop(context, {op!r}, ") + self.visit(node.left, frame) + self.write(", ") + self.visit(node.right, frame) + else: + self.write("(") + self.visit(node.left, frame) + self.write(f" {op} ") + self.visit(node.right, frame) + + self.write(")") + + return visitor + + +def _make_unop( + op: str, +) -> t.Callable[["CodeGenerator", nodes.UnaryExpr, "Frame"], None]: + @optimizeconst + def visitor(self: "CodeGenerator", node: nodes.UnaryExpr, frame: Frame) -> None: + if ( + self.environment.sandboxed and op in self.environment.intercepted_unops # type: ignore + ): + self.write(f"environment.call_unop(context, {op!r}, ") + self.visit(node.node, frame) + else: + self.write("(" + op) + self.visit(node.node, frame) + + self.write(")") + + return visitor + + +def generate( + node: nodes.Template, + environment: "Environment", + name: t.Optional[str], + filename: t.Optional[str], + stream: t.Optional[t.TextIO] = None, + defer_init: bool = False, + optimized: bool = True, +) -> t.Optional[str]: + """Generate the python source for a node tree.""" + if not isinstance(node, nodes.Template): + raise TypeError("Can't compile non template nodes") + + generator = environment.code_generator_class( + environment, name, filename, stream, defer_init, optimized + ) + generator.visit(node) + + if stream is None: + return generator.stream.getvalue() # type: ignore + + return None + + +def has_safe_repr(value: t.Any) -> bool: + """Does the node have a safe representation?""" + if value is None or value is NotImplemented or value is Ellipsis: + return True + + if type(value) in {bool, int, float, complex, range, str, Markup}: + return True + + if type(value) in {tuple, list, set, frozenset}: + return all(has_safe_repr(v) for v in value) + + if type(value) is dict: # noqa E721 + return all(has_safe_repr(k) and has_safe_repr(v) for k, v in value.items()) + + return False + + +def find_undeclared( + nodes: t.Iterable[nodes.Node], names: t.Iterable[str] +) -> t.Set[str]: + """Check if the names passed are accessed undeclared. The return value + is a set of all the undeclared names from the sequence of names found. + """ + visitor = UndeclaredNameVisitor(names) + try: + for node in nodes: + visitor.visit(node) + except VisitorExit: + pass + return visitor.undeclared + + +class MacroRef: + def __init__(self, node: t.Union[nodes.Macro, nodes.CallBlock]) -> None: + self.node = node + self.accesses_caller = False + self.accesses_kwargs = False + self.accesses_varargs = False + + +class Frame: + """Holds compile time information for us.""" + + def __init__( + self, + eval_ctx: EvalContext, + parent: t.Optional["Frame"] = None, + level: t.Optional[int] = None, + ) -> None: + self.eval_ctx = eval_ctx + + # the parent of this frame + self.parent = parent + + if parent is None: + self.symbols = Symbols(level=level) + + # in some dynamic inheritance situations the compiler needs to add + # write tests around output statements. + self.require_output_check = False + + # inside some tags we are using a buffer rather than yield statements. + # this for example affects {% filter %} or {% macro %}. If a frame + # is buffered this variable points to the name of the list used as + # buffer. + self.buffer: t.Optional[str] = None + + # the name of the block we're in, otherwise None. + self.block: t.Optional[str] = None + + else: + self.symbols = Symbols(parent.symbols, level=level) + self.require_output_check = parent.require_output_check + self.buffer = parent.buffer + self.block = parent.block + + # a toplevel frame is the root + soft frames such as if conditions. + self.toplevel = False + + # the root frame is basically just the outermost frame, so no if + # conditions. This information is used to optimize inheritance + # situations. + self.rootlevel = False + + # variables set inside of loops and blocks should not affect outer frames, + # but they still needs to be kept track of as part of the active context. + self.loop_frame = False + self.block_frame = False + + # track whether the frame is being used in an if-statement or conditional + # expression as it determines which errors should be raised during runtime + # or compile time. + self.soft_frame = False + + def copy(self) -> "te.Self": + """Create a copy of the current one.""" + rv = object.__new__(self.__class__) + rv.__dict__.update(self.__dict__) + rv.symbols = self.symbols.copy() + return rv + + def inner(self, isolated: bool = False) -> "Frame": + """Return an inner frame.""" + if isolated: + return Frame(self.eval_ctx, level=self.symbols.level + 1) + return Frame(self.eval_ctx, self) + + def soft(self) -> "te.Self": + """Return a soft frame. A soft frame may not be modified as + standalone thing as it shares the resources with the frame it + was created of, but it's not a rootlevel frame any longer. + + This is only used to implement if-statements and conditional + expressions. + """ + rv = self.copy() + rv.rootlevel = False + rv.soft_frame = True + return rv + + __copy__ = copy + + +class VisitorExit(RuntimeError): + """Exception used by the `UndeclaredNameVisitor` to signal a stop.""" + + +class DependencyFinderVisitor(NodeVisitor): + """A visitor that collects filter and test calls.""" + + def __init__(self) -> None: + self.filters: t.Set[str] = set() + self.tests: t.Set[str] = set() + + def visit_Filter(self, node: nodes.Filter) -> None: + self.generic_visit(node) + self.filters.add(node.name) + + def visit_Test(self, node: nodes.Test) -> None: + self.generic_visit(node) + self.tests.add(node.name) + + def visit_Block(self, node: nodes.Block) -> None: + """Stop visiting at blocks.""" + + +class UndeclaredNameVisitor(NodeVisitor): + """A visitor that checks if a name is accessed without being + declared. This is different from the frame visitor as it will + not stop at closure frames. + """ + + def __init__(self, names: t.Iterable[str]) -> None: + self.names = set(names) + self.undeclared: t.Set[str] = set() + + def visit_Name(self, node: nodes.Name) -> None: + if node.ctx == "load" and node.name in self.names: + self.undeclared.add(node.name) + if self.undeclared == self.names: + raise VisitorExit() + else: + self.names.discard(node.name) + + def visit_Block(self, node: nodes.Block) -> None: + """Stop visiting a blocks.""" + + +class CompilerExit(Exception): + """Raised if the compiler encountered a situation where it just + doesn't make sense to further process the code. Any block that + raises such an exception is not further processed. + """ + + +class CodeGenerator(NodeVisitor): + def __init__( + self, + environment: "Environment", + name: t.Optional[str], + filename: t.Optional[str], + stream: t.Optional[t.TextIO] = None, + defer_init: bool = False, + optimized: bool = True, + ) -> None: + if stream is None: + stream = StringIO() + self.environment = environment + self.name = name + self.filename = filename + self.stream = stream + self.created_block_context = False + self.defer_init = defer_init + self.optimizer: t.Optional[Optimizer] = None + + if optimized: + self.optimizer = Optimizer(environment) + + # aliases for imports + self.import_aliases: t.Dict[str, str] = {} + + # a registry for all blocks. Because blocks are moved out + # into the global python scope they are registered here + self.blocks: t.Dict[str, nodes.Block] = {} + + # the number of extends statements so far + self.extends_so_far = 0 + + # some templates have a rootlevel extends. In this case we + # can safely assume that we're a child template and do some + # more optimizations. + self.has_known_extends = False + + # the current line number + self.code_lineno = 1 + + # registry of all filters and tests (global, not block local) + self.tests: t.Dict[str, str] = {} + self.filters: t.Dict[str, str] = {} + + # the debug information + self.debug_info: t.List[t.Tuple[int, int]] = [] + self._write_debug_info: t.Optional[int] = None + + # the number of new lines before the next write() + self._new_lines = 0 + + # the line number of the last written statement + self._last_line = 0 + + # true if nothing was written so far. + self._first_write = True + + # used by the `temporary_identifier` method to get new + # unique, temporary identifier + self._last_identifier = 0 + + # the current indentation + self._indentation = 0 + + # Tracks toplevel assignments + self._assign_stack: t.List[t.Set[str]] = [] + + # Tracks parameter definition blocks + self._param_def_block: t.List[t.Set[str]] = [] + + # Tracks the current context. + self._context_reference_stack = ["context"] + + @property + def optimized(self) -> bool: + return self.optimizer is not None + + # -- Various compilation helpers + + def fail(self, msg: str, lineno: int) -> "te.NoReturn": + """Fail with a :exc:`TemplateAssertionError`.""" + raise TemplateAssertionError(msg, lineno, self.name, self.filename) + + def temporary_identifier(self) -> str: + """Get a new unique identifier.""" + self._last_identifier += 1 + return f"t_{self._last_identifier}" + + def buffer(self, frame: Frame) -> None: + """Enable buffering for the frame from that point onwards.""" + frame.buffer = self.temporary_identifier() + self.writeline(f"{frame.buffer} = []") + + def return_buffer_contents( + self, frame: Frame, force_unescaped: bool = False + ) -> None: + """Return the buffer contents of the frame.""" + if not force_unescaped: + if frame.eval_ctx.volatile: + self.writeline("if context.eval_ctx.autoescape:") + self.indent() + self.writeline(f"return Markup(concat({frame.buffer}))") + self.outdent() + self.writeline("else:") + self.indent() + self.writeline(f"return concat({frame.buffer})") + self.outdent() + return + elif frame.eval_ctx.autoescape: + self.writeline(f"return Markup(concat({frame.buffer}))") + return + self.writeline(f"return concat({frame.buffer})") + + def indent(self) -> None: + """Indent by one.""" + self._indentation += 1 + + def outdent(self, step: int = 1) -> None: + """Outdent by step.""" + self._indentation -= step + + def start_write(self, frame: Frame, node: t.Optional[nodes.Node] = None) -> None: + """Yield or write into the frame buffer.""" + if frame.buffer is None: + self.writeline("yield ", node) + else: + self.writeline(f"{frame.buffer}.append(", node) + + def end_write(self, frame: Frame) -> None: + """End the writing process started by `start_write`.""" + if frame.buffer is not None: + self.write(")") + + def simple_write( + self, s: str, frame: Frame, node: t.Optional[nodes.Node] = None + ) -> None: + """Simple shortcut for start_write + write + end_write.""" + self.start_write(frame, node) + self.write(s) + self.end_write(frame) + + def blockvisit(self, nodes: t.Iterable[nodes.Node], frame: Frame) -> None: + """Visit a list of nodes as block in a frame. If the current frame + is no buffer a dummy ``if 0: yield None`` is written automatically. + """ + try: + self.writeline("pass") + for node in nodes: + self.visit(node, frame) + except CompilerExit: + pass + + def write(self, x: str) -> None: + """Write a string into the output stream.""" + if self._new_lines: + if not self._first_write: + self.stream.write("\n" * self._new_lines) + self.code_lineno += self._new_lines + if self._write_debug_info is not None: + self.debug_info.append((self._write_debug_info, self.code_lineno)) + self._write_debug_info = None + self._first_write = False + self.stream.write(" " * self._indentation) + self._new_lines = 0 + self.stream.write(x) + + def writeline( + self, x: str, node: t.Optional[nodes.Node] = None, extra: int = 0 + ) -> None: + """Combination of newline and write.""" + self.newline(node, extra) + self.write(x) + + def newline(self, node: t.Optional[nodes.Node] = None, extra: int = 0) -> None: + """Add one or more newlines before the next write.""" + self._new_lines = max(self._new_lines, 1 + extra) + if node is not None and node.lineno != self._last_line: + self._write_debug_info = node.lineno + self._last_line = node.lineno + + def signature( + self, + node: t.Union[nodes.Call, nodes.Filter, nodes.Test], + frame: Frame, + extra_kwargs: t.Optional[t.Mapping[str, t.Any]] = None, + ) -> None: + """Writes a function call to the stream for the current node. + A leading comma is added automatically. The extra keyword + arguments may not include python keywords otherwise a syntax + error could occur. The extra keyword arguments should be given + as python dict. + """ + # if any of the given keyword arguments is a python keyword + # we have to make sure that no invalid call is created. + kwarg_workaround = any( + is_python_keyword(t.cast(str, k)) + for k in chain((x.key for x in node.kwargs), extra_kwargs or ()) + ) + + for arg in node.args: + self.write(", ") + self.visit(arg, frame) + + if not kwarg_workaround: + for kwarg in node.kwargs: + self.write(", ") + self.visit(kwarg, frame) + if extra_kwargs is not None: + for key, value in extra_kwargs.items(): + self.write(f", {key}={value}") + if node.dyn_args: + self.write(", *") + self.visit(node.dyn_args, frame) + + if kwarg_workaround: + if node.dyn_kwargs is not None: + self.write(", **dict({") + else: + self.write(", **{") + for kwarg in node.kwargs: + self.write(f"{kwarg.key!r}: ") + self.visit(kwarg.value, frame) + self.write(", ") + if extra_kwargs is not None: + for key, value in extra_kwargs.items(): + self.write(f"{key!r}: {value}, ") + if node.dyn_kwargs is not None: + self.write("}, **") + self.visit(node.dyn_kwargs, frame) + self.write(")") + else: + self.write("}") + + elif node.dyn_kwargs is not None: + self.write(", **") + self.visit(node.dyn_kwargs, frame) + + def pull_dependencies(self, nodes: t.Iterable[nodes.Node]) -> None: + """Find all filter and test names used in the template and + assign them to variables in the compiled namespace. Checking + that the names are registered with the environment is done when + compiling the Filter and Test nodes. If the node is in an If or + CondExpr node, the check is done at runtime instead. + + .. versionchanged:: 3.0 + Filters and tests in If and CondExpr nodes are checked at + runtime instead of compile time. + """ + visitor = DependencyFinderVisitor() + + for node in nodes: + visitor.visit(node) + + for id_map, names, dependency in ( + (self.filters, visitor.filters, "filters"), + ( + self.tests, + visitor.tests, + "tests", + ), + ): + for name in sorted(names): + if name not in id_map: + id_map[name] = self.temporary_identifier() + + # add check during runtime that dependencies used inside of executed + # blocks are defined, as this step may be skipped during compile time + self.writeline("try:") + self.indent() + self.writeline(f"{id_map[name]} = environment.{dependency}[{name!r}]") + self.outdent() + self.writeline("except KeyError:") + self.indent() + self.writeline("@internalcode") + self.writeline(f"def {id_map[name]}(*unused):") + self.indent() + self.writeline( + f'raise TemplateRuntimeError("No {dependency[:-1]}' + f' named {name!r} found.")' + ) + self.outdent() + self.outdent() + + def enter_frame(self, frame: Frame) -> None: + undefs = [] + for target, (action, param) in frame.symbols.loads.items(): + if action == VAR_LOAD_PARAMETER: + pass + elif action == VAR_LOAD_RESOLVE: + self.writeline(f"{target} = {self.get_resolve_func()}({param!r})") + elif action == VAR_LOAD_ALIAS: + self.writeline(f"{target} = {param}") + elif action == VAR_LOAD_UNDEFINED: + undefs.append(target) + else: + raise NotImplementedError("unknown load instruction") + if undefs: + self.writeline(f"{' = '.join(undefs)} = missing") + + def leave_frame(self, frame: Frame, with_python_scope: bool = False) -> None: + if not with_python_scope: + undefs = [] + for target in frame.symbols.loads: + undefs.append(target) + if undefs: + self.writeline(f"{' = '.join(undefs)} = missing") + + def choose_async(self, async_value: str = "async ", sync_value: str = "") -> str: + return async_value if self.environment.is_async else sync_value + + def func(self, name: str) -> str: + return f"{self.choose_async()}def {name}" + + def macro_body( + self, node: t.Union[nodes.Macro, nodes.CallBlock], frame: Frame + ) -> t.Tuple[Frame, MacroRef]: + """Dump the function def of a macro or call block.""" + frame = frame.inner() + frame.symbols.analyze_node(node) + macro_ref = MacroRef(node) + + explicit_caller = None + skip_special_params = set() + args = [] + + for idx, arg in enumerate(node.args): + if arg.name == "caller": + explicit_caller = idx + if arg.name in ("kwargs", "varargs"): + skip_special_params.add(arg.name) + args.append(frame.symbols.ref(arg.name)) + + undeclared = find_undeclared(node.body, ("caller", "kwargs", "varargs")) + + if "caller" in undeclared: + # In older Jinja versions there was a bug that allowed caller + # to retain the special behavior even if it was mentioned in + # the argument list. However thankfully this was only really + # working if it was the last argument. So we are explicitly + # checking this now and error out if it is anywhere else in + # the argument list. + if explicit_caller is not None: + try: + node.defaults[explicit_caller - len(node.args)] + except IndexError: + self.fail( + "When defining macros or call blocks the " + 'special "caller" argument must be omitted ' + "or be given a default.", + node.lineno, + ) + else: + args.append(frame.symbols.declare_parameter("caller")) + macro_ref.accesses_caller = True + if "kwargs" in undeclared and "kwargs" not in skip_special_params: + args.append(frame.symbols.declare_parameter("kwargs")) + macro_ref.accesses_kwargs = True + if "varargs" in undeclared and "varargs" not in skip_special_params: + args.append(frame.symbols.declare_parameter("varargs")) + macro_ref.accesses_varargs = True + + # macros are delayed, they never require output checks + frame.require_output_check = False + frame.symbols.analyze_node(node) + self.writeline(f"{self.func('macro')}({', '.join(args)}):", node) + self.indent() + + self.buffer(frame) + self.enter_frame(frame) + + self.push_parameter_definitions(frame) + for idx, arg in enumerate(node.args): + ref = frame.symbols.ref(arg.name) + self.writeline(f"if {ref} is missing:") + self.indent() + try: + default = node.defaults[idx - len(node.args)] + except IndexError: + self.writeline( + f'{ref} = undefined("parameter {arg.name!r} was not provided",' + f" name={arg.name!r})" + ) + else: + self.writeline(f"{ref} = ") + self.visit(default, frame) + self.mark_parameter_stored(ref) + self.outdent() + self.pop_parameter_definitions() + + self.blockvisit(node.body, frame) + self.return_buffer_contents(frame, force_unescaped=True) + self.leave_frame(frame, with_python_scope=True) + self.outdent() + + return frame, macro_ref + + def macro_def(self, macro_ref: MacroRef, frame: Frame) -> None: + """Dump the macro definition for the def created by macro_body.""" + arg_tuple = ", ".join(repr(x.name) for x in macro_ref.node.args) + name = getattr(macro_ref.node, "name", None) + if len(macro_ref.node.args) == 1: + arg_tuple += "," + self.write( + f"Macro(environment, macro, {name!r}, ({arg_tuple})," + f" {macro_ref.accesses_kwargs!r}, {macro_ref.accesses_varargs!r}," + f" {macro_ref.accesses_caller!r}, context.eval_ctx.autoescape)" + ) + + def position(self, node: nodes.Node) -> str: + """Return a human readable position for the node.""" + rv = f"line {node.lineno}" + if self.name is not None: + rv = f"{rv} in {self.name!r}" + return rv + + def dump_local_context(self, frame: Frame) -> str: + items_kv = ", ".join( + f"{name!r}: {target}" + for name, target in frame.symbols.dump_stores().items() + ) + return f"{{{items_kv}}}" + + def write_commons(self) -> None: + """Writes a common preamble that is used by root and block functions. + Primarily this sets up common local helpers and enforces a generator + through a dead branch. + """ + self.writeline("resolve = context.resolve_or_missing") + self.writeline("undefined = environment.undefined") + self.writeline("concat = environment.concat") + # always use the standard Undefined class for the implicit else of + # conditional expressions + self.writeline("cond_expr_undefined = Undefined") + self.writeline("if 0: yield None") + + def push_parameter_definitions(self, frame: Frame) -> None: + """Pushes all parameter targets from the given frame into a local + stack that permits tracking of yet to be assigned parameters. In + particular this enables the optimization from `visit_Name` to skip + undefined expressions for parameters in macros as macros can reference + otherwise unbound parameters. + """ + self._param_def_block.append(frame.symbols.dump_param_targets()) + + def pop_parameter_definitions(self) -> None: + """Pops the current parameter definitions set.""" + self._param_def_block.pop() + + def mark_parameter_stored(self, target: str) -> None: + """Marks a parameter in the current parameter definitions as stored. + This will skip the enforced undefined checks. + """ + if self._param_def_block: + self._param_def_block[-1].discard(target) + + def push_context_reference(self, target: str) -> None: + self._context_reference_stack.append(target) + + def pop_context_reference(self) -> None: + self._context_reference_stack.pop() + + def get_context_ref(self) -> str: + return self._context_reference_stack[-1] + + def get_resolve_func(self) -> str: + target = self._context_reference_stack[-1] + if target == "context": + return "resolve" + return f"{target}.resolve" + + def derive_context(self, frame: Frame) -> str: + return f"{self.get_context_ref()}.derived({self.dump_local_context(frame)})" + + def parameter_is_undeclared(self, target: str) -> bool: + """Checks if a given target is an undeclared parameter.""" + if not self._param_def_block: + return False + return target in self._param_def_block[-1] + + def push_assign_tracking(self) -> None: + """Pushes a new layer for assignment tracking.""" + self._assign_stack.append(set()) + + def pop_assign_tracking(self, frame: Frame) -> None: + """Pops the topmost level for assignment tracking and updates the + context variables if necessary. + """ + vars = self._assign_stack.pop() + if ( + not frame.block_frame + and not frame.loop_frame + and not frame.toplevel + or not vars + ): + return + public_names = [x for x in vars if x[:1] != "_"] + if len(vars) == 1: + name = next(iter(vars)) + ref = frame.symbols.ref(name) + if frame.loop_frame: + self.writeline(f"_loop_vars[{name!r}] = {ref}") + return + if frame.block_frame: + self.writeline(f"_block_vars[{name!r}] = {ref}") + return + self.writeline(f"context.vars[{name!r}] = {ref}") + else: + if frame.loop_frame: + self.writeline("_loop_vars.update({") + elif frame.block_frame: + self.writeline("_block_vars.update({") + else: + self.writeline("context.vars.update({") + for idx, name in enumerate(sorted(vars)): + if idx: + self.write(", ") + ref = frame.symbols.ref(name) + self.write(f"{name!r}: {ref}") + self.write("})") + if not frame.block_frame and not frame.loop_frame and public_names: + if len(public_names) == 1: + self.writeline(f"context.exported_vars.add({public_names[0]!r})") + else: + names_str = ", ".join(map(repr, sorted(public_names))) + self.writeline(f"context.exported_vars.update(({names_str}))") + + # -- Statement Visitors + + def visit_Template( + self, node: nodes.Template, frame: t.Optional[Frame] = None + ) -> None: + assert frame is None, "no root frame allowed" + eval_ctx = EvalContext(self.environment, self.name) + + from .runtime import async_exported + from .runtime import exported + + if self.environment.is_async: + exported_names = sorted(exported + async_exported) + else: + exported_names = sorted(exported) + + self.writeline("from jinja2.runtime import " + ", ".join(exported_names)) + + # if we want a deferred initialization we cannot move the + # environment into a local name + envenv = "" if self.defer_init else ", environment=environment" + + # do we have an extends tag at all? If not, we can save some + # overhead by just not processing any inheritance code. + have_extends = node.find(nodes.Extends) is not None + + # find all blocks + for block in node.find_all(nodes.Block): + if block.name in self.blocks: + self.fail(f"block {block.name!r} defined twice", block.lineno) + self.blocks[block.name] = block + + # find all imports and import them + for import_ in node.find_all(nodes.ImportedName): + if import_.importname not in self.import_aliases: + imp = import_.importname + self.import_aliases[imp] = alias = self.temporary_identifier() + if "." in imp: + module, obj = imp.rsplit(".", 1) + self.writeline(f"from {module} import {obj} as {alias}") + else: + self.writeline(f"import {imp} as {alias}") + + # add the load name + self.writeline(f"name = {self.name!r}") + + # generate the root render function. + self.writeline( + f"{self.func('root')}(context, missing=missing{envenv}):", extra=1 + ) + self.indent() + self.write_commons() + + # process the root + frame = Frame(eval_ctx) + if "self" in find_undeclared(node.body, ("self",)): + ref = frame.symbols.declare_parameter("self") + self.writeline(f"{ref} = TemplateReference(context)") + frame.symbols.analyze_node(node) + frame.toplevel = frame.rootlevel = True + frame.require_output_check = have_extends and not self.has_known_extends + if have_extends: + self.writeline("parent_template = None") + self.enter_frame(frame) + self.pull_dependencies(node.body) + self.blockvisit(node.body, frame) + self.leave_frame(frame, with_python_scope=True) + self.outdent() + + # make sure that the parent root is called. + if have_extends: + if not self.has_known_extends: + self.indent() + self.writeline("if parent_template is not None:") + self.indent() + if not self.environment.is_async: + self.writeline("yield from parent_template.root_render_func(context)") + else: + self.writeline("agen = parent_template.root_render_func(context)") + self.writeline("try:") + self.indent() + self.writeline("async for event in agen:") + self.indent() + self.writeline("yield event") + self.outdent() + self.outdent() + self.writeline("finally: await agen.aclose()") + self.outdent(1 + (not self.has_known_extends)) + + # at this point we now have the blocks collected and can visit them too. + for name, block in self.blocks.items(): + self.writeline( + f"{self.func('block_' + name)}(context, missing=missing{envenv}):", + block, + 1, + ) + self.indent() + self.write_commons() + # It's important that we do not make this frame a child of the + # toplevel template. This would cause a variety of + # interesting issues with identifier tracking. + block_frame = Frame(eval_ctx) + block_frame.block_frame = True + undeclared = find_undeclared(block.body, ("self", "super")) + if "self" in undeclared: + ref = block_frame.symbols.declare_parameter("self") + self.writeline(f"{ref} = TemplateReference(context)") + if "super" in undeclared: + ref = block_frame.symbols.declare_parameter("super") + self.writeline(f"{ref} = context.super({name!r}, block_{name})") + block_frame.symbols.analyze_node(block) + block_frame.block = name + self.writeline("_block_vars = {}") + self.enter_frame(block_frame) + self.pull_dependencies(block.body) + self.blockvisit(block.body, block_frame) + self.leave_frame(block_frame, with_python_scope=True) + self.outdent() + + blocks_kv_str = ", ".join(f"{x!r}: block_{x}" for x in self.blocks) + self.writeline(f"blocks = {{{blocks_kv_str}}}", extra=1) + debug_kv_str = "&".join(f"{k}={v}" for k, v in self.debug_info) + self.writeline(f"debug_info = {debug_kv_str!r}") + + def visit_Block(self, node: nodes.Block, frame: Frame) -> None: + """Call a block and register it for the template.""" + level = 0 + if frame.toplevel: + # if we know that we are a child template, there is no need to + # check if we are one + if self.has_known_extends: + return + if self.extends_so_far > 0: + self.writeline("if parent_template is None:") + self.indent() + level += 1 + + if node.scoped: + context = self.derive_context(frame) + else: + context = self.get_context_ref() + + if node.required: + self.writeline(f"if len(context.blocks[{node.name!r}]) <= 1:", node) + self.indent() + self.writeline( + f'raise TemplateRuntimeError("Required block {node.name!r} not found")', + node, + ) + self.outdent() + + if not self.environment.is_async and frame.buffer is None: + self.writeline( + f"yield from context.blocks[{node.name!r}][0]({context})", node + ) + else: + self.writeline(f"gen = context.blocks[{node.name!r}][0]({context})") + self.writeline("try:") + self.indent() + self.writeline( + f"{self.choose_async()}for event in gen:", + node, + ) + self.indent() + self.simple_write("event", frame) + self.outdent() + self.outdent() + self.writeline( + f"finally: {self.choose_async('await gen.aclose()', 'gen.close()')}" + ) + + self.outdent(level) + + def visit_Extends(self, node: nodes.Extends, frame: Frame) -> None: + """Calls the extender.""" + if not frame.toplevel: + self.fail("cannot use extend from a non top-level scope", node.lineno) + + # if the number of extends statements in general is zero so + # far, we don't have to add a check if something extended + # the template before this one. + if self.extends_so_far > 0: + # if we have a known extends we just add a template runtime + # error into the generated code. We could catch that at compile + # time too, but i welcome it not to confuse users by throwing the + # same error at different times just "because we can". + if not self.has_known_extends: + self.writeline("if parent_template is not None:") + self.indent() + self.writeline('raise TemplateRuntimeError("extended multiple times")') + + # if we have a known extends already we don't need that code here + # as we know that the template execution will end here. + if self.has_known_extends: + raise CompilerExit() + else: + self.outdent() + + self.writeline("parent_template = environment.get_template(", node) + self.visit(node.template, frame) + self.write(f", {self.name!r})") + self.writeline("for name, parent_block in parent_template.blocks.items():") + self.indent() + self.writeline("context.blocks.setdefault(name, []).append(parent_block)") + self.outdent() + + # if this extends statement was in the root level we can take + # advantage of that information and simplify the generated code + # in the top level from this point onwards + if frame.rootlevel: + self.has_known_extends = True + + # and now we have one more + self.extends_so_far += 1 + + def visit_Include(self, node: nodes.Include, frame: Frame) -> None: + """Handles includes.""" + if node.ignore_missing: + self.writeline("try:") + self.indent() + + func_name = "get_or_select_template" + if isinstance(node.template, nodes.Const): + if isinstance(node.template.value, str): + func_name = "get_template" + elif isinstance(node.template.value, (tuple, list)): + func_name = "select_template" + elif isinstance(node.template, (nodes.Tuple, nodes.List)): + func_name = "select_template" + + self.writeline(f"template = environment.{func_name}(", node) + self.visit(node.template, frame) + self.write(f", {self.name!r})") + if node.ignore_missing: + self.outdent() + self.writeline("except TemplateNotFound:") + self.indent() + self.writeline("pass") + self.outdent() + self.writeline("else:") + self.indent() + + def loop_body() -> None: + self.indent() + self.simple_write("event", frame) + self.outdent() + + if node.with_context: + self.writeline( + f"gen = template.root_render_func(" + "template.new_context(context.get_all(), True," + f" {self.dump_local_context(frame)}))" + ) + self.writeline("try:") + self.indent() + self.writeline(f"{self.choose_async()}for event in gen:") + loop_body() + self.outdent() + self.writeline( + f"finally: {self.choose_async('await gen.aclose()', 'gen.close()')}" + ) + elif self.environment.is_async: + self.writeline( + "for event in (await template._get_default_module_async())" + "._body_stream:" + ) + loop_body() + else: + self.writeline("yield from template._get_default_module()._body_stream") + + if node.ignore_missing: + self.outdent() + + def _import_common( + self, node: t.Union[nodes.Import, nodes.FromImport], frame: Frame + ) -> None: + self.write(f"{self.choose_async('await ')}environment.get_template(") + self.visit(node.template, frame) + self.write(f", {self.name!r}).") + + if node.with_context: + f_name = f"make_module{self.choose_async('_async')}" + self.write( + f"{f_name}(context.get_all(), True, {self.dump_local_context(frame)})" + ) + else: + self.write(f"_get_default_module{self.choose_async('_async')}(context)") + + def visit_Import(self, node: nodes.Import, frame: Frame) -> None: + """Visit regular imports.""" + self.writeline(f"{frame.symbols.ref(node.target)} = ", node) + if frame.toplevel: + self.write(f"context.vars[{node.target!r}] = ") + + self._import_common(node, frame) + + if frame.toplevel and not node.target.startswith("_"): + self.writeline(f"context.exported_vars.discard({node.target!r})") + + def visit_FromImport(self, node: nodes.FromImport, frame: Frame) -> None: + """Visit named imports.""" + self.newline(node) + self.write("included_template = ") + self._import_common(node, frame) + var_names = [] + discarded_names = [] + for name in node.names: + if isinstance(name, tuple): + name, alias = name + else: + alias = name + self.writeline( + f"{frame.symbols.ref(alias)} =" + f" getattr(included_template, {name!r}, missing)" + ) + self.writeline(f"if {frame.symbols.ref(alias)} is missing:") + self.indent() + # The position will contain the template name, and will be formatted + # into a string that will be compiled into an f-string. Curly braces + # in the name must be replaced with escapes so that they will not be + # executed as part of the f-string. + position = self.position(node).replace("{", "{{").replace("}", "}}") + message = ( + "the template {included_template.__name__!r}" + f" (imported on {position})" + f" does not export the requested name {name!r}" + ) + self.writeline( + f"{frame.symbols.ref(alias)} = undefined(f{message!r}, name={name!r})" + ) + self.outdent() + if frame.toplevel: + var_names.append(alias) + if not alias.startswith("_"): + discarded_names.append(alias) + + if var_names: + if len(var_names) == 1: + name = var_names[0] + self.writeline(f"context.vars[{name!r}] = {frame.symbols.ref(name)}") + else: + names_kv = ", ".join( + f"{name!r}: {frame.symbols.ref(name)}" for name in var_names + ) + self.writeline(f"context.vars.update({{{names_kv}}})") + if discarded_names: + if len(discarded_names) == 1: + self.writeline(f"context.exported_vars.discard({discarded_names[0]!r})") + else: + names_str = ", ".join(map(repr, discarded_names)) + self.writeline( + f"context.exported_vars.difference_update(({names_str}))" + ) + + def visit_For(self, node: nodes.For, frame: Frame) -> None: + loop_frame = frame.inner() + loop_frame.loop_frame = True + test_frame = frame.inner() + else_frame = frame.inner() + + # try to figure out if we have an extended loop. An extended loop + # is necessary if the loop is in recursive mode if the special loop + # variable is accessed in the body if the body is a scoped block. + extended_loop = ( + node.recursive + or "loop" + in find_undeclared(node.iter_child_nodes(only=("body",)), ("loop",)) + or any(block.scoped for block in node.find_all(nodes.Block)) + ) + + loop_ref = None + if extended_loop: + loop_ref = loop_frame.symbols.declare_parameter("loop") + + loop_frame.symbols.analyze_node(node, for_branch="body") + if node.else_: + else_frame.symbols.analyze_node(node, for_branch="else") + + if node.test: + loop_filter_func = self.temporary_identifier() + test_frame.symbols.analyze_node(node, for_branch="test") + self.writeline(f"{self.func(loop_filter_func)}(fiter):", node.test) + self.indent() + self.enter_frame(test_frame) + self.writeline(self.choose_async("async for ", "for ")) + self.visit(node.target, loop_frame) + self.write(" in ") + self.write(self.choose_async("auto_aiter(fiter)", "fiter")) + self.write(":") + self.indent() + self.writeline("if ", node.test) + self.visit(node.test, test_frame) + self.write(":") + self.indent() + self.writeline("yield ") + self.visit(node.target, loop_frame) + self.outdent(3) + self.leave_frame(test_frame, with_python_scope=True) + + # if we don't have an recursive loop we have to find the shadowed + # variables at that point. Because loops can be nested but the loop + # variable is a special one we have to enforce aliasing for it. + if node.recursive: + self.writeline( + f"{self.func('loop')}(reciter, loop_render_func, depth=0):", node + ) + self.indent() + self.buffer(loop_frame) + + # Use the same buffer for the else frame + else_frame.buffer = loop_frame.buffer + + # make sure the loop variable is a special one and raise a template + # assertion error if a loop tries to write to loop + if extended_loop: + self.writeline(f"{loop_ref} = missing") + + for name in node.find_all(nodes.Name): + if name.ctx == "store" and name.name == "loop": + self.fail( + "Can't assign to special loop variable in for-loop target", + name.lineno, + ) + + if node.else_: + iteration_indicator = self.temporary_identifier() + self.writeline(f"{iteration_indicator} = 1") + + self.writeline(self.choose_async("async for ", "for "), node) + self.visit(node.target, loop_frame) + if extended_loop: + self.write(f", {loop_ref} in {self.choose_async('Async')}LoopContext(") + else: + self.write(" in ") + + if node.test: + self.write(f"{loop_filter_func}(") + if node.recursive: + self.write("reciter") + else: + if self.environment.is_async and not extended_loop: + self.write("auto_aiter(") + self.visit(node.iter, frame) + if self.environment.is_async and not extended_loop: + self.write(")") + if node.test: + self.write(")") + + if node.recursive: + self.write(", undefined, loop_render_func, depth):") + else: + self.write(", undefined):" if extended_loop else ":") + + self.indent() + self.enter_frame(loop_frame) + + self.writeline("_loop_vars = {}") + self.blockvisit(node.body, loop_frame) + if node.else_: + self.writeline(f"{iteration_indicator} = 0") + self.outdent() + self.leave_frame( + loop_frame, with_python_scope=node.recursive and not node.else_ + ) + + if node.else_: + self.writeline(f"if {iteration_indicator}:") + self.indent() + self.enter_frame(else_frame) + self.blockvisit(node.else_, else_frame) + self.leave_frame(else_frame) + self.outdent() + + # if the node was recursive we have to return the buffer contents + # and start the iteration code + if node.recursive: + self.return_buffer_contents(loop_frame) + self.outdent() + self.start_write(frame, node) + self.write(f"{self.choose_async('await ')}loop(") + if self.environment.is_async: + self.write("auto_aiter(") + self.visit(node.iter, frame) + if self.environment.is_async: + self.write(")") + self.write(", loop)") + self.end_write(frame) + + # at the end of the iteration, clear any assignments made in the + # loop from the top level + if self._assign_stack: + self._assign_stack[-1].difference_update(loop_frame.symbols.stores) + + def visit_If(self, node: nodes.If, frame: Frame) -> None: + if_frame = frame.soft() + self.writeline("if ", node) + self.visit(node.test, if_frame) + self.write(":") + self.indent() + self.blockvisit(node.body, if_frame) + self.outdent() + for elif_ in node.elif_: + self.writeline("elif ", elif_) + self.visit(elif_.test, if_frame) + self.write(":") + self.indent() + self.blockvisit(elif_.body, if_frame) + self.outdent() + if node.else_: + self.writeline("else:") + self.indent() + self.blockvisit(node.else_, if_frame) + self.outdent() + + def visit_Macro(self, node: nodes.Macro, frame: Frame) -> None: + macro_frame, macro_ref = self.macro_body(node, frame) + self.newline() + if frame.toplevel: + if not node.name.startswith("_"): + self.write(f"context.exported_vars.add({node.name!r})") + self.writeline(f"context.vars[{node.name!r}] = ") + self.write(f"{frame.symbols.ref(node.name)} = ") + self.macro_def(macro_ref, macro_frame) + + def visit_CallBlock(self, node: nodes.CallBlock, frame: Frame) -> None: + call_frame, macro_ref = self.macro_body(node, frame) + self.writeline("caller = ") + self.macro_def(macro_ref, call_frame) + self.start_write(frame, node) + self.visit_Call(node.call, frame, forward_caller=True) + self.end_write(frame) + + def visit_FilterBlock(self, node: nodes.FilterBlock, frame: Frame) -> None: + filter_frame = frame.inner() + filter_frame.symbols.analyze_node(node) + self.enter_frame(filter_frame) + self.buffer(filter_frame) + self.blockvisit(node.body, filter_frame) + self.start_write(frame, node) + self.visit_Filter(node.filter, filter_frame) + self.end_write(frame) + self.leave_frame(filter_frame) + + def visit_With(self, node: nodes.With, frame: Frame) -> None: + with_frame = frame.inner() + with_frame.symbols.analyze_node(node) + self.enter_frame(with_frame) + for target, expr in zip(node.targets, node.values): + self.newline() + self.visit(target, with_frame) + self.write(" = ") + self.visit(expr, frame) + self.blockvisit(node.body, with_frame) + self.leave_frame(with_frame) + + def visit_ExprStmt(self, node: nodes.ExprStmt, frame: Frame) -> None: + self.newline(node) + self.visit(node.node, frame) + + class _FinalizeInfo(t.NamedTuple): + const: t.Optional[t.Callable[..., str]] + src: t.Optional[str] + + @staticmethod + def _default_finalize(value: t.Any) -> t.Any: + """The default finalize function if the environment isn't + configured with one. Or, if the environment has one, this is + called on that function's output for constants. + """ + return str(value) + + _finalize: t.Optional[_FinalizeInfo] = None + + def _make_finalize(self) -> _FinalizeInfo: + """Build the finalize function to be used on constants and at + runtime. Cached so it's only created once for all output nodes. + + Returns a ``namedtuple`` with the following attributes: + + ``const`` + A function to finalize constant data at compile time. + + ``src`` + Source code to output around nodes to be evaluated at + runtime. + """ + if self._finalize is not None: + return self._finalize + + finalize: t.Optional[t.Callable[..., t.Any]] + finalize = default = self._default_finalize + src = None + + if self.environment.finalize: + src = "environment.finalize(" + env_finalize = self.environment.finalize + pass_arg = { + _PassArg.context: "context", + _PassArg.eval_context: "context.eval_ctx", + _PassArg.environment: "environment", + }.get( + _PassArg.from_obj(env_finalize) # type: ignore + ) + finalize = None + + if pass_arg is None: + + def finalize(value: t.Any) -> t.Any: # noqa: F811 + return default(env_finalize(value)) + + else: + src = f"{src}{pass_arg}, " + + if pass_arg == "environment": + + def finalize(value: t.Any) -> t.Any: # noqa: F811 + return default(env_finalize(self.environment, value)) + + self._finalize = self._FinalizeInfo(finalize, src) + return self._finalize + + def _output_const_repr(self, group: t.Iterable[t.Any]) -> str: + """Given a group of constant values converted from ``Output`` + child nodes, produce a string to write to the template module + source. + """ + return repr(concat(group)) + + def _output_child_to_const( + self, node: nodes.Expr, frame: Frame, finalize: _FinalizeInfo + ) -> str: + """Try to optimize a child of an ``Output`` node by trying to + convert it to constant, finalized data at compile time. + + If :exc:`Impossible` is raised, the node is not constant and + will be evaluated at runtime. Any other exception will also be + evaluated at runtime for easier debugging. + """ + const = node.as_const(frame.eval_ctx) + + if frame.eval_ctx.autoescape: + const = escape(const) + + # Template data doesn't go through finalize. + if isinstance(node, nodes.TemplateData): + return str(const) + + return finalize.const(const) # type: ignore + + def _output_child_pre( + self, node: nodes.Expr, frame: Frame, finalize: _FinalizeInfo + ) -> None: + """Output extra source code before visiting a child of an + ``Output`` node. + """ + if frame.eval_ctx.volatile: + self.write("(escape if context.eval_ctx.autoescape else str)(") + elif frame.eval_ctx.autoescape: + self.write("escape(") + else: + self.write("str(") + + if finalize.src is not None: + self.write(finalize.src) + + def _output_child_post( + self, node: nodes.Expr, frame: Frame, finalize: _FinalizeInfo + ) -> None: + """Output extra source code after visiting a child of an + ``Output`` node. + """ + self.write(")") + + if finalize.src is not None: + self.write(")") + + def visit_Output(self, node: nodes.Output, frame: Frame) -> None: + # If an extends is active, don't render outside a block. + if frame.require_output_check: + # A top-level extends is known to exist at compile time. + if self.has_known_extends: + return + + self.writeline("if parent_template is None:") + self.indent() + + finalize = self._make_finalize() + body: t.List[t.Union[t.List[t.Any], nodes.Expr]] = [] + + # Evaluate constants at compile time if possible. Each item in + # body will be either a list of static data or a node to be + # evaluated at runtime. + for child in node.nodes: + try: + if not ( + # If the finalize function requires runtime context, + # constants can't be evaluated at compile time. + finalize.const + # Unless it's basic template data that won't be + # finalized anyway. + or isinstance(child, nodes.TemplateData) + ): + raise nodes.Impossible() + + const = self._output_child_to_const(child, frame, finalize) + except (nodes.Impossible, Exception): + # The node was not constant and needs to be evaluated at + # runtime. Or another error was raised, which is easier + # to debug at runtime. + body.append(child) + continue + + if body and isinstance(body[-1], list): + body[-1].append(const) + else: + body.append([const]) + + if frame.buffer is not None: + if len(body) == 1: + self.writeline(f"{frame.buffer}.append(") + else: + self.writeline(f"{frame.buffer}.extend((") + + self.indent() + + for item in body: + if isinstance(item, list): + # A group of constant data to join and output. + val = self._output_const_repr(item) + + if frame.buffer is None: + self.writeline("yield " + val) + else: + self.writeline(val + ",") + else: + if frame.buffer is None: + self.writeline("yield ", item) + else: + self.newline(item) + + # A node to be evaluated at runtime. + self._output_child_pre(item, frame, finalize) + self.visit(item, frame) + self._output_child_post(item, frame, finalize) + + if frame.buffer is not None: + self.write(",") + + if frame.buffer is not None: + self.outdent() + self.writeline(")" if len(body) == 1 else "))") + + if frame.require_output_check: + self.outdent() + + def visit_Assign(self, node: nodes.Assign, frame: Frame) -> None: + self.push_assign_tracking() + + # ``a.b`` is allowed for assignment, and is parsed as an NSRef. However, + # it is only valid if it references a Namespace object. Emit a check for + # that for each ref here, before assignment code is emitted. This can't + # be done in visit_NSRef as the ref could be in the middle of a tuple. + seen_refs: t.Set[str] = set() + + for nsref in node.find_all(nodes.NSRef): + if nsref.name in seen_refs: + # Only emit the check for each reference once, in case the same + # ref is used multiple times in a tuple, `ns.a, ns.b = c, d`. + continue + + seen_refs.add(nsref.name) + ref = frame.symbols.ref(nsref.name) + self.writeline(f"if not isinstance({ref}, Namespace):") + self.indent() + self.writeline( + "raise TemplateRuntimeError" + '("cannot assign attribute on non-namespace object")' + ) + self.outdent() + + self.newline(node) + self.visit(node.target, frame) + self.write(" = ") + self.visit(node.node, frame) + self.pop_assign_tracking(frame) + + def visit_AssignBlock(self, node: nodes.AssignBlock, frame: Frame) -> None: + self.push_assign_tracking() + block_frame = frame.inner() + # This is a special case. Since a set block always captures we + # will disable output checks. This way one can use set blocks + # toplevel even in extended templates. + block_frame.require_output_check = False + block_frame.symbols.analyze_node(node) + self.enter_frame(block_frame) + self.buffer(block_frame) + self.blockvisit(node.body, block_frame) + self.newline(node) + self.visit(node.target, frame) + self.write(" = (Markup if context.eval_ctx.autoescape else identity)(") + if node.filter is not None: + self.visit_Filter(node.filter, block_frame) + else: + self.write(f"concat({block_frame.buffer})") + self.write(")") + self.pop_assign_tracking(frame) + self.leave_frame(block_frame) + + # -- Expression Visitors + + def visit_Name(self, node: nodes.Name, frame: Frame) -> None: + if node.ctx == "store" and ( + frame.toplevel or frame.loop_frame or frame.block_frame + ): + if self._assign_stack: + self._assign_stack[-1].add(node.name) + ref = frame.symbols.ref(node.name) + + # If we are looking up a variable we might have to deal with the + # case where it's undefined. We can skip that case if the load + # instruction indicates a parameter which are always defined. + if node.ctx == "load": + load = frame.symbols.find_load(ref) + if not ( + load is not None + and load[0] == VAR_LOAD_PARAMETER + and not self.parameter_is_undeclared(ref) + ): + self.write( + f"(undefined(name={node.name!r}) if {ref} is missing else {ref})" + ) + return + + self.write(ref) + + def visit_NSRef(self, node: nodes.NSRef, frame: Frame) -> None: + # NSRef is a dotted assignment target a.b=c, but uses a[b]=c internally. + # visit_Assign emits code to validate that each ref is to a Namespace + # object only. That can't be emitted here as the ref could be in the + # middle of a tuple assignment. + ref = frame.symbols.ref(node.name) + self.writeline(f"{ref}[{node.attr!r}]") + + def visit_Const(self, node: nodes.Const, frame: Frame) -> None: + val = node.as_const(frame.eval_ctx) + if isinstance(val, float): + self.write(str(val)) + else: + self.write(repr(val)) + + def visit_TemplateData(self, node: nodes.TemplateData, frame: Frame) -> None: + try: + self.write(repr(node.as_const(frame.eval_ctx))) + except nodes.Impossible: + self.write( + f"(Markup if context.eval_ctx.autoescape else identity)({node.data!r})" + ) + + def visit_Tuple(self, node: nodes.Tuple, frame: Frame) -> None: + self.write("(") + idx = -1 + for idx, item in enumerate(node.items): + if idx: + self.write(", ") + self.visit(item, frame) + self.write(",)" if idx == 0 else ")") + + def visit_List(self, node: nodes.List, frame: Frame) -> None: + self.write("[") + for idx, item in enumerate(node.items): + if idx: + self.write(", ") + self.visit(item, frame) + self.write("]") + + def visit_Dict(self, node: nodes.Dict, frame: Frame) -> None: + self.write("{") + for idx, item in enumerate(node.items): + if idx: + self.write(", ") + self.visit(item.key, frame) + self.write(": ") + self.visit(item.value, frame) + self.write("}") + + visit_Add = _make_binop("+") + visit_Sub = _make_binop("-") + visit_Mul = _make_binop("*") + visit_Div = _make_binop("/") + visit_FloorDiv = _make_binop("//") + visit_Pow = _make_binop("**") + visit_Mod = _make_binop("%") + visit_And = _make_binop("and") + visit_Or = _make_binop("or") + visit_Pos = _make_unop("+") + visit_Neg = _make_unop("-") + visit_Not = _make_unop("not ") + + @optimizeconst + def visit_Concat(self, node: nodes.Concat, frame: Frame) -> None: + if frame.eval_ctx.volatile: + func_name = "(markup_join if context.eval_ctx.volatile else str_join)" + elif frame.eval_ctx.autoescape: + func_name = "markup_join" + else: + func_name = "str_join" + self.write(f"{func_name}((") + for arg in node.nodes: + self.visit(arg, frame) + self.write(", ") + self.write("))") + + @optimizeconst + def visit_Compare(self, node: nodes.Compare, frame: Frame) -> None: + self.write("(") + self.visit(node.expr, frame) + for op in node.ops: + self.visit(op, frame) + self.write(")") + + def visit_Operand(self, node: nodes.Operand, frame: Frame) -> None: + self.write(f" {operators[node.op]} ") + self.visit(node.expr, frame) + + @optimizeconst + def visit_Getattr(self, node: nodes.Getattr, frame: Frame) -> None: + if self.environment.is_async: + self.write("(await auto_await(") + + self.write("environment.getattr(") + self.visit(node.node, frame) + self.write(f", {node.attr!r})") + + if self.environment.is_async: + self.write("))") + + @optimizeconst + def visit_Getitem(self, node: nodes.Getitem, frame: Frame) -> None: + # slices bypass the environment getitem method. + if isinstance(node.arg, nodes.Slice): + self.visit(node.node, frame) + self.write("[") + self.visit(node.arg, frame) + self.write("]") + else: + if self.environment.is_async: + self.write("(await auto_await(") + + self.write("environment.getitem(") + self.visit(node.node, frame) + self.write(", ") + self.visit(node.arg, frame) + self.write(")") + + if self.environment.is_async: + self.write("))") + + def visit_Slice(self, node: nodes.Slice, frame: Frame) -> None: + if node.start is not None: + self.visit(node.start, frame) + self.write(":") + if node.stop is not None: + self.visit(node.stop, frame) + if node.step is not None: + self.write(":") + self.visit(node.step, frame) + + @contextmanager + def _filter_test_common( + self, node: t.Union[nodes.Filter, nodes.Test], frame: Frame, is_filter: bool + ) -> t.Iterator[None]: + if self.environment.is_async: + self.write("(await auto_await(") + + if is_filter: + self.write(f"{self.filters[node.name]}(") + func = self.environment.filters.get(node.name) + else: + self.write(f"{self.tests[node.name]}(") + func = self.environment.tests.get(node.name) + + # When inside an If or CondExpr frame, allow the filter to be + # undefined at compile time and only raise an error if it's + # actually called at runtime. See pull_dependencies. + if func is None and not frame.soft_frame: + type_name = "filter" if is_filter else "test" + self.fail(f"No {type_name} named {node.name!r}.", node.lineno) + + pass_arg = { + _PassArg.context: "context", + _PassArg.eval_context: "context.eval_ctx", + _PassArg.environment: "environment", + }.get( + _PassArg.from_obj(func) # type: ignore + ) + + if pass_arg is not None: + self.write(f"{pass_arg}, ") + + # Back to the visitor function to handle visiting the target of + # the filter or test. + yield + + self.signature(node, frame) + self.write(")") + + if self.environment.is_async: + self.write("))") + + @optimizeconst + def visit_Filter(self, node: nodes.Filter, frame: Frame) -> None: + with self._filter_test_common(node, frame, True): + # if the filter node is None we are inside a filter block + # and want to write to the current buffer + if node.node is not None: + self.visit(node.node, frame) + elif frame.eval_ctx.volatile: + self.write( + f"(Markup(concat({frame.buffer}))" + f" if context.eval_ctx.autoescape else concat({frame.buffer}))" + ) + elif frame.eval_ctx.autoescape: + self.write(f"Markup(concat({frame.buffer}))") + else: + self.write(f"concat({frame.buffer})") + + @optimizeconst + def visit_Test(self, node: nodes.Test, frame: Frame) -> None: + with self._filter_test_common(node, frame, False): + self.visit(node.node, frame) + + @optimizeconst + def visit_CondExpr(self, node: nodes.CondExpr, frame: Frame) -> None: + frame = frame.soft() + + def write_expr2() -> None: + if node.expr2 is not None: + self.visit(node.expr2, frame) + return + + self.write( + f'cond_expr_undefined("the inline if-expression on' + f" {self.position(node)} evaluated to false and no else" + f' section was defined.")' + ) + + self.write("(") + self.visit(node.expr1, frame) + self.write(" if ") + self.visit(node.test, frame) + self.write(" else ") + write_expr2() + self.write(")") + + @optimizeconst + def visit_Call( + self, node: nodes.Call, frame: Frame, forward_caller: bool = False + ) -> None: + if self.environment.is_async: + self.write("(await auto_await(") + if self.environment.sandboxed: + self.write("environment.call(context, ") + else: + self.write("context.call(") + self.visit(node.node, frame) + extra_kwargs = {"caller": "caller"} if forward_caller else None + loop_kwargs = {"_loop_vars": "_loop_vars"} if frame.loop_frame else {} + block_kwargs = {"_block_vars": "_block_vars"} if frame.block_frame else {} + if extra_kwargs: + extra_kwargs.update(loop_kwargs, **block_kwargs) + elif loop_kwargs or block_kwargs: + extra_kwargs = dict(loop_kwargs, **block_kwargs) + self.signature(node, frame, extra_kwargs) + self.write(")") + if self.environment.is_async: + self.write("))") + + def visit_Keyword(self, node: nodes.Keyword, frame: Frame) -> None: + self.write(node.key + "=") + self.visit(node.value, frame) + + # -- Unused nodes for extensions + + def visit_MarkSafe(self, node: nodes.MarkSafe, frame: Frame) -> None: + self.write("Markup(") + self.visit(node.expr, frame) + self.write(")") + + def visit_MarkSafeIfAutoescape( + self, node: nodes.MarkSafeIfAutoescape, frame: Frame + ) -> None: + self.write("(Markup if context.eval_ctx.autoescape else identity)(") + self.visit(node.expr, frame) + self.write(")") + + def visit_EnvironmentAttribute( + self, node: nodes.EnvironmentAttribute, frame: Frame + ) -> None: + self.write("environment." + node.name) + + def visit_ExtensionAttribute( + self, node: nodes.ExtensionAttribute, frame: Frame + ) -> None: + self.write(f"environment.extensions[{node.identifier!r}].{node.name}") + + def visit_ImportedName(self, node: nodes.ImportedName, frame: Frame) -> None: + self.write(self.import_aliases[node.importname]) + + def visit_InternalName(self, node: nodes.InternalName, frame: Frame) -> None: + self.write(node.name) + + def visit_ContextReference( + self, node: nodes.ContextReference, frame: Frame + ) -> None: + self.write("context") + + def visit_DerivedContextReference( + self, node: nodes.DerivedContextReference, frame: Frame + ) -> None: + self.write(self.derive_context(frame)) + + def visit_Continue(self, node: nodes.Continue, frame: Frame) -> None: + self.writeline("continue", node) + + def visit_Break(self, node: nodes.Break, frame: Frame) -> None: + self.writeline("break", node) + + def visit_Scope(self, node: nodes.Scope, frame: Frame) -> None: + scope_frame = frame.inner() + scope_frame.symbols.analyze_node(node) + self.enter_frame(scope_frame) + self.blockvisit(node.body, scope_frame) + self.leave_frame(scope_frame) + + def visit_OverlayScope(self, node: nodes.OverlayScope, frame: Frame) -> None: + ctx = self.temporary_identifier() + self.writeline(f"{ctx} = {self.derive_context(frame)}") + self.writeline(f"{ctx}.vars = ") + self.visit(node.context, frame) + self.push_context_reference(ctx) + + scope_frame = frame.inner(isolated=True) + scope_frame.symbols.analyze_node(node) + self.enter_frame(scope_frame) + self.blockvisit(node.body, scope_frame) + self.leave_frame(scope_frame) + self.pop_context_reference() + + def visit_EvalContextModifier( + self, node: nodes.EvalContextModifier, frame: Frame + ) -> None: + for keyword in node.options: + self.writeline(f"context.eval_ctx.{keyword.key} = ") + self.visit(keyword.value, frame) + try: + val = keyword.value.as_const(frame.eval_ctx) + except nodes.Impossible: + frame.eval_ctx.volatile = True + else: + setattr(frame.eval_ctx, keyword.key, val) + + def visit_ScopedEvalContextModifier( + self, node: nodes.ScopedEvalContextModifier, frame: Frame + ) -> None: + old_ctx_name = self.temporary_identifier() + saved_ctx = frame.eval_ctx.save() + self.writeline(f"{old_ctx_name} = context.eval_ctx.save()") + self.visit_EvalContextModifier(node, frame) + for child in node.body: + self.visit(child, frame) + frame.eval_ctx.revert(saved_ctx) + self.writeline(f"context.eval_ctx.revert({old_ctx_name})") diff --git a/venv/Lib/site-packages/jinja2/constants.py b/venv/Lib/site-packages/jinja2/constants.py new file mode 100644 index 0000000..41a1c23 --- /dev/null +++ b/venv/Lib/site-packages/jinja2/constants.py @@ -0,0 +1,20 @@ +#: list of lorem ipsum words used by the lipsum() helper function +LOREM_IPSUM_WORDS = """\ +a ac accumsan ad adipiscing aenean aliquam aliquet amet ante aptent arcu at +auctor augue bibendum blandit class commodo condimentum congue consectetuer +consequat conubia convallis cras cubilia cum curabitur curae cursus dapibus +diam dictum dictumst dignissim dis dolor donec dui duis egestas eget eleifend +elementum elit enim erat eros est et etiam eu euismod facilisi facilisis fames +faucibus felis fermentum feugiat fringilla fusce gravida habitant habitasse hac +hendrerit hymenaeos iaculis id imperdiet in inceptos integer interdum ipsum +justo lacinia lacus laoreet lectus leo libero ligula litora lobortis lorem +luctus maecenas magna magnis malesuada massa mattis mauris metus mi molestie +mollis montes morbi mus nam nascetur natoque nec neque netus nibh nisi nisl non +nonummy nostra nulla nullam nunc odio orci ornare parturient pede pellentesque +penatibus per pharetra phasellus placerat platea porta porttitor posuere +potenti praesent pretium primis proin pulvinar purus quam quis quisque rhoncus +ridiculus risus rutrum sagittis sapien scelerisque sed sem semper senectus sit +sociis sociosqu sodales sollicitudin suscipit suspendisse taciti tellus tempor +tempus tincidunt torquent tortor tristique turpis ullamcorper ultrices +ultricies urna ut varius vehicula vel velit venenatis vestibulum vitae vivamus +viverra volutpat vulputate""" diff --git a/venv/Lib/site-packages/jinja2/debug.py b/venv/Lib/site-packages/jinja2/debug.py new file mode 100644 index 0000000..eeeeee7 --- /dev/null +++ b/venv/Lib/site-packages/jinja2/debug.py @@ -0,0 +1,191 @@ +import sys +import typing as t +from types import CodeType +from types import TracebackType + +from .exceptions import TemplateSyntaxError +from .utils import internal_code +from .utils import missing + +if t.TYPE_CHECKING: + from .runtime import Context + + +def rewrite_traceback_stack(source: t.Optional[str] = None) -> BaseException: + """Rewrite the current exception to replace any tracebacks from + within compiled template code with tracebacks that look like they + came from the template source. + + This must be called within an ``except`` block. + + :param source: For ``TemplateSyntaxError``, the original source if + known. + :return: The original exception with the rewritten traceback. + """ + _, exc_value, tb = sys.exc_info() + exc_value = t.cast(BaseException, exc_value) + tb = t.cast(TracebackType, tb) + + if isinstance(exc_value, TemplateSyntaxError) and not exc_value.translated: + exc_value.translated = True + exc_value.source = source + # Remove the old traceback, otherwise the frames from the + # compiler still show up. + exc_value.with_traceback(None) + # Outside of runtime, so the frame isn't executing template + # code, but it still needs to point at the template. + tb = fake_traceback( + exc_value, None, exc_value.filename or "", exc_value.lineno + ) + else: + # Skip the frame for the render function. + tb = tb.tb_next + + stack = [] + + # Build the stack of traceback object, replacing any in template + # code with the source file and line information. + while tb is not None: + # Skip frames decorated with @internalcode. These are internal + # calls that aren't useful in template debugging output. + if tb.tb_frame.f_code in internal_code: + tb = tb.tb_next + continue + + template = tb.tb_frame.f_globals.get("__jinja_template__") + + if template is not None: + lineno = template.get_corresponding_lineno(tb.tb_lineno) + fake_tb = fake_traceback(exc_value, tb, template.filename, lineno) + stack.append(fake_tb) + else: + stack.append(tb) + + tb = tb.tb_next + + tb_next = None + + # Assign tb_next in reverse to avoid circular references. + for tb in reversed(stack): + tb.tb_next = tb_next + tb_next = tb + + return exc_value.with_traceback(tb_next) + + +def fake_traceback( # type: ignore + exc_value: BaseException, tb: t.Optional[TracebackType], filename: str, lineno: int +) -> TracebackType: + """Produce a new traceback object that looks like it came from the + template source instead of the compiled code. The filename, line + number, and location name will point to the template, and the local + variables will be the current template context. + + :param exc_value: The original exception to be re-raised to create + the new traceback. + :param tb: The original traceback to get the local variables and + code info from. + :param filename: The template filename. + :param lineno: The line number in the template source. + """ + if tb is not None: + # Replace the real locals with the context that would be + # available at that point in the template. + locals = get_template_locals(tb.tb_frame.f_locals) + locals.pop("__jinja_exception__", None) + else: + locals = {} + + globals = { + "__name__": filename, + "__file__": filename, + "__jinja_exception__": exc_value, + } + # Raise an exception at the correct line number. + code: CodeType = compile( + "\n" * (lineno - 1) + "raise __jinja_exception__", filename, "exec" + ) + + # Build a new code object that points to the template file and + # replaces the location with a block name. + location = "template" + + if tb is not None: + function = tb.tb_frame.f_code.co_name + + if function == "root": + location = "top-level template code" + elif function.startswith("block_"): + location = f"block {function[6:]!r}" + + if sys.version_info >= (3, 8): + code = code.replace(co_name=location) + else: + code = CodeType( + code.co_argcount, + code.co_kwonlyargcount, + code.co_nlocals, + code.co_stacksize, + code.co_flags, + code.co_code, + code.co_consts, + code.co_names, + code.co_varnames, + code.co_filename, + location, + code.co_firstlineno, + code.co_lnotab, + code.co_freevars, + code.co_cellvars, + ) + + # Execute the new code, which is guaranteed to raise, and return + # the new traceback without this frame. + try: + exec(code, globals, locals) + except BaseException: + return sys.exc_info()[2].tb_next # type: ignore + + +def get_template_locals(real_locals: t.Mapping[str, t.Any]) -> t.Dict[str, t.Any]: + """Based on the runtime locals, get the context that would be + available at that point in the template. + """ + # Start with the current template context. + ctx: t.Optional[Context] = real_locals.get("context") + + if ctx is not None: + data: t.Dict[str, t.Any] = ctx.get_all().copy() + else: + data = {} + + # Might be in a derived context that only sets local variables + # rather than pushing a context. Local variables follow the scheme + # l_depth_name. Find the highest-depth local that has a value for + # each name. + local_overrides: t.Dict[str, t.Tuple[int, t.Any]] = {} + + for name, value in real_locals.items(): + if not name.startswith("l_") or value is missing: + # Not a template variable, or no longer relevant. + continue + + try: + _, depth_str, name = name.split("_", 2) + depth = int(depth_str) + except ValueError: + continue + + cur_depth = local_overrides.get(name, (-1,))[0] + + if cur_depth < depth: + local_overrides[name] = (depth, value) + + # Modify the context with any derived context. + for name, (_, value) in local_overrides.items(): + if value is missing: + data.pop(name, None) + else: + data[name] = value + + return data diff --git a/venv/Lib/site-packages/jinja2/defaults.py b/venv/Lib/site-packages/jinja2/defaults.py new file mode 100644 index 0000000..638cad3 --- /dev/null +++ b/venv/Lib/site-packages/jinja2/defaults.py @@ -0,0 +1,48 @@ +import typing as t + +from .filters import FILTERS as DEFAULT_FILTERS # noqa: F401 +from .tests import TESTS as DEFAULT_TESTS # noqa: F401 +from .utils import Cycler +from .utils import generate_lorem_ipsum +from .utils import Joiner +from .utils import Namespace + +if t.TYPE_CHECKING: + import typing_extensions as te + +# defaults for the parser / lexer +BLOCK_START_STRING = "{%" +BLOCK_END_STRING = "%}" +VARIABLE_START_STRING = "{{" +VARIABLE_END_STRING = "}}" +COMMENT_START_STRING = "{#" +COMMENT_END_STRING = "#}" +LINE_STATEMENT_PREFIX: t.Optional[str] = None +LINE_COMMENT_PREFIX: t.Optional[str] = None +TRIM_BLOCKS = False +LSTRIP_BLOCKS = False +NEWLINE_SEQUENCE: "te.Literal['\\n', '\\r\\n', '\\r']" = "\n" +KEEP_TRAILING_NEWLINE = False + +# default filters, tests and namespace + +DEFAULT_NAMESPACE = { + "range": range, + "dict": dict, + "lipsum": generate_lorem_ipsum, + "cycler": Cycler, + "joiner": Joiner, + "namespace": Namespace, +} + +# default policies +DEFAULT_POLICIES: t.Dict[str, t.Any] = { + "compiler.ascii_str": True, + "urlize.rel": "noopener", + "urlize.target": None, + "urlize.extra_schemes": None, + "truncate.leeway": 5, + "json.dumps_function": None, + "json.dumps_kwargs": {"sort_keys": True}, + "ext.i18n.trimmed": False, +} diff --git a/venv/Lib/site-packages/jinja2/environment.py b/venv/Lib/site-packages/jinja2/environment.py new file mode 100644 index 0000000..0fc6e5b --- /dev/null +++ b/venv/Lib/site-packages/jinja2/environment.py @@ -0,0 +1,1672 @@ +"""Classes for managing templates and their runtime and compile time +options. +""" + +import os +import typing +import typing as t +import weakref +from collections import ChainMap +from functools import lru_cache +from functools import partial +from functools import reduce +from types import CodeType + +from markupsafe import Markup + +from . import nodes +from .compiler import CodeGenerator +from .compiler import generate +from .defaults import BLOCK_END_STRING +from .defaults import BLOCK_START_STRING +from .defaults import COMMENT_END_STRING +from .defaults import COMMENT_START_STRING +from .defaults import DEFAULT_FILTERS # type: ignore[attr-defined] +from .defaults import DEFAULT_NAMESPACE +from .defaults import DEFAULT_POLICIES +from .defaults import DEFAULT_TESTS # type: ignore[attr-defined] +from .defaults import KEEP_TRAILING_NEWLINE +from .defaults import LINE_COMMENT_PREFIX +from .defaults import LINE_STATEMENT_PREFIX +from .defaults import LSTRIP_BLOCKS +from .defaults import NEWLINE_SEQUENCE +from .defaults import TRIM_BLOCKS +from .defaults import VARIABLE_END_STRING +from .defaults import VARIABLE_START_STRING +from .exceptions import TemplateNotFound +from .exceptions import TemplateRuntimeError +from .exceptions import TemplatesNotFound +from .exceptions import TemplateSyntaxError +from .exceptions import UndefinedError +from .lexer import get_lexer +from .lexer import Lexer +from .lexer import TokenStream +from .nodes import EvalContext +from .parser import Parser +from .runtime import Context +from .runtime import new_context +from .runtime import Undefined +from .utils import _PassArg +from .utils import concat +from .utils import consume +from .utils import import_string +from .utils import internalcode +from .utils import LRUCache +from .utils import missing + +if t.TYPE_CHECKING: + import typing_extensions as te + + from .bccache import BytecodeCache + from .ext import Extension + from .loaders import BaseLoader + +_env_bound = t.TypeVar("_env_bound", bound="Environment") + + +# for direct template usage we have up to ten living environments +@lru_cache(maxsize=10) +def get_spontaneous_environment(cls: t.Type[_env_bound], *args: t.Any) -> _env_bound: + """Return a new spontaneous environment. A spontaneous environment + is used for templates created directly rather than through an + existing environment. + + :param cls: Environment class to create. + :param args: Positional arguments passed to environment. + """ + env = cls(*args) + env.shared = True + return env + + +def create_cache( + size: int, +) -> t.Optional[t.MutableMapping[t.Tuple["weakref.ref[t.Any]", str], "Template"]]: + """Return the cache class for the given size.""" + if size == 0: + return None + + if size < 0: + return {} + + return LRUCache(size) # type: ignore + + +def copy_cache( + cache: t.Optional[t.MutableMapping[t.Any, t.Any]], +) -> t.Optional[t.MutableMapping[t.Tuple["weakref.ref[t.Any]", str], "Template"]]: + """Create an empty copy of the given cache.""" + if cache is None: + return None + + if type(cache) is dict: # noqa E721 + return {} + + return LRUCache(cache.capacity) # type: ignore + + +def load_extensions( + environment: "Environment", + extensions: t.Sequence[t.Union[str, t.Type["Extension"]]], +) -> t.Dict[str, "Extension"]: + """Load the extensions from the list and bind it to the environment. + Returns a dict of instantiated extensions. + """ + result = {} + + for extension in extensions: + if isinstance(extension, str): + extension = t.cast(t.Type["Extension"], import_string(extension)) + + result[extension.identifier] = extension(environment) + + return result + + +def _environment_config_check(environment: _env_bound) -> _env_bound: + """Perform a sanity check on the environment.""" + assert issubclass( + environment.undefined, Undefined + ), "'undefined' must be a subclass of 'jinja2.Undefined'." + assert ( + environment.block_start_string + != environment.variable_start_string + != environment.comment_start_string + ), "block, variable and comment start strings must be different." + assert environment.newline_sequence in { + "\r", + "\r\n", + "\n", + }, "'newline_sequence' must be one of '\\n', '\\r\\n', or '\\r'." + return environment + + +class Environment: + r"""The core component of Jinja is the `Environment`. It contains + important shared variables like configuration, filters, tests, + globals and others. Instances of this class may be modified if + they are not shared and if no template was loaded so far. + Modifications on environments after the first template was loaded + will lead to surprising effects and undefined behavior. + + Here are the possible initialization parameters: + + `block_start_string` + The string marking the beginning of a block. Defaults to ``'{%'``. + + `block_end_string` + The string marking the end of a block. Defaults to ``'%}'``. + + `variable_start_string` + The string marking the beginning of a print statement. + Defaults to ``'{{'``. + + `variable_end_string` + The string marking the end of a print statement. Defaults to + ``'}}'``. + + `comment_start_string` + The string marking the beginning of a comment. Defaults to ``'{#'``. + + `comment_end_string` + The string marking the end of a comment. Defaults to ``'#}'``. + + `line_statement_prefix` + If given and a string, this will be used as prefix for line based + statements. See also :ref:`line-statements`. + + `line_comment_prefix` + If given and a string, this will be used as prefix for line based + comments. See also :ref:`line-statements`. + + .. versionadded:: 2.2 + + `trim_blocks` + If this is set to ``True`` the first newline after a block is + removed (block, not variable tag!). Defaults to `False`. + + `lstrip_blocks` + If this is set to ``True`` leading spaces and tabs are stripped + from the start of a line to a block. Defaults to `False`. + + `newline_sequence` + The sequence that starts a newline. Must be one of ``'\r'``, + ``'\n'`` or ``'\r\n'``. The default is ``'\n'`` which is a + useful default for Linux and OS X systems as well as web + applications. + + `keep_trailing_newline` + Preserve the trailing newline when rendering templates. + The default is ``False``, which causes a single newline, + if present, to be stripped from the end of the template. + + .. versionadded:: 2.7 + + `extensions` + List of Jinja extensions to use. This can either be import paths + as strings or extension classes. For more information have a + look at :ref:`the extensions documentation `. + + `optimized` + should the optimizer be enabled? Default is ``True``. + + `undefined` + :class:`Undefined` or a subclass of it that is used to represent + undefined values in the template. + + `finalize` + A callable that can be used to process the result of a variable + expression before it is output. For example one can convert + ``None`` implicitly into an empty string here. + + `autoescape` + If set to ``True`` the XML/HTML autoescaping feature is enabled by + default. For more details about autoescaping see + :class:`~markupsafe.Markup`. As of Jinja 2.4 this can also + be a callable that is passed the template name and has to + return ``True`` or ``False`` depending on autoescape should be + enabled by default. + + .. versionchanged:: 2.4 + `autoescape` can now be a function + + `loader` + The template loader for this environment. + + `cache_size` + The size of the cache. Per default this is ``400`` which means + that if more than 400 templates are loaded the loader will clean + out the least recently used template. If the cache size is set to + ``0`` templates are recompiled all the time, if the cache size is + ``-1`` the cache will not be cleaned. + + .. versionchanged:: 2.8 + The cache size was increased to 400 from a low 50. + + `auto_reload` + Some loaders load templates from locations where the template + sources may change (ie: file system or database). If + ``auto_reload`` is set to ``True`` (default) every time a template is + requested the loader checks if the source changed and if yes, it + will reload the template. For higher performance it's possible to + disable that. + + `bytecode_cache` + If set to a bytecode cache object, this object will provide a + cache for the internal Jinja bytecode so that templates don't + have to be parsed if they were not changed. + + See :ref:`bytecode-cache` for more information. + + `enable_async` + If set to true this enables async template execution which + allows using async functions and generators. + """ + + #: if this environment is sandboxed. Modifying this variable won't make + #: the environment sandboxed though. For a real sandboxed environment + #: have a look at jinja2.sandbox. This flag alone controls the code + #: generation by the compiler. + sandboxed = False + + #: True if the environment is just an overlay + overlayed = False + + #: the environment this environment is linked to if it is an overlay + linked_to: t.Optional["Environment"] = None + + #: shared environments have this set to `True`. A shared environment + #: must not be modified + shared = False + + #: the class that is used for code generation. See + #: :class:`~jinja2.compiler.CodeGenerator` for more information. + code_generator_class: t.Type["CodeGenerator"] = CodeGenerator + + concat = "".join + + #: the context class that is used for templates. See + #: :class:`~jinja2.runtime.Context` for more information. + context_class: t.Type[Context] = Context + + template_class: t.Type["Template"] + + def __init__( + self, + block_start_string: str = BLOCK_START_STRING, + block_end_string: str = BLOCK_END_STRING, + variable_start_string: str = VARIABLE_START_STRING, + variable_end_string: str = VARIABLE_END_STRING, + comment_start_string: str = COMMENT_START_STRING, + comment_end_string: str = COMMENT_END_STRING, + line_statement_prefix: t.Optional[str] = LINE_STATEMENT_PREFIX, + line_comment_prefix: t.Optional[str] = LINE_COMMENT_PREFIX, + trim_blocks: bool = TRIM_BLOCKS, + lstrip_blocks: bool = LSTRIP_BLOCKS, + newline_sequence: "te.Literal['\\n', '\\r\\n', '\\r']" = NEWLINE_SEQUENCE, + keep_trailing_newline: bool = KEEP_TRAILING_NEWLINE, + extensions: t.Sequence[t.Union[str, t.Type["Extension"]]] = (), + optimized: bool = True, + undefined: t.Type[Undefined] = Undefined, + finalize: t.Optional[t.Callable[..., t.Any]] = None, + autoescape: t.Union[bool, t.Callable[[t.Optional[str]], bool]] = False, + loader: t.Optional["BaseLoader"] = None, + cache_size: int = 400, + auto_reload: bool = True, + bytecode_cache: t.Optional["BytecodeCache"] = None, + enable_async: bool = False, + ): + # !!Important notice!! + # The constructor accepts quite a few arguments that should be + # passed by keyword rather than position. However it's important to + # not change the order of arguments because it's used at least + # internally in those cases: + # - spontaneous environments (i18n extension and Template) + # - unittests + # If parameter changes are required only add parameters at the end + # and don't change the arguments (or the defaults!) of the arguments + # existing already. + + # lexer / parser information + self.block_start_string = block_start_string + self.block_end_string = block_end_string + self.variable_start_string = variable_start_string + self.variable_end_string = variable_end_string + self.comment_start_string = comment_start_string + self.comment_end_string = comment_end_string + self.line_statement_prefix = line_statement_prefix + self.line_comment_prefix = line_comment_prefix + self.trim_blocks = trim_blocks + self.lstrip_blocks = lstrip_blocks + self.newline_sequence = newline_sequence + self.keep_trailing_newline = keep_trailing_newline + + # runtime information + self.undefined: t.Type[Undefined] = undefined + self.optimized = optimized + self.finalize = finalize + self.autoescape = autoescape + + # defaults + self.filters = DEFAULT_FILTERS.copy() + self.tests = DEFAULT_TESTS.copy() + self.globals = DEFAULT_NAMESPACE.copy() + + # set the loader provided + self.loader = loader + self.cache = create_cache(cache_size) + self.bytecode_cache = bytecode_cache + self.auto_reload = auto_reload + + # configurable policies + self.policies = DEFAULT_POLICIES.copy() + + # load extensions + self.extensions = load_extensions(self, extensions) + + self.is_async = enable_async + _environment_config_check(self) + + def add_extension(self, extension: t.Union[str, t.Type["Extension"]]) -> None: + """Adds an extension after the environment was created. + + .. versionadded:: 2.5 + """ + self.extensions.update(load_extensions(self, [extension])) + + def extend(self, **attributes: t.Any) -> None: + """Add the items to the instance of the environment if they do not exist + yet. This is used by :ref:`extensions ` to register + callbacks and configuration values without breaking inheritance. + """ + for key, value in attributes.items(): + if not hasattr(self, key): + setattr(self, key, value) + + def overlay( + self, + block_start_string: str = missing, + block_end_string: str = missing, + variable_start_string: str = missing, + variable_end_string: str = missing, + comment_start_string: str = missing, + comment_end_string: str = missing, + line_statement_prefix: t.Optional[str] = missing, + line_comment_prefix: t.Optional[str] = missing, + trim_blocks: bool = missing, + lstrip_blocks: bool = missing, + newline_sequence: "te.Literal['\\n', '\\r\\n', '\\r']" = missing, + keep_trailing_newline: bool = missing, + extensions: t.Sequence[t.Union[str, t.Type["Extension"]]] = missing, + optimized: bool = missing, + undefined: t.Type[Undefined] = missing, + finalize: t.Optional[t.Callable[..., t.Any]] = missing, + autoescape: t.Union[bool, t.Callable[[t.Optional[str]], bool]] = missing, + loader: t.Optional["BaseLoader"] = missing, + cache_size: int = missing, + auto_reload: bool = missing, + bytecode_cache: t.Optional["BytecodeCache"] = missing, + enable_async: bool = missing, + ) -> "te.Self": + """Create a new overlay environment that shares all the data with the + current environment except for cache and the overridden attributes. + Extensions cannot be removed for an overlayed environment. An overlayed + environment automatically gets all the extensions of the environment it + is linked to plus optional extra extensions. + + Creating overlays should happen after the initial environment was set + up completely. Not all attributes are truly linked, some are just + copied over so modifications on the original environment may not shine + through. + + .. versionchanged:: 3.1.5 + ``enable_async`` is applied correctly. + + .. versionchanged:: 3.1.2 + Added the ``newline_sequence``, ``keep_trailing_newline``, + and ``enable_async`` parameters to match ``__init__``. + """ + args = dict(locals()) + del args["self"], args["cache_size"], args["extensions"], args["enable_async"] + + rv = object.__new__(self.__class__) + rv.__dict__.update(self.__dict__) + rv.overlayed = True + rv.linked_to = self + + for key, value in args.items(): + if value is not missing: + setattr(rv, key, value) + + if cache_size is not missing: + rv.cache = create_cache(cache_size) + else: + rv.cache = copy_cache(self.cache) + + rv.extensions = {} + for key, value in self.extensions.items(): + rv.extensions[key] = value.bind(rv) + if extensions is not missing: + rv.extensions.update(load_extensions(rv, extensions)) + + if enable_async is not missing: + rv.is_async = enable_async + + return _environment_config_check(rv) + + @property + def lexer(self) -> Lexer: + """The lexer for this environment.""" + return get_lexer(self) + + def iter_extensions(self) -> t.Iterator["Extension"]: + """Iterates over the extensions by priority.""" + return iter(sorted(self.extensions.values(), key=lambda x: x.priority)) + + def getitem( + self, obj: t.Any, argument: t.Union[str, t.Any] + ) -> t.Union[t.Any, Undefined]: + """Get an item or attribute of an object but prefer the item.""" + try: + return obj[argument] + except (AttributeError, TypeError, LookupError): + if isinstance(argument, str): + try: + attr = str(argument) + except Exception: + pass + else: + try: + return getattr(obj, attr) + except AttributeError: + pass + return self.undefined(obj=obj, name=argument) + + def getattr(self, obj: t.Any, attribute: str) -> t.Any: + """Get an item or attribute of an object but prefer the attribute. + Unlike :meth:`getitem` the attribute *must* be a string. + """ + try: + return getattr(obj, attribute) + except AttributeError: + pass + try: + return obj[attribute] + except (TypeError, LookupError, AttributeError): + return self.undefined(obj=obj, name=attribute) + + def _filter_test_common( + self, + name: t.Union[str, Undefined], + value: t.Any, + args: t.Optional[t.Sequence[t.Any]], + kwargs: t.Optional[t.Mapping[str, t.Any]], + context: t.Optional[Context], + eval_ctx: t.Optional[EvalContext], + is_filter: bool, + ) -> t.Any: + if is_filter: + env_map = self.filters + type_name = "filter" + else: + env_map = self.tests + type_name = "test" + + func = env_map.get(name) # type: ignore + + if func is None: + msg = f"No {type_name} named {name!r}." + + if isinstance(name, Undefined): + try: + name._fail_with_undefined_error() + except Exception as e: + msg = f"{msg} ({e}; did you forget to quote the callable name?)" + + raise TemplateRuntimeError(msg) + + args = [value, *(args if args is not None else ())] + kwargs = kwargs if kwargs is not None else {} + pass_arg = _PassArg.from_obj(func) + + if pass_arg is _PassArg.context: + if context is None: + raise TemplateRuntimeError( + f"Attempted to invoke a context {type_name} without context." + ) + + args.insert(0, context) + elif pass_arg is _PassArg.eval_context: + if eval_ctx is None: + if context is not None: + eval_ctx = context.eval_ctx + else: + eval_ctx = EvalContext(self) + + args.insert(0, eval_ctx) + elif pass_arg is _PassArg.environment: + args.insert(0, self) + + return func(*args, **kwargs) + + def call_filter( + self, + name: str, + value: t.Any, + args: t.Optional[t.Sequence[t.Any]] = None, + kwargs: t.Optional[t.Mapping[str, t.Any]] = None, + context: t.Optional[Context] = None, + eval_ctx: t.Optional[EvalContext] = None, + ) -> t.Any: + """Invoke a filter on a value the same way the compiler does. + + This might return a coroutine if the filter is running from an + environment in async mode and the filter supports async + execution. It's your responsibility to await this if needed. + + .. versionadded:: 2.7 + """ + return self._filter_test_common( + name, value, args, kwargs, context, eval_ctx, True + ) + + def call_test( + self, + name: str, + value: t.Any, + args: t.Optional[t.Sequence[t.Any]] = None, + kwargs: t.Optional[t.Mapping[str, t.Any]] = None, + context: t.Optional[Context] = None, + eval_ctx: t.Optional[EvalContext] = None, + ) -> t.Any: + """Invoke a test on a value the same way the compiler does. + + This might return a coroutine if the test is running from an + environment in async mode and the test supports async execution. + It's your responsibility to await this if needed. + + .. versionchanged:: 3.0 + Tests support ``@pass_context``, etc. decorators. Added + the ``context`` and ``eval_ctx`` parameters. + + .. versionadded:: 2.7 + """ + return self._filter_test_common( + name, value, args, kwargs, context, eval_ctx, False + ) + + @internalcode + def parse( + self, + source: str, + name: t.Optional[str] = None, + filename: t.Optional[str] = None, + ) -> nodes.Template: + """Parse the sourcecode and return the abstract syntax tree. This + tree of nodes is used by the compiler to convert the template into + executable source- or bytecode. This is useful for debugging or to + extract information from templates. + + If you are :ref:`developing Jinja extensions ` + this gives you a good overview of the node tree generated. + """ + try: + return self._parse(source, name, filename) + except TemplateSyntaxError: + self.handle_exception(source=source) + + def _parse( + self, source: str, name: t.Optional[str], filename: t.Optional[str] + ) -> nodes.Template: + """Internal parsing function used by `parse` and `compile`.""" + return Parser(self, source, name, filename).parse() + + def lex( + self, + source: str, + name: t.Optional[str] = None, + filename: t.Optional[str] = None, + ) -> t.Iterator[t.Tuple[int, str, str]]: + """Lex the given sourcecode and return a generator that yields + tokens as tuples in the form ``(lineno, token_type, value)``. + This can be useful for :ref:`extension development ` + and debugging templates. + + This does not perform preprocessing. If you want the preprocessing + of the extensions to be applied you have to filter source through + the :meth:`preprocess` method. + """ + source = str(source) + try: + return self.lexer.tokeniter(source, name, filename) + except TemplateSyntaxError: + self.handle_exception(source=source) + + def preprocess( + self, + source: str, + name: t.Optional[str] = None, + filename: t.Optional[str] = None, + ) -> str: + """Preprocesses the source with all extensions. This is automatically + called for all parsing and compiling methods but *not* for :meth:`lex` + because there you usually only want the actual source tokenized. + """ + return reduce( + lambda s, e: e.preprocess(s, name, filename), + self.iter_extensions(), + str(source), + ) + + def _tokenize( + self, + source: str, + name: t.Optional[str], + filename: t.Optional[str] = None, + state: t.Optional[str] = None, + ) -> TokenStream: + """Called by the parser to do the preprocessing and filtering + for all the extensions. Returns a :class:`~jinja2.lexer.TokenStream`. + """ + source = self.preprocess(source, name, filename) + stream = self.lexer.tokenize(source, name, filename, state) + + for ext in self.iter_extensions(): + stream = ext.filter_stream(stream) # type: ignore + + if not isinstance(stream, TokenStream): + stream = TokenStream(stream, name, filename) + + return stream + + def _generate( + self, + source: nodes.Template, + name: t.Optional[str], + filename: t.Optional[str], + defer_init: bool = False, + ) -> str: + """Internal hook that can be overridden to hook a different generate + method in. + + .. versionadded:: 2.5 + """ + return generate( # type: ignore + source, + self, + name, + filename, + defer_init=defer_init, + optimized=self.optimized, + ) + + def _compile(self, source: str, filename: str) -> CodeType: + """Internal hook that can be overridden to hook a different compile + method in. + + .. versionadded:: 2.5 + """ + return compile(source, filename, "exec") + + @typing.overload + def compile( + self, + source: t.Union[str, nodes.Template], + name: t.Optional[str] = None, + filename: t.Optional[str] = None, + raw: "te.Literal[False]" = False, + defer_init: bool = False, + ) -> CodeType: ... + + @typing.overload + def compile( + self, + source: t.Union[str, nodes.Template], + name: t.Optional[str] = None, + filename: t.Optional[str] = None, + raw: "te.Literal[True]" = ..., + defer_init: bool = False, + ) -> str: ... + + @internalcode + def compile( + self, + source: t.Union[str, nodes.Template], + name: t.Optional[str] = None, + filename: t.Optional[str] = None, + raw: bool = False, + defer_init: bool = False, + ) -> t.Union[str, CodeType]: + """Compile a node or template source code. The `name` parameter is + the load name of the template after it was joined using + :meth:`join_path` if necessary, not the filename on the file system. + the `filename` parameter is the estimated filename of the template on + the file system. If the template came from a database or memory this + can be omitted. + + The return value of this method is a python code object. If the `raw` + parameter is `True` the return value will be a string with python + code equivalent to the bytecode returned otherwise. This method is + mainly used internally. + + `defer_init` is use internally to aid the module code generator. This + causes the generated code to be able to import without the global + environment variable to be set. + + .. versionadded:: 2.4 + `defer_init` parameter added. + """ + source_hint = None + try: + if isinstance(source, str): + source_hint = source + source = self._parse(source, name, filename) + source = self._generate(source, name, filename, defer_init=defer_init) + if raw: + return source + if filename is None: + filename = " + +

+
+

Summary

+
+ {%- for p in additional_summary['prefix'] %} + {{ p|safe }} + {%- endfor %} +
+

{{ run_count }}

+

(Un)check the boxes to filter the results.

+
+
+
There are still tests running.
Reload this page to get the latest results!
+
+
+
+
+
+ {%- for result, values in outcomes.items() %} + + {{ values["value"] }} {{ values["label"] }}{{ "," if result != "rerun" }} + {%- endfor %} +
+
+  /  +
+
+
+
+ {%- for s in additional_summary['summary'] %} + {{ s|safe }} + {%- endfor %} +
+
+ {%- for p in additional_summary['postfix'] %} + {{ p|safe }} + {%- endfor %} +
+
+ + + + {%- for th in table_head %} + {{ th|safe }} + {%- endfor %} + + +
+ +
+
+ +
+ diff --git a/venv/Lib/site-packages/pytest_html/resources/style.css b/venv/Lib/site-packages/pytest_html/resources/style.css new file mode 100644 index 0000000..561524c --- /dev/null +++ b/venv/Lib/site-packages/pytest_html/resources/style.css @@ -0,0 +1,319 @@ +body { + font-family: Helvetica, Arial, sans-serif; + font-size: 12px; + /* do not increase min-width as some may use split screens */ + min-width: 800px; + color: #999; +} + +h1 { + font-size: 24px; + color: black; +} + +h2 { + font-size: 16px; + color: black; +} + +p { + color: black; +} + +a { + color: #999; +} + +table { + border-collapse: collapse; +} + +/****************************** + * SUMMARY INFORMATION + ******************************/ +#environment td { + padding: 5px; + border: 1px solid #e6e6e6; + vertical-align: top; +} +#environment tr:nth-child(odd) { + background-color: #f6f6f6; +} +#environment ul { + margin: 0; + padding: 0 20px; +} + +/****************************** + * TEST RESULT COLORS + ******************************/ +span.passed, +.passed .col-result { + color: green; +} + +span.skipped, +span.xfailed, +span.rerun, +.skipped .col-result, +.xfailed .col-result, +.rerun .col-result { + color: orange; +} + +span.error, +span.failed, +span.xpassed, +.error .col-result, +.failed .col-result, +.xpassed .col-result { + color: red; +} + +.col-links__extra { + margin-right: 3px; +} + +/****************************** + * RESULTS TABLE + * + * 1. Table Layout + * 2. Extra + * 3. Sorting items + * + ******************************/ +/*------------------ + * 1. Table Layout + *------------------*/ +#results-table { + border: 1px solid #e6e6e6; + color: #999; + font-size: 12px; + width: 100%; +} +#results-table th, +#results-table td { + padding: 5px; + border: 1px solid #e6e6e6; + text-align: left; +} +#results-table th { + font-weight: bold; +} + +/*------------------ + * 2. Extra + *------------------*/ +.logwrapper { + max-height: 230px; + overflow-y: scroll; + background-color: #e6e6e6; +} +.logwrapper.expanded { + max-height: none; +} +.logwrapper.expanded .logexpander:after { + content: "collapse [-]"; +} +.logwrapper .logexpander { + z-index: 1; + position: sticky; + top: 10px; + width: max-content; + border: 1px solid; + border-radius: 3px; + padding: 5px 7px; + margin: 10px 0 10px calc(100% - 80px); + cursor: pointer; + background-color: #e6e6e6; +} +.logwrapper .logexpander:after { + content: "expand [+]"; +} +.logwrapper .logexpander:hover { + color: #000; + border-color: #000; +} +.logwrapper .log { + min-height: 40px; + position: relative; + top: -50px; + height: calc(100% + 50px); + border: 1px solid #e6e6e6; + color: black; + display: block; + font-family: "Courier New", Courier, monospace; + padding: 5px; + padding-right: 80px; + white-space: pre-wrap; +} + +div.media { + border: 1px solid #e6e6e6; + float: right; + height: 240px; + margin: 0 5px; + overflow: hidden; + width: 320px; +} + +.media-container { + display: grid; + grid-template-columns: 25px auto 25px; + align-items: center; + flex: 1 1; + overflow: hidden; + height: 200px; +} + +.media-container--fullscreen { + grid-template-columns: 0px auto 0px; +} + +.media-container__nav--right, +.media-container__nav--left { + text-align: center; + cursor: pointer; +} + +.media-container__viewport { + cursor: pointer; + text-align: center; + height: inherit; +} +.media-container__viewport img, +.media-container__viewport video { + object-fit: cover; + width: 100%; + max-height: 100%; +} + +.media__name, +.media__counter { + display: flex; + flex-direction: row; + justify-content: space-around; + flex: 0 0 25px; + align-items: center; +} + +.collapsible td:not(.col-links) { + cursor: pointer; +} +.collapsible td:not(.col-links):hover::after { + color: #bbb; + font-style: italic; + cursor: pointer; +} + +.col-result { + width: 130px; +} +.col-result:hover::after { + content: " (hide details)"; +} + +.col-result.collapsed:hover::after { + content: " (show details)"; +} + +#environment-header h2:hover::after { + content: " (hide details)"; + color: #bbb; + font-style: italic; + cursor: pointer; + font-size: 12px; +} + +#environment-header.collapsed h2:hover::after { + content: " (show details)"; + color: #bbb; + font-style: italic; + cursor: pointer; + font-size: 12px; +} + +/*------------------ + * 3. Sorting items + *------------------*/ +.sortable { + cursor: pointer; +} +.sortable.desc:after { + content: " "; + position: relative; + left: 5px; + bottom: -12.5px; + border: 10px solid #4caf50; + border-bottom: 0; + border-left-color: transparent; + border-right-color: transparent; +} +.sortable.asc:after { + content: " "; + position: relative; + left: 5px; + bottom: 12.5px; + border: 10px solid #4caf50; + border-top: 0; + border-left-color: transparent; + border-right-color: transparent; +} + +.hidden, .summary__reload__button.hidden { + display: none; +} + +.summary__data { + flex: 0 0 550px; +} +.summary__reload { + flex: 1 1; + display: flex; + justify-content: center; +} +.summary__reload__button { + flex: 0 0 300px; + display: flex; + color: white; + font-weight: bold; + background-color: #4caf50; + text-align: center; + justify-content: center; + align-items: center; + border-radius: 3px; + cursor: pointer; +} +.summary__reload__button:hover { + background-color: #46a049; +} +.summary__spacer { + flex: 0 0 550px; +} + +.controls { + display: flex; + justify-content: space-between; +} + +.filters, +.collapse { + display: flex; + align-items: center; +} +.filters button, +.collapse button { + color: #999; + border: none; + background: none; + cursor: pointer; + text-decoration: underline; +} +.filters button:hover, +.collapse button:hover { + color: #ccc; +} + +.filter__label { + margin-right: 10px; +} diff --git a/venv/Lib/site-packages/pytest_html/selfcontained_report.py b/venv/Lib/site-packages/pytest_html/selfcontained_report.py new file mode 100644 index 0000000..ecc4460 --- /dev/null +++ b/venv/Lib/site-packages/pytest_html/selfcontained_report.py @@ -0,0 +1,39 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +import base64 +import binascii +import warnings + +from pytest_html.basereport import BaseReport + + +class SelfContainedReport(BaseReport): + def __init__(self, report_path, config, report_data, template, css): + super().__init__(report_path, config, report_data, template, css) + + @property + def css(self): + return self._css + + def _data_content(self, content, mime_type, *args, **kwargs): + charset = "utf-8" + data = base64.b64encode(content.encode(charset)).decode(charset) + return f"data:{mime_type};charset={charset};base64,{data}" + + def _media_content(self, content, mime_type, *args, **kwargs): + try: + # test if content is base64 + base64.b64decode(content.encode("utf-8"), validate=True) + return f"data:{mime_type};base64,{content}" + except binascii.Error: + # if not base64, issue warning and just return as it's a file or link + warnings.warn( + "Self-contained HTML report " + "includes link to external " + f"resource: {content}" + ) + return content + + def _generate_report(self, *args, **kwargs): + super()._generate_report(self_contained=True) diff --git a/venv/Lib/site-packages/pytest_html/util.py b/venv/Lib/site-packages/pytest_html/util.py new file mode 100644 index 0000000..a654791 --- /dev/null +++ b/venv/Lib/site-packages/pytest_html/util.py @@ -0,0 +1,56 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +from functools import partial + +from jinja2 import Environment +from jinja2 import FileSystemLoader +from jinja2 import select_autoescape + +try: + from ansi2html import Ansi2HTMLConverter, style + + converter = Ansi2HTMLConverter(inline=False, escaped=False) + _handle_ansi = partial(converter.convert, full=False) + _ansi_styles = style.get_styles() +except ImportError: + from _pytest.logging import _remove_ansi_escape_sequences + + _handle_ansi = _remove_ansi_escape_sequences + _ansi_styles = [] + + +def _read_template(search_paths, template_name="index.jinja2"): + env = Environment( + loader=FileSystemLoader(search_paths), + autoescape=select_autoescape( + enabled_extensions=("jinja2",), + ), + ) + return env.get_template(template_name) + + +def _process_css(default_css, extra_css): + with open(default_css, encoding="utf-8") as f: + css = f.read() + + # Add user-provided CSS + for path in extra_css: + css += "\n/******************************" + css += "\n * CUSTOM CSS" + css += f"\n * {path}" + css += "\n ******************************/\n\n" + with open(path, encoding="utf-8") as f: + css += f.read() + + # ANSI support + if _ansi_styles: + ansi_css = [ + "\n/******************************", + " * ANSI2HTML STYLES", + " ******************************/\n", + ] + ansi_css.extend([str(r) for r in _ansi_styles]) + css += "\n".join(ansi_css) + + return css diff --git a/venv/Lib/site-packages/pytest_metadata-3.1.1.dist-info/INSTALLER b/venv/Lib/site-packages/pytest_metadata-3.1.1.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/venv/Lib/site-packages/pytest_metadata-3.1.1.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/Lib/site-packages/pytest_metadata-3.1.1.dist-info/METADATA b/venv/Lib/site-packages/pytest_metadata-3.1.1.dist-info/METADATA new file mode 100644 index 0000000..aff6b08 --- /dev/null +++ b/venv/Lib/site-packages/pytest_metadata-3.1.1.dist-info/METADATA @@ -0,0 +1,235 @@ +Metadata-Version: 2.1 +Name: pytest-metadata +Version: 3.1.1 +Summary: pytest plugin for test session metadata +Project-URL: Homepage, https://github.com/pytest-dev/pytest-metadata +Project-URL: Tracker, https://github.com/pytest-dev/pytest-metadata/issues +Project-URL: Source, https://github.com/pytest-dev/pytest-metadata +Author-email: Dave Hunt , Jim Brannlund +License-Expression: MPL-2.0 +License-File: AUTHORS +License-File: LICENSE +Keywords: metadata,pytest +Classifier: Development Status :: 5 - Production/Stable +Classifier: Framework :: Pytest +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0) +Classifier: Natural Language :: English +Classifier: Operating System :: MacOS :: MacOS X +Classifier: Operating System :: Microsoft :: Windows +Classifier: Operating System :: POSIX +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 :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Topic :: Software Development :: Quality Assurance +Classifier: Topic :: Software Development :: Testing +Classifier: Topic :: Utilities +Requires-Python: >=3.8 +Requires-Dist: pytest>=7.0.0 +Provides-Extra: test +Requires-Dist: black>=22.1.0; extra == 'test' +Requires-Dist: flake8>=4.0.1; extra == 'test' +Requires-Dist: pre-commit>=2.17.0; extra == 'test' +Requires-Dist: tox>=3.24.5; extra == 'test' +Description-Content-Type: text/x-rst + +pytest-metadata +=============== + +pytest-metadata is a plugin for `pytest `_ that provides +access to test session metadata. + +.. image:: https://img.shields.io/badge/license-MPL%202.0-blue.svg + :target: https://github.com/pytest-dev/pytest-metadata/blob/master/LICENSE + :alt: License +.. image:: https://img.shields.io/pypi/v/pytest-metadata.svg + :target: https://pypi.python.org/pypi/pytest-metadata/ + :alt: PyPI +.. image:: https://img.shields.io/travis/pytest-dev/pytest-metadata.svg + :target: https://travis-ci.org/pytest-dev/pytest-metadata/ +.. image:: https://img.shields.io/badge/code%20style-black-000000.svg + :target: https://github.com/ambv/black + :alt: Travis +.. image:: https://img.shields.io/github/issues-raw/pytest-dev/pytest-metadata.svg + :target: https://github.com/pytest-dev/pytest-metadata/issues + :alt: Issues +.. image:: https://img.shields.io/requires/github/pytest-dev/pytest-metadata.svg + :target: https://requires.io/github/pytest-dev/pytest-metadata/requirements/?branch=master + :alt: Requirements + +Requirements +------------ + +You will need the following in order to use pytest-metadata: + +- Python 3.8+ or PyPy3 + +Installation +------------ + +To install pytest-metadata: + +.. code-block:: bash + + $ pip install pytest-metadata + +Contributing +------------ + +We welcome contributions. + +To learn more, see `Development `_ + +Available metadata +------------------ + +The following metadata is gathered by this plugin: + +======== =============== =================================== +Key Description Example +======== =============== =================================== +Python Python version 3.6.4 +Platform Platform Darwin-17.4.0-x86_64-i386-64bit +Packages pytest packages {'py': '1.5.2', 'pytest': '3.4.1'} +Plugins pytest plugins {'metadata': '1.6.0'} +======== =============== =================================== + +Additional metadata +------------------- + +You can provide your own metadata (key, value pair) by specifying ``--metadata`` on the commandline:: + + pytest --metadata foo bar + +Note: You can provide multiple sets of ``--metadata``:: + + pytest --metadata foo bar --metadata baz zoo + +There's also the possibility of passing in metadata as a JSON string:: + + pytest --metadata-from-json '{"cat_says": "bring the cat nip", "human_says": "yes kitty"}' + +Alternatively a JSON can be read from a given file:: + + pytest --metadata-from-json-file path/to/valid/file.json + +Continuous integration +---------------------- + +When run in a continuous integration environment, additional metadata is added +from environment variables. Below is a list of the supported continuous +integration providers, along with links to the environment variables that are +added to metadata if they're present. + +* `AppVeyor `_ +* `Bitbucket `_ +* `CircleCI `_ +* `GitLab CI `_ +* `Jenkins `_ +* `TaskCluster `_ +* `Travis CI `_ + +Note that if you're using `Tox `_ to run your tests +then you will need to `pass down any additional environment variables `_ +for these to be picked up. + +Viewing metadata +---------------- + +If you pass ``--verbose`` on the command line when running your tests, then the +metadata will be displayed in the terminal report header:: + + pytest --verbose + ============================ test session starts ============================ + platform darwin -- Python 3.6.4, pytest-3.4.1, py-1.5.2, pluggy-0.6.0 -- /usr/bin/python + cachedir: .pytest_cache + metadata: {'Python': '3.6.4', 'Platform': 'Darwin-17.4.0-x86_64-i386-64bit', 'Packages': {'pytest': '3.4.1', 'py': '1.5.2', 'pluggy': '0.6.0'}, 'Plugins': {'metadata': '1.6.0'}} + plugins: metadata-1.6.0 + +Including metadata in Junit XML +------------------------------- + +Pytest-metadata provides the session scoped fixture :code:`include_metadata_in_junit_xml` that you may use to include any metadata in Junit XML as ``property`` tags. +For example the following test module + +.. code-block:: python + + import pytest + + pytestmark = pytest.mark.usefixtures('include_metadata_in_junit_xml') + + def test(): + pass + +when called with + +.. code-block:: bash + + pytest --metadata Daffy Duck --junit-xml=results.xml + +would produce the following XML + +.. code-block:: xml + + + + + + + ... + +Accessing metadata +------------------ + +To add/modify/delete metadata at the end of metadata collection, you can use the ``pytest_metadata`` hook: + +.. code-block:: python + + import pytest + @pytest.hookimpl(optionalhook=True) + def pytest_metadata(metadata): + metadata.pop("password", None) + +To access the metadata from a test or fixture, you can use the ``metadata`` +fixture: + +.. code-block:: python + + def test_metadata(metadata): + assert 'metadata' in metadata['Plugins'] + +To access the metadata from a plugin, you can use the ``stash`` attribute of +the ``config`` object. This can be used to read/add/modify the metadata: + +.. code-block:: python + + def pytest_configure(config): + metadata = config.pluginmanager.getplugin("metadata") + if metadata: + from pytest_metadata.plugin import metadata_key + config.stash[metadata_key]['foo'] = 'bar' + +Plugin integrations +------------------- + +Here's a handy list of plugins that either read or contribute to the metadata: + +* `pytest-base-url `_ - Adds the + base URL to the metadata. +* `pytest-html `_ - Displays the + metadata at the start of each report. +* `pytest-reporter-html1 `_ - + Presents metadata as part of the report. +* `pytest-selenium `_ - Adds the + driver, capabilities, and remote server to the metadata. + +Resources +--------- + +- `Release Notes `_ +- `Issue Tracker `_ +- `Code `_ diff --git a/venv/Lib/site-packages/pytest_metadata-3.1.1.dist-info/RECORD b/venv/Lib/site-packages/pytest_metadata-3.1.1.dist-info/RECORD new file mode 100644 index 0000000..60b8248 --- /dev/null +++ b/venv/Lib/site-packages/pytest_metadata-3.1.1.dist-info/RECORD @@ -0,0 +1,33 @@ +pytest_metadata-3.1.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +pytest_metadata-3.1.1.dist-info/METADATA,sha256=JDmTYIELmmIDOheEfY8lDssAVMT_9CN_037GYNOfk78,8601 +pytest_metadata-3.1.1.dist-info/RECORD,, +pytest_metadata-3.1.1.dist-info/WHEEL,sha256=TJPnKdtrSue7xZ_AVGkp9YXcvDrobsjBds1du3Nx6dc,87 +pytest_metadata-3.1.1.dist-info/entry_points.txt,sha256=uQcs0QOkcVxpcg55-ycejBXm2ZIs5ROztHT46j9Wf_0,45 +pytest_metadata-3.1.1.dist-info/licenses/AUTHORS,sha256=LZSytlykQYLANO-tfOp7B711eaiFI19HuBpUt5VxnM0,120 +pytest_metadata-3.1.1.dist-info/licenses/LICENSE,sha256=K_3KYK34AxCNTH8AkAC-p2rQDmIeFjGXiBsOqukbUw4,193 +pytest_metadata/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pytest_metadata/__pycache__/__init__.cpython-312.pyc,, +pytest_metadata/__pycache__/__version.cpython-312.pyc,, +pytest_metadata/__pycache__/hooks.cpython-312.pyc,, +pytest_metadata/__pycache__/plugin.cpython-312.pyc,, +pytest_metadata/__version.py,sha256=i2QElBRUyBgs7VeUYF5A-2YFaTnKaUMYR2mutc9aMPY,411 +pytest_metadata/ci/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pytest_metadata/ci/__pycache__/__init__.cpython-312.pyc,, +pytest_metadata/ci/__pycache__/appveyor.cpython-312.pyc,, +pytest_metadata/ci/__pycache__/bitbucket.cpython-312.pyc,, +pytest_metadata/ci/__pycache__/circleci.cpython-312.pyc,, +pytest_metadata/ci/__pycache__/codebuild.cpython-312.pyc,, +pytest_metadata/ci/__pycache__/gitlab_ci.cpython-312.pyc,, +pytest_metadata/ci/__pycache__/jenkins.cpython-312.pyc,, +pytest_metadata/ci/__pycache__/taskcluster.cpython-312.pyc,, +pytest_metadata/ci/__pycache__/travis_ci.cpython-312.pyc,, +pytest_metadata/ci/appveyor.py,sha256=ZLJGVXEY9sDyO0Y9S_9W0hpVwg5UfImIVCNLTciNXkw,1108 +pytest_metadata/ci/bitbucket.py,sha256=CJO9lj4NLCga4SKpUxF2CCUaiFPlNbUpmS7gkArHczM,573 +pytest_metadata/ci/circleci.py,sha256=INYb_AVsXZmoGmJfj_8v-K0iKFVzB8gNCK0ci6-7Uzo,757 +pytest_metadata/ci/codebuild.py,sha256=0BockMGcYWhLnWkJPl0Npbm1mOjE5Tf2dWJf14krsh8,503 +pytest_metadata/ci/gitlab_ci.py,sha256=tM0iMdmK5rjeXqkG8vCSthuXxVew9DEM0Y-xSF9cm7M,1060 +pytest_metadata/ci/jenkins.py,sha256=vD9YArriAIqVriqNB2fEoXMztuEu-CccmdNYPf1BhcM,496 +pytest_metadata/ci/taskcluster.py,sha256=-XZ2wmi2Mx7jyjBu5PUtF8ttkYLoFrvAg131kFLFO-I,246 +pytest_metadata/ci/travis_ci.py,sha256=1ffMVCwiwpVDYwpBq_rvW4beAwIK-3gGDDTRTAZX1CM,654 +pytest_metadata/hooks.py,sha256=bMRZPVQQx4ziPdxW8e-NG8fwKJw61SKjRUIfOmheweQ,283 +pytest_metadata/plugin.py,sha256=OoB3ZOLy3x7UDndjPQDnD7ZbY9H88opvVZ6BsRjF9lI,3901 diff --git a/venv/Lib/site-packages/pytest_metadata-3.1.1.dist-info/WHEEL b/venv/Lib/site-packages/pytest_metadata-3.1.1.dist-info/WHEEL new file mode 100644 index 0000000..5998f3a --- /dev/null +++ b/venv/Lib/site-packages/pytest_metadata-3.1.1.dist-info/WHEEL @@ -0,0 +1,4 @@ +Wheel-Version: 1.0 +Generator: hatchling 1.21.1 +Root-Is-Purelib: true +Tag: py3-none-any diff --git a/venv/Lib/site-packages/pytest_metadata-3.1.1.dist-info/entry_points.txt b/venv/Lib/site-packages/pytest_metadata-3.1.1.dist-info/entry_points.txt new file mode 100644 index 0000000..e9fed6e --- /dev/null +++ b/venv/Lib/site-packages/pytest_metadata-3.1.1.dist-info/entry_points.txt @@ -0,0 +1,2 @@ +[pytest11] +metadata = pytest_metadata.plugin diff --git a/venv/Lib/site-packages/pytest_metadata-3.1.1.dist-info/licenses/AUTHORS b/venv/Lib/site-packages/pytest_metadata-3.1.1.dist-info/licenses/AUTHORS new file mode 100644 index 0000000..2c88d99 --- /dev/null +++ b/venv/Lib/site-packages/pytest_metadata-3.1.1.dist-info/licenses/AUTHORS @@ -0,0 +1,8 @@ +Agustín Herranz +Arminius (numirias) +Dave Hunt +Imran Mumtaz +Jim Brännlund +Sam Clements +Tomas Krizek +Zac Hatfield-Dodds diff --git a/venv/Lib/site-packages/pytest_metadata-3.1.1.dist-info/licenses/LICENSE b/venv/Lib/site-packages/pytest_metadata-3.1.1.dist-info/licenses/LICENSE new file mode 100644 index 0000000..41dc232 --- /dev/null +++ b/venv/Lib/site-packages/pytest_metadata-3.1.1.dist-info/licenses/LICENSE @@ -0,0 +1,3 @@ +This Source Code Form is subject to the terms of the Mozilla Public +License, v. 2.0. If a copy of the MPL was not distributed with this +file, You can obtain one at http://mozilla.org/MPL/2.0/. diff --git a/venv/Lib/site-packages/pytest_metadata/__init__.py b/venv/Lib/site-packages/pytest_metadata/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/venv/Lib/site-packages/pytest_metadata/__pycache__/__init__.cpython-312-pytest-9.0.1.pyc b/venv/Lib/site-packages/pytest_metadata/__pycache__/__init__.cpython-312-pytest-9.0.1.pyc new file mode 100644 index 0000000000000000000000000000000000000000..33b688071b0ee6840bb74b42ab6effb65db08021 GIT binary patch literal 187 zcmX@j%ge<81l#``WrFC(AOanHW&w&!XQ*V*Wb|9fP{ah}eFmxd73E?TV_;wq5E+x3 zSdyxnn4FlBnp+uDP^nvzSe&h!UYePbnv~l4 zSdv;?5}%t|l9-ZMk{A;opP83g5+AQuQ2C3)CO1E&G$+-rh!toRBM=vZ7$2D#85xV1 Gfh+(V+cMVx literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/pytest_metadata/__pycache__/__init__.cpython-312.pyc b/venv/Lib/site-packages/pytest_metadata/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0386a8b34b8dc801e411859ae672b9e8b6f4ab99 GIT binary patch literal 187 zcmX@j%ge<81l#``WrFC(AOanHW&w&!XQ*V*Wb|9fP{ah}eFmxd73E?TV_;wq5E+x3 zSdyxnn4FlBnp+uDP^nvzSe&h!UYePbnv~l4 zSdv;?5}%t|l9-ZMlBgdapP83g5+AQuQ2C3)CO1E&G$+-rh!toRBM=vZ7$2D#85xV1 Gfh+(N#WJb@ literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/pytest_metadata/__pycache__/__version.cpython-312.pyc b/venv/Lib/site-packages/pytest_metadata/__pycache__/__version.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e276ca2f07569ad4e5bebb9df9bbdaf51f994287 GIT binary patch literal 590 zcmYLFzi-n(6h8Z6$N6D3RZ~Q(O05J88RDdLLlsL2P|<{lCP+1wPS@NKnZ}M~Un0yD z#KH&@AeM~q7w|{01Z6R|5Q!;UrEXo|F0|!z`rh~6`|jR-_uVwJK&)?nZo8iq0DjBl za-}ZF8586KXafRlKtZ@l6r%PNPqkGK+E4*hrI4nm_FP>PN&^2yTlZC(wsopegQRvv zXTh`u(?w0cVrj{U@lvrfc9vvB%-Q)kTgu&^iv}pETx$)29_3owcl*A@(?c3Yg7F+e z7Zf4B{6Aq*E=uNQcTsqy?O5B^b_vd*xOOfKgHZZ!luT~6j+!;J_prA2Xut7*>nsjj zzZ*(nD1U-O*Jm6?Eac|X+LOcm#|_kKHxFuD?;pRQ9mbas!oJ^Un2DD~2!*;lt4T;p zFa&%BX9B_bvwL@(YPH%tay-mv8Fw(DUhD*Mnc?W9+#R@t_FSJjL+THm1NYd8T%ika z=LC0YB!rAc40)7cf*Gz{Ik$rN+}QQ{WYD8`!&Na$x{MqFr%+LpQynPR-f1KACoKF* z-AIaSA8(D}_;74|s(jg*tXn^dmDlxIaeY*uW%6(GiCLI}Lb9?o1zX9|#uRMKu5V7j V<|y~Jo7^l*0zR@E12*rg#7V literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/pytest_metadata/__pycache__/hooks.cpython-312-pytest-9.0.1.pyc b/venv/Lib/site-packages/pytest_metadata/__pycache__/hooks.cpython-312-pytest-9.0.1.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7f513b8d423058e75f4aa0de93b6c8e9b3cf26da GIT binary patch literal 460 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 EKNZ~Tx&QzG literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/pytest_metadata/__version.py b/venv/Lib/site-packages/pytest_metadata/__version.py new file mode 100644 index 0000000..e1fb961 --- /dev/null +++ b/venv/Lib/site-packages/pytest_metadata/__version.py @@ -0,0 +1,16 @@ +# file generated by setuptools_scm +# don't change, don't track in version control +TYPE_CHECKING = False +if TYPE_CHECKING: + from typing import Tuple, Union + VERSION_TUPLE = Tuple[Union[int, str], ...] +else: + VERSION_TUPLE = object + +version: str +__version__: str +__version_tuple__: VERSION_TUPLE +version_tuple: VERSION_TUPLE + +__version__ = version = '3.1.1' +__version_tuple__ = version_tuple = (3, 1, 1) diff --git a/venv/Lib/site-packages/pytest_metadata/ci/__init__.py b/venv/Lib/site-packages/pytest_metadata/ci/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/venv/Lib/site-packages/pytest_metadata/ci/__pycache__/__init__.cpython-312-pytest-9.0.1.pyc b/venv/Lib/site-packages/pytest_metadata/ci/__pycache__/__init__.cpython-312-pytest-9.0.1.pyc new file mode 100644 index 0000000000000000000000000000000000000000..43a0b8cd352bd6b3b0e3a1fec06ee59da9bf85ab GIT binary patch literal 190 zcmX@j%ge<81l#``WrFC(AOanHW&w&!XQ*V*Wb|9fP{ah}eFmxd73*RZV_;wq5E+x3 zSdyxnn4FlBnp+uDP^nvzSe&h!UYePbnv~l4 zSdv;?5}%t|l9-ZMk{FYm8519$nU`4-AFo$X`HRCQH$SB`C)KWq6=)kH5Ep|OADI~$ J8H<>KEC9_=Gy(ts literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/pytest_metadata/ci/__pycache__/__init__.cpython-312.pyc b/venv/Lib/site-packages/pytest_metadata/ci/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b4a5ecd8f4d7329cd8bda641f4150997ea6a71e4 GIT binary patch literal 190 zcmX@j%ge<81l#``WrFC(AOanHW&w&!XQ*V*Wb|9fP{ah}eFmxd73*RZV_;wq5E+x3 zSdyxnn4FlBnp+uDP^nvzSe&h!UYePbnv~l4 zSdv;?5}%t|l9-ZMlBl1YsUIJonU`4-AFo$X`HRCQH$SB`C)KWq6=)kH5Ep|OADI~$ J8H<>KEC9TnGpGOn literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/pytest_metadata/ci/__pycache__/appveyor.cpython-312-pytest-9.0.1.pyc b/venv/Lib/site-packages/pytest_metadata/ci/__pycache__/appveyor.cpython-312-pytest-9.0.1.pyc new file mode 100644 index 0000000000000000000000000000000000000000..928bcab74c2d29014238e46767090d20c901c3bb GIT binary patch literal 1049 zcmZvbO^ee&7{@2=>egcMZ7VLyiU)HkAC3nAr zcW-_IKZGZ*bM@p++=H+eou+M?E<2E9=J))cx0%WJTCEEB`1bFmZ|wo_bGg`z(t2|7 zf=@mJ4|u`^42A4l3d*CB0D`^Y4FQaSSNb9hZ>B<{{A=l?HICR_Xcz-@s$&8Tnb=ri z^(_%a-PSCkK^3tFIX19FOR&!J8@S? zv(*k)KD?qFAZ*II#-8NduoZ=1^v*^muY2Y7_7`ObuiS#ej!h$v|z8$-UhAmUkM=Usw!J-+g7GtQ37qN zuVeOXH|LI#AQj3AJGxe4$tvGEs2YV$Eym|o6ZQ~$ey#V~O3-@?X%dpyqg-Z+eI(fm zl1O%>h0D;@v6$oS_G7eGRtyDNT%@v^sB2xhXJcsbV{h!U%GunXB>psJx8H^-$ybLY zOcNR>EjNy7l=!o0E21AGKcVbaj@bT&Rk`z0PTYvqkv5RAuBnLM`~k!=>?p|m3NDs^ z%;g8^mD6swjZ-IZ6WVk~u1AB^3Daic#^=rP-1q3jpHgQ*rwd2%&zzViZHDgX+#S=H z3$r`{Os3IyR3_ye9E LmVSVG)Zm8y9p*~t literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/pytest_metadata/ci/__pycache__/appveyor.cpython-312.pyc b/venv/Lib/site-packages/pytest_metadata/ci/__pycache__/appveyor.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..20e38d9f14d436299094c2d5ee0e0327cbee44fb GIT binary patch literal 928 zcmZvaO>fgM7=V+)VVbD;ECPXsKyc`xtm23el9RZtCnpY$T`?47g(j+^ty|P&mGVot zbK@8ABRFyOl|O*kZ5MEpHXlG!@B@dQT6a0? z$?hma3`2Rl3LX(--BP`pH7MGt%TnkPp?aF4_QDJ9nYO%Fr7KV|{Xv@<6gcLjQKv8& z7&qG6qO$(A1SYX<#AMx8VV6_@&L)J)=k1!Upnx_&SRU+D7_!yY0o!eCYBRrzdSDFY ztDUnqQ2{?P=vdH_k1EYGC(z;;TB118#$^~0YF4=3ehN3{o{Irfk}bO?aYks&DPZc@ zoxU!6(3v7i92=^8It3H~14Hk-cDcUvSD&r7{%|;SkNriEMT1}#gwZ1RlYEc`>B(Sz z5r@%fd=~ku=xpWV_=}&$%1eUTNidI6C1z2YiA9tJVUPufv-mJbl2w$SFJCA5)!w_s jd3bReyh!}5*5>?5BTfo1my z4jcn{@^!^c+r^0VEKhE(K-Y0BUv5{#Lj#w%AF(nwpQ0oPtlsM0(lE4xlYd}2xe{i0 zIyO&bqgw0hCvvNjhU@kn-RsM}iVbv6w{2vTp^pag?SCGK=?2(ut;>z^g3kn>=kn!; zv>=bFNzy{FTy$ujvrO=dc_(9^GcK5X@rdO51FI|Za-7gi?jU?-c`kMk_Q{#pR@3i%`ZcnkHcJrwo@8S z>4fEKu*^xApfMFRjCeTaV!Vi^Omx!Xe&aZ~h!->VK6|6$pgQ@sx^8O_!ryfO;TP}= OylQGc!A`cX9{vI{zrJ<= literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/pytest_metadata/ci/__pycache__/bitbucket.cpython-312.pyc b/venv/Lib/site-packages/pytest_metadata/ci/__pycache__/bitbucket.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a926010591ccd3f437da13b7a595dfc14cea5991 GIT binary patch literal 496 zcmZ{g%}T>S6on^sBgKeXt90Q5bWvMeiVFD|>d<5o(xjpUhiNjD(59AVTF8^QcjpuM z5U$+L))x@F>qe&qNkKg@b2;CfdoRqZs+NJ&%hy?QSpwjFWqu_6T8kZ79>D;_;7Pdy zxl%9HRZ%jKIMR`?2Sx|_L=TW<5**k%^hBlLhUH*LOyr5Z4d~c5^2K36Jm@;axxuoz z_7^2VfXvOg>xQlsw12^Ma$}k9Y1p_HYBAQ=+Tx(Fy5n?g&FhHcg1MTfSr)WN*N3h+ z`R9QcPJn$}S40)!9`YP)L+q2D<{?eDVD}j;RzJABH_yXXtL5H?Ddns|BO0?b4<~tp z(`?un%#xUmk}(VCY&;LGq#tI9%uQ%Cqyv`8AZHmTDdRMzoHnDR*-vRhN1WCMGiQ zKLvtqt1k2!#grgZ-51>^bv*DTMW8*){Wa%y|JP6>YQVaIjEmA{aRQjdvRK++|AaJihF`S)n_)Bq?cw2$ z*gafFaS^R@arsGF(l#5=v@CdDj9H%ZtcZ?R2I>plrDSjYz>% literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/pytest_metadata/ci/__pycache__/circleci.cpython-312.pyc b/venv/Lib/site-packages/pytest_metadata/ci/__pycache__/circleci.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b3a2a9e42fae7a61cc44c60b5430821ce60f00a6 GIT binary patch literal 631 zcmZvZOK#gR5QZst6F4=R2b{vl3A}LQb{PZ(Xqkx_NTf$+Xin8R#|N{PT-L{%ADnz{K}I@3P-D0B*}<6(+CZ?xlHs z0V9xtug*DGIPF@ysb9O8xgHV$qo>H_A_@^B&__=S3zqnn5fF{8vsgo`0$81_6Aegk*qN#1|zAI9Bo zH#mzYvQQnF$W%=h@pRECWPZ^ZU1zBpXO}9TtIK)pWuN0bGrFlvF62n%Mp&r45EE6% zR2K3e$;>rN#wy9)Oc(bXN0Y1cdaT~h_RZfMbLS27`1!(doL}|5R_z8fXDvhi0*PY3 A;{X5v literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/pytest_metadata/ci/__pycache__/codebuild.cpython-312-pytest-9.0.1.pyc b/venv/Lib/site-packages/pytest_metadata/ci/__pycache__/codebuild.cpython-312-pytest-9.0.1.pyc new file mode 100644 index 0000000000000000000000000000000000000000..acbb8c80d46d549f65348e20f49cbe3eb76e4fbb GIT binary patch literal 494 zcmZWmze@u#6i%*cwW#1v?C2tNXeo{&V%rOHaNd=itym62yo3_kYmd9Ma=ZV7yPJQ3 ze}t23Rwp;nL2%JrtqS^vm-oK+eeVm&{nAna=(zjX6>kuL$E^7dV_LlK=;9hMz+fMA z;N&#ITwp*D&U9uW=mKWkz|LF>OSz{^PKl=(xg}Jjs)XM`H2f`1AjAs{Tl_-6m-w%gu)aM8Bnzqy52a ztGlPa-z_0$e-~G$P){G1cnIm~a!U+d%rDf}$u;PfB0@aKHQ3)L%+)16*grJL*)=}Q zH7FQloFqaCR3^wZz&}1T$mf;>h7?qjpC;oiQCGh(&mez4Usu17_%O#HPe&&o*Wi^5 zpFtk^73X3VV_;wq5E+x3Sdyxnn4FlBnp+uDP^nvzSe&h!UYePbnv~l4Sdv;?5}%t|l9-ZMlBl1Ysh^ymlA2VSnUkVdQ2C3)CO1E& jG$+-r2ozqxz+@=q1ri^a85tQr@v%!XU1Sg~0);jJZ6j@o literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/pytest_metadata/ci/__pycache__/gitlab_ci.cpython-312-pytest-9.0.1.pyc b/venv/Lib/site-packages/pytest_metadata/ci/__pycache__/gitlab_ci.cpython-312-pytest-9.0.1.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6c6d0ff29537505b5e80d124f0faae9fd9ad0abe GIT binary patch literal 966 zcmZ8fO>fgM7ZP9YCEF?56wOMTsEXEfRgw<%&VS&} zjbFfz;KbEePTUYXAngKnnzdCUC4QgheV_L${$8u?0Qr3T_c}h>1mLFvcTc4>ZeB>^ zGYCPbjlj89oRwr_P|-lLd45j=Ll9QJXy^B5T4Upv64{L-QPHt@tYfY_mW3H7=!DzQ zLhBqgJww!%akL9pA%lk4lim($dj@9&>PS417}-5c9NR)Ro2OkTi@a4CdFr$|?i4wd zX+hhArg%_#ik5auKxH)~4kRL;Z6hM9Qk`IzVaFCKsH9~&)l-S2h!)tzE;6x=R5_Jo zff>Pufe4z*T;iM}o$(GPMOSxNsvas;=`K2}uMyN$3s?5t@ z{xlxt@i-H^?@gFtjFYW*U literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/pytest_metadata/ci/__pycache__/gitlab_ci.cpython-312.pyc b/venv/Lib/site-packages/pytest_metadata/ci/__pycache__/gitlab_ci.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0af97698905eec3b9a1428b3612620c83fc74551 GIT binary patch literal 845 zcmZ8f&2G~`5VivcTAD(kL4tFU2vTUo5h0|FJ$1IS*VgVjsK{dFCRSuMDN*BA%9C*C z#uM-moVf0l7eMsZ3(Pv(Cdx{-=lgc%n;*~5W^>!Y^W)#!^5DMX{5IhBG^}yGgT_~9 z?2Mdm?k8vAwj1pO-SDV>;!)+rQADNUaHJTCV3QMJ8tCTAPxQ$;lqAr*c z1N{^+umR;Ui-5_c>?T?3ZlULK+*8!AHH{e&mXc6Ev^RB0I~EAc2D*iar;Gu|tit0o zku+x7I9VFyOidPHkf?D&69_2-lg^0TVaaI_0EcB*!sBD`r1B}RtGdM)^~glo>$|)AY{xzdBp3(oeFEay0yLy1Hvikg&@8=*JeR)KKt*oZnvA9WYfGVI{7dk z71KpFTXd>?e%2XZmZM@)o)_6waekGB6GN5`s|S87^R`6#dQ!(n+i xE~`m?s)ps8+2ZEG`{|d_<)rv<@fy>)_{^L5`g6~9-M`x}pErIvt&3O4{09)z1&{y$ literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/pytest_metadata/ci/__pycache__/jenkins.cpython-312-pytest-9.0.1.pyc b/venv/Lib/site-packages/pytest_metadata/ci/__pycache__/jenkins.cpython-312-pytest-9.0.1.pyc new file mode 100644 index 0000000000000000000000000000000000000000..448d2324da0a1811a7f55be0ab21566393646787 GIT binary patch literal 526 zcmX|8F^|(Q6t+{KZRqJ3xZw~pQioO*BSMHIweH+&oKunps4Q8&#;Q!4T$H$nvhyF< z+4u$g2qspqOl&7^K)MB-_Ihu4{@#1v_dePFad2P*6W{;6l-Ce|Urn(O>kiz!FyISF zK?>Jk4!5V3wH6ix*@O8*2$mqVzQXyV8unYi8xiRr>Yh8p(=j76-$kMB>?%Cg-5r{R zQ{5%O7%}4bNOvZIyF+*L*g4UM=soggaS-meJf0wOhDpS>>WPy$>@+ay?z0Xo&7NP`4I3U{gl8CvXL%dUMpWv8zf$!s3w;S9n**15Cep`1gHgc_GUg_T3qGtEi zF)L~%DmCO)B}yeP^I<7Il~M`ay>IFEzGoY8Lub6yM~Eai42X|N%n~QWjypxsH*nLu z(=8ppqNCC1`~%In5(B>AsmN+t)C0w<)nK`isaVTg&?}K&(W$(kl`$O@e6iw7Q5oXa zCliXNTyeUP^rOgE<_5!}zU{ouF4N6gyeXd>GxNQmdTP>j3qttU1`vJ*f54Mt>nAuW HpP9jbGybFN literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/pytest_metadata/ci/__pycache__/jenkins.cpython-312.pyc b/venv/Lib/site-packages/pytest_metadata/ci/__pycache__/jenkins.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8341c6b19ab3bf979106b359d023a82bac3b2833 GIT binary patch literal 405 zcmXw#%}&BV5XZN85Gn){!@)=3fDn(ynDk=<3+(B`eS)2!pypz#%)|sxN zkj`cM^49STc)}^lFjss<-iLxSF%O cWlJm*&jx8jxXpW#t>RmKP%P4~e~bjQ{`u literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/pytest_metadata/ci/__pycache__/taskcluster.cpython-312-pytest-9.0.1.pyc b/venv/Lib/site-packages/pytest_metadata/ci/__pycache__/taskcluster.cpython-312-pytest-9.0.1.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e00c3f4fa0d0e07edef74683810170d0971b02a9 GIT binary patch literal 369 zcmX|5&r1S96rNd4O_EMV{sV!B73mle8460ag_@8IY~#*D26xwGX4cqy|3e+S_80U= zbgAoHJ9P_$0xvZy>4W#a_v8Ec-fO8;L?&K-5Bayjk91gv_1`QGO!I&kV%SF`yh1C; zcos%McC>{Nvsh+gjdBUDW~~b*^{q7_8=XVH|T`@lfiW(kbo+%sYe+E35gP0Q8~3IF=ycO5Xc4W-RvMMC=+vY*Ox-@|D3D{}d6%_vo?m)PCEy-%(kV%;^s{ CNOFGw literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/pytest_metadata/ci/__pycache__/taskcluster.cpython-312.pyc b/venv/Lib/site-packages/pytest_metadata/ci/__pycache__/taskcluster.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e6b96020c542280414fe76dcf9b7a0d38d52aba5 GIT binary patch literal 251 zcmX@j%ge<81l#``Wqt$Fk3k$5V1hC}^8gvs8B!Qh7}J@e7%G`GnQyU&I0k#id%E0W z3kvlE(tetZw?tk2!aRfg{d`^hLgK?5gFGFbd|ZQ9GJFP^^DDu{D#pOTARsa(H?bsD zH!(ReB{jD)rl3-{B(XSKH@!47B{e5AFEyqtHLonjCo?IgI1{L@ATc>RF+H^ysIVlp zxFkL|wInemu_RGHIa42`F*&ESxFoemub}c5hfQvNN@-52T@eS+LPj7i76K9C%v WKX5TfDKGH2AfbDaL9~bsC=38y?MR;h literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/pytest_metadata/ci/__pycache__/travis_ci.cpython-312-pytest-9.0.1.pyc b/venv/Lib/site-packages/pytest_metadata/ci/__pycache__/travis_ci.cpython-312-pytest-9.0.1.pyc new file mode 100644 index 0000000000000000000000000000000000000000..72a73139796ed85aebdeaa7bfca0e186ef17abc4 GIT binary patch literal 674 zcmZuuzmL-}7`0R2jzWP0Dma7~kvg=h7!g9GSF0D4A0y2BcfSDNX?CKFR*{-uIrLY=7=_+CbH(Ur+cu1Awm;s7v8(ojq3V zJxD+TmtYRJ-AG%p0YQ3cei?!VNQ@6~ex-!H*0;*y_V#2a@XeWp$;da|gF|_Rbs8E zpw7FYO&%t$>HIx;8rnAT(W?;QKt9l-f64k<_b!^@LsQtR^9%e4 zS8iwP4~X4$qZ6G}gz6mJ zGr!SjbbJ1YB(z2X64FuXPtsaK;z6xHi$Xe##?+tF@!Z#=TR)DtZbE_q>C>1C6B;LY zL=zH{gw%tmo=nL+ig6H~Ow#4%`RFd34e78 0: + return "metadata: {0}".format(config.stash[metadata_key]) + + +@pytest.hookimpl(optionalhook=True) +def pytest_testnodedown(node): + # note that any metadata from remote workers will be replaced with the + # environment from the final worker to quit + if hasattr(node, "workeroutput"): + node.config.stash[metadata_key].update(node.workeroutput["metadata"]) diff --git a/venv/Scripts/Activate.ps1 b/venv/Scripts/Activate.ps1 new file mode 100644 index 0000000..b63e7b7 --- /dev/null +++ b/venv/Scripts/Activate.ps1 @@ -0,0 +1,502 @@ +<# +.Synopsis +Activate a Python virtual environment for the current PowerShell session. + +.Description +Pushes the python executable for a virtual environment to the front of the +$Env:PATH environment variable and sets the prompt to signify that you are +in a Python virtual environment. Makes use of the command line switches as +well as the `pyvenv.cfg` file values present in the virtual environment. + +.Parameter VenvDir +Path to the directory that contains the virtual environment to activate. The +default value for this is the parent of the directory that the Activate.ps1 +script is located within. + +.Parameter Prompt +The prompt prefix to display when this virtual environment is activated. By +default, this prompt is the name of the virtual environment folder (VenvDir) +surrounded by parentheses and followed by a single space (ie. '(.venv) '). + +.Example +Activate.ps1 +Activates the Python virtual environment that contains the Activate.ps1 script. + +.Example +Activate.ps1 -Verbose +Activates the Python virtual environment that contains the Activate.ps1 script, +and shows extra information about the activation as it executes. + +.Example +Activate.ps1 -VenvDir C:\Users\MyUser\Common\.venv +Activates the Python virtual environment located in the specified location. + +.Example +Activate.ps1 -Prompt "MyPython" +Activates the Python virtual environment that contains the Activate.ps1 script, +and prefixes the current prompt with the specified string (surrounded in +parentheses) while the virtual environment is active. + +.Notes +On Windows, it may be required to enable this Activate.ps1 script by setting the +execution policy for the user. You can do this by issuing the following PowerShell +command: + +PS C:\> Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser + +For more information on Execution Policies: +https://go.microsoft.com/fwlink/?LinkID=135170 + +#> +Param( + [Parameter(Mandatory = $false)] + [String] + $VenvDir, + [Parameter(Mandatory = $false)] + [String] + $Prompt +) + +<# Function declarations --------------------------------------------------- #> + +<# +.Synopsis +Remove all shell session elements added by the Activate script, including the +addition of the virtual environment's Python executable from the beginning of +the PATH variable. + +.Parameter NonDestructive +If present, do not remove this function from the global namespace for the +session. + +#> +function global:deactivate ([switch]$NonDestructive) { + # Revert to original values + + # The prior prompt: + if (Test-Path -Path Function:_OLD_VIRTUAL_PROMPT) { + Copy-Item -Path Function:_OLD_VIRTUAL_PROMPT -Destination Function:prompt + Remove-Item -Path Function:_OLD_VIRTUAL_PROMPT + } + + # The prior PYTHONHOME: + if (Test-Path -Path Env:_OLD_VIRTUAL_PYTHONHOME) { + Copy-Item -Path Env:_OLD_VIRTUAL_PYTHONHOME -Destination Env:PYTHONHOME + Remove-Item -Path Env:_OLD_VIRTUAL_PYTHONHOME + } + + # The prior PATH: + if (Test-Path -Path Env:_OLD_VIRTUAL_PATH) { + Copy-Item -Path Env:_OLD_VIRTUAL_PATH -Destination Env:PATH + Remove-Item -Path Env:_OLD_VIRTUAL_PATH + } + + # Just remove the VIRTUAL_ENV altogether: + if (Test-Path -Path Env:VIRTUAL_ENV) { + Remove-Item -Path env:VIRTUAL_ENV + } + + # Just remove VIRTUAL_ENV_PROMPT altogether. + if (Test-Path -Path Env:VIRTUAL_ENV_PROMPT) { + Remove-Item -Path env:VIRTUAL_ENV_PROMPT + } + + # Just remove the _PYTHON_VENV_PROMPT_PREFIX altogether: + if (Get-Variable -Name "_PYTHON_VENV_PROMPT_PREFIX" -ErrorAction SilentlyContinue) { + Remove-Variable -Name _PYTHON_VENV_PROMPT_PREFIX -Scope Global -Force + } + + # Leave deactivate function in the global namespace if requested: + if (-not $NonDestructive) { + Remove-Item -Path function:deactivate + } +} + +<# +.Description +Get-PyVenvConfig parses the values from the pyvenv.cfg file located in the +given folder, and returns them in a map. + +For each line in the pyvenv.cfg file, if that line can be parsed into exactly +two strings separated by `=` (with any amount of whitespace surrounding the =) +then it is considered a `key = value` line. The left hand string is the key, +the right hand is the value. + +If the value starts with a `'` or a `"` then the first and last character is +stripped from the value before being captured. + +.Parameter ConfigDir +Path to the directory that contains the `pyvenv.cfg` file. +#> +function Get-PyVenvConfig( + [String] + $ConfigDir +) { + Write-Verbose "Given ConfigDir=$ConfigDir, obtain values in pyvenv.cfg" + + # Ensure the file exists, and issue a warning if it doesn't (but still allow the function to continue). + $pyvenvConfigPath = Join-Path -Resolve -Path $ConfigDir -ChildPath 'pyvenv.cfg' -ErrorAction Continue + + # An empty map will be returned if no config file is found. + $pyvenvConfig = @{ } + + if ($pyvenvConfigPath) { + + Write-Verbose "File exists, parse `key = value` lines" + $pyvenvConfigContent = Get-Content -Path $pyvenvConfigPath + + $pyvenvConfigContent | ForEach-Object { + $keyval = $PSItem -split "\s*=\s*", 2 + if ($keyval[0] -and $keyval[1]) { + $val = $keyval[1] + + # Remove extraneous quotations around a string value. + if ("'""".Contains($val.Substring(0, 1))) { + $val = $val.Substring(1, $val.Length - 2) + } + + $pyvenvConfig[$keyval[0]] = $val + Write-Verbose "Adding Key: '$($keyval[0])'='$val'" + } + } + } + return $pyvenvConfig +} + + +<# Begin Activate script --------------------------------------------------- #> + +# Determine the containing directory of this script +$VenvExecPath = Split-Path -Parent $MyInvocation.MyCommand.Definition +$VenvExecDir = Get-Item -Path $VenvExecPath + +Write-Verbose "Activation script is located in path: '$VenvExecPath'" +Write-Verbose "VenvExecDir Fullname: '$($VenvExecDir.FullName)" +Write-Verbose "VenvExecDir Name: '$($VenvExecDir.Name)" + +# Set values required in priority: CmdLine, ConfigFile, Default +# First, get the location of the virtual environment, it might not be +# VenvExecDir if specified on the command line. +if ($VenvDir) { + Write-Verbose "VenvDir given as parameter, using '$VenvDir' to determine values" +} +else { + Write-Verbose "VenvDir not given as a parameter, using parent directory name as VenvDir." + $VenvDir = $VenvExecDir.Parent.FullName.TrimEnd("\\/") + Write-Verbose "VenvDir=$VenvDir" +} + +# Next, read the `pyvenv.cfg` file to determine any required value such +# as `prompt`. +$pyvenvCfg = Get-PyVenvConfig -ConfigDir $VenvDir + +# Next, set the prompt from the command line, or the config file, or +# just use the name of the virtual environment folder. +if ($Prompt) { + Write-Verbose "Prompt specified as argument, using '$Prompt'" +} +else { + Write-Verbose "Prompt not specified as argument to script, checking pyvenv.cfg value" + if ($pyvenvCfg -and $pyvenvCfg['prompt']) { + Write-Verbose " Setting based on value in pyvenv.cfg='$($pyvenvCfg['prompt'])'" + $Prompt = $pyvenvCfg['prompt']; + } + else { + Write-Verbose " Setting prompt based on parent's directory's name. (Is the directory name passed to venv module when creating the virtual environment)" + Write-Verbose " Got leaf-name of $VenvDir='$(Split-Path -Path $venvDir -Leaf)'" + $Prompt = Split-Path -Path $venvDir -Leaf + } +} + +Write-Verbose "Prompt = '$Prompt'" +Write-Verbose "VenvDir='$VenvDir'" + +# Deactivate any currently active virtual environment, but leave the +# deactivate function in place. +deactivate -nondestructive + +# Now set the environment variable VIRTUAL_ENV, used by many tools to determine +# that there is an activated venv. +$env:VIRTUAL_ENV = $VenvDir + +if (-not $Env:VIRTUAL_ENV_DISABLE_PROMPT) { + + Write-Verbose "Setting prompt to '$Prompt'" + + # Set the prompt to include the env name + # Make sure _OLD_VIRTUAL_PROMPT is global + function global:_OLD_VIRTUAL_PROMPT { "" } + Copy-Item -Path function:prompt -Destination function:_OLD_VIRTUAL_PROMPT + New-Variable -Name _PYTHON_VENV_PROMPT_PREFIX -Description "Python virtual environment prompt prefix" -Scope Global -Option ReadOnly -Visibility Public -Value $Prompt + + function global:prompt { + Write-Host -NoNewline -ForegroundColor Green "($_PYTHON_VENV_PROMPT_PREFIX) " + _OLD_VIRTUAL_PROMPT + } + $env:VIRTUAL_ENV_PROMPT = $Prompt +} + +# Clear PYTHONHOME +if (Test-Path -Path Env:PYTHONHOME) { + Copy-Item -Path Env:PYTHONHOME -Destination Env:_OLD_VIRTUAL_PYTHONHOME + Remove-Item -Path Env:PYTHONHOME +} + +# Add the venv to the PATH +Copy-Item -Path Env:PATH -Destination Env:_OLD_VIRTUAL_PATH +$Env:PATH = "$VenvExecDir$([System.IO.Path]::PathSeparator)$Env:PATH" + +# SIG # Begin signature block +# MIIvIwYJKoZIhvcNAQcCoIIvFDCCLxACAQExDzANBglghkgBZQMEAgEFADB5Bgor +# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG +# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCBnL745ElCYk8vk +# dBtMuQhLeWJ3ZGfzKW4DHCYzAn+QB6CCE8MwggWQMIIDeKADAgECAhAFmxtXno4h +# MuI5B72nd3VcMA0GCSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQK +# EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNV +# BAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBHNDAeFw0xMzA4MDExMjAwMDBaFw0z +# ODAxMTUxMjAwMDBaMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ +# bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0 +# IFRydXN0ZWQgUm9vdCBHNDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB +# AL/mkHNo3rvkXUo8MCIwaTPswqclLskhPfKK2FnC4SmnPVirdprNrnsbhA3EMB/z +# G6Q4FutWxpdtHauyefLKEdLkX9YFPFIPUh/GnhWlfr6fqVcWWVVyr2iTcMKyunWZ +# anMylNEQRBAu34LzB4TmdDttceItDBvuINXJIB1jKS3O7F5OyJP4IWGbNOsFxl7s +# Wxq868nPzaw0QF+xembud8hIqGZXV59UWI4MK7dPpzDZVu7Ke13jrclPXuU15zHL +# 2pNe3I6PgNq2kZhAkHnDeMe2scS1ahg4AxCN2NQ3pC4FfYj1gj4QkXCrVYJBMtfb +# BHMqbpEBfCFM1LyuGwN1XXhm2ToxRJozQL8I11pJpMLmqaBn3aQnvKFPObURWBf3 +# JFxGj2T3wWmIdph2PVldQnaHiZdpekjw4KISG2aadMreSx7nDmOu5tTvkpI6nj3c +# AORFJYm2mkQZK37AlLTSYW3rM9nF30sEAMx9HJXDj/chsrIRt7t/8tWMcCxBYKqx +# YxhElRp2Yn72gLD76GSmM9GJB+G9t+ZDpBi4pncB4Q+UDCEdslQpJYls5Q5SUUd0 +# viastkF13nqsX40/ybzTQRESW+UQUOsxxcpyFiIJ33xMdT9j7CFfxCBRa2+xq4aL +# T8LWRV+dIPyhHsXAj6KxfgommfXkaS+YHS312amyHeUbAgMBAAGjQjBAMA8GA1Ud +# EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBTs1+OC0nFdZEzf +# Lmc/57qYrhwPTzANBgkqhkiG9w0BAQwFAAOCAgEAu2HZfalsvhfEkRvDoaIAjeNk +# aA9Wz3eucPn9mkqZucl4XAwMX+TmFClWCzZJXURj4K2clhhmGyMNPXnpbWvWVPjS +# PMFDQK4dUPVS/JA7u5iZaWvHwaeoaKQn3J35J64whbn2Z006Po9ZOSJTROvIXQPK +# 7VB6fWIhCoDIc2bRoAVgX+iltKevqPdtNZx8WorWojiZ83iL9E3SIAveBO6Mm0eB +# cg3AFDLvMFkuruBx8lbkapdvklBtlo1oepqyNhR6BvIkuQkRUNcIsbiJeoQjYUIp +# 5aPNoiBB19GcZNnqJqGLFNdMGbJQQXE9P01wI4YMStyB0swylIQNCAmXHE/A7msg +# dDDS4Dk0EIUhFQEI6FUy3nFJ2SgXUE3mvk3RdazQyvtBuEOlqtPDBURPLDab4vri +# RbgjU2wGb2dVf0a1TD9uKFp5JtKkqGKX0h7i7UqLvBv9R0oN32dmfrJbQdA75PQ7 +# 9ARj6e/CVABRoIoqyc54zNXqhwQYs86vSYiv85KZtrPmYQ/ShQDnUBrkG5WdGaG5 +# nLGbsQAe79APT0JsyQq87kP6OnGlyE0mpTX9iV28hWIdMtKgK1TtmlfB2/oQzxm3 +# i0objwG2J5VT6LaJbVu8aNQj6ItRolb58KaAoNYes7wPD1N1KarqE3fk3oyBIa0H +# EEcRrYc9B9F1vM/zZn4wggawMIIEmKADAgECAhAIrUCyYNKcTJ9ezam9k67ZMA0G +# CSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ +# bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0 +# IFRydXN0ZWQgUm9vdCBHNDAeFw0yMTA0MjkwMDAwMDBaFw0zNjA0MjgyMzU5NTla +# MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UE +# AxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEz +# ODQgMjAyMSBDQTEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDVtC9C +# 0CiteLdd1TlZG7GIQvUzjOs9gZdwxbvEhSYwn6SOaNhc9es0JAfhS0/TeEP0F9ce +# 2vnS1WcaUk8OoVf8iJnBkcyBAz5NcCRks43iCH00fUyAVxJrQ5qZ8sU7H/Lvy0da +# E6ZMswEgJfMQ04uy+wjwiuCdCcBlp/qYgEk1hz1RGeiQIXhFLqGfLOEYwhrMxe6T +# SXBCMo/7xuoc82VokaJNTIIRSFJo3hC9FFdd6BgTZcV/sk+FLEikVoQ11vkunKoA +# FdE3/hoGlMJ8yOobMubKwvSnowMOdKWvObarYBLj6Na59zHh3K3kGKDYwSNHR7Oh +# D26jq22YBoMbt2pnLdK9RBqSEIGPsDsJ18ebMlrC/2pgVItJwZPt4bRc4G/rJvmM +# 1bL5OBDm6s6R9b7T+2+TYTRcvJNFKIM2KmYoX7BzzosmJQayg9Rc9hUZTO1i4F4z +# 8ujo7AqnsAMrkbI2eb73rQgedaZlzLvjSFDzd5Ea/ttQokbIYViY9XwCFjyDKK05 +# huzUtw1T0PhH5nUwjewwk3YUpltLXXRhTT8SkXbev1jLchApQfDVxW0mdmgRQRNY +# mtwmKwH0iU1Z23jPgUo+QEdfyYFQc4UQIyFZYIpkVMHMIRroOBl8ZhzNeDhFMJlP +# /2NPTLuqDQhTQXxYPUez+rbsjDIJAsxsPAxWEQIDAQABo4IBWTCCAVUwEgYDVR0T +# AQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUaDfg67Y7+F8Rhvv+YXsIiGX0TkIwHwYD +# VR0jBBgwFoAU7NfjgtJxXWRM3y5nP+e6mK4cD08wDgYDVR0PAQH/BAQDAgGGMBMG +# A1UdJQQMMAoGCCsGAQUFBwMDMHcGCCsGAQUFBwEBBGswaTAkBggrBgEFBQcwAYYY +# aHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEEGCCsGAQUFBzAChjVodHRwOi8vY2Fj +# ZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNydDBDBgNV +# HR8EPDA6MDigNqA0hjJodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRU +# cnVzdGVkUm9vdEc0LmNybDAcBgNVHSAEFTATMAcGBWeBDAEDMAgGBmeBDAEEATAN +# BgkqhkiG9w0BAQwFAAOCAgEAOiNEPY0Idu6PvDqZ01bgAhql+Eg08yy25nRm95Ry +# sQDKr2wwJxMSnpBEn0v9nqN8JtU3vDpdSG2V1T9J9Ce7FoFFUP2cvbaF4HZ+N3HL +# IvdaqpDP9ZNq4+sg0dVQeYiaiorBtr2hSBh+3NiAGhEZGM1hmYFW9snjdufE5Btf +# Q/g+lP92OT2e1JnPSt0o618moZVYSNUa/tcnP/2Q0XaG3RywYFzzDaju4ImhvTnh +# OE7abrs2nfvlIVNaw8rpavGiPttDuDPITzgUkpn13c5UbdldAhQfQDN8A+KVssIh +# dXNSy0bYxDQcoqVLjc1vdjcshT8azibpGL6QB7BDf5WIIIJw8MzK7/0pNVwfiThV +# 9zeKiwmhywvpMRr/LhlcOXHhvpynCgbWJme3kuZOX956rEnPLqR0kq3bPKSchh/j +# wVYbKyP/j7XqiHtwa+aguv06P0WmxOgWkVKLQcBIhEuWTatEQOON8BUozu3xGFYH +# Ki8QxAwIZDwzj64ojDzLj4gLDb879M4ee47vtevLt/B3E+bnKD+sEq6lLyJsQfmC +# XBVmzGwOysWGw/YmMwwHS6DTBwJqakAwSEs0qFEgu60bhQjiWQ1tygVQK+pKHJ6l +# /aCnHwZ05/LWUpD9r4VIIflXO7ScA+2GRfS0YW6/aOImYIbqyK+p/pQd52MbOoZW +# eE4wggd3MIIFX6ADAgECAhAHHxQbizANJfMU6yMM0NHdMA0GCSqGSIb3DQEBCwUA +# MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UE +# AxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEz +# ODQgMjAyMSBDQTEwHhcNMjIwMTE3MDAwMDAwWhcNMjUwMTE1MjM1OTU5WjB8MQsw +# CQYDVQQGEwJVUzEPMA0GA1UECBMGT3JlZ29uMRIwEAYDVQQHEwlCZWF2ZXJ0b24x +# IzAhBgNVBAoTGlB5dGhvbiBTb2Z0d2FyZSBGb3VuZGF0aW9uMSMwIQYDVQQDExpQ +# eXRob24gU29mdHdhcmUgRm91bmRhdGlvbjCCAiIwDQYJKoZIhvcNAQEBBQADggIP +# ADCCAgoCggIBAKgc0BTT+iKbtK6f2mr9pNMUTcAJxKdsuOiSYgDFfwhjQy89koM7 +# uP+QV/gwx8MzEt3c9tLJvDccVWQ8H7mVsk/K+X+IufBLCgUi0GGAZUegEAeRlSXx +# xhYScr818ma8EvGIZdiSOhqjYc4KnfgfIS4RLtZSrDFG2tN16yS8skFa3IHyvWdb +# D9PvZ4iYNAS4pjYDRjT/9uzPZ4Pan+53xZIcDgjiTwOh8VGuppxcia6a7xCyKoOA +# GjvCyQsj5223v1/Ig7Dp9mGI+nh1E3IwmyTIIuVHyK6Lqu352diDY+iCMpk9Zanm +# SjmB+GMVs+H/gOiofjjtf6oz0ki3rb7sQ8fTnonIL9dyGTJ0ZFYKeb6BLA66d2GA +# LwxZhLe5WH4Np9HcyXHACkppsE6ynYjTOd7+jN1PRJahN1oERzTzEiV6nCO1M3U1 +# HbPTGyq52IMFSBM2/07WTJSbOeXjvYR7aUxK9/ZkJiacl2iZI7IWe7JKhHohqKuc +# eQNyOzxTakLcRkzynvIrk33R9YVqtB4L6wtFxhUjvDnQg16xot2KVPdfyPAWd81w +# tZADmrUtsZ9qG79x1hBdyOl4vUtVPECuyhCxaw+faVjumapPUnwo8ygflJJ74J+B +# Yxf6UuD7m8yzsfXWkdv52DjL74TxzuFTLHPyARWCSCAbzn3ZIly+qIqDAgMBAAGj +# ggIGMIICAjAfBgNVHSMEGDAWgBRoN+Drtjv4XxGG+/5hewiIZfROQjAdBgNVHQ4E +# FgQUt/1Teh2XDuUj2WW3siYWJgkZHA8wDgYDVR0PAQH/BAQDAgeAMBMGA1UdJQQM +# MAoGCCsGAQUFBwMDMIG1BgNVHR8Ega0wgaowU6BRoE+GTWh0dHA6Ly9jcmwzLmRp +# Z2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNENvZGVTaWduaW5nUlNBNDA5NlNI +# QTM4NDIwMjFDQTEuY3JsMFOgUaBPhk1odHRwOi8vY3JsNC5kaWdpY2VydC5jb20v +# RGlnaUNlcnRUcnVzdGVkRzRDb2RlU2lnbmluZ1JTQTQwOTZTSEEzODQyMDIxQ0Ex +# LmNybDA+BgNVHSAENzA1MDMGBmeBDAEEATApMCcGCCsGAQUFBwIBFhtodHRwOi8v +# d3d3LmRpZ2ljZXJ0LmNvbS9DUFMwgZQGCCsGAQUFBwEBBIGHMIGEMCQGCCsGAQUF +# BzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wXAYIKwYBBQUHMAKGUGh0dHA6 +# Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNENvZGVTaWdu +# aW5nUlNBNDA5NlNIQTM4NDIwMjFDQTEuY3J0MAwGA1UdEwEB/wQCMAAwDQYJKoZI +# hvcNAQELBQADggIBABxv4AeV/5ltkELHSC63fXAFYS5tadcWTiNc2rskrNLrfH1N +# s0vgSZFoQxYBFKI159E8oQQ1SKbTEubZ/B9kmHPhprHya08+VVzxC88pOEvz68nA +# 82oEM09584aILqYmj8Pj7h/kmZNzuEL7WiwFa/U1hX+XiWfLIJQsAHBla0i7QRF2 +# de8/VSF0XXFa2kBQ6aiTsiLyKPNbaNtbcucaUdn6vVUS5izWOXM95BSkFSKdE45O +# q3FForNJXjBvSCpwcP36WklaHL+aHu1upIhCTUkzTHMh8b86WmjRUqbrnvdyR2yd +# I5l1OqcMBjkpPpIV6wcc+KY/RH2xvVuuoHjlUjwq2bHiNoX+W1scCpnA8YTs2d50 +# jDHUgwUo+ciwpffH0Riq132NFmrH3r67VaN3TuBxjI8SIZM58WEDkbeoriDk3hxU +# 8ZWV7b8AW6oyVBGfM06UgkfMb58h+tJPrFx8VI/WLq1dTqMfZOm5cuclMnUHs2uq +# rRNtnV8UfidPBL4ZHkTcClQbCoz0UbLhkiDvIS00Dn+BBcxw/TKqVL4Oaz3bkMSs +# M46LciTeucHY9ExRVt3zy7i149sd+F4QozPqn7FrSVHXmem3r7bjyHTxOgqxRCVa +# 18Vtx7P/8bYSBeS+WHCKcliFCecspusCDSlnRUjZwyPdP0VHxaZg2unjHY3rMYIa +# tjCCGrICAQEwfTBpMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIElu +# Yy4xQTA/BgNVBAMTOERpZ2lDZXJ0IFRydXN0ZWQgRzQgQ29kZSBTaWduaW5nIFJT +# QTQwOTYgU0hBMzg0IDIwMjEgQ0ExAhAHHxQbizANJfMU6yMM0NHdMA0GCWCGSAFl +# AwQCAQUAoIHIMBkGCSqGSIb3DQEJAzEMBgorBgEEAYI3AgEEMBwGCisGAQQBgjcC +# AQsxDjAMBgorBgEEAYI3AgEVMC8GCSqGSIb3DQEJBDEiBCBnAZ6P7YvTwq0fbF62 +# o7E75R0LxsW5OtyYiFESQckLhjBcBgorBgEEAYI3AgEMMU4wTKBGgEQAQgB1AGkA +# bAB0ADoAIABSAGUAbABlAGEAcwBlAF8AdgAzAC4AMQAyAC4ANABfADIAMAAyADQA +# MAA2ADAANgAuADAAMaECgAAwDQYJKoZIhvcNAQEBBQAEggIAV29hYhi09QNyGtav +# HZIo33y/iqXsIa4o88S5gzBa7Nnkwra0QLitSjvRfVbcFvq54Id+VIn00di4Nde0 +# maAUKPGXtTQL48esG/F/TLDOWd/jb9qCYHyNZYpJjKdXqI8IbyG6Pl05IMSas7wX +# DHsK19ZEGuGrmKCAxh6JbFXADgeUbftg3i9UxpMnfSugZjjdKIdyVWlzUnpYkKuI +# fpafwvNHfIYzfxOeV9CWsdqe34D6fRrEs8ZDEZSQl+Mw9aGaT39vuryFE1iKOzj0 +# uqrX/wN/wwu8oLWNC7JWE8SDG3eD0QLy+x7zEnlPkWsRV9nGOgrP9Khge0LgL+jP +# Km8iDs7fSGEOB/7PPxAl8yshEULOZAhBhcsGeGs+kQrVzlqZ9WlrU1Z1cylpLWzX +# Kkvs2DXD+zrplhpiVv6Gnn3YMBr4BKf0mXESTX9/BzIwvxlkhpv/BT0OWwrDlgPM +# hNj8jA5r2/WSqCg15DYjJ0RlnCerC/ORhSbs7v/HjpmH3DhaICJF7tdyFSIFXgNV +# W0GyQJMulQDEPd2+o+PNyAPElvGC3SYTjVnRLPcJTGhAt+VuHfnMG4HNkmyeU+nk +# OAMShxEax6NLeRsjKqqABUgZb2g4FSmXzHy7HgQOPmCQMv8xH4m8u992YMLyxh5U +# gGRUOUiAhrHXNZ6wG6T52NGQppehghc/MIIXOwYKKwYBBAGCNwMDATGCFyswghcn +# BgkqhkiG9w0BBwKgghcYMIIXFAIBAzEPMA0GCWCGSAFlAwQCAQUAMHcGCyqGSIb3 +# DQEJEAEEoGgEZjBkAgEBBglghkgBhv1sBwEwMTANBglghkgBZQMEAgEFAAQg+eJt +# Pwl5Hz89rrpf2qbsjNAUNlBq9SGjVuw+Erci2HcCEDPjoeI//+uRP30fqUoeHIAY +# DzIwMjQwNjA2MTk1MDE0WqCCEwkwggbCMIIEqqADAgECAhAFRK/zlJ0IOaa/2z9f +# 5WEWMA0GCSqGSIb3DQEBCwUAMGMxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdp +# Q2VydCwgSW5jLjE7MDkGA1UEAxMyRGlnaUNlcnQgVHJ1c3RlZCBHNCBSU0E0MDk2 +# IFNIQTI1NiBUaW1lU3RhbXBpbmcgQ0EwHhcNMjMwNzE0MDAwMDAwWhcNMzQxMDEz +# MjM1OTU5WjBIMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4x +# IDAeBgNVBAMTF0RpZ2lDZXJ0IFRpbWVzdGFtcCAyMDIzMIICIjANBgkqhkiG9w0B +# AQEFAAOCAg8AMIICCgKCAgEAo1NFhx2DjlusPlSzI+DPn9fl0uddoQ4J3C9Io5d6 +# OyqcZ9xiFVjBqZMRp82qsmrdECmKHmJjadNYnDVxvzqX65RQjxwg6seaOy+WZuNp +# 52n+W8PWKyAcwZeUtKVQgfLPywemMGjKg0La/H8JJJSkghraarrYO8pd3hkYhftF +# 6g1hbJ3+cV7EBpo88MUueQ8bZlLjyNY+X9pD04T10Mf2SC1eRXWWdf7dEKEbg8G4 +# 5lKVtUfXeCk5a+B4WZfjRCtK1ZXO7wgX6oJkTf8j48qG7rSkIWRw69XloNpjsy7p +# Be6q9iT1HbybHLK3X9/w7nZ9MZllR1WdSiQvrCuXvp/k/XtzPjLuUjT71Lvr1KAs +# NJvj3m5kGQc3AZEPHLVRzapMZoOIaGK7vEEbeBlt5NkP4FhB+9ixLOFRr7StFQYU +# 6mIIE9NpHnxkTZ0P387RXoyqq1AVybPKvNfEO2hEo6U7Qv1zfe7dCv95NBB+plwK +# WEwAPoVpdceDZNZ1zY8SdlalJPrXxGshuugfNJgvOuprAbD3+yqG7HtSOKmYCaFx +# smxxrz64b5bV4RAT/mFHCoz+8LbH1cfebCTwv0KCyqBxPZySkwS0aXAnDU+3tTbR +# yV8IpHCj7ArxES5k4MsiK8rxKBMhSVF+BmbTO77665E42FEHypS34lCh8zrTioPL +# QHsCAwEAAaOCAYswggGHMA4GA1UdDwEB/wQEAwIHgDAMBgNVHRMBAf8EAjAAMBYG +# A1UdJQEB/wQMMAoGCCsGAQUFBwMIMCAGA1UdIAQZMBcwCAYGZ4EMAQQCMAsGCWCG +# SAGG/WwHATAfBgNVHSMEGDAWgBS6FtltTYUvcyl2mi91jGogj57IbzAdBgNVHQ4E +# FgQUpbbvE+fvzdBkodVWqWUxo97V40kwWgYDVR0fBFMwUTBPoE2gS4ZJaHR0cDov +# L2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZEc0UlNBNDA5NlNIQTI1 +# NlRpbWVTdGFtcGluZ0NBLmNybDCBkAYIKwYBBQUHAQEEgYMwgYAwJAYIKwYBBQUH +# MAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBYBggrBgEFBQcwAoZMaHR0cDov +# L2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZEc0UlNBNDA5NlNI +# QTI1NlRpbWVTdGFtcGluZ0NBLmNydDANBgkqhkiG9w0BAQsFAAOCAgEAgRrW3qCp +# tZgXvHCNT4o8aJzYJf/LLOTN6l0ikuyMIgKpuM+AqNnn48XtJoKKcS8Y3U623mzX +# 4WCcK+3tPUiOuGu6fF29wmE3aEl3o+uQqhLXJ4Xzjh6S2sJAOJ9dyKAuJXglnSoF +# eoQpmLZXeY/bJlYrsPOnvTcM2Jh2T1a5UsK2nTipgedtQVyMadG5K8TGe8+c+nji +# kxp2oml101DkRBK+IA2eqUTQ+OVJdwhaIcW0z5iVGlS6ubzBaRm6zxbygzc0brBB +# Jt3eWpdPM43UjXd9dUWhpVgmagNF3tlQtVCMr1a9TMXhRsUo063nQwBw3syYnhmJ +# A+rUkTfvTVLzyWAhxFZH7doRS4wyw4jmWOK22z75X7BC1o/jF5HRqsBV44a/rCcs +# QdCaM0qoNtS5cpZ+l3k4SF/Kwtw9Mt911jZnWon49qfH5U81PAC9vpwqbHkB3NpE +# 5jreODsHXjlY9HxzMVWggBHLFAx+rrz+pOt5Zapo1iLKO+uagjVXKBbLafIymrLS +# 2Dq4sUaGa7oX/cR3bBVsrquvczroSUa31X/MtjjA2Owc9bahuEMs305MfR5ocMB3 +# CtQC4Fxguyj/OOVSWtasFyIjTvTs0xf7UGv/B3cfcZdEQcm4RtNsMnxYL2dHZeUb +# c7aZ+WssBkbvQR7w8F/g29mtkIBEr4AQQYowggauMIIElqADAgECAhAHNje3JFR8 +# 2Ees/ShmKl5bMA0GCSqGSIb3DQEBCwUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQK +# EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNV +# BAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBHNDAeFw0yMjAzMjMwMDAwMDBaFw0z +# NzAzMjIyMzU5NTlaMGMxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwg +# SW5jLjE7MDkGA1UEAxMyRGlnaUNlcnQgVHJ1c3RlZCBHNCBSU0E0MDk2IFNIQTI1 +# NiBUaW1lU3RhbXBpbmcgQ0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC +# AQDGhjUGSbPBPXJJUVXHJQPE8pE3qZdRodbSg9GeTKJtoLDMg/la9hGhRBVCX6SI +# 82j6ffOciQt/nR+eDzMfUBMLJnOWbfhXqAJ9/UO0hNoR8XOxs+4rgISKIhjf69o9 +# xBd/qxkrPkLcZ47qUT3w1lbU5ygt69OxtXXnHwZljZQp09nsad/ZkIdGAHvbREGJ +# 3HxqV3rwN3mfXazL6IRktFLydkf3YYMZ3V+0VAshaG43IbtArF+y3kp9zvU5Emfv +# DqVjbOSmxR3NNg1c1eYbqMFkdECnwHLFuk4fsbVYTXn+149zk6wsOeKlSNbwsDET +# qVcplicu9Yemj052FVUmcJgmf6AaRyBD40NjgHt1biclkJg6OBGz9vae5jtb7IHe +# IhTZgirHkr+g3uM+onP65x9abJTyUpURK1h0QCirc0PO30qhHGs4xSnzyqqWc0Jo +# n7ZGs506o9UD4L/wojzKQtwYSH8UNM/STKvvmz3+DrhkKvp1KCRB7UK/BZxmSVJQ +# 9FHzNklNiyDSLFc1eSuo80VgvCONWPfcYd6T/jnA+bIwpUzX6ZhKWD7TA4j+s4/T +# Xkt2ElGTyYwMO1uKIqjBJgj5FBASA31fI7tk42PgpuE+9sJ0sj8eCXbsq11GdeJg +# o1gJASgADoRU7s7pXcheMBK9Rp6103a50g5rmQzSM7TNsQIDAQABo4IBXTCCAVkw +# EgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUuhbZbU2FL3MpdpovdYxqII+e +# yG8wHwYDVR0jBBgwFoAU7NfjgtJxXWRM3y5nP+e6mK4cD08wDgYDVR0PAQH/BAQD +# AgGGMBMGA1UdJQQMMAoGCCsGAQUFBwMIMHcGCCsGAQUFBwEBBGswaTAkBggrBgEF +# BQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEEGCCsGAQUFBzAChjVodHRw +# Oi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNy +# dDBDBgNVHR8EPDA6MDigNqA0hjJodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGln +# aUNlcnRUcnVzdGVkUm9vdEc0LmNybDAgBgNVHSAEGTAXMAgGBmeBDAEEAjALBglg +# hkgBhv1sBwEwDQYJKoZIhvcNAQELBQADggIBAH1ZjsCTtm+YqUQiAX5m1tghQuGw +# GC4QTRPPMFPOvxj7x1Bd4ksp+3CKDaopafxpwc8dB+k+YMjYC+VcW9dth/qEICU0 +# MWfNthKWb8RQTGIdDAiCqBa9qVbPFXONASIlzpVpP0d3+3J0FNf/q0+KLHqrhc1D +# X+1gtqpPkWaeLJ7giqzl/Yy8ZCaHbJK9nXzQcAp876i8dU+6WvepELJd6f8oVInw +# 1YpxdmXazPByoyP6wCeCRK6ZJxurJB4mwbfeKuv2nrF5mYGjVoarCkXJ38SNoOeY +# +/umnXKvxMfBwWpx2cYTgAnEtp/Nh4cku0+jSbl3ZpHxcpzpSwJSpzd+k1OsOx0I +# SQ+UzTl63f8lY5knLD0/a6fxZsNBzU+2QJshIUDQtxMkzdwdeDrknq3lNHGS1yZr +# 5Dhzq6YBT70/O3itTK37xJV77QpfMzmHQXh6OOmc4d0j/R0o08f56PGYX/sr2H7y +# Rp11LB4nLCbbbxV7HhmLNriT1ObyF5lZynDwN7+YAN8gFk8n+2BnFqFmut1VwDop +# hrCYoCvtlUG3OtUVmDG0YgkPCr2B2RP+v6TR81fZvAT6gt4y3wSJ8ADNXcL50CN/ +# AAvkdgIm2fBldkKmKYcJRyvmfxqkhQ/8mJb2VVQrH4D6wPIOK+XW+6kvRBVK5xMO +# Hds3OBqhK/bt1nz8MIIFjTCCBHWgAwIBAgIQDpsYjvnQLefv21DiCEAYWjANBgkq +# hkiG9w0BAQwFADBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5j +# MRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBB +# c3N1cmVkIElEIFJvb3QgQ0EwHhcNMjIwODAxMDAwMDAwWhcNMzExMTA5MjM1OTU5 +# WjBiMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQL +# ExB3d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJv +# b3QgRzQwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC/5pBzaN675F1K +# PDAiMGkz7MKnJS7JIT3yithZwuEppz1Yq3aaza57G4QNxDAf8xukOBbrVsaXbR2r +# snnyyhHS5F/WBTxSD1Ifxp4VpX6+n6lXFllVcq9ok3DCsrp1mWpzMpTREEQQLt+C +# 8weE5nQ7bXHiLQwb7iDVySAdYyktzuxeTsiT+CFhmzTrBcZe7FsavOvJz82sNEBf +# sXpm7nfISKhmV1efVFiODCu3T6cw2Vbuyntd463JT17lNecxy9qTXtyOj4DatpGY +# QJB5w3jHtrHEtWoYOAMQjdjUN6QuBX2I9YI+EJFwq1WCQTLX2wRzKm6RAXwhTNS8 +# rhsDdV14Ztk6MUSaM0C/CNdaSaTC5qmgZ92kJ7yhTzm1EVgX9yRcRo9k98FpiHaY +# dj1ZXUJ2h4mXaXpI8OCiEhtmmnTK3kse5w5jrubU75KSOp493ADkRSWJtppEGSt+ +# wJS00mFt6zPZxd9LBADMfRyVw4/3IbKyEbe7f/LVjHAsQWCqsWMYRJUadmJ+9oCw +# ++hkpjPRiQfhvbfmQ6QYuKZ3AeEPlAwhHbJUKSWJbOUOUlFHdL4mrLZBdd56rF+N +# P8m800ERElvlEFDrMcXKchYiCd98THU/Y+whX8QgUWtvsauGi0/C1kVfnSD8oR7F +# wI+isX4KJpn15GkvmB0t9dmpsh3lGwIDAQABo4IBOjCCATYwDwYDVR0TAQH/BAUw +# AwEB/zAdBgNVHQ4EFgQU7NfjgtJxXWRM3y5nP+e6mK4cD08wHwYDVR0jBBgwFoAU +# Reuir/SSy4IxLVGLp6chnfNtyA8wDgYDVR0PAQH/BAQDAgGGMHkGCCsGAQUFBwEB +# BG0wazAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEMGCCsG +# AQUFBzAChjdodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRBc3N1 +# cmVkSURSb290Q0EuY3J0MEUGA1UdHwQ+MDwwOqA4oDaGNGh0dHA6Ly9jcmwzLmRp +# Z2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcmwwEQYDVR0gBAow +# CDAGBgRVHSAAMA0GCSqGSIb3DQEBDAUAA4IBAQBwoL9DXFXnOF+go3QbPbYW1/e/ +# Vwe9mqyhhyzshV6pGrsi+IcaaVQi7aSId229GhT0E0p6Ly23OO/0/4C5+KH38nLe +# JLxSA8hO0Cre+i1Wz/n096wwepqLsl7Uz9FDRJtDIeuWcqFItJnLnU+nBgMTdydE +# 1Od/6Fmo8L8vC6bp8jQ87PcDx4eo0kxAGTVGamlUsLihVo7spNU96LHc/RzY9Hda +# XFSMb++hUD38dglohJ9vytsgjTVgHAIDyyCwrFigDkBjxZgiwbJZ9VVrzyerbHbO +# byMt9H5xaiNrIv8SuFQtJ37YOtnwtoeW/VvRXKwYw02fc7cBqZ9Xql4o4rmUMYID +# djCCA3ICAQEwdzBjMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIElu +# Yy4xOzA5BgNVBAMTMkRpZ2lDZXJ0IFRydXN0ZWQgRzQgUlNBNDA5NiBTSEEyNTYg +# VGltZVN0YW1waW5nIENBAhAFRK/zlJ0IOaa/2z9f5WEWMA0GCWCGSAFlAwQCAQUA +# oIHRMBoGCSqGSIb3DQEJAzENBgsqhkiG9w0BCRABBDAcBgkqhkiG9w0BCQUxDxcN +# MjQwNjA2MTk1MDE0WjArBgsqhkiG9w0BCRACDDEcMBowGDAWBBRm8CsywsLJD4Jd +# zqqKycZPGZzPQDAvBgkqhkiG9w0BCQQxIgQgUvswt0fWRoofHUAuTE0/8V9tLmHP +# zr/l2RTobZjBdqYwNwYLKoZIhvcNAQkQAi8xKDAmMCQwIgQg0vbkbe10IszR1EBX +# aEE2b4KK2lWarjMWr00amtQMeCgwDQYJKoZIhvcNAQEBBQAEggIAc7/uG/S8kf0i +# 2kaDQkE8NSfiXCYfN7z/2sgi6RNrkipvs/KTWfEKuMbhu9qWjjusZFgywn/IrZqw +# td4Js1kmaN+HJ02t/HXYUCr+KTJye4mDaBGvaXXHllCqsK7bhsJxJYE0uYiL03MP +# g64jyu9WdJD3N26MW/DkO6HTVhYzRzjafbAKbrr8KCvaFan1KZERzYwbA8XVjm88 +# HOodLCA9h+91Iqdc+uSz3Sg9/+Ns4zCp4BonvnsPYTlWTitiB5cpfPe/v4lBvCNu +# x0ha6whvKMdRLZJgXsiDXo2NwwB55kkWEBwD3a1RnBJQmyJxFEGpSXOrhmdcEWPg +# fjoHVIfowKBrIgINdWJbvIu+pLzQRMkVhuJzB32xpiZBIvbzkPETYQMOmKIu40I9 +# 5EAL0xNakPxYiT3nTkncn6woLOhiOXFm7crE+gO4IzDNauYuT9Vfe36K1CqtuYSy +# JesLIey9Z81OQqOo6n2/lW110MKMEV2PkPU7YW/bYO2uKsZ3OAjUWr63nMT+M2wk +# VdUAcqm0QdZsELY75Q3ekRxHje/B9ePP4Q4RMQGOZvmgqdtEeFhsmRwufR4fzfqx +# WMttmOHelTd8Sc0sfA9B+1dxtiC9GFn3de5/o+T2s/jQn6eNp2hvlCqGV0iFzSQp +# InPTBa9Na/+5UeXZ3NBWRvarfZ62TVM= +# SIG # End signature block diff --git a/venv/Scripts/activate b/venv/Scripts/activate new file mode 100644 index 0000000..227e911 --- /dev/null +++ b/venv/Scripts/activate @@ -0,0 +1,70 @@ +# This file must be used with "source bin/activate" *from bash* +# You cannot run it directly + +deactivate () { + # reset old environment variables + if [ -n "${_OLD_VIRTUAL_PATH:-}" ] ; then + PATH="${_OLD_VIRTUAL_PATH:-}" + export PATH + unset _OLD_VIRTUAL_PATH + fi + if [ -n "${_OLD_VIRTUAL_PYTHONHOME:-}" ] ; then + PYTHONHOME="${_OLD_VIRTUAL_PYTHONHOME:-}" + export PYTHONHOME + unset _OLD_VIRTUAL_PYTHONHOME + fi + + # Call hash to forget past commands. Without forgetting + # past commands the $PATH changes we made may not be respected + hash -r 2> /dev/null + + if [ -n "${_OLD_VIRTUAL_PS1:-}" ] ; then + PS1="${_OLD_VIRTUAL_PS1:-}" + export PS1 + unset _OLD_VIRTUAL_PS1 + fi + + unset VIRTUAL_ENV + unset VIRTUAL_ENV_PROMPT + if [ ! "${1:-}" = "nondestructive" ] ; then + # Self destruct! + unset -f deactivate + fi +} + +# unset irrelevant variables +deactivate nondestructive + +# on Windows, a path can contain colons and backslashes and has to be converted: +if [ "${OSTYPE:-}" = "cygwin" ] || [ "${OSTYPE:-}" = "msys" ] ; then + # transform D:\path\to\venv to /d/path/to/venv on MSYS + # and to /cygdrive/d/path/to/venv on Cygwin + export VIRTUAL_ENV=$(cygpath "D:\000PY\mate-academy\py-task-guideline\venv") +else + # use the path as-is + export VIRTUAL_ENV="D:\000PY\mate-academy\py-task-guideline\venv" +fi + +_OLD_VIRTUAL_PATH="$PATH" +PATH="$VIRTUAL_ENV/Scripts:$PATH" +export PATH + +# unset PYTHONHOME if set +# this will fail if PYTHONHOME is set to the empty string (which is bad anyway) +# could use `if (set -u; : $PYTHONHOME) ;` in bash +if [ -n "${PYTHONHOME:-}" ] ; then + _OLD_VIRTUAL_PYTHONHOME="${PYTHONHOME:-}" + unset PYTHONHOME +fi + +if [ -z "${VIRTUAL_ENV_DISABLE_PROMPT:-}" ] ; then + _OLD_VIRTUAL_PS1="${PS1:-}" + PS1="(venv) ${PS1:-}" + export PS1 + VIRTUAL_ENV_PROMPT="(venv) " + export VIRTUAL_ENV_PROMPT +fi + +# Call hash to forget past commands. Without forgetting +# past commands the $PATH changes we made may not be respected +hash -r 2> /dev/null diff --git a/venv/Scripts/activate.bat b/venv/Scripts/activate.bat new file mode 100644 index 0000000..a0c91d8 --- /dev/null +++ b/venv/Scripts/activate.bat @@ -0,0 +1,34 @@ +@echo off + +rem This file is UTF-8 encoded, so we need to update the current code page while executing it +for /f "tokens=2 delims=:." %%a in ('"%SystemRoot%\System32\chcp.com"') do ( + set _OLD_CODEPAGE=%%a +) +if defined _OLD_CODEPAGE ( + "%SystemRoot%\System32\chcp.com" 65001 > nul +) + +set VIRTUAL_ENV=D:\000PY\mate-academy\py-task-guideline\venv + +if not defined PROMPT set PROMPT=$P$G + +if defined _OLD_VIRTUAL_PROMPT set PROMPT=%_OLD_VIRTUAL_PROMPT% +if defined _OLD_VIRTUAL_PYTHONHOME set PYTHONHOME=%_OLD_VIRTUAL_PYTHONHOME% + +set _OLD_VIRTUAL_PROMPT=%PROMPT% +set PROMPT=(venv) %PROMPT% + +if defined PYTHONHOME set _OLD_VIRTUAL_PYTHONHOME=%PYTHONHOME% +set PYTHONHOME= + +if defined _OLD_VIRTUAL_PATH set PATH=%_OLD_VIRTUAL_PATH% +if not defined _OLD_VIRTUAL_PATH set _OLD_VIRTUAL_PATH=%PATH% + +set PATH=%VIRTUAL_ENV%\Scripts;%PATH% +set VIRTUAL_ENV_PROMPT=(venv) + +:END +if defined _OLD_CODEPAGE ( + "%SystemRoot%\System32\chcp.com" %_OLD_CODEPAGE% > nul + set _OLD_CODEPAGE= +) diff --git a/venv/Scripts/coverage-3.12.exe b/venv/Scripts/coverage-3.12.exe new file mode 100644 index 0000000000000000000000000000000000000000..dc07cfa8de764ec8f315f8d8c9cba49d651be744 GIT binary patch literal 108416 zcmeFadw5jU)%ZWjWXKQ_P7p@IO-Bic#!G0tBo5RJ%;*`JC{}2xf}+8Qib}(bU_}i* zNt@v~ed)#4zP;$%+PC)dzP-K@u*HN(5-vi(8(ykWyqs}B0W}HN^ZTrQW|Da6`@GNh z?;nrOIeVXdS$plZ*IsMwwRUQ*Tjz4ST&_I+w{4fJg{Suk zDk#k~{i~yk?|JX1Bd28lkG=4tDesa#KJ3?1I@I&=Dc@7ibyGgz`N6)QPkD>ydq35t zw5a^YGUb1mdHz5>zj9mcQfc#FjbLurNVL)nYxs88p%GSZYD=wU2mVCNzLw{@99Q)S$;kf8bu9yca(9kvVm9ml^vrR!I-q`G>GNZ^tcvmFj1Tw`fDZD% z5W|pvewS(+{hSy`MGklppb3cC_!< z@h|$MW%{fb(kD6pOP~L^oj#w3zJ~Vs2kG-#R!FALiJ3n2#KKaqo`{tee@!>``%TYZ zAvWDSs+)%@UX7YtqsdvvwN2d-bF206snTti-qaeKWO__hZf7u%6VXC1N9?vp8HGbt z$J5=q87r;S&34^f$e4|1{5Q7m80e=&PpmHW&kxQE&JTVy_%+?!PrubsGZjsG&H_mA zQ+};HYAVAOZ$}fiR9ee5mn&%QXlmtKAw{$wwpraLZCf`f17340_E;ehEotl68O}?z z_Fyo%={Uuj?4YI}4_CCBFIkf)7FE?&m*#BB1OGwurHJ`#$n3Cu6PQBtS>5cm-c_yd zm7$&vBt6p082K;-_NUj{k+KuI`&jBbOy5(mhdgt;_4`wte(4luajXgG4i5JF>$9DH zLuPx#d`UNVTE7`D<#$S>tLTmKF}kZpFmlFe?$sV{v-Y20jP$OX&jnkAUs(V7XVtyb zD?14U)*?`&hGB*eDs)t|y2JbRvVO)oJ=15@?4VCZW>wIq(@~Mrk@WIydI@Ul!>+o3 z=M=Kzo*MI=be*)8{ISB{9>(!J__N-a=8R&n#W%-gTYRcuDCpB^^s3~-GP@@5&-(G& zdQS_V>w;D8SV2wM8)U9HoOaik`_z>Ep^Rpe3rnjb<}(rV`tpdmg4g@>h`BF#WAKLH zqTs?sEDwi<=6_WPwY&oS9!h@ge4(br)-Q{|OY*#YAspuHyx;~|kASS3FIH@oGSl?L zvQoe8yKukD)zqprHiFKlW%;G=hwx4l;FI%8m&(#zU|j&_bW@ThNpr9D0V}xa)%aIb zI$i2CA2mPU{0nJmK0dxe)dY-`z>ln($ z;r!UXuLDDi42|Zd3Erx&m8GqlFWbIX0V<*Gn6lVNq%gD>gw}da}r}ZQB~ns?p8uy4i0%1Ti$Vt|~OUth4=+yEmPu8{3(w zUDkd@?w?`_J9HBkx&ZF8v{+9phcT@3J8VI~wN7Ez)oJS6^dhb2N;;{RTXB`K*E$64 z3rDqRtY&&*}9yq2oUcvD7K)=@bWqC1X%l0jk)W<5-WBYC(#rn4H5)gp#eHMmwlLJq=^%|*gMQ*pq4VV(QhHA4CGj<;!d8i*#Z8CaN#*>VcCnj~;kkeUa{LUoKxFCaoQ) z(Lz++&x3Lwz;=6UnhwM!MvN17>{Qmb?dwgsTmzkLB~jD#wiGz73hc0bFE|C9KA#|= zH}%FQ>c&Y5z*TJD-<$$Y*WZx>5NNe-E-TfAt1!)%Wc@I;ZuNwxDGGasDIMyUNiVvG zq;Q70PYHcLO=Xgv2698@cJrkun-^>P2}|fMHlm7xaZmE<{&cQtb`{N9zj0bRmpW^T zzQV7oTs0ENHe&mxQ6DI7qd0SU4;3o*2qRd`X1>(=ew})X5Dx zx$lyzZM^emtdsbk^u+xwdSX$lp7h*2CkHCqDohShL)V4hM9k+UQLP(GN-H7!C8gyq zex`xuPQ(!g4}S>0r+CyH+xIAMP9Z&+?BT1!*kA<}dqRn*FwJPGe}l-sw(lGYN1b8} zWQQjQN`9tdtF?#aqMN?wu4E3)qGxzOhwr*vb;kX_%&U*-=KLr0raiGc^x8|=Wqt`N z?L0luR(~BF;DS@~yKDN7|*TJkj*-B%s1{65$`jY_(C#P&^rVi0?Ro4iaFbR)Z2NLxS0 zTL;%Kt22(A8JiL`U$i!iR&zLxx^E%H=*c-=+h@sisygu-_#m4J4LQqB?~vXvP4@yQo0-^oki(PiH+=FZl}&W)S-qI zk>W;2Zl-vl6rbe4X6feZb)l-Mv2oh^5t8q5@(Y-SPoUZ;N<5Tdl!h|=x!1}5)E;}=RcAXJ8(<$^13IV==^rU>wwq$hX3V4iuA0>h< zuxK^)myr=p7a)oeZ+g4u^9(OmpFl8J@{{UJfy=DjAf8lTTD00iSF3Kb9|GdM-PQp)0<* zZkW*V-TPpIXEKDks>&FQ?qoV&Tfa*;TJyB^yJa8xcch+*-cYj6E7HdBX!5)TIXSNM z4C2L57KVd0rioelfI{ELMrb&Y}?h%mk5iSTXrmJ zwlk6qsS{}3<}Uc!G}Wr;Tek1Tym8$SrWokvCzU(FVIAWTEa1pwE zBJ6JdS@$4RFBV*~g^Eo9MAFafx2rt|uRsR%xpNVyj8!g>2u0v=>eO zS~4nHBgR%cVxB-_OwP@%JN(CpY3qHvqsbt-TUGivY2Dr$b+=`6PJSkbWF)!Jn=iZJ zMt}mOG~-m{)L*SV+yRH!c@XR%)K^BqVRh zq&wib)2#d0V3BD*|F5o2J6$vbdJGh`O-30SrMI;e*Y&m8c0Bi^cD-$Daq1haK*i4o zS^0dLE!U;Du-W5i&*6##L30bjy7q7@lQPyCc8<%{>0)|vQlrFG_D_+v^1uh+p+bhA?!)dFEqi$(hoT?=hJt20DQXmOiJ``9LY)@=HE zO1esvSjV70vmITir9t{Om5D&<%?UTa#`5Sp-x@^?6JCK@(Y_-+ye_agHcB_zSUEYe zay}#@o~N5_?G>%q2t<~g3s!Y+G*Mj=P3Zn>mA2=HCm`lzap|)*f|(31R{)36WvAyz zfea$wK&B|2YxO{n>twI{fk3f0YVK4T;XDy#cUe=*$V6#=30zz**pkdJOUUdHcyGKx z={=%tU83}-sM&@LFz=EaBy8m5*VS4ZYhB<>lI{BnIk4cD&H_E|%!spiL(( z$1W0V$;KX^P(?<}XYHqoplpQo7H>!m)d{bdPaLde+h7(tf+ZB(6MxWZnoX6&>|)(q z*DB~wjMmL&u~F-ZIbJ>BJ5ZM6ik)gUbdlBM`Quqove#M~lf*ebB4nBg}NN8q8e!? zVj>HOMJZ@LQzOdvHUSih8gCt%IxvyHLmO^Ea(*!Nd-Zuw>`f87{SkAwbrcIp6hiff zt7^x@FVoBVwDl9eTxT2$))(-5-O9W=qunp;*yvYT{VJ=~FI-x;pN&=5ArA%W0()Z} z=?f87g#Y@j2_ct@T|gzY^?R)mq?NdksZ}7gJW^{18>hCuy{s)%iDWGzC?-DRKLl?l zlnO5zQf3*!v6nJ;)xm`Sjm!6zf=o%-07p#e5?cL}gBtB`Nq!dTtt@<7#(o8m8xm*XOvN65AL(=C_D} zJM9UyYteSSwriu8{DkKl6tSk&09e8kMrjh@N|SS;@9l|6^W@_Q=i{`@$NUzI6|VF> zN{Rev95oVSa&%)ew#+uKZf{3cFg?f64ASokLt$^COgO2#BW71L>H7~o2Zg;=Z|nCM zZ=N18^ET^uY+VpF$K*teqc&2xaTF!LhIKrwGne_WBX+B_9vi@rt2GKHy|kQxSUJ18@{fEswY{>va~$3%JGyYfr29k%@bck16c zdf9Hh?|r@PC`@3R-j=#7868z@m3)O|u0`Iw|bd&(6~U$UMGD@Vncn>Lm}{NqU9US&{gYu`~lU+m1n zi1g$#vC1#v|9B;ObTzhRor!#90$^5b(Gy`buihHrRfjV>-l^6#?Dg3lZ}@PRD|I(> zVcp1Kiyr8xABHMWk$xp&hFzvUhIKbDi1339ve8Ac5ON73NDM}^^I8O?+8zk+GVA0S zG|7G=o9JQQO;-x!z=zz5c@^<{-AWi)tG`b65v40t#CwnzKA}>?+z|q4`eNlNfRXZK%L4$WHQ)8Sgo0 zwE~@9)+4fUIf8fW?9TihJ6Hgttrta)MqB{FTBqxu|CDLzEKWn{Cn*>&wx$DtvzSvC z(4Jr-g8~qe!NL-;BVhBlx}Y;!It5;VT~^q_HdZcH!a^(MA3%zpy!zmpD(NfkvF=9= z6p^lmDSFnrRVn4npverH%%I5(CT}SgTNGB)0sCY%@`7%@lG#4Gt*2;3c3;0E8(QyS zoo-l-h2)DEIh-3t!@^Gefe~>Aq|Sbf{goW=Op7FDAB-5amdpAhatG_BQh1V>p|DF2 zoM~XblmiX(kl0U_veatKBQ+uz9@Z1{N|y`0j<11Sd^JtI@w2S`$mW?%;MWLc4%=HL zi!p2d7Nf9k{=Kw;xt19k$vh+UMEX9C2D?jRP0wn3ihvj zIKqjR_QyB+t|%#l=^@PkY$HlM{<4z$Jve9n{#ZUhYv#%_q#uJnen z7S7e0{d|oCJ_u>EJ_(yUqk*m3cisoGsENRi9?F=l*A~&-*(<$4vm*-sUaFT_dJdnX zrOQM7ERMPl>SbN2|4`NV9yZ$|0jqv#7_|5qM&SK>FdA$Qn}>sahte?IEg|!hNZ-Lw z+2M47yawJ6YgZhmd7`)o7cpN%77HvCf^&@h2FBhy;L2rI>K+Cp6&?pq zlFhyiSR(126>L@rL1c*79q1?uBeI5<%2ZP3K!*8bJ8n5Vkdy&9Re{a#rI- z6fv$Y@#|&(1pg>!eIKW$IeEqD_akO!YCNey`?q5Uh$a^MgG!T#n1>V}I*O@Oh-I-5 z%k{Du%Iw6?)MXzjh?<)@`1%M|Z2fN100q^u)YBKp;(8NX!a7BpNWL}bB60|{!@3IM z&!_-j!}^5^fVs3)8n2d}7M6&L95t6HGcO7O>k8tJiY2gy{mtC0V*s z;mM4hWAvYlP0?$+)i!p-gT`AH%yAiSovz=pXFBCU*-y1#y_wmwf!PgMrEDEyp_Y+h-3$ZW$Ny$8H)g+M&odOm3D+qCuDCyTVF4s8_v zmEyLRLz)cEXCoqszT`H8*!|T3k)9}efv(zxR?xmMPtJ#z>B&Eo77PE!jE`0XJbxM^ zJEbz?Lu5g--#l!-Y#gzXP3G6p>XOps?99>9SjC=T%MY0{>#J9bVPGK(CmAlr@LDVu zdtE8Cwy$lsu#8`O8L={lK%5}c`pb6GjOmh$5gX((WMNF8jU#kU?6HQLb+0+w?hE$3nE@wxIvFA6~zB7QMVyoEeHQuBH-S!>tRw89F zyIi51ALX;4mfyl>Gbw7NUa`Y^`9s-NepV{j;n;E-$Ceyj?qimR?nQpJ7Zt@YCfL5$ zX%(74|FeDDa8Ol;N-078H81eqW|LX(_9$cc`%a*!#=7{V2=)|lNG5a40)v6g4t z01XUUv68UZ2|@vkl?ceW7{YVw!nCy? z+sAnJ?mvd`Ab`J#GpRgV_N#doE}<~&Z?VHb%c3L;ua)NW2qzfhmeh>}dH zGKiE|U&0iVSyyQ$NO;+GkhAqI3{1v-UXl6k&ogShm<+H}bDWf8ZLbv`!7=F`^V*WW z%|fH`g0dA}vmj?dt{;}&QQW)P9h)H{A4EQ&PP7V>>J53l4KOcs^mIW( zWkEdG-lC&N1l;w9;87FIEh#42)wpNXA?u;BStwK2f%x9dIa=c%`6v*^^D7Rdeo3P2 zK9dB;uN>7oyTltCA%$60W`E3W-dBpg zuqcq@x{}^i&v~(2yR)n>8M=s-@@eAy%xR>v4&Y%h*z7^|kj=+ut-*SgnXpUQ2Za%i zw_32)!m77h`9S6v$7W)#c5Gu%xh%>rSYMFAD@|Kh-5MzR0ebF=8}-^F_#pg>cMe^Q z_fFTrqJD?X&Jg+pQE^7T9S;~YZ`N{LIq@lM=%?CSV`D_iRT3c{J=yaikxU5%rHT=TI9ln9_p;9*QY6sX)@dJei;QU6QC|w1dx9PPU z-k*1jcMjN$eZXl0=c@we30H5Z#G4Zf18#{O`?4|fubhbI#LpT6?u0J@S5*J&gl|g| zx>4w6bp!F}L5Qb)5yTF=Q~b_2auNe$u2af-1--x-Y8ugJ)$~A7xqyDQUb~z9yjp?2 zS$2CCh3xpcnb+1EDhBdlycVY?TH-GQhOBi1Em;xS%mih!zz5d%5ZTK)kgI(;YVM1) z9Y?6R=*3Ee3NQqA=9m}0tBfPY>WV^F{KDkb!>u=FvBx{<@$4HF#Ty?(D_|c16@7ar z?3sMj4pkIxD3B@pYY^(UW7-_E@LkG|E4F$T>^}02mQUF3kyHzn_+N+p{xB`ffEMeA9vW5-D%{ zZltI*4Xan_uaQoJoSn85x~zjwdZGe`c|L&8DFe`!Uzz7`w0>!xulJ>+=37i-p5mR> zWl?vJ+1b|P3AuYhVyI7#LAPEYZ87i$tRpmE}@el^F1lN0erixJ1-N#3v0fp0!puf z11^VLsS9qh<=8A zl(KovC21r`^>K0LV;-uDR<&qv-K@mIx|7<^+mo|TDsK^_F=k^064`x9BFi|CeU^vI zA`v->wGlB>5s}S`2Vld*+LS4GWdW#Z9=Ld+EhF-ng5iU)X7A68`i# zO|AEyO~DJK*d*(2vK_TGJ;J(KCFF$1nt-h(v%kz8V%#2jMxD`gWt|!-@k5${77Q@!{4z;ze=7&BScC z{l96Ke7GeU{#P5P(1-)>pb!x>_limI(??L33;=E&UU`S^Xg(o6V~Xzp2+b869oyFB~+oK91m(zDG}-Ce|yro;clXhx0fm zqA!a1;w8|CgOIS{tHtHPM)Qnv&@IQrVjZ>Cz6}8;hEX6s#`+#jXAT>_&8rE)U3h@u(3Rj2wHPF8HLr_+u|u2h!@v|soMqnSEk8Zd`9UErc zRN_h>v@U-yBXM8Ej^Rk$+sR6^P!=M|4(TT&#@8NU-8`?Hjo1~wjxi#DFXslCbHj#H zR5!NB>1Vtka3nsdw|a3-Y^?Qbif>?ajCQZ}h|~?V$4;Z2hvePt!VjWV5kP_Mdzd#2 z(Ya9OE~}OG95vq%MZN6^iVy-|(zl&p4c#oK!g~#g9ul0wCtz5||XBmlcb|@y+~5^oMA2 z%2&t|Z30b#v!su;P0>oP@n%l!68gTFk*t&4-cTiC(g?CTh0XM*M_NA`XrI~P!(S-N zL`<-L&IbV?K2X3qpYwnLW)JqoQsvmwRaiiIOAWlUuFCW7CR}XuDqc-j>a`x<)1Wa~ zw1+(1-L|GuLWkn}HjH3W>Zkjq4e-!WA;hn0iSIXW`S*t~{JgUpYShtg%LoE=slzv~<=K*WA*ElMAxu<+e5ER>PXppG$|uZeA(Temu%&q(p;3AFN2!kq zm=?vfxfpqDEN!LF)Xm0H1wg{HMEXo-l13}ryyuWqH$7J>Xgp69ORBMSo%EOR{GE@T zp6`=69Ftb3=ONylwdwgfFVgK&D$mcnFSmVb{~?FB$0_H`z~O7eOlSLUCm#&_o;kIB z^GO&pU!)Lg-zm3^a<;FL4;!T`wb1X9I%}R0*ioufT+j91NaBu?NMeOwVtj_4-Bj0@ z_j+s0>1Gh!;oi!cvc4Mg&8Yc4=Cmj3w59_z5~=-$9!bpUA~dL*qwByWnz05DbT{~4 z*jZ@K?vDlzYTtT-qUP-5@^1W$cjLZ1m)7`wc?;yk#>sw)Ni$-;5OH_f-AMb*3BElL zTXVmwcEz1Nab&8Q-#V9uW2Z6VdwH||2KhpVBR4w8!{_^EvduYpj=@m1wadC|nCyj2 zt$A%;w3fp&nPJJ87ID86l?_lyq<-5M`#ZFGH^n*bFxrb{B4*!>glHD=IX zaR4E?rmXV`e=Jb3r)umy9O_=}HG_<;wLag>;c-u)&Cx(xabWC&VP!^jmFM&Ib z$EM)|j1Ueju0pu}b54-q=pis$~y&T*+xHtN5ij^Dv z^%7mNlKsbrMJuxz??mDQn__!^I>*gYDhiq>gCh>6y-yP!!np!os_nT!v)geY)f(H$ zMdxVz82saUVjQ{l!Fyx32g`P8jl0P*QX^tlU_Sb?kt&IuWuyvXIfW6 zvj(<2h5p+D2H`EwSwH=TECv*ISR}=U4K0jI?@X;}rSnDnja37_hg1U|)xdV^hSx;N zR_l)tW>JcPb8F@5C~uO{c@SQX_Wc-vx12+X_zdyQjX9DVg;djzhq7W0o z))<;YTY1Kqwi$lJ9G%8d#&=Y2g-5J9EDiLvQu;DVkGayNG;o{qwO{JmzR6Uh$UG@x zPCO=Jtf)bg*6_lp#3+w^Tg=a7c|p*fGtm(jE${gPmO7HD77SR?ytQ3_Bxr`(@-qAT zWfSOxaSdnVed(w}=&i-FC`!Pi=?<=yrTgx#ws#DU@R`1IyXR+k0R7~IY6mXQnIYJ=|Dqf4+{O?83Q*D35 zm~q?{FH`;v)-R{BFDCMi3*t-k>{7fQ)8nw?9TyWqG3`Ursw{KR7s%pMMe3iM)dT*M`1?|}%AZgc@ zX30+IPfbP!7X!AEjBUyvWF0|-nESBQh0Mtj(=rdU9mNVG#;RgmWP&-P(zBuAracc- zp+(j}^q7=iuyEi?+-C&NiI3TU^)U0@n#|Xx-UoNc*6NmU3HqR;Wl%dL zkIaY`kZ}eU*h+@_w{SA-$LNPRs?I`9&yRXRk~$gghBqUHqL4xmtMtVD2F!n`DBU&Y zA@L!Y3w6XoW)F{rN=O!R5%FX>|1Ypcy+BCeYqX6PttY}QV(d8A+D=AhCvAj2I9Ci+ zE_xz1LN~*Y8IN@_s1s-}DbcJjI5vpO#CDDjrv=T!AxN@1Y#t5bfti^9CyoyfXpL_T z2V8Sei{e7KzA*ct9Fu(Nld9;CL z?d=gOO0=h4Y+4Jb!Gh3(cScOi?2L8L!@ zXRz-XiI$JM!z1>gk%aITI}Ha2`#~+lD$VpAZrrCeDp|VeRi;hXLX+MU&wulyCi{V@ zp~_QZXJ}92zB_-Nbp#$k+W_m_M`OPZC+5?&W-o>zKXw6;Mw zPZVMo6>O;(y{(rJ))j>Jj--v{g0^&C9d>R#xu`p+I!;{+20Fvd@~tlHPH#Z}#D#80 zwJKsBYO=M&SD3rt(@+KWTkw{8Sk2`v+CyWht11NA9@xI&HVQx{ji8>XzDsLtBV)te zncQFSH2RmvZZP^+XpO58RW`&kpI(%5tDHnrJ71E)Kc>S>es<7(F(N@%94gfc zt}u%Qr8lQ*gBzd@RpP2l;SukoBN6k<1H@t7b$bS(TH|}1=7p2j`DH3Rgr=l(6PIL> zoLb8o5hMoHL6p-P+JoNWY5<8%Jy_)&dQZbMH@;n1k5gZVSDG59CRwN@mS3YieR+R+ zBAkSWPvs4(spUN{Y+l|!Sg;6&bFUYtQyI6H=HmrUtM0Jb+GO9GuVy+uB51tb7Yv*T zYFD3tL}TJ3oc#GNW=rR=aO>o4-~yYIy{l>KgSZEC^?)4Dv_{}AeTN7(PtHQSsCppR z-O&ueZ%;ojbgn0xqy?c1=D}`fMTVQ+(Hf7#GMidk%E4&NTj|ys)55Ur?JSdKcj|Q# z@lkkIq~gI09sUQhXE1Oi`1G%+0*FVX$zZ^K;H)*Biv-5nT~_VsJQLwR!63B8U?hW)?=-Hdlqq`a)%WG*cKqMfqu&U6`6B@bTa*hHb`MGTvKIJRjs3NL+*6oUu`f zPz-+a;yzVqgUnl|_Ft%7(MqVuf;hXE{lHCF2ZJV3dw8A0ZK9=1GTeu=CHDQBU?IYD zYb`v2rzovi+{2bQ@h4?87jd5uw$%IJMg@8LZ1vzM6o{&c7{V%n5d_#@0$C223kja0 zjv%e6ch#8!Yiyzet6(Ps>o6M6;8nan=LVmWkAUisOgL8(UDj`QAml+b0wtTWQz})) zSJ`rn{zz=D(Z4h{djmEwSX!(^ZPaMhTGKdHXyg77DUCNG*u3gne57pNGR1|dUZ|DD zUz|F?3wuqfM>2#Z)dh{pi{q#ASe1LBs*PR_05B!hk@A>Ki}d9}v5yvdfiOihrQ8wUSumgQPT z^#CeUufkXX@5DLrvx5#hRD)I=NS3K=5*W_V>qWl{rNnBGEPPs!nOv=RtGrjq3z|oz z%TQ`338%qxgAOAc(jbx<>pSsBsbK8L>)Xq6SeSZ@BwFdhWMPA9H$=OVZ%8pZ3SwOU zve7>|_N5K7hM2X<8_siH#wcItPcL%K1u0ta&UGs3R;U zDFUi^?@j0u_Vu&Ua)bjE8WCg%lxXp`R{m?P8%2g!!Sm&i8ysliZz-Pe)W~iKi$2@- z%_3*UuodHBQkRe`Gg%(oKyxZiY$9Kkf}%9HjO|Gs??vP=@Th3JlaO^YUi*R06`J)L zM<&jp6-PabbnTBvoEC@yMN~q%Hte32CG^+Hq!Y-3#Bck`o&Ye^n)8gAcjrS3G3;f# ztlv78_U$6c{iV}g2vq6cNn)6j5UD?NVll)n<{W@3DD~vmQD0afGzl}{o*aCRADki_ z=2bm;e{nE5XBgAp9!e}Kj3yT4)qV7PJvnnErUkw1#M->mWvgOe+8O_dh*2zSE)^88 zHm|BVM?!u%g)5yXB(SvQ%{h1(*lmIK`cKw|O268HNamNIhp(p3)}H)Y zPDp#QH5Ayq^3-4%J5cMD$!OkkaoPKe-}-JTT@VzuHovho{+xMvA)b$wYN|zTDK{_A z!=;ipwz8(>5Q?(SiryT8!!Lqar~p8UnO`j=uM&6I*a>7SB%*^ANS&jk`adDWz7Sx2zfof8}0FuZtes9;}u zB+1-Zal>$baBaxDuX&9iE1ln=o-T=^!RCgr5bsJ~CbW6gB=GQPFj?(4`p2#G(oAxe zKV8Tn{kWAQX$9i_OdFVjLG*L=sG>-tI9wRH1Q$&*H~5=?sf z00n0WnNK)qk3fD%dRC{TQE?y+baCD^r9)P~=SLLO6W>vFO;58*F`ox*%F>k6!x3eP zc{T1$&hc9d;0GDo(7-vRvd2`T@-mUcE?7|-H>ONK0Yq}-H>J~aChwpa{&C^2T`ni| zz*%QM45LVV0&)-tQ>Q{NTp92^7BAbrnT{X= z{9VAVs&sD53A%Sg-2258V;u3+r`FgO<8l;^HMYd#YmI#r=S~9KckScO`lDlr5YJ*H zTi?`7<`$KC)kJX=7tUgxcLwDBKwjd8!cf(cQor`?hg6AB>D0=FrBh?)RW8VhP1ByN z)SlFH0!LQ*%68G_C6fTCp&&2fem+vRBmRkKB$Xxc=k(;|r)@Y%0}Wnp#Qlu=W?q%I zCiOVHU(Drsu?a?sn+Gsw=b_S!Z^?s&q(`@$B9FqBJoJ#Xr)3nW#N~ydM4dP7PTb(t zlMfWb={ATW2Afk+3ssZm9Am&uE$q-@f_UMx1Dod;oX)$GpGoCu2*2&EynoQJ>*{3a zoZ^Vt6|5|YO|SfVPV8Lm$x+&q!JI(%%5kuSFHH)rbqC$g2l1>Ux5m8#4#{F8PY=8VI@V4ed8Ja-K;lqb{X!#!&;aj>ZKK?0ZXiqsqd&(KwQ!=z@*^8i? z#a%onx%!-sH_EUGHPGr3#5%U+M#`Q?w}Uk52@(;DP87;v74K_x_RR*0!>X&5ktlO# zmEzeP1rG74R6Zc)k)ZLcZFSRy+?rG@s)+duS#@ktn@C|03e3*a8spHy20vtI^`9bT z_u`f)O#Ei@b@NBgI_(O!s3JdE!u(*Tcut&)y=WsL6Nwiyyej-%DU2D=c!%rQ?BN9R zn<^_3*dgnGGaw`s2nTI<@3*@soU1iqFLm{L9%O65oe^%}+Em03Ncf~gPHAW7B|LXy z0XAoQ6Q0}EOJTxui@bz$6>16rPWHPuQ*dpY}NlQP&(W~Yj6k}hp_|woF2JBV+Dt3<`-hr%Ezr=pxxW7j1 zQwQya#XN8`!r~?-DhW$G7|LP$7=SE~H0T%rEt}55mQ81YbJ9bhyDkeI2OSDJDZ<&H zfCpc7z{})0@Nt=f179eoSpdWVRPk$8P4*5(N=#E;;=Ie`upgiM9uKzS z@x}&0gFt?wmMqhh0#=h0PTsd*lS2lcL+|pf>WYJ00cC2+LrF&Ku@*@=<3Z4k@6y#! z1HMbnm)Yt|r(a~xO`^ssNf!ar*|t-Y`Oe|QKy0%RQc&v8h?=9KfjzMc^aKlRn{_^f zPOx^2NbYUce~}0pm&&~$NzXK7ifEu4c5>-SK}EYd6hM6C<_M=<>z^`Oj3k*G7N#-` zxyvde%Z#-Cp}s%T3I@_;8$>*}*5a{_4bhZ5PS`}wwZ3Xg`+J=Nw~gilc5$!BBVGAY zD&t7Tcn~`6DR*<+%e&|>X3_gVDM4CAw(lkKjiS9|fHYi7ehib9a)?dYa0xv1kYhY| zK1s8QHID&!cPqsnt$usgt_PNiBC$i=EUeC-oJTG8+^^rP-j9@t9;JJwN>$ z4<-AaP5#qrU)yC(0;$ZBDYK-ka?;jB*)PXZ=Ze?K%?i!Ktb-ew40db_8Q7VV*EtTO zdUh6LWukK?5E%5p%-dPvF~TA|IkI*G{jrh8Wn3>JB}N<@nAM*td3w9`L)w-lniZ-u zc$M{GEz?Alj4g%}{#i}WSxk1qGl~wxM_gCa>p1@eM+n3+@v-S<(TCEr%<+pqQ7xQ? zGQ;jyC|j5B74kB3+(IwtKkA%G?O`f>Qqfnj3f7$OTvI!j;|gTIK$q6|JB8Jn9_vO0 z_@W-;zA>)&S=##f=tfTy!#_^$B-!k5xF6oc-c@rjBk6M~M|wHubj3;$=AMofQ<_AOs>}JJ5>u%(%)41kNIq1IvFKc1K))za8*eVg&hY`m|wpzYQxnde<~ z0>F0FV=72u2bV~!IPY^z3hyaE&K20W0xTUoB(F?-BcLgo=QC)WAQ$vR`^$PY!pZ4@cA({mL4nip57 zdCG^p;&{{ayb!lpWN|AY_dYVga-|DRmxFPw@mJ2*&FX8R`r5DPFlu7wmpdZSrh4hXG*R{@B@?OJgoIBda|NU)=bHI zoUCH*`Sx;vs` zPpS@9wL>DBnYNtN0#XtqD+Z<19QA2O#!3`2H>av3C%Z1K->_Y=GO9r|_0?TF(ug(M zsfVgD>2Z;^IabF9Wh7QDV{@_5e`@_9uF=vT!SfDZzgBP77YHt~taOO48%DIb^uUh$ z`infoEYMh5Eqxxb9)of#dL0(3HGTkLB(HK?r`|5C7LpMKO)@-WK;T8j%OIznZiwbB>UnP8=V#ywX^ z#w%pd#G^D3+yFp;7Y+X%**j9Ug~Lnk%jW3BS_}vJqIQ=_yHuY?brm}Bto2{Fs__T8 z>m`%(QzwTF&)35W3APj?m@{JQo40Vp&ghxSY@oCQu1}i%Y^G~yrc>?!%GwSUbZPtE z`JSM$UpOC{HJjhnCYC-NJ=cy1Hhb%;Dq^GT&FVg(_S`i`KL)?`?}%Bdy1Myqr4=Ft z)m|;AP?7ZW#NlI?Tw^Wh|f_hvJC4dygPAxw|6lgr!oKdcOn%DRBs|th9xAZWd^SbKBpPvt@oi4p4n^m-7BH#T&!dE0YfwmPv zJvr9_xZ&mt8a@SddBG5X^FI&lR@2vs84pvpH}Kr*=JYUg(t6T3t2Vv*z-nBnO6}NE zd7O;h6zmPVa$?uX!^?4*Sy;-w*#D+hP*|`1P)`;;LRIC&r<+@dCU=5$4=m8#=W_95 z9$r6TS8#2ZQPdPShq=FYud1yz-Ugeq!-aNd#NHAyp792bt!@mP??z0FA2Vkw_-1e$ zFc%5V;5y)fhG@XskZJ;5K~{qJfOyyR?QP)%$eys(X!`_~u7!y9`0aNY8C#Pqn;O9) zHV(3XM>dH7)_*;5Za{8E&zB~v(*;JqJMNKpY=6-}Hh^_{2F%S6Fae{5=^|BJ@5~Db z;0P59g7!1|nqyvOS9?e&k39|Qw|(EGD!0KUe^x5=>4YiXF%YJxZn}qQ55!Upy%(K@ z<~L{lgng+3LFW)>Wk^rl5&0K-bTpl5L`;>+E#Q^(V$QsaqM_u^Eyz6-cq3@0gW47Q zgMs~Vq_Bar7K}V#VNjuQ?ySq&@jlx>);I}-OG)PvYaoGb&st}{GXTOlRh~YW`8{XK zCi!O&8%jRv05ItdVe*_@YgZf(29C$6{J#S6FL59%7jaI(AhDDH&{8WCD?)$#0*U1U zif=ejaG`mbg5nn$D88S>9m1==H>n7{S z-m<4;{-#Kz1XZOyO--#9yrgMw?PQ#+F}XR?6Uq7(IU_p z*UZ@^jji`;M$ZZU{z^LEm{a1HU~O|wvH0%FS+3Y}66jWgl5kevkUa$Fb1ZQfV^SBg z)~s7uhAeXr{66iM`zERZg8MVJTQ8v1(eKDRRM39wpb=*f=Yuiz3j0JdaH)}79jJ^bPd-8#dQb7oZ4CAoR2{*B&Yq;uo2y@+8FZ| z&34nQ-JV*`uQN$pq=D`8L=KVU&RjtdF$wI!^$qlh=Qw+LyDFS2pxOY(1!G1jS^{~Dde#<9}X zTh;FEOqiNIfN*GhA@?=5i`;6IJ_CnLzdCeZm;2I%{XJa@R#BtYy#(Fi08_?wT%6?G zN8}q53FEtj9)%%X@jGF|;@92I{Rlhb&r_+EN)QjC6Sr;n9EP5^1?f3rtY%N+B&s8Q?}lkqvyO=}aXDxXS++z+i%7g{o)&7W4e~2kZ8xiz11ICtT@a)-*m*yU3z*{=Nj2(#97} ziWm#jI2HEQwIMUdP)B#a3U7HsY_^}U<6QPH`N6RFKJh_Az5^He)_fo?j;zw zh@gUt2+okp1-!bth#+0e5xU$yV6&)&Ps#-YBe`H;R`bHC_W$92fq$`YA~b*Ib^&%F zE>!r`?E){8MTpQlJRni6ajSa4eYlkuxm}>fdS;i%iRaJzu` zVoHGjGV8n4Qnw3;Kxs9QN|dA@uvYS-CyNe3N`qGm&={u?;>Uo9I@p-VH65YTZICi} zv%tkpyYUL^T;4+5EO0h%kkdNyRjEnVspJk^EHGRpP8A3?|BsqLp_1yMJD&4*Matnt zEF})9GZ#)x%iJsQC@{dU(;I~T8|sCze8 zyG1AOj?}ipd5hImMY>ma&++yK-CC@WV^ufTU+RxU-Cfa&ZQMofY!^9?!vuk08i8-X z!H3;e0@8Arm(o~<@<_EKL~0Rf_nJq|Lj*lNz@F4CYw!}rE4LjkRbiCiR@v?34oJWG zQpoHQk>Cdit{Gem*+P}w0L6@Rhf`1;E(NGG$tfH&5ybcVbQndp_T|1j6XbW!L{L z5{)Z8}}E{XmeqjG2}{hcnqYd6KY8b0_hg z==3`dGPXA}I?Psdn8MBJeAdt7-HbEn^~c8I9Jv$g4tHbS&8T1>TH}X8vj{AB8kt=EsIb%i8orF&A`kcVoopxh&F_8Wyi|68R+Du~Bt( zb?es2VHdX>%N@iYi|=tk^C42IYA$M>dxn28V4+DGYHJ2m)ms_?Q`QmPV9OA-g=r$63(u%WQjm72$7 ze0Ht*G8#Mw+($ej>mYBcEOevu~(tx*WziE6D$ESpc{vf+36xm6@}2>cse zIlMZgm2b_sODzAo8N^7&sr4?a^S{NB;0ipkzgCP?*q_f)!xi4F-BV2~rw=afrTkX> zMyc>4D#&IrLlOydA|~`vLP_yH{^J=CSHj2YcmO0l7;c>Yn&|Iv?+l z>vkfjt)1;H{nm_c#XZ`_yGx4JJg6=*iBF(6Z_Ec&+{x-f=vUE9TBt1{aBB9|UhPTc zPM6TqWAG(!HF}DT*5ct;lo+>qhujjDJ^YmQ4HGKH`Pw_5EA~aH8T?~>3-sDHt~}`s z_dt|(V$s{e^~YItTQS?&iArlGFPV!AwhUv_ve~YhALlLLS&Po88ISOe#h9QEBIf@3 z0M`O@!p0Spjmg(R%Tr-_{P2I?6 zE)41(~C3dM|P)!0etmm?S)~ig9%2R3(F^1wW{Mn8njlaS1+%r9>fqN3|z(K z{=R=hJz-d{-7od_&M_O+kYKyz)!77>&jwoxgh)c=(0e0?hOV{I^5MZtIXFTc6&riw zw|NGeM`r5;xl}diekGFpYEC%0xG&TkDjyzhJP^A%TYv_tXdreCUTrna1=(!s==Nr+ z^h=ehU<3NY`Pq-uxm4;*qRzO%I!=WnRFyiHW~T*j^4D-fM1-5JtoF9gen2=YQAFTa zubuxI(M-*&d8bgITl>y8c*QKbdo?S@{T7|}%k0Xa8??rY_y{z)TH`}VQ_NRUu;I%E zVp=Kp=A}IiOUk{+BDK$8)R8}k=I+oFVM_(da~(Hk<03&1#-SPGwZ`}5{nBS*Mar2J zqflxGImm35Zg+7SuwrZ^8P1VQ5DC}WlAC^j!+_MUD8k4TNHQ`+y9F{dCsvzAGGm;e z#u(=gkngQl`$%2Y{jbGtVq8b=v+bdS(qrQr?q5(4J3Z7qIotBu@Pg*h^x^41gumG~ zLO#bm9qxj383g0>q;AW-ZYj=ae5BQ1(P~VS74Lb3SK7isHX69o(!N#5GDx#Z2Ju+! z;43#hTyUX=A2Roa%ie9ce=#0PyTPnjw;JVq8-LAScSGDubE!Wwcy+pv){LWh4~_-8 z`co)iZ`Pi4&#L^pYxy-?9`v^Mj?mr6@zd()%APv0vU4At(j zlsp@LJ8IrJH(2)iZVPwX8nZ(rQU08rcoxcEdcl^v<(t9}dPH=#eLW;#(FgD=6>zsf zIDvL^Q4b2+%x~KEl^H~G;ZtYW{dQt?xt{t@$~5iSD2p>zgd_f`|0_W*Rs?y=AVG4t z%HK8XhbGS_vo08TCdL7=8yzxNC@&@Q3Us*`VdbO{=6DE`KPprlAI|5z)PK>f(B?mR zX0er_&Akq7f^qc0Ex8%ueBeGsk|S;3$M?#c*7PF^K%kCr0}ai)_p?MAP@}7>n!lI7 zdO=|4+Av(oSqDO@Yr`)ONmgZNw0U0nrRk_paq&R?IB`{@)0Z$+dgo@@3t)h5>$|r= zTY^A(e{mIo3DVQ4>B4N@X33L)Qjh{&FV?;#!cF?jY)`@;2I#sF-*HgtpwJ<0CQ!(r zCh$qj8$mw%=D#z&$4+AIcnuGmuiL)VD#)|n6Q5xHmBSKeC$hTKE1cSu3SyTv`tOYA znQx^32l{xHPpNas#I7*jdXyA<%&Nhv(|=2ObuHwAfkV6-uFu@zi&%j9K{m?4T@p<{ zDBIin-1uqOvNv8yYZb2&czwn|v#CwMQt_(njX&otF!Qc=WpCs_0}^;IYWB$`tI_1l z6=V|_hAi+lcTDE>u^^*V8{WZjl>Hmc~ zud4Qj{MbT9;iS(A8eio8K7#Ij)>>6V0jP_R@5p5JLX8(S|R^)bin<3&Qf2Q-fdM;3B zw|UX(z7!dZ8;RvQ^HOdplAFr5@OL~{6k5CSHg&GO+N5IX1s-JNK|#jR1+l7Cqko|# z8Q)Yv(Y7l+#lF(J3MahWW>{jb_GDYyt8Ln9O~y)rxE9YF?oQ|0EL|rSp781D7ulSM zx@KVJE7fbc&mV907pvDkYj3xjm=@zQECfxjKKNb+r~yl|V>ud-TmRo;y1(qibYB=; zJ0zrgB;B%g(R2J1iRd2X*q#4;ne{PijDW7)|A%mHWz)&}hbyr!`G?YS>T@pKEgOmH z>1g3m!MSi#7aUD2{VJY&xk!ymv8psU0p0NDB{<#kSTGRF9VNAp|L0lZA7gh`7jv*A0o~-iX{SMpf8n=K!@o0r=sbuuu`oJEe|29ViRx#awqL9&lx8u_+ z@!Yj4o;zRoQGeXIi`3{}r8TwFP|I1APS3TwFd@mG$H9KYK0?Iyc76Aev>!wW0@k!E ze5MQRt`L7kCm+3^Qisd7v+L=p`)DT{)O}zesC$VM)QyI6@4~!mh@_fZ9!y?yn2`8u z(pP5#xewf19UhTJHg;kbtv{WcK^UYUo;1B%{6j;x6$VrC2PFkTPUyBduQZwo+P32P zLLY@I24c6*S5qskaR29)fq?C?PQZ4t${P}}t2&wPgk`pVIM41Y*2O-h)C~|XSs)#>ramEx4ajCWvW0r@? zme6R~dlbpWX){LLlK$+s`iXI78+uHIHOn%e%O{D`4wd??3y`I#f>bf<52 z4x;$**dbn0)ln)#D3V@-my3;s=YC4t$DD5SPBmf>P&mty~Xa~TEJa`D33TGJJrR1s&Z z_V1c?L*r~ka1bY=zdj^L{aLA>bxoYD2pEG>_M&#^BND6RcWLZwewT@v;P}e;ql%TM z9|<;8E{hkiHA=cL-3(_aPJfGEzq&>$xK{Rz1KNy>yCkG(g6kFvTN|L83hX(Ot6G8mRfCXYg@Ff(rQ~?S8!`sgy0Ie;ZjYlZJ!vmu~op0{J-bk z=b21Gu=ag_{q^(y{vEhE=ehemcR%;sa~WJG3uH(gFOV^Gq`*~lOM&Q4@c?B8DwJ03 z^E~v7o{p^5r?NCU4B22Yb6441;okU+RW3_dY|64Xj)v8u*Gzi8M>!<(SESc-@M_mV z+jm)kQTEeDaavkCyd7 zcv*PIk9h4jBY0cePdGc}9;KX&9d}2j_*L`%%+uBrKZV?~qEEJdrX%T#f3_~|^BKsH zQV}5)#C$R<7*~#pKO~Jr#z4;bWzeO`-$S@|jy#?gxeMg?IOlfW1F~Q5t1EH4zcAZ{>yl zn!Do*d3B%=tMID>F(0rYOw}909JXxPlvXx-9~{;XHOO9%?u>)z2w<-_*!s!+;Z5=V zpd@TId-oBN?HBrAjja{z@;FKM*v@W`?Tb++FFIgPyuTW3Z5a(G+DOFj2*%c!I6gm&sPu)rv`%3$%p8J;WdZ_xb#PsWZ%U97u#ii?3=^c9SA|t1)zbi1= zR^vw6lx8C(oErmNGnh9hBVC$heh%Td?&{Hy~(g(7P z8mdwFWBuQZSWDA|mt;46eN?WafeJ?JQQEO6R*2L+!KbW-h*{wX@CWN9fnspe^& zRJUt)wh5y_vN-|E*1B6{0Z`#tf0^t{v<|1qFnJhi-a&`c;TV{342w&{bAMY3u03^G z&2aV@={iOUoKQQM{YG|E)r&unHz=}gWmfIq5lvQ%P%<)Qi&VsjV%Z9_E}1aa-q{^( zyPU=vsV54_PIQc(K$q15N<-_hby=n8*ksv%(@YT z`^ywm-NQ`d>}6~PRc0SUpRayGHsLu<<+89@y+-s?!Nsf?yHxfyLf)^pU+HXY-dTN- z_MM&ZXLzQO3aXwRX;akGP)Cbpp3RC-QWb}isyJ5S70^JnZKBf%Da}qtN9cQ;J*{Gi z;B0#SJ({Zeil(Z}W1e|DJ`xyP-J7DSZkr#J9`vH9iree9rm7dTG9Z6gRh6g=)2gbn z*Z-OJ&t6a_;_QqG=n~+Ag9_ACWp9|!_VH(7Jyqx0daAxp9cCUiYN|Z*j?(-6J+xFk z{vuI0TB^$MuD3vd;ma1=P zPcKAz(&N%`TB^30#)O8d_E<9(%Ba}(?x&0d-L+LMZTr+%Mrx~CYP415X>C<`+q|?a zsZPBQ>P=gf-pssg&1R#+u+gQh3iVduUC<&p#-!bgwkkVx4539>@kFYs3cIPQdI(tp zVVCt#RaL0h(pDWilrB|O!u4I%K2ZY>OJy2u9}~`~PTr`ik{!^m@6}T`Jt=Gb!Bv-Q zbyb(>ZPj+6gPqyMB%qrnc`!<-Bmi;BZphQHfB`{vL`T=La-#J}PMN@&uEm?JwQ4$^ zB6MA~?~pnBOI29)Cj@iQdkJlEV4@AmC`Rfhv%febwtc_=!O)Q0_9qZgVRc9>aPo+j zs$NxCJ%o=Fs<8S2ju9%XHp*u?bTCS(zA2w<%I!}Xow}>Ax*VG(pV#=F&xd5%=$({_ zQj0gOGW#E+!b)=~tY&sM(5&q_hI6BBimj{O+UNp1>Z=g(^E4t|tU|{)Yw>F#jqcj3 z{B5j=S-a>hj=$|`omEkX)vNX@z1v|SC=@i>tCqCM5lnc~gH|kO(^Dtj{u%96i;2|T zevw4oK9|3)_AIHFI9M{Gy=tnXx~f75<7{}|HYGEQieza@v>`1RCd))kj4stxM}=w# zsrF&j78jg#ycVmS{w^(6i`GhKz5PU5tgP>F=3=i{&%a4(v@<*Xu3alFDHqJ@ygTo2yml~HLyoN zi`qP4NBeo%JU|@U`-m$U#u|4IzHmkPN+?rb4zm^~w@>OpvOs|-EHhf}gz zVR>kJ5Cm<`uy(rWkvHKW?JZ`&@x_imzSujX5WtEk_LEMrO~l0BmQCN{9-HT3WUA!l zn1jKO{D^#Ur>(O^;^oMCeRPs=HaFl82l+K3mKgzOurL9Q@horcg_$yhIQ#Isxp zle>zYDHmUguVSBeTdmXpNL@+6XqXZI93pA@MAEIZ{^duL_x(md=SX3igA4Y&y^N2zwh!*J33~ ziMY+t82jA)*pPFs297w$X+3=NF@XgV!EG{zp;Er7+7+1OFaAK&LS)UKe@4g=C!ye$ z!oqw>ri>52ujQgIlABaW$@`mz&yl!-4-m1|Pf3(_ApVipIPMD4;qjrpv87L$JEw*+ zS-s1~cHI}uYoxZU{f#258cG^O&aHVSMmKodVKQvjKT>+(Ge}`ibf%m`1);yqTqMj} zK4T;YveJBJqy~>T$OjYlV&yNkq?F}P3yC_Ul$<%DCWfiD#Tqg~8WFd$xb5@DuL(~1 z^#Sd1XQ4J9fyanAOAL(WDuY|}V&^7XKfI>16UEp^Sn5%7Bmo-dBqN|nn~+=h(%<|c z*SZY-AjX9HRjDz-aiJ{lEHCQC11Ymc3FtR#w1Bu-D(eRb_FI49+~XM{lkO)pkT}pC zKu_mB&?WjnQ};|G!{3cITyWwR?46IxSc$y9Tq;6>i7C$?+O%2POX#T?Gq{h~bbYgY z@!o}8@_Wzu=H=!X+@nR9SoYa6S>}a&Zdd_mALaw;%-CR3USqBsb!wk$Fd?$c(z*ZgJO4CKn1LyvCd zE9lu1~A_lJqhsi*}FsNpRhl#m^Aa2vrXxGMQ6#e}ra*+570)b|b_`z@SL`P^QwqFoi zU8V{Y$Qa=!bX~*{L2XiF&sz6NP%}i-b`23%jn;G215qjF~p89@W=ICI5n5pk)Jv7>LOEX)$ zki~kaGY5aXoV_u6L!7^Jujiqu;_{sJQm&pI2KMxTYgWVIz%X_Xzs{;V<_+}WZ{Oe@ z5=q}Z=ONMoPvq&Thar=v;g95^E|c@ay3D>o9!uNR{-L&)wV~V$;dP&xVag&`kP$ z_QWlv43cHmF747h0`quh**()6IB#a(z#Is2mgfof3VxwZC#B$#o{eO9moB^nwCT{E zfD;7SC3czy2<%-V)nU>>kWZ)6HV8X?$%RW%WATY@# zgvUbDp9A9=t(>>9Trv0TWoUb4PwYncChS);7D;;>F$&-Q##yfk4;6t?D2uLk7}N4b zlwa?i;HJY4bxxTcm#uYifH@l`u>OtoXMR|_)L+cGu^*K~wHKil|3iP~ff}ayr>t>L z;@?a;8F@{-AsdcYPbc=-)e2(G)&*^xHIl6OsPg9Q#t|Oy_Gr4SP=W3y8(H1xPrNqB z;(e%vdTC&i^)%?76gtFI%$cz)EA^y&IE=j~lWGP6iUQO92R_p)p={nyL30CEX?oJ_ zOzB6o%#2jzMbg19KmyU89ep|m9bAI3G}UXPityU#g$26XC&=a9pVo@7%13(s{2BIK zHE73y+4NSv%qT}uD;yClb`E6}I!o@z$lN8>?B#CTw*rK1npFqrU9X6ql$lUjzea|; z+=N^56~mcZc>YlA-M5e)V@kbr|-c!U+6=&ZF_U9RBW=FR=671 z9?IIVc8R}nZAVVSvjKPG+M~XQliTC68%vL7Z)9x9KV&^JR~n{g{i(3}waCT#j$rbU zJt`}XA!J6*p+Iy_{1>6;jQ$MR*s9q#W*({j_BWW z*U8zFY*btD&oOWvAo3VEJJiuWH0$slcfd`OiX`9ni2!9*J8~Hvq5MLgL2C9rP8IR? zRdQgW{23#EhRPpL{U=$$hMdff&?}x>c5?n7I)HZC&`a%coQ<_dgF19Xj+6|+v?ogovVvn4w9_vgQoKGHGtTB|qdh>e}B%|#|&{rSa#^c6@@d6V~_LoKT zJllS5)g7{4BMwU6+L`hWR;=}YX?+W;y()>)wBPQ_d@|U_SND8YdtXuU5CiJ=hZePl z60AXWgwz>+jXk8vuq~#}Tk|>bM5XB7Fy_6}V&bM*zSpSBc{hsx* z49{tR#q|rCny=yGKrob$gF=j_I<4^t>NMuGNUaXF`jEkO8R9#TPewX9fozitWN52u zTJ)mH!}7+pFIql!oDgKl^7^$eo)k>xVnz%8zndlJDxHDd#4gjc^;9d24J__AL3I{J zlZ8j5M{ienU;npYQYh!pn4Q6xgb&-J5;~~#oiz73vt*SSIF;=bU^HJ*x;tb6M)4J+ z^j0fI1xI9W$XU`pWV^g+XSbMmZs06wkCEZV^kjs+XhS|8pUV!dZEjrK;#vPwu|PtP zvNn&|L5wQP(;#Akg4PA9IrdpEOi6vWp+=C*KV6mVtN%Ras)_uKY_0zn>GhUb$C#XgCs79%uo<^bz9l^Fg+6P0 zkzCA@`~*kpv>BDG^tbF3Qb<9_rMF{F)&>~Y_F0rZu!@pzK|h&4)t8 znnHOR{%$OFt#?c}1q+_jCK|6GhUD7!xD+jvkXyW)u-rh5ZONIi+sZsuw;49LvgnF# z&B=W4y4Tv#WxlrAZu7+n*&9naF_1Ryt9$1`PHihPR$HW4OMwAJ^|yYtp<*SF4w>HypQ?1Xw6K*2b{e%eZ(gGp%9@*K#HV|)tS9v38 z6?#p5M|NCC1S!lD|lnbb=G&6jm9m2FO z|1J4Hi0IFlx*AaeiTaCu510{lIxBQ*GfpBn4s+^x>$~C)sY&~WX9J%sWt|(I z`O(AQXphbd{hr&M8Dp=T$(1-6>m=aUbS#|#9c6xGlv&-QJmbrwr)avT&b;tHG?u8DGWYjHP3}*Pi2Vsu(+#OQ@>`a~W0csd14u&hrowoz1X4+WRq3 zleJf@EnEf(wTLd-$C35yd@_^JYxa5`-qW7tFPd>+=# z$Mg-{RW#$c<&Ek7`Z(CQdZ+XX*|W}=DJ7@*i@0HSi4;;R=HpEsvsrT9vJUT;e)~OS zni0MsSORjdIUxE55;=Z8*e=0IM63T0*6Q|e>AhI}K9_$+QVFX&dLe6Bn|IQs>wJ-| zBotP(xeKGU&>Rd56gi-N*)SN!(YXULh!u=7d%Hr}#+K>PArA>v$u1f?S&g^KiAn5o zIWf7cHD^Zgpx_wUlK1gE1OcM6GfI!@3lkmoA%Z+hlDhBNvOp%jXDb@>}V@1N_D7B(R?s zdU<|rg)86f-V+^Gk0$Gi}*&?0`6a2LTD zJI}x4-DL0?;FE296!;Kh9p7*`xE-d7i_XR0WBTtG`tRrZ?`Qh&r~2yHO~#8%uPK1HsL%_q6bS${OZwaRKaA&}0M`Jw0AF+etMWz42&;qb&| zAE{LkPg^VWqTnk`!Tm>ITv2co4(6SioSWHlHIH(eLdW~Vgwkby^HIC(!a$UHo&iwp zjdsdkEMuk|bp-l3<=>SI=izl3bSfir6Fy=^e=-CRHJ*W)p`2=RM8;v@a2N}ZiNTm! zOOUeYt+begR$1P3&}{+ye^Atu?V5*E8p#(`m9y< zb;&1akruWdkk}f=%1SC5Rzx#UJ7+W8 zWRbxP9OV!KG~Exr1w7AiJJa~w%%`X*dl`4H)&cJVs0qWhQ%12|Oi_Q6urY=k4K4ZstiwB^m>oh`)LT*Z%PWU>!~~LzRg8X%B}UY>>}ZP(USyDH zc-Od#!V+6$3(r@!#>sM<8`HbAz82EZ35W)lzl$XbT;%5&$#BjO)Y0eSWpzDUBFqad zjF(lI*Wc)C%@Z{)q3n3>IWL6kA$nbW9atU>zDQyt+rGgl92wsx&LZWpw3-LE5ux&= z#>9J4v*WY;>vq)fO*UXrwuz5zS$yY(5>0w}o?U%0GXLkrCre_feC8&LU8>l5#V(C( zWr=;O*jr+6GKK;OY&*pEXz*9L>nuqD=@S8-ddZ~GB(t5$Jih$UU{h{1igCJEkiT=E zQ%Aaj{Pk^75tXDX2)meYB{>yT&{aY8ZEm5dCY&o6uAn$mK^*dgllY4DlO2ClDA7T} zQbDQIMY2>7gd1d%@gdCEKlqZa9v1iA%d6{$+4E{sKh%X(OSqa${p^USpFBG~q3=br=F%riMN739XU|CiOzBh-&#iTr zmeq48*KJ+%HR=5qBwODwNUBw45U+K)LDH;?4U%rtyF`QSssIASbYpqZGCZxPJEU1kw!v7Gs`mg2EpGj_$I;k8(hX0Yq!BS3%7<|9r)doK#c!|MV1z%!tOYl5{cL<(k@S}oH zGq`Yrtu%wX1s`s3{Qyj|!BfRP#^7GTk1i1+m?vf4Gq`@yrPbgW;^#$!%fj1gF}U1; zwH`CLJP2cLHF&k)KR5U)!EZBoo!~bbe1qV12Hzxjz~HwDUS{wz!Iv6*i{J$Y-zs>v z!M6#XVen?bPd9jr;9i687krSxHw*4I_#weRU#!dCDtL#%Ey3S0c!%JJ41QGbXABO< zR9VdimuI`J2MnGp_!fhw3Vyr6y@GEtc$(l122U4!mBBLvuP`{QSY;I&+%Nb-gBJ+y zH~134XBxav@N|Qh2|m`~)q#8tO_fHx-Y=jmH!d)QimkV-sy`(y(zG zn-3RBu`l2S!K7n1=xn}aY%;L<$k;q-j?C1ieG>kSq|d7-Cd4K!?{Yxc%Leb3$*yqKHjM77v|WJerfgMZ%CwH-dc zX;9zg>)!74EMNEOQP0&+vj|3sBTZyy@OQb7INRsE=!5?H4hn|mx~V&J*Y67KZTI+x zvEe(^xeLytta8{ek7tuS#@;XwlMS}Dio_aWRp#ELByibxJkiatelP`ak)V~`YSWy3NOkh&|yL|$KJD&j$KjJV1E{YqKx(^^OzN!8*cc6d$ zX9M8|1H0p*>bEuoQ~p zj8IY|M?0Yd@EE+I*mdC1Etv<_p2nk!T2u24n+brBN{gG97m>yHhLV=xsr?1(RnC8M z8)L?jvp8~g5`x>mbK^PlEsjIKCuxPAM@MjbY=~<}FJ->P!&PLtFIo1iPo)XvHR}9k zzU9$u$?Qg*%eF6M19?>Mfc>7?`~A`TQ2|)fU;JD|-i1}v96U+$jG8WH8hyDYSKOvcxr9gL-+`{B zrr}5Rk^b`&iM26S6l0;`t20F|H~HbfH}T?H%6-PMSUbKcFR z81cflrNl=)>t7PGG$sAaFZ9dT^pfu7Y51;mt)`S~aL}c>LozH5*XTaSUGu-5u6_8m z4>)+S*Ai)G$|~_FchR3W?#W^I<=TCTohiwVzZDWsV{9s(&}|)x^$5}rqz?!>{o^Dwa$C!grV3o9vo=$Lgp%IBNkB(u z%IP|(R#C|{QxZC>^JM|BSK;yb^eb?3@h3yG`C#LJOf0_67x5Bzm^%VUW1|%yg#(^Y z(mIJV^ZCFu-pvw$G5nm0T(4m~j>JQm?O|YN%7eBC_R#YB7=A)YBI4Yc@*~?NnQI5I znNW15z0gjY9ahiv48usxvYph53A*~8(9C(zhxUuAG_s-p91ME#!0Q$JSe%fv0pf`Iy`k-vUY&tiPqL?X zvbdHFYS-%QRTNw0a;_E}ofZE#A@+KUZ!$4dp*1|c4o(ssj&>wkjNm~aX$iNMcV14@ZI|{H zteO#9yn&@U{r+j|$KTficN6^epS51~xY&fSu_`(9-m4Oc$sEe1%lMrkgUjW+tc!5e zgK{8^X`#jX1dbAKLcU~WI1ZN@hgR(%0-TSU^Zzg(+AFW7aED6TPGE$v?$2xWANhN3 zW^=8_`jB8w;_b6g-wYRiU%+k67$s$3wB$Xs=d4%s)FPu#V6f=L>+hd{RBmFN6nK~Q zA^ONfNwq$`Yr+CA|pKr0h>E5yX|AZ((`Y_fSPl*yW&O<`6hpr$o84=fePl5_C zaAEblI|_9p=={%tjKW&}Qy)B05hJb3$n&TS>r9<>y=?g_8$~(U+kv0F5JIzmL=C|Y zZ)J4f@p-JT{x2itfeVp|Ey%yJbBS+bz>^`fePLGA;jI0~kn)bwvfi#>U*yiT&fXvT z4rhDNs-1*Z?WeU??I8oHfTyh&-;zr7G(5#-l0>GH$oZj|R=mf_>Gl0sTV>q8Vl3wn zdnv2JW@#f$u?hH`amgUb2{IfW&n>$;Q@%~zNn~pY1t+^N;^&?Q*%BichZ7V)-sAVM z`bpKsGH=pT&i!vuH0x=%)GL8)31qNbEr*FT7eaVPc5%> zpSU6JKHQejp@j%9+xp|%wukSC2Lw+t^xt&FptzLtz_Eqqf~G!ooqABDH)4e{92UxX zMrX>|0LWzQKOtB?ny+XZb^=4+M+5=f4>c;9Ej z7tu5vdBuH+=f+sr}mV#cafb!(7!3=m#mFD z_fnX*eH*epc{IzneS5Rx3ZQ|aZ|1dqqFdH!WBEMP_8uSFwjBftUrA^ogl_n>2W*^$!WUD&UoL(n6bH?yJyA+6E+Oy7Cl-d z*t+q5LmxrcebPxks(H>oiW7E!(|QSy3YqK)OrF`)cT>_IS*7|zi958qAz7j8nwEO^ z`gOEPNKGP&=L73boh(8E8x%Eb4b zzCsCqKgN_WpON=OB|MFS^ekbfl(0Vzx?I)bW1CPw`Y4B_T@^LCdx;WhZE~8UMWaMK z%03I?P-P1wuh|pXqop@jPoOUXq#rLL1;pD$P4W*WphWe+QQnqt>cn*J%P0?e1f6Rp^+8hqunvz;&Sx6HQKa3hu^Pxm{_Jlp?Umh)V2_!_b2+z(u zcHOpiR_segNsE@x6z*V}0y7Ty&>(SrGz8JD28qn_-zOuCpD~#2Ct1kRYrW2tIXVZ7^q;c=qU}w6z5VCR3nEV6wuJZbuMb_Fh^uaF_0jc?m?bbGyY)f%N3*m#X-rb81yl(n$b5OyH4h^jj z?;S>*F8#NTsyxwu`zS6w^xr;oqkHS{Nd33A(yL}}@yzu+)X;Z7uD%@>8n5(9>nI8; zWWMo*T3Et*8j8u8h>G9nHgK8^|8CpAX~WxX*gzIUq%yV^w8t3upxNUace9#R_-3US>Dy7DPR zH-)(8{clrsI!>Z{|SY-y7{zE zl2~;tT?%o}JK8P^aRFh4xZp84q4Rh&3#GaLe^7{f&ql_}6Dq_-9x>@zw!oTrkqU9s zhtdxIM+$LoB3j;6PL+6iQ;54@oX!^J)DhX;)xaF))?PH z#uF>V{p6=%Li-~X;(l_LPRdb;YgD_+(m1RU_xThA%r=hJ8gZwykYvIM#QW-x#-WCr zrP-G&$h~>GS!8~hg4|gsU@Z$w;;*A1cN5oL-cM+6tUJ4cI~AQfkN}=GnIX}UEB2_!we3-nJ4x(IQ1C9W+|zKfKvd)o z7Kn=6egaXE+eaX(9OYh;s5dHBKPasgRLU>A}1PDexrbo}5QDqzeS^fby<-qp+v|cr^tiSI#wx0<1w^RUtBPDx8gX9O_ES7s zPhJ*YIbNG>tH}N4;mG?&EYL;JRWuG~upaoiA1cE%;+@V$9agpqUSN2^Q-L6iU zbJBmXKT0Ncwkei{jHg-6x4{Sz-MCj}&dMaM+RARaakH`NZGR*eT+%3S#Qtc2eh0L$EcL`h|cCwTyo7meir45qW_ypeM~7y_JZ z!o4-OO5no44Mw7whm8*g&6N^i6-SLi^G4f7iHoo3`o5hAKhi0$yDG)Hg>ww&z#wln z-Dp=k3PBe!lIOQtcTY99OMLa;9Hcz!g{{VA#ti*NEh@III$w@_28a+m&$Pf=7e4g2 zzD+Ychgi++4r?lC-P)rnq~tnE_!fw4nd>A+^}7o%mwhrZr4v)|RLez(rprgOeS6d= zO?WMLNMwkL2;H`bZ@5+L_4@3MX8XmI5|qfxsj}$AfKM?%H|l})Yttw(<>zSf^}rqQ^MA}coYYVK(Q7>GhiUuc z${xCjvd`w&MIU}pfKRhb;XMsMXINmy2i-}^sUw=|1pn$$98FRi2rB9+R;a;6~fxl?~TJ;rMl$xRda5T${3Oy zd3HcHr@kNhl%wU)@8x_Z#hQLecs%;xTy`Fx5_w)|6e>%MdX`6KVIhaWG3nCOEP4Zc zd-0UnYP0|^pHUX&4^3ZECd?_G@4IEMKXdwgzJgU;s0@9;twqtX(*89#du}e1&FB~W zxU)H|w`<`#p%2|cPDbPn;=b1QYjjo68JYvb{1g7l*k-L~rzh%nWP=ro;f$?0Xia_J z-#8hPuJSide|3d)9@zT7Aa5Lph|XG?eXhijZ9Vz`F*e5TE`nKf_5H%GU%lG8>pso5 zueQ!u;?O`358-y-b@osD&mp!Lj`!Y@q{lS*-PTEUI?{PM<>mmKq%`PIU@{W)YAs0C z$Jc33XWO2BVmwWd&(H_br*8Cz`s7b|&mTILd*BOsAgwyT7?G^zK+Y3F`h3yTwO=aW zy#Hbv=Bh?;sNA5NJ!4v#r{NBKfF^>lzq zb$pN|ZU^7_g)Bk$*;kFFs=e0BnN0oS?Gody?T2{karT%c2aoy=41CE?U`<+E@hn+O zlbdqBhBeV6f+J~4DPrg4v@DAOSKpi)vqz59DP*iZW$o<_9b-s=3?DLb$R**>0pE6R zH?fFs=9V4@q$r^4b<9J@lzrO!?$l0sSMxj<5-Zb>m|=n?NT2|_D0xvAH7I0QtdNQO zJ(_tKvOPELAeGLPRQL_P-^s+nJ=g@#ux^GYXpUE{ZwY%4mtMy` zdD-kT#=b{X9jwOZtT&0DvoK!6%*}kuA9^XrlfM`1d(0Ud7u{|%Ik|RN`|DOdG1q6r z1{16?I=LhQ`+2%b^zuJvamYnhSH{cONPldZdayI)YQEYRt-cIG5jmdDW*H}iH2NvA zXgf!$iFMgbydF8^ABJ4ZTij0d*P{@5ob|{8DVHQnpw}3AsEltK@!{1nR%n)CuKi>d2T@PY-k9ymfU~yL<&J9ht@~pg zsbzbf*zY^=DK|Z`I8|Q)#5N!|KM<`AqzObvgjXQiA^fxJ@?7pZ4#J-1X1&T-$G6IG zwWs&6zh2u%wWs3C<-V>x*>NWm*ksh9a3>h2b<*&_(vjDOHIGxx3MDOMLMqg4%m2u< zG{pMJd}m0u7SG_YTUf2_@uAq!aCI78P`uu`56<9JF*em1t$8(4-nZr^QMU)K7yX6e z$OG3;c^em`w#}qp_VU1WdywMw^1$`3MHICA1J`3eavIco(vn!eGQfG;himmbayZOd zF+21mmL+5T*2{mEFA5+U{qO65&=u9G-(S%t(!U9u$k=_u#4Agc&UD^ zGa+fiXkX27H zll;60td$0~ShuqcVcI}V-QM<8lXBOjVC{hjqV&=bm-9K2MXRc$TmK#(B`Ad84-00! zBIKOUPopJ*M<^S2;j|FIWpNa_G4`${Qu5t?qnCl{`BrVg&HY3nNT5$=N+?!)N!!&q z&I0Wm_pbgc>~fOi&LgRM{h@bR*%w$JOb}s2b~jwpjC9GeUhL@tStLxM^@#0~9vNmk z!=bWPtm!2>Ct{ZaWhL_dg=sbxtI`?UY(s{cWdi36hm`YjV#_nu1YR2SRS^ z!Fzhk4da8dp7>^OPI}yycYu#0iI%6cHuUPGL#>Q(>QOw_6w1nva1Rr@{_#58*rSS#BR!2%5`H^JUW8LYM5t6CBi-t*er=)B!pCRzmQ8EXmAzy>l%Hj7up{f%TBR9RMK}mW|MUBQmIAG3NCQ{u z0~@L-=DVK_(`hN3LD;F!`p258yoJnVXF-f+t5AL#Gh)z(``7@hIuwzYQrmR zc)bmOXu~vFnD85H!#*~A?<`~gk?l`SGvA3e9BadwHoVY=SJ-fa4R5#MRvSKL!#8dC zfenw@aKLnv&M7v$(1wLJth8Z+4R5yLW*gpX!-s6R(}pkF@NFA**zi*u#-C}@_1f@s z8=hms`8NEz4XbUq!G@b`xY>sH+VBY*9d$J8PZ0NV)*KN4UhBw&odp7*J z4Ii-K9vi-9!)bOs>dNKMGj=^bWWz&Fy*eIF05^{lrEW?MDl)L}pn=caZD7w}?$3;U z-6_4hNBVaqeXvZvWhs-7X+5lf9K$B+5tt0KOO70fdIn~UFN*aWqGWIRR0(`9SQqm;?N zf}WCJu0`s6O4%h}PJRrmb5 z_^R#UZ!!5O(IxNhvJl^;5x(=Gab-l<1-N(rmV7wrDq5MOr<93bz9l{>hr}cKmhh~6 z{AaIRd3J5ML6z`3-J8$PE68eo_##~X9U$&QBAml&o8Rf zpQNiuOA)`st%y_N!&DM}wIVKwN6jr=rU;`J6a|7cB{=Y#TT^ah(4{O`Qycz*UZo|K zr4bejgXSy0s#5z}5VT=YK;n_`5=P-q;YZ;vNhnuTbWCiYICtOpgv6wNp5*=m1`bLY zJS27KNyCPZIC-RZ)aWr|$DJ}h?bOpIoIY{Vz5Z6Eh{c5UB05M{E90pR#sM3f1{>0 z5WMQ@RjaT0=9;zFUZ>_%)#R)y4;0i?6_-lwuB0s$Q};Erf>Je!mQ1^kQj$ap5>jf{=b z56da_3cf0J|1H;JTV!0~UQU|jxL5G^8rz@ro_O86O#I@n1ovX?Ek%|D6Jgeb?QlKSvM87ZZSbtSekQhK$|E6Kmfdw^aorI%W)CB_Qvr%Ely zPU4d~bxJ1VQx}~kYC5eXZ5dN#%<-x;W`ttCYSgKGEhoN8zNO5PC$W*1AoP?H9Z#uB zokwXwW)6_@Nehb%nXU6Aqp9R;lCE88PfmSL3DqbeZN0_i)ooDPv6H7R z`c6@2h2wMb^VRC}YSQXG#op`G&|wOrhLiuVo}Tn9>9hZx^rnZ?tEP>bHgFYj)extw zIx3*r@jc1un_U!h@;@yc-&fE7<>Xw}N~=gWKpz$gIbYHuom%Wl&8hD*)QoU?z14RW zwJP;xMndV|ReH3LQL~gWQbw&(9fQ-39B9gOMvwL+xsn)Vd@y5MC@_T%IE1|lKfkF|&gSBdxJJjbsld zzrtj*-;$G6{j?eC%Xx7YqY$^PD&X#8`vLjSVtZ@HWyzm5ds&J_Ut+hTu@w7*;9jl0+WuC~8N z+23_;()`k9?#x3GPbjc&-~JeK}L)U`k?&MDuWdjps?}#aHhxMYIGmf zCn`B6CnqOXe$&&5OFVir3YNsV)miE3iwoeNd%e1exeLn*`6;!kdKEu6K6rV-?FP8{ zC!hcMK>_b^|I!!-&A;Q_j<@ksGhgz_+~wSSQ@T(7$RMZxp=D*v4D z-v6|L>tB@XtNnArAK#+?S(|^<10RkcF}imB>egLf-?09MZ*6GY7`n0Prf+Zh&duMw z<<{?g|F$3e@JF}*_$NQze8-(X`}r^Kx_iqne|68jzy8f{xBl0C_doF9Ll1A;{>Y<` zJ^sY+ns@Bnwfo6Edt3HB_4G5(KKK0o0|#Gt@uinvIrQplufOs8H{WXg!`pv+=TCqB zi`DjS`+M(y@YjwH|MvHfK0bWp=qI0k_BpC+{>KcO6Ek4G5`*U7UH*S}`u}74|04$3 ziQP4W?B8AfSk8mxfZq9y;9F$LoF6iZ-M*Xnj$BLJ)Z?4mzunw7_4wuvcsKW(dwhSl z$G1FL8JV6uYZ>`1(kHT}ZpO$-{CTAguW@mCWl7c53j#%fa`>UxFRCrAnYZkU(&9jF z*`q0Mc+_&!}WE8Vq;m+tzW+$!l$R#71V7|Zk0AZqhN6z z>opd21qB-j>P@TLP)8`mvaYPG%X6^@^t?zN?XK!meeS#+g*)&@!_eR(BCFW1F#!gsk>1p~c#u=CgD4_bbS zzeUuG!zXcg%f-};a3_RUA-hr8K?uJ?ILLQ+pNIj<;)4aPup!stnXrRd~ya zDoZL#YrH+n*;RilN&{41dB9s-RZ{A$TJEiOc=Zy~B+^}laek9&Kegm&GVMTeF&Q`6 z)jPkORn>Gb(=trW6Yt8E6X0`$Usb$wOqb8}>qxrm+(r5?Db-CO(vLS-D}-6JaPCBN zVjSsTr#yblcyEzi3TZ`=p-JI*|D(o3+KP&*t0iIy-J>}eq8%5mdyV!;rI&PyYE}fL z!fU;0rB^Xhl`r>}uB;BMKJ_1`w~VG{4`M}Rw77`Y;524wu-=uWE351y!O?b49IZ!G z>4#o*ydC_r1=$O3T{GeF-?yBX^Mk`lj~;vLYw0eEI_K=AGC$QWy_iP0dMW2+GEvno ztu0?!T~T_uGY&5;DX$GI4V*b`Qgw+Lhz*%e_*dfYKhUiPmL#fy(-PFc`JVkr%?Z_S z%rWu;cY2k25|bqY{rsNtD)lDD`R;#Gj5=w`;OdmZLFp1k;@dY$slQ{sW`}VNjaNeh zNopu*3|*L@hEC(VCZ&1k#H8sXcYD;ZKtDC4B#HDBm1k;vO`q17{ZYcqSi>9$aK*={ zc*5XP?MiT|1WM)_6t4zN^Qb{nk~{jfChm`Kc2~z0_9^HuY3(MB0I;MlX}Q(V`6>II zytSOJ)E_VbCvUv(5kq|ahsUbnvs0T*NtAN@Z|uz2brSq&?pKBo0k!)_k5e?W6`fh#p$rBZLH)LSZbkUC%6 zSN9*(M-3`*QwMQU2fDpTxpHSJwFDC`SDz@=XMWU|){ErtGH%9vgn7r#PZaF4AsFYo zHyRe7%Xu-zNvnVVKB_-?>_0_XaD1Udt9!DPdLHxFFGz@AU)`Sis`&YR!uj6j<4k?F zQbRvC(1o6)L|1?1@+K;8Nq^;Cn5?|e#alDHMYWcpDQj(#kqc@`;E{~o8&%x%-G@%@t4 zZify%esd{8`b!yWoIFS!)kLKa9qA@b_Tn{N{Ym@RUni3*Pi z*Oe%BD`usgrpcG-A5I&c%QB(>v%&UL3NH6Iw?yW13TrdLxd&{Xi z1Z14Bavf_KCLDG^j2bX4Ne#F;p}?j4qutMj$D2B&Zim-&)t^JF*RMb`(3L2N?VgA9 zp%WA6D;KF@3k&Ek^VBfc`O4HhnOVblL8e^86V&iPD(zzk?PIVS?i!#>uf$D{iS%#k zb13y`_wVNZCuldnLJs9*1ZA9dWBNP&yu=<)=cjZ;_V?v1xqgNDi=FR@;JYwG>^|U1 zajO)@mK4U86xveCl>W{AkGI?J(BWq=>i>Y5;)K`vC+!l(*@fY8w%OGq|1KF{Ih1e> zaWlsERYMj6skoRm1Nj|E>M^dzzD~6AKg4<7vbFWlUo18OFRcY|4-h zLpxLF(oeRs6M7rtJ|-~{mmaGaqsUL{G`C8fV)sQU7jaO=Rx`VGjSWBk9%BQhD-Oa@ zC#lp)Ds&-^>Y?cgYUH%L)JWIus{3q1qSW>N7}6djeX}2ZGl{;Ls0Q7fT&-!bFrG1h zaey(v_+j26e}l;1p!v2R>d?curTyss>el_Wuh5P$$*F_ITTyR_DWDDny2i$Lh+95aM;2Ttu*(=%LpIGl%Y{gmgvglZ>USHCFLZ%Vv)(e0)u>`AZ3pI2%J zM%s$N{zKwvgRC_e2Zqca*x|GWhenGIDD_9oqc)99AB$K=F#kGzOyb;gkn!mSrCxPt zdNO1E%?Yi2_s2EIR>u@Z7eu8CO}l8(HNOu%GeM1;_KoOquI16awJGl~^7|$2_6My> zJ&keN?TO~TEB~O>Z!yl?XWDWJZTV}xw&fPatuIS=`}<10k8#pVm~)T#81>lyP;k5VVO8qHdferUe&1l`l!_)F}g66srs z^UeCuH8N3+4D?qcOOol+{nW^=G2dS6bQ?cfSp%IYudR~Tp;Hso=s>A!bV-S8^t58v zXxGz7)@6QM zrV8#-&5pb~Ulw+oqq_XqUN!iSe7vE{f8^s09sak;$B%SHii0+};JeN-{GmK{)Qi=G zm<6T6AS@^flr2`*@)gOgg?nc>xN3`{{{b*X*tc{w}+L*u_QVfw@&R z3t%)y6x>0Nv!l^KXP`BFU4aekD>Pi!;#1xt_TfT*hog?g9rEU?5EC__%Kb0~_J{PX8 zE>)T0I;X0#wyL6ZPN1g3#8RU!)%L-f8ki>83 zj#*S$rkg}b&Z=TWzX=Zkh*YWjrJN^pj*8B$%`ROQT(P3Grl6*@7GkJVV&(@bE-t5% ziYgXW!nb0-Gg9pGs;aIGR?mf1E(wrnVG5;+%bcQWO89(N@`42punm8KtTHlJ;YI8{#E8#scxLDh2n=VTL+@7t?@rvs7y&4dY@6qz+O86{UfmROHZWK}9L@ z{F9^e=HwSu(~4eHm z>RPTqEG#FTT1inb^=*565sSsj7oAsCRFYS|tcEKOl=?N@2IiLO_3<~_LlMN!&ee&RkDtBlgoV z^39a1zd26P-%M*d%zWE^femGLk@zpcNZKrZb-0y4FNUc}4acy+)cKcki2pi_M`QpfRX$lAEPCLe`0^%0hIjx93$!7jS+tjW28*aVZ{9vjJT&l6rqn8q07Ja zmwdvXN!NSA-@i6r|F>d4vGASA!HI>x{%_^*U!Tqin}9t_pRfsd|MhwMH>B{tyh#+~ znDv({Dn<_=`)vOY;s5zN-?{T7^`|?nJ2~j=@e9X)?HxMAMNB9cz4rCjyz27Tu6S)q z58sT(FC2Qa^%JGexYmS3RaWPm2w#5t-buC%vurrih8Z@TX2WzFrrFSI!&Do(ZFsbg zq4Rq-Y_;JVHauj*7j3xThR@ir#fH0W*lfecY`D#a57=<44Y%0vHXGh(!v-5V@vpJJ z12(L%VWAC|*wAmo3>&7~@N^q`ZRob)(O6UNzD)S82s(Gz_LdD>ZFtCr`)$}_!)6<9 zwc%zPZnEJj8y4EIz=jz%Ot)d04ZSu@wPCUi-8NJ67^?HGPnht$A)*?=`K|O{LVnuoY>z2TssI^0Ps5CKFk~7 z&j6E9R9ctjQiFiYFk8mDR0%L`2)ujz2%N`-=uO}Sz@=>5mx2pCG*YPtzy-dIkvNr? z^BzpW7?<(_zrZX6SED%3!bn;HVC-n(#NG|e!PJqi==^LH96vV#Cyp_AI&kh-(!#$V z*ou*~1b%OvDeq<=dcbs8fp=rX&lX_9cw?UkoMq!J!23@{R~d0W0PMtkB>6c_snalu z{G1LfJ{=x`&;*z;k>Y_T0#C&hh#%nBXaq~ZmjZWUq%6CE?_wkm9|6xzM=lThEZ{dW zLgzKWUt`42R^Z4plzNPp8@<4DFcNWNV zux2J@!A}4;->+am1XP&M*H9i5q}Ku zo3qhD1il7%6GrmC3HTbDjxy{;R_WCo@+mlQyB`@O@W+4y&nHgsrNA{92`lh+8yEOC zM)IaEpqerJ@t+R#V-A5A058J40bU3!!nA^y0H^06j|-jwtipT*UJZ=TC;!x4B9Lo1 zDj+X#0x!l$9+m+AhLL*z2v`SmOz0`F`cmq0Jn;ZeTS`9#KOOiOW+Ax1GcKp!flmVt zDB_F}96fnzCPw0~SfPi2)u3u>axM>fUYuQ9|L?9lY#vkz?5=hp9-90<9=Ys#%~1v4wH@lX5c3np~L6E zd#*6}y}-;0+8cfXz#n2H4=uoPRkSzoG~ksO$$tQNH%9zy0bT<$@m}yXz)vwP;GYAp zt2KBXFg9RtH*gb1>Pz6+LFyO(Gl36cWc=I)jJe7#FR%mSK9xAd?rPc!xWKqorXIb( zKC7uC?A^dTjFeH}6cji}|C$C|^G(WvAAvu_NdLMW*ol#{h`iJYjFiy}T#MO^|E<7d zn62PyEn4NTC7csuorkQM#|U%Z2AS?*lz+pd6%J23o!p~L)!x2w=fd_2H-x7ghel;ddJ2E zKJZK9U*J2xGGnR0`|mYl<^#ZA{Tf=4*1f>ZzcF))z(W|RFM-LwHMqcCm{$B3Y^7Y7 z_rPxf&fEt7cmiz(*l#=I2zWAZHb&~S8u&a$^0{B|M`<(o*$?dVn2FyDy!CNTeX-vR z{1Zm{y9J#5gu%0b7N!nA0`J=a9~}Gv;Q2eD8+ab@SGy=L_`Sf>c2j=vEMQI>x7rku!F9D8!#o%ec zGK}~an0d&w!A)nZ<0X~Kidx0O@_)*|RpHd&#F9hzx$e8d9Fzz$z2zzv)s?#tM zR_^J@y`#@*O9JJdkKh93uFO`(B7t%bM(hRdwsE-&Blk_jUZC775&r^*es1gqiVVK^ z5h(W^1Q#fG8w3|9_YedZ_%j=qy9jcRK4*h{2a#nJvb@yloP3GDZuz`pea_8lj%S3(5)7nyGI3GBTmuut#BUii0J*caT% z*bRKgB%m^W!5Bk+obSTB7)#w<-|pWs#!(55d-VgjkL&tQeT{D_*>P`v7yrcVe5d`D zZ_4C+Z{picB|G1@{f%)UBKsz;prkm90|EjnD2jRYTG~CKf}(;9j-`S_W#v`5w_A=WU}l_y z;cRK5W@TpKRGAH!X*pz2PL$O8e!tDxcpy}=`~N@R_dIv&W}o+c_psL5YpuQZTKl|b z$`mXexXhD+;?%o~r%ahXofQh?KyjWNxQfT`;o21!1!#-wDN}fTCJ(Ru*Vni!$`yYK zq&P-DpEAAB^?NjycOBPL@?4A9J|+hJGo`TpfxMWQJdCfeb^kFj<8dE#QrLeno-?72 zuFv*N!J_yR(skH=(>E4cHakemm)O3Ti4G!@KNSp4-^&WrS9T2=p(-l&Mhj| zk69n|J-fU}^kYqbc5$ZUG3)E!UN~0rSog1CW9RH|o+Itjm#WxfSIUVhsR5}lN1bvN ze0D+BtZ{>q&sop89s8*$r{KP%Di`D`$kmZ+BYOz>+zr`+d@DZh(4hm~ql`D#4jQDM zc;X2So8j?Aix#P@tSq%@(A`~s=f?EwU_)sg7oIpk zWV2OfKI@e`Y_L_ONM`Ql!R+r1{P15PuI0kjt2s(@@smo;^zab3>K>x1I5Kbt=_<~q z4(l`5E};`?6sYM0#dMR@tXVVV=o5`+JE&QY^}6t%ff)I0CIquzi2ef8moAF4x#4ymI@ zkE-LxkE<`g{8AO1KBkWRBPC~Hp_Z-R1O(^s_sL3&q%EQ39#K4 zVc(g9b2n%|x)8R2)KSl&{c^NlhxWVB{xI5~a%$fk?E@oWFDAfFTm<_t2X-Oa?)Xc8?--*_62Bv#i_k-bF6PcB|Z%9rE?ZwuY>(p;oA_x!*!G#YpdkTfl5v# zDmj;?q;S2GD~C$q_q`A8J<+}mY?dBqAA$D6&^{LJr`1vNTw5h81}b?yQOWK!^toQi z=|k7sV_l1^iuQM+eGRm)iT2Pxg)oN1a<7seZIuiksN{)6B`>8Z*|J{A(L>kUt6F$} zNLwk&w_66FXM*G6j_7CChAbrt8EP8kbJ-mV*_M(Tc(L-S! zQ9rd6b!nieD~Y16riuD_y{K!4q_q75Xx|F$1JM3qv>%D~lhJ-2+ApaiYE4^Fn+A&7 zlPD@LP1G0bMV&u%y}c{ilLGgn{R3#<810**{X=NqqmHPdZAB#x6g58)eWi)YT`%hR zq3i9-ss?LGa=sZH+%u?WP>8KPsu77`L391su`Vr}K^<<+cd z@7_(UjtU`Rc#QvH_z)6e^=ZKhy?a|6&%+~vL&F2meQ0n{K+hoSL#)uOnP+3i^RS4( zpzxsJkYKbYWUU|a@#*1!kyXMY!cFS+fO|b_YgAwlO+6c1ngGZ!_8%S|77%W2RJXo6 zo}+@dqe62l+6M%&gWz!M19j`xFL|zEZ0czNkYT0$TkpTGZr#5Ax&t4LWn(-C{2^=~ z6prWj)os+*0b9S81Oe@>0YMS{!^1i&C1goKAighcduu)+O~Auxiqg?Fodz@PmG>mdx) ztlixK06yXn2H1Lc?h25BRMiZ378HctnJ?L9bqQ>(&JlrO&OM?z9H_ zhGK~Rc>Z9oUK-1WrO$(FSF7z;t4(NlNCc?d%R^(?sF#C10=fnTwX0IQTAKiCgf${8 zG@=AbsF0Er7Tlp{uy0kDn*ALQNUN|As4K^FYZqU)8h5(5)ae5V31UQe02t}|^PtWh z+qADy#r57WJPhs$UhdE#IQaT=tF>p)fNr&_SL=ikg01X5Eadw5)?o78fNuBPQ9Y2) zg9E^>;ift0NBV%l0`IC*Yk)O4BA^?1t@JUTo7BgxPM3%v&@s&EG3g(oYv6w8_QBmC zD$b8}|CrzK5HMm`XvePQ9y|4qi4F_uTDCRhqil*O(>e63>`E!Bof>cg#>Fn=)RO#X z?70;5Q9esWwp^+TC}+>z+)gp}+`|t)tnHDR(Y;G;k5{MmQ1fC#)#9XJwP|*!%2_&7 zb>0V?0(!2`r=o_ycA(8L73GUBzNnThTc%d5SfSF>)79$LtF^7LapOj955D>4n~Ju= z$Gcut%V2Nhz*gA1cdz>BqmQ(0aOz~fI(hP>I(6!lI)DDW`u_Xx)lWYbXxrfO<;&{V zUw>6;S4FLdt#Ig?lb-c$jD@Z<7COk~qLEnWreUF5h=p#Asw10JTiK}w%087SM^u`8 zq1MZ3tl!ty8Rd%wG149FvDhwp2<DV5F+?7>+^2|_h!DnzAgF5(x8ERgT^gA+-lW&5cY6=Pw!^U zyqfveajV_p9$YtQ+{DWZb?dl!`1<(zsD_>`y?qKG;pui()TV`*P1n|RlB=I?b;2h-r1mKqq|&d@`kUQ$Nkk^ zs(YfYuWzFo)!6@E-0!H1tGk-itWm=U`+&yH9;ng~Ptl|it}(t(!~49N`1tzzcmaNl z-_pmYsgF-%AB|r_BnwjG#`;pF)P^s5B3Yh2Waby=| z!QVfX?m%l;?E32qu-bK54JAkZ|B-Hl{msTEymukr@8RLm7$tL4=(P6N+r_P4Vmn3S-EoM->~j%FDNMZ_J<#S_zr8&$qN@Q zoJN^{>C&YF>`PC5@x>SG-+S-9=W$-hr+fGA;Czh}77g7Gc2|1d{s{dz*IbzIZQi_j z6O^PC-PDyy?!$%+3+RM|$`8PgxDfkj$<57`@4ov^u$~J1E_6^yety1S|1EqD90Yst zFVCDga~8OMefaRB#&{8uQs-kQ*V_gRi1} zQBe`1E!Y-S9{&2EcOGQ01N-RQxpQ;E2e>r+`}gk`_(%ot$+m6VG~DnZ$+zErD+doA z6!;W$J+^_3!274rX65?z>t}4;x^)K3zR)>y<_t(kNC;W5U_m78YQok6*{5E;dhOxc zc(h%+cBGwqNJxk``^D!!1pmd07X`LVT9YPCI)a7^&Ye3a@adM)IWI3yKKtx54JYPD z!_K)mapHu0{`u$nnmCyZn@4{J&t3ZQ#~+Kpch}J8nO}bSMSl9}r;8td{P7##eDh5{ z#`>9Ufakv;Zyf={0=E65@WY-Gj8&QmzsaPl$($3D2|sDY(tPGMv%!NU;V~I7i^-UC zG1=TR*s){!GjX5}Aiq&gI7ftS%MT*a z$3+4@66x^1i0?ZhKD$L)?-F@13^Z(UXU+K)xq&ewLc*zUIg>aMp=ny1@nC z88L(Q`+&n`6Y??WPaH4@ojwq02OQ!*MZBXJ{_aB}ZFXw5f@iT(sXqLqnlE>jK+S&p4+cry!%m;Y*P);1CKNaxOU6xZ&p?@nq=@kg5Ok&p!*+ znKGF;m~8NBK-hUAK*|b&&0tY&#kGGpi`Sc zCp88R9@|QEE4rCg4hIdtr>AGB{>K{6b^dqrLHXr+VCIAJz-uEXgdciOcD*!8Y}2~S znkoL00UTBU2c{_K&*8vfD0KW^Pl^x+BhSRa$TM|P+xI~Cw?$eI2hiX0hR6WuDWs1o zf*<-`^FL&50qr5moWaB3!a3l2;5;V=7nX#ddUEruAX)chH^~AHuKLl;&8h)H4DwFC{&bYq7&MM8s!Q}FI7+G<~ zgP(~5)7F$OvSE5xS@%RYd2K3inCvf?3(v`?5NG0G1rCU@$={shnL3H`90XaQPV$3J z;`(fKl2@)slM?vL>m?08?ECp0J9hMhjWU;bP%pA1&kzsVJS>R|?{Td)b{U^>y`{~- z^k!-o*)p?>tOpL+z+o+L*s)}STq*$bR3e=Po^hBHKd#Tf!PppHJ4+gup+59K^@ZX8 zu3ft}=UkXfc_rM0jj$VBsQan!xv!z#KY0T)ImunjnDngJXj3zTQ#KzXK4S^~XF92OCWu|4IP zF~A`vL}rc()i@AN+8A7)jg8R}>l@PV_@6q1YcTBy=Cy0rip^#d+NCC&`Cxf7Ie}kR z4!#^EyXSP49kYM~WMLz4c-!#RfV`1tt!gq8A08JIb9 zrpCdUzsloMeolBzoj_T*Rr+-Ax>6g1^862nJeO;CQ~QVTbME`}>BD)D*Is)~+g`M3 z;2)65lP8zra5Fq^HYbFKI%yMi(yinfI51T}>xypGC!KKCW;b-FczJn=)oK--%@Epk z$;rtwapFXoKYzZKYq)&0-k~j25iX=X*9Ph;+CsEPxgTJnJm;;MB*njAzmYjvug}gl z#uA4-FM=#ERRpZL2VX^P*!q9PwT8b{t5%(Gv?m@v)qWm!w4R5Ij0_ntV1VFkk^JKy z{}9BKuK2ccf>guJ7ie!Oz_)@0+|qA_wCzP+dViB zqxC0YC#;+!;$SlGk>2Dz?sI6{aV{tilxZ!`mGm3&UNdljiv}n;nU|VZg2nX#Y=S&<-Hh8dPf6U}5(>HbNhbu_LMFCW2tSK9x8vy?J-?%azwqa?Fu&%XTVqmRn? z@#8h!v9Hv;L>wp=^!pGt;!z$K`XIOtlkaIGQC8@W$i;q=_h=(=oggk1B#_|euQ*?S z)c?c2AiJyJqs)Ku$tQm!oH#eCGiBP~(6VJq!PzxE4_q_I^R#^o9)y?hljn&i_XaE} z50nwggP9YSEIS0hSWa41{f3YajYCval=SP@PxBUH@)~~9 znlvXagqw0e{loj*BanY+Yv)1^C>LE3hqis#wbd=3BsTng_T>kVdePuwa9~M#Q#WDl*82qV6xUj3`Vv-N6MoK(u^|}O zA>WgiNOQ^%bjD)9aXYY=!tc!g)Dh0}QPG?bFZ$Sr4<9Z#m#g{P(A>FXJ@OKHo%gv0 z5OxzE!5(SFpMYJ%&oRvY7i-S~@+H?G;z1cCov1&k>$n~{&k1R}Wy=;>x^$`LXVTkb z;!WP6?9guFHF<{paV>z(d=;-?xLvvvb|&qAa9$S)zsbbGL3+W!Oj()(Ym-*brz>Ce*O!P4Ntk~F1$p)4?SZg`LL zK;20><(yF$F_yp>6mcQlF~;rand=aj{iNYH{1029IqAfFvpJ!CPy2^>F)qhU-eURg zyYFhfOBzsK$?t@pasYl@jp6?+&p2aHpY$~TPw0tK{||BC+DZF``j33T^@wXHY0Nz* zY0r6}Enwz>*VOscwbWzSQ|Pz`aQ;ujPCPXKJM%_)J~!7!Zk-bdppoF1b^t6szj;Tfmel zC-D88!jcl7^!0zPtm5d;#ZlthFBlK$PnM=X)4vnksq7xtyBy_-KU1bS;FOZm#Zgvq zl+_(&ZAav1f^D$ohet<84}`rR0-t{n?zcgv&3!*|DW9_MFN;JbBi7UBlad%4 zeZh<=v_ouXIDEsO{G>s;84Sd=VYO=2I%98_2$?RTK7-A98T$4t)`IV$myW|e&w!t7 zENtKq)W@4xlqc+sg3*X^Fiu8aXdwKzjCnI&eDtD79=vk+J`&RpsDGo>2s%6yV-`_= zQb%&XL>QP&48vjb{|3E!5jyt>^yZuFm@!kv8yV+fe1$%7#)m$>ByteAS0dKyK|Kw> zLKJ*YLkI(HC++ca$HOniOuW4{?# z*n)m#`T{fI&t%LO-=&jTkTGW%$d5zH@gvUfYnk9#8uDYgIGqQ_M?578euW{sbN=nLD+Mvt^ z4*E?<3nu#48EayUgRwHknHb+-oQyFh#x@uuV|;1)$ddfx>{uZTnjaB&HL-8@=R7`j z05feHW@k7E3+I3LGXcVQ*9O2qpC*0djIod(8Ruerg|S8*<8q8YV^l8=rDekKqd(|? zR675(%@Bu(!uKwRP#18&Py3vB6Bg2d<>Aa&9XDjWim_Y93>kY~>4+CnCKw-KtS}WZ zDnkPf=B%R`yG1JHf65lV+Zzx4KbEpcy~BM0_jaTSbr0!r>C9)c|6h?hPC)-O`H?Yv z`lgLcFwVqSALC>x4j4FCWPV8;e>lee;wtZy>L3r=m*hdpA#Hw^goBy%U?v=-6X)S( z>Byefb0krC-=-4#gsUXii_nh9}5hm(I z$^qdZ4LCof&BZgv<)id*^2VGXeHOs*W5%>zvfABSyT3?{CYvapSZf7-uB4@51bTNORJI^ymJLyg+&o{_=@^qTXTfmh)O$W_(^C zE;bl)ulIpm>lv3}YzcOj;M}*y+w8vx2Q%qGILglr;p4hR-NAh+y6=Qc`|f7RgL_ce zALkfLWLyH@GZp$@2}5FHqNY3b0oOtHZFqoVQ7;%-#X3=pxXftqax!cryk$o5%+sy* zR34-iXuoL8m@)4%#zP-HzVI)c3&z*5F6(h<50LI0pP4opZ8TG2t@;J@d=h=FM7-hx z#^zSwR!Q?8WsrT*wj;gi%Owxem(I8b<%4h<-xcS8dV%c-3u!^UO`SG2IeldjB!gPG%TJ;FN@v~J*8RpL&b<9w0U#G5)9zLzzBaBMw3$APZV`oAJLj17$M z903+ZzrKmLia_4vfstiR+X;>$jnsq-jvlx^Zk9^?5Pp1r{L0f

NjdKxu zuw{UmeQ+<%y%&8E-1l+c>4$wMeblt62siZ>6ZbIOLv8$bqOcF=bRH^}J{|fIxMw5% zI8R)IXa_NEe>zm#Tikze@4>wfZC&m+?8~1O_Q8F4AHc!+!@Fm6oSQm~bU3j$UACn9 z>-{8cciQdTH*oJXBf7U9V^F@xTI_4_J#R@#NzrFS7+2$5l3xt{b!GfHxI0JN{WGxl zaU>$Une@ZDp!F7Gay&o4wua}{&+H-RP8`wnU!!O&LzIPu4zhL z&AI$)Pljxm-CLjMUNN<=?0@rB4!_y7$9Gq>tw#MtzMS@Bwen<;E*Yd=BSA))Vb49)1u$HRvqtFqq#Rk4N_M84aIv{2I1{715|%phv9E8 zd}ey9Y5{CR7yXY4iJw>o@F7xF`~z5JQVgs}7xXs~zm^rN;?-#M7K>lhibl`zDn?B} z?Vh?$8|4RgX&2-Ucn=MLSK%;6TvRaX#Nq8OQK&x+&_v-XT8}XZJ@E;9v*7(TQEC$E zjKsU+qA{994OYqe6D$86Uu|_M9XkZ~lTni}B>=Wqj1ga{K11<-oM^P1gjP|2lVdZr z!!LbZ?|+CMH`%G*ws_Mza~oZf-gFMTq7CUVQP+=0kK>&%oQQ2e2h@+&W8%HostM}% z!}VxglaxsSO^C&4U_B9i{{Ef?#&j^gSZTp5RGwab8nywBzBzM*&CDIIN@k%vjDau- z@TN<&o1jL4N+SVP<>!nWAJVaG`|{MRh?aD2yMTM6FtUO2I)~8-Pjln1cuGD>1};&6 z?E1XdsG3u?dsK3AbliyX(=1ct#>Xf7drnG<@0vVvOmtjSa;v!5kx3JhC&Wx_HF82+ z*Qn&U)|1POV`HL|%`fTtd$w)e#)+h1WN+=79h859x{*1z}Aq4>a=;+L|IWt_n8I~8WQX1ZlYW)96%Zg|Hu?(bW5Y}J`nMXOxX z-P66({nGoS4^1DFJ~e$_`r`Dp>D$uxr5{T_lU|hWn&F{QuB?4o2eXc4oyaOlCsn)XdaO66U|@ zzZCf2MS&)bL%I%!lPL0$;c-zDqgzFdj2aakH*I*rv{noNnYNqk~I>;0nn7`!-+$SfTms#S-g;)+O#O`BzkJ>L~oAh-J%7Mb-Cj{{az0I gC_x42YC7Ogs9@yji=)ViPoU297H58MhmzyJUM literal 0 HcmV?d00001 diff --git a/venv/Scripts/coverage.exe b/venv/Scripts/coverage.exe new file mode 100644 index 0000000000000000000000000000000000000000..dc07cfa8de764ec8f315f8d8c9cba49d651be744 GIT binary patch literal 108416 zcmeFadw5jU)%ZWjWXKQ_P7p@IO-Bic#!G0tBo5RJ%;*`JC{}2xf}+8Qib}(bU_}i* zNt@v~ed)#4zP;$%+PC)dzP-K@u*HN(5-vi(8(ykWyqs}B0W}HN^ZTrQW|Da6`@GNh z?;nrOIeVXdS$plZ*IsMwwRUQ*Tjz4ST&_I+w{4fJg{Suk zDk#k~{i~yk?|JX1Bd28lkG=4tDesa#KJ3?1I@I&=Dc@7ibyGgz`N6)QPkD>ydq35t zw5a^YGUb1mdHz5>zj9mcQfc#FjbLurNVL)nYxs88p%GSZYD=wU2mVCNzLw{@99Q)S$;kf8bu9yca(9kvVm9ml^vrR!I-q`G>GNZ^tcvmFj1Tw`fDZD% z5W|pvewS(+{hSy`MGklppb3cC_!< z@h|$MW%{fb(kD6pOP~L^oj#w3zJ~Vs2kG-#R!FALiJ3n2#KKaqo`{tee@!>``%TYZ zAvWDSs+)%@UX7YtqsdvvwN2d-bF206snTti-qaeKWO__hZf7u%6VXC1N9?vp8HGbt z$J5=q87r;S&34^f$e4|1{5Q7m80e=&PpmHW&kxQE&JTVy_%+?!PrubsGZjsG&H_mA zQ+};HYAVAOZ$}fiR9ee5mn&%QXlmtKAw{$wwpraLZCf`f17340_E;ehEotl68O}?z z_Fyo%={Uuj?4YI}4_CCBFIkf)7FE?&m*#BB1OGwurHJ`#$n3Cu6PQBtS>5cm-c_yd zm7$&vBt6p082K;-_NUj{k+KuI`&jBbOy5(mhdgt;_4`wte(4luajXgG4i5JF>$9DH zLuPx#d`UNVTE7`D<#$S>tLTmKF}kZpFmlFe?$sV{v-Y20jP$OX&jnkAUs(V7XVtyb zD?14U)*?`&hGB*eDs)t|y2JbRvVO)oJ=15@?4VCZW>wIq(@~Mrk@WIydI@Ul!>+o3 z=M=Kzo*MI=be*)8{ISB{9>(!J__N-a=8R&n#W%-gTYRcuDCpB^^s3~-GP@@5&-(G& zdQS_V>w;D8SV2wM8)U9HoOaik`_z>Ep^Rpe3rnjb<}(rV`tpdmg4g@>h`BF#WAKLH zqTs?sEDwi<=6_WPwY&oS9!h@ge4(br)-Q{|OY*#YAspuHyx;~|kASS3FIH@oGSl?L zvQoe8yKukD)zqprHiFKlW%;G=hwx4l;FI%8m&(#zU|j&_bW@ThNpr9D0V}xa)%aIb zI$i2CA2mPU{0nJmK0dxe)dY-`z>ln($ z;r!UXuLDDi42|Zd3Erx&m8GqlFWbIX0V<*Gn6lVNq%gD>gw}da}r}ZQB~ns?p8uy4i0%1Ti$Vt|~OUth4=+yEmPu8{3(w zUDkd@?w?`_J9HBkx&ZF8v{+9phcT@3J8VI~wN7Ez)oJS6^dhb2N;;{RTXB`K*E$64 z3rDqRtY&&*}9yq2oUcvD7K)=@bWqC1X%l0jk)W<5-WBYC(#rn4H5)gp#eHMmwlLJq=^%|*gMQ*pq4VV(QhHA4CGj<;!d8i*#Z8CaN#*>VcCnj~;kkeUa{LUoKxFCaoQ) z(Lz++&x3Lwz;=6UnhwM!MvN17>{Qmb?dwgsTmzkLB~jD#wiGz73hc0bFE|C9KA#|= zH}%FQ>c&Y5z*TJD-<$$Y*WZx>5NNe-E-TfAt1!)%Wc@I;ZuNwxDGGasDIMyUNiVvG zq;Q70PYHcLO=Xgv2698@cJrkun-^>P2}|fMHlm7xaZmE<{&cQtb`{N9zj0bRmpW^T zzQV7oTs0ENHe&mxQ6DI7qd0SU4;3o*2qRd`X1>(=ew})X5Dx zx$lyzZM^emtdsbk^u+xwdSX$lp7h*2CkHCqDohShL)V4hM9k+UQLP(GN-H7!C8gyq zex`xuPQ(!g4}S>0r+CyH+xIAMP9Z&+?BT1!*kA<}dqRn*FwJPGe}l-sw(lGYN1b8} zWQQjQN`9tdtF?#aqMN?wu4E3)qGxzOhwr*vb;kX_%&U*-=KLr0raiGc^x8|=Wqt`N z?L0luR(~BF;DS@~yKDN7|*TJkj*-B%s1{65$`jY_(C#P&^rVi0?Ro4iaFbR)Z2NLxS0 zTL;%Kt22(A8JiL`U$i!iR&zLxx^E%H=*c-=+h@sisygu-_#m4J4LQqB?~vXvP4@yQo0-^oki(PiH+=FZl}&W)S-qI zk>W;2Zl-vl6rbe4X6feZb)l-Mv2oh^5t8q5@(Y-SPoUZ;N<5Tdl!h|=x!1}5)E;}=RcAXJ8(<$^13IV==^rU>wwq$hX3V4iuA0>h< zuxK^)myr=p7a)oeZ+g4u^9(OmpFl8J@{{UJfy=DjAf8lTTD00iSF3Kb9|GdM-PQp)0<* zZkW*V-TPpIXEKDks>&FQ?qoV&Tfa*;TJyB^yJa8xcch+*-cYj6E7HdBX!5)TIXSNM z4C2L57KVd0rioelfI{ELMrb&Y}?h%mk5iSTXrmJ zwlk6qsS{}3<}Uc!G}Wr;Tek1Tym8$SrWokvCzU(FVIAWTEa1pwE zBJ6JdS@$4RFBV*~g^Eo9MAFafx2rt|uRsR%xpNVyj8!g>2u0v=>eO zS~4nHBgR%cVxB-_OwP@%JN(CpY3qHvqsbt-TUGivY2Dr$b+=`6PJSkbWF)!Jn=iZJ zMt}mOG~-m{)L*SV+yRH!c@XR%)K^BqVRh zq&wib)2#d0V3BD*|F5o2J6$vbdJGh`O-30SrMI;e*Y&m8c0Bi^cD-$Daq1haK*i4o zS^0dLE!U;Du-W5i&*6##L30bjy7q7@lQPyCc8<%{>0)|vQlrFG_D_+v^1uh+p+bhA?!)dFEqi$(hoT?=hJt20DQXmOiJ``9LY)@=HE zO1esvSjV70vmITir9t{Om5D&<%?UTa#`5Sp-x@^?6JCK@(Y_-+ye_agHcB_zSUEYe zay}#@o~N5_?G>%q2t<~g3s!Y+G*Mj=P3Zn>mA2=HCm`lzap|)*f|(31R{)36WvAyz zfea$wK&B|2YxO{n>twI{fk3f0YVK4T;XDy#cUe=*$V6#=30zz**pkdJOUUdHcyGKx z={=%tU83}-sM&@LFz=EaBy8m5*VS4ZYhB<>lI{BnIk4cD&H_E|%!spiL(( z$1W0V$;KX^P(?<}XYHqoplpQo7H>!m)d{bdPaLde+h7(tf+ZB(6MxWZnoX6&>|)(q z*DB~wjMmL&u~F-ZIbJ>BJ5ZM6ik)gUbdlBM`Quqove#M~lf*ebB4nBg}NN8q8e!? zVj>HOMJZ@LQzOdvHUSih8gCt%IxvyHLmO^Ea(*!Nd-Zuw>`f87{SkAwbrcIp6hiff zt7^x@FVoBVwDl9eTxT2$))(-5-O9W=qunp;*yvYT{VJ=~FI-x;pN&=5ArA%W0()Z} z=?f87g#Y@j2_ct@T|gzY^?R)mq?NdksZ}7gJW^{18>hCuy{s)%iDWGzC?-DRKLl?l zlnO5zQf3*!v6nJ;)xm`Sjm!6zf=o%-07p#e5?cL}gBtB`Nq!dTtt@<7#(o8m8xm*XOvN65AL(=C_D} zJM9UyYteSSwriu8{DkKl6tSk&09e8kMrjh@N|SS;@9l|6^W@_Q=i{`@$NUzI6|VF> zN{Rev95oVSa&%)ew#+uKZf{3cFg?f64ASokLt$^COgO2#BW71L>H7~o2Zg;=Z|nCM zZ=N18^ET^uY+VpF$K*teqc&2xaTF!LhIKrwGne_WBX+B_9vi@rt2GKHy|kQxSUJ18@{fEswY{>va~$3%JGyYfr29k%@bck16c zdf9Hh?|r@PC`@3R-j=#7868z@m3)O|u0`Iw|bd&(6~U$UMGD@Vncn>Lm}{NqU9US&{gYu`~lU+m1n zi1g$#vC1#v|9B;ObTzhRor!#90$^5b(Gy`buihHrRfjV>-l^6#?Dg3lZ}@PRD|I(> zVcp1Kiyr8xABHMWk$xp&hFzvUhIKbDi1339ve8Ac5ON73NDM}^^I8O?+8zk+GVA0S zG|7G=o9JQQO;-x!z=zz5c@^<{-AWi)tG`b65v40t#CwnzKA}>?+z|q4`eNlNfRXZK%L4$WHQ)8Sgo0 zwE~@9)+4fUIf8fW?9TihJ6Hgttrta)MqB{FTBqxu|CDLzEKWn{Cn*>&wx$DtvzSvC z(4Jr-g8~qe!NL-;BVhBlx}Y;!It5;VT~^q_HdZcH!a^(MA3%zpy!zmpD(NfkvF=9= z6p^lmDSFnrRVn4npverH%%I5(CT}SgTNGB)0sCY%@`7%@lG#4Gt*2;3c3;0E8(QyS zoo-l-h2)DEIh-3t!@^Gefe~>Aq|Sbf{goW=Op7FDAB-5amdpAhatG_BQh1V>p|DF2 zoM~XblmiX(kl0U_veatKBQ+uz9@Z1{N|y`0j<11Sd^JtI@w2S`$mW?%;MWLc4%=HL zi!p2d7Nf9k{=Kw;xt19k$vh+UMEX9C2D?jRP0wn3ihvj zIKqjR_QyB+t|%#l=^@PkY$HlM{<4z$Jve9n{#ZUhYv#%_q#uJnen z7S7e0{d|oCJ_u>EJ_(yUqk*m3cisoGsENRi9?F=l*A~&-*(<$4vm*-sUaFT_dJdnX zrOQM7ERMPl>SbN2|4`NV9yZ$|0jqv#7_|5qM&SK>FdA$Qn}>sahte?IEg|!hNZ-Lw z+2M47yawJ6YgZhmd7`)o7cpN%77HvCf^&@h2FBhy;L2rI>K+Cp6&?pq zlFhyiSR(126>L@rL1c*79q1?uBeI5<%2ZP3K!*8bJ8n5Vkdy&9Re{a#rI- z6fv$Y@#|&(1pg>!eIKW$IeEqD_akO!YCNey`?q5Uh$a^MgG!T#n1>V}I*O@Oh-I-5 z%k{Du%Iw6?)MXzjh?<)@`1%M|Z2fN100q^u)YBKp;(8NX!a7BpNWL}bB60|{!@3IM z&!_-j!}^5^fVs3)8n2d}7M6&L95t6HGcO7O>k8tJiY2gy{mtC0V*s z;mM4hWAvYlP0?$+)i!p-gT`AH%yAiSovz=pXFBCU*-y1#y_wmwf!PgMrEDEyp_Y+h-3$ZW$Ny$8H)g+M&odOm3D+qCuDCyTVF4s8_v zmEyLRLz)cEXCoqszT`H8*!|T3k)9}efv(zxR?xmMPtJ#z>B&Eo77PE!jE`0XJbxM^ zJEbz?Lu5g--#l!-Y#gzXP3G6p>XOps?99>9SjC=T%MY0{>#J9bVPGK(CmAlr@LDVu zdtE8Cwy$lsu#8`O8L={lK%5}c`pb6GjOmh$5gX((WMNF8jU#kU?6HQLb+0+w?hE$3nE@wxIvFA6~zB7QMVyoEeHQuBH-S!>tRw89F zyIi51ALX;4mfyl>Gbw7NUa`Y^`9s-NepV{j;n;E-$Ceyj?qimR?nQpJ7Zt@YCfL5$ zX%(74|FeDDa8Ol;N-078H81eqW|LX(_9$cc`%a*!#=7{V2=)|lNG5a40)v6g4t z01XUUv68UZ2|@vkl?ceW7{YVw!nCy? z+sAnJ?mvd`Ab`J#GpRgV_N#doE}<~&Z?VHb%c3L;ua)NW2qzfhmeh>}dH zGKiE|U&0iVSyyQ$NO;+GkhAqI3{1v-UXl6k&ogShm<+H}bDWf8ZLbv`!7=F`^V*WW z%|fH`g0dA}vmj?dt{;}&QQW)P9h)H{A4EQ&PP7V>>J53l4KOcs^mIW( zWkEdG-lC&N1l;w9;87FIEh#42)wpNXA?u;BStwK2f%x9dIa=c%`6v*^^D7Rdeo3P2 zK9dB;uN>7oyTltCA%$60W`E3W-dBpg zuqcq@x{}^i&v~(2yR)n>8M=s-@@eAy%xR>v4&Y%h*z7^|kj=+ut-*SgnXpUQ2Za%i zw_32)!m77h`9S6v$7W)#c5Gu%xh%>rSYMFAD@|Kh-5MzR0ebF=8}-^F_#pg>cMe^Q z_fFTrqJD?X&Jg+pQE^7T9S;~YZ`N{LIq@lM=%?CSV`D_iRT3c{J=yaikxU5%rHT=TI9ln9_p;9*QY6sX)@dJei;QU6QC|w1dx9PPU z-k*1jcMjN$eZXl0=c@we30H5Z#G4Zf18#{O`?4|fubhbI#LpT6?u0J@S5*J&gl|g| zx>4w6bp!F}L5Qb)5yTF=Q~b_2auNe$u2af-1--x-Y8ugJ)$~A7xqyDQUb~z9yjp?2 zS$2CCh3xpcnb+1EDhBdlycVY?TH-GQhOBi1Em;xS%mih!zz5d%5ZTK)kgI(;YVM1) z9Y?6R=*3Ee3NQqA=9m}0tBfPY>WV^F{KDkb!>u=FvBx{<@$4HF#Ty?(D_|c16@7ar z?3sMj4pkIxD3B@pYY^(UW7-_E@LkG|E4F$T>^}02mQUF3kyHzn_+N+p{xB`ffEMeA9vW5-D%{ zZltI*4Xan_uaQoJoSn85x~zjwdZGe`c|L&8DFe`!Uzz7`w0>!xulJ>+=37i-p5mR> zWl?vJ+1b|P3AuYhVyI7#LAPEYZ87i$tRpmE}@el^F1lN0erixJ1-N#3v0fp0!puf z11^VLsS9qh<=8A zl(KovC21r`^>K0LV;-uDR<&qv-K@mIx|7<^+mo|TDsK^_F=k^064`x9BFi|CeU^vI zA`v->wGlB>5s}S`2Vld*+LS4GWdW#Z9=Ld+EhF-ng5iU)X7A68`i# zO|AEyO~DJK*d*(2vK_TGJ;J(KCFF$1nt-h(v%kz8V%#2jMxD`gWt|!-@k5${77Q@!{4z;ze=7&BScC z{l96Ke7GeU{#P5P(1-)>pb!x>_limI(??L33;=E&UU`S^Xg(o6V~Xzp2+b869oyFB~+oK91m(zDG}-Ce|yro;clXhx0fm zqA!a1;w8|CgOIS{tHtHPM)Qnv&@IQrVjZ>Cz6}8;hEX6s#`+#jXAT>_&8rE)U3h@u(3Rj2wHPF8HLr_+u|u2h!@v|soMqnSEk8Zd`9UErc zRN_h>v@U-yBXM8Ej^Rk$+sR6^P!=M|4(TT&#@8NU-8`?Hjo1~wjxi#DFXslCbHj#H zR5!NB>1Vtka3nsdw|a3-Y^?Qbif>?ajCQZ}h|~?V$4;Z2hvePt!VjWV5kP_Mdzd#2 z(Ya9OE~}OG95vq%MZN6^iVy-|(zl&p4c#oK!g~#g9ul0wCtz5||XBmlcb|@y+~5^oMA2 z%2&t|Z30b#v!su;P0>oP@n%l!68gTFk*t&4-cTiC(g?CTh0XM*M_NA`XrI~P!(S-N zL`<-L&IbV?K2X3qpYwnLW)JqoQsvmwRaiiIOAWlUuFCW7CR}XuDqc-j>a`x<)1Wa~ zw1+(1-L|GuLWkn}HjH3W>Zkjq4e-!WA;hn0iSIXW`S*t~{JgUpYShtg%LoE=slzv~<=K*WA*ElMAxu<+e5ER>PXppG$|uZeA(Temu%&q(p;3AFN2!kq zm=?vfxfpqDEN!LF)Xm0H1wg{HMEXo-l13}ryyuWqH$7J>Xgp69ORBMSo%EOR{GE@T zp6`=69Ftb3=ONylwdwgfFVgK&D$mcnFSmVb{~?FB$0_H`z~O7eOlSLUCm#&_o;kIB z^GO&pU!)Lg-zm3^a<;FL4;!T`wb1X9I%}R0*ioufT+j91NaBu?NMeOwVtj_4-Bj0@ z_j+s0>1Gh!;oi!cvc4Mg&8Yc4=Cmj3w59_z5~=-$9!bpUA~dL*qwByWnz05DbT{~4 z*jZ@K?vDlzYTtT-qUP-5@^1W$cjLZ1m)7`wc?;yk#>sw)Ni$-;5OH_f-AMb*3BElL zTXVmwcEz1Nab&8Q-#V9uW2Z6VdwH||2KhpVBR4w8!{_^EvduYpj=@m1wadC|nCyj2 zt$A%;w3fp&nPJJ87ID86l?_lyq<-5M`#ZFGH^n*bFxrb{B4*!>glHD=IX zaR4E?rmXV`e=Jb3r)umy9O_=}HG_<;wLag>;c-u)&Cx(xabWC&VP!^jmFM&Ib z$EM)|j1Ueju0pu}b54-q=pis$~y&T*+xHtN5ij^Dv z^%7mNlKsbrMJuxz??mDQn__!^I>*gYDhiq>gCh>6y-yP!!np!os_nT!v)geY)f(H$ zMdxVz82saUVjQ{l!Fyx32g`P8jl0P*QX^tlU_Sb?kt&IuWuyvXIfW6 zvj(<2h5p+D2H`EwSwH=TECv*ISR}=U4K0jI?@X;}rSnDnja37_hg1U|)xdV^hSx;N zR_l)tW>JcPb8F@5C~uO{c@SQX_Wc-vx12+X_zdyQjX9DVg;djzhq7W0o z))<;YTY1Kqwi$lJ9G%8d#&=Y2g-5J9EDiLvQu;DVkGayNG;o{qwO{JmzR6Uh$UG@x zPCO=Jtf)bg*6_lp#3+w^Tg=a7c|p*fGtm(jE${gPmO7HD77SR?ytQ3_Bxr`(@-qAT zWfSOxaSdnVed(w}=&i-FC`!Pi=?<=yrTgx#ws#DU@R`1IyXR+k0R7~IY6mXQnIYJ=|Dqf4+{O?83Q*D35 zm~q?{FH`;v)-R{BFDCMi3*t-k>{7fQ)8nw?9TyWqG3`Ursw{KR7s%pMMe3iM)dT*M`1?|}%AZgc@ zX30+IPfbP!7X!AEjBUyvWF0|-nESBQh0Mtj(=rdU9mNVG#;RgmWP&-P(zBuAracc- zp+(j}^q7=iuyEi?+-C&NiI3TU^)U0@n#|Xx-UoNc*6NmU3HqR;Wl%dL zkIaY`kZ}eU*h+@_w{SA-$LNPRs?I`9&yRXRk~$gghBqUHqL4xmtMtVD2F!n`DBU&Y zA@L!Y3w6XoW)F{rN=O!R5%FX>|1Ypcy+BCeYqX6PttY}QV(d8A+D=AhCvAj2I9Ci+ zE_xz1LN~*Y8IN@_s1s-}DbcJjI5vpO#CDDjrv=T!AxN@1Y#t5bfti^9CyoyfXpL_T z2V8Sei{e7KzA*ct9Fu(Nld9;CL z?d=gOO0=h4Y+4Jb!Gh3(cScOi?2L8L!@ zXRz-XiI$JM!z1>gk%aITI}Ha2`#~+lD$VpAZrrCeDp|VeRi;hXLX+MU&wulyCi{V@ zp~_QZXJ}92zB_-Nbp#$k+W_m_M`OPZC+5?&W-o>zKXw6;Mw zPZVMo6>O;(y{(rJ))j>Jj--v{g0^&C9d>R#xu`p+I!;{+20Fvd@~tlHPH#Z}#D#80 zwJKsBYO=M&SD3rt(@+KWTkw{8Sk2`v+CyWht11NA9@xI&HVQx{ji8>XzDsLtBV)te zncQFSH2RmvZZP^+XpO58RW`&kpI(%5tDHnrJ71E)Kc>S>es<7(F(N@%94gfc zt}u%Qr8lQ*gBzd@RpP2l;SukoBN6k<1H@t7b$bS(TH|}1=7p2j`DH3Rgr=l(6PIL> zoLb8o5hMoHL6p-P+JoNWY5<8%Jy_)&dQZbMH@;n1k5gZVSDG59CRwN@mS3YieR+R+ zBAkSWPvs4(spUN{Y+l|!Sg;6&bFUYtQyI6H=HmrUtM0Jb+GO9GuVy+uB51tb7Yv*T zYFD3tL}TJ3oc#GNW=rR=aO>o4-~yYIy{l>KgSZEC^?)4Dv_{}AeTN7(PtHQSsCppR z-O&ueZ%;ojbgn0xqy?c1=D}`fMTVQ+(Hf7#GMidk%E4&NTj|ys)55Ur?JSdKcj|Q# z@lkkIq~gI09sUQhXE1Oi`1G%+0*FVX$zZ^K;H)*Biv-5nT~_VsJQLwR!63B8U?hW)?=-Hdlqq`a)%WG*cKqMfqu&U6`6B@bTa*hHb`MGTvKIJRjs3NL+*6oUu`f zPz-+a;yzVqgUnl|_Ft%7(MqVuf;hXE{lHCF2ZJV3dw8A0ZK9=1GTeu=CHDQBU?IYD zYb`v2rzovi+{2bQ@h4?87jd5uw$%IJMg@8LZ1vzM6o{&c7{V%n5d_#@0$C223kja0 zjv%e6ch#8!Yiyzet6(Ps>o6M6;8nan=LVmWkAUisOgL8(UDj`QAml+b0wtTWQz})) zSJ`rn{zz=D(Z4h{djmEwSX!(^ZPaMhTGKdHXyg77DUCNG*u3gne57pNGR1|dUZ|DD zUz|F?3wuqfM>2#Z)dh{pi{q#ASe1LBs*PR_05B!hk@A>Ki}d9}v5yvdfiOihrQ8wUSumgQPT z^#CeUufkXX@5DLrvx5#hRD)I=NS3K=5*W_V>qWl{rNnBGEPPs!nOv=RtGrjq3z|oz z%TQ`338%qxgAOAc(jbx<>pSsBsbK8L>)Xq6SeSZ@BwFdhWMPA9H$=OVZ%8pZ3SwOU zve7>|_N5K7hM2X<8_siH#wcItPcL%K1u0ta&UGs3R;U zDFUi^?@j0u_Vu&Ua)bjE8WCg%lxXp`R{m?P8%2g!!Sm&i8ysliZz-Pe)W~iKi$2@- z%_3*UuodHBQkRe`Gg%(oKyxZiY$9Kkf}%9HjO|Gs??vP=@Th3JlaO^YUi*R06`J)L zM<&jp6-PabbnTBvoEC@yMN~q%Hte32CG^+Hq!Y-3#Bck`o&Ye^n)8gAcjrS3G3;f# ztlv78_U$6c{iV}g2vq6cNn)6j5UD?NVll)n<{W@3DD~vmQD0afGzl}{o*aCRADki_ z=2bm;e{nE5XBgAp9!e}Kj3yT4)qV7PJvnnErUkw1#M->mWvgOe+8O_dh*2zSE)^88 zHm|BVM?!u%g)5yXB(SvQ%{h1(*lmIK`cKw|O268HNamNIhp(p3)}H)Y zPDp#QH5Ayq^3-4%J5cMD$!OkkaoPKe-}-JTT@VzuHovho{+xMvA)b$wYN|zTDK{_A z!=;ipwz8(>5Q?(SiryT8!!Lqar~p8UnO`j=uM&6I*a>7SB%*^ANS&jk`adDWz7Sx2zfof8}0FuZtes9;}u zB+1-Zal>$baBaxDuX&9iE1ln=o-T=^!RCgr5bsJ~CbW6gB=GQPFj?(4`p2#G(oAxe zKV8Tn{kWAQX$9i_OdFVjLG*L=sG>-tI9wRH1Q$&*H~5=?sf z00n0WnNK)qk3fD%dRC{TQE?y+baCD^r9)P~=SLLO6W>vFO;58*F`ox*%F>k6!x3eP zc{T1$&hc9d;0GDo(7-vRvd2`T@-mUcE?7|-H>ONK0Yq}-H>J~aChwpa{&C^2T`ni| zz*%QM45LVV0&)-tQ>Q{NTp92^7BAbrnT{X= z{9VAVs&sD53A%Sg-2258V;u3+r`FgO<8l;^HMYd#YmI#r=S~9KckScO`lDlr5YJ*H zTi?`7<`$KC)kJX=7tUgxcLwDBKwjd8!cf(cQor`?hg6AB>D0=FrBh?)RW8VhP1ByN z)SlFH0!LQ*%68G_C6fTCp&&2fem+vRBmRkKB$Xxc=k(;|r)@Y%0}Wnp#Qlu=W?q%I zCiOVHU(Drsu?a?sn+Gsw=b_S!Z^?s&q(`@$B9FqBJoJ#Xr)3nW#N~ydM4dP7PTb(t zlMfWb={ATW2Afk+3ssZm9Am&uE$q-@f_UMx1Dod;oX)$GpGoCu2*2&EynoQJ>*{3a zoZ^Vt6|5|YO|SfVPV8Lm$x+&q!JI(%%5kuSFHH)rbqC$g2l1>Ux5m8#4#{F8PY=8VI@V4ed8Ja-K;lqb{X!#!&;aj>ZKK?0ZXiqsqd&(KwQ!=z@*^8i? z#a%onx%!-sH_EUGHPGr3#5%U+M#`Q?w}Uk52@(;DP87;v74K_x_RR*0!>X&5ktlO# zmEzeP1rG74R6Zc)k)ZLcZFSRy+?rG@s)+duS#@ktn@C|03e3*a8spHy20vtI^`9bT z_u`f)O#Ei@b@NBgI_(O!s3JdE!u(*Tcut&)y=WsL6Nwiyyej-%DU2D=c!%rQ?BN9R zn<^_3*dgnGGaw`s2nTI<@3*@soU1iqFLm{L9%O65oe^%}+Em03Ncf~gPHAW7B|LXy z0XAoQ6Q0}EOJTxui@bz$6>16rPWHPuQ*dpY}NlQP&(W~Yj6k}hp_|woF2JBV+Dt3<`-hr%Ezr=pxxW7j1 zQwQya#XN8`!r~?-DhW$G7|LP$7=SE~H0T%rEt}55mQ81YbJ9bhyDkeI2OSDJDZ<&H zfCpc7z{})0@Nt=f179eoSpdWVRPk$8P4*5(N=#E;;=Ie`upgiM9uKzS z@x}&0gFt?wmMqhh0#=h0PTsd*lS2lcL+|pf>WYJ00cC2+LrF&Ku@*@=<3Z4k@6y#! z1HMbnm)Yt|r(a~xO`^ssNf!ar*|t-Y`Oe|QKy0%RQc&v8h?=9KfjzMc^aKlRn{_^f zPOx^2NbYUce~}0pm&&~$NzXK7ifEu4c5>-SK}EYd6hM6C<_M=<>z^`Oj3k*G7N#-` zxyvde%Z#-Cp}s%T3I@_;8$>*}*5a{_4bhZ5PS`}wwZ3Xg`+J=Nw~gilc5$!BBVGAY zD&t7Tcn~`6DR*<+%e&|>X3_gVDM4CAw(lkKjiS9|fHYi7ehib9a)?dYa0xv1kYhY| zK1s8QHID&!cPqsnt$usgt_PNiBC$i=EUeC-oJTG8+^^rP-j9@t9;JJwN>$ z4<-AaP5#qrU)yC(0;$ZBDYK-ka?;jB*)PXZ=Ze?K%?i!Ktb-ew40db_8Q7VV*EtTO zdUh6LWukK?5E%5p%-dPvF~TA|IkI*G{jrh8Wn3>JB}N<@nAM*td3w9`L)w-lniZ-u zc$M{GEz?Alj4g%}{#i}WSxk1qGl~wxM_gCa>p1@eM+n3+@v-S<(TCEr%<+pqQ7xQ? zGQ;jyC|j5B74kB3+(IwtKkA%G?O`f>Qqfnj3f7$OTvI!j;|gTIK$q6|JB8Jn9_vO0 z_@W-;zA>)&S=##f=tfTy!#_^$B-!k5xF6oc-c@rjBk6M~M|wHubj3;$=AMofQ<_AOs>}JJ5>u%(%)41kNIq1IvFKc1K))za8*eVg&hY`m|wpzYQxnde<~ z0>F0FV=72u2bV~!IPY^z3hyaE&K20W0xTUoB(F?-BcLgo=QC)WAQ$vR`^$PY!pZ4@cA({mL4nip57 zdCG^p;&{{ayb!lpWN|AY_dYVga-|DRmxFPw@mJ2*&FX8R`r5DPFlu7wmpdZSrh4hXG*R{@B@?OJgoIBda|NU)=bHI zoUCH*`Sx;vs` zPpS@9wL>DBnYNtN0#XtqD+Z<19QA2O#!3`2H>av3C%Z1K->_Y=GO9r|_0?TF(ug(M zsfVgD>2Z;^IabF9Wh7QDV{@_5e`@_9uF=vT!SfDZzgBP77YHt~taOO48%DIb^uUh$ z`infoEYMh5Eqxxb9)of#dL0(3HGTkLB(HK?r`|5C7LpMKO)@-WK;T8j%OIznZiwbB>UnP8=V#ywX^ z#w%pd#G^D3+yFp;7Y+X%**j9Ug~Lnk%jW3BS_}vJqIQ=_yHuY?brm}Bto2{Fs__T8 z>m`%(QzwTF&)35W3APj?m@{JQo40Vp&ghxSY@oCQu1}i%Y^G~yrc>?!%GwSUbZPtE z`JSM$UpOC{HJjhnCYC-NJ=cy1Hhb%;Dq^GT&FVg(_S`i`KL)?`?}%Bdy1Myqr4=Ft z)m|;AP?7ZW#NlI?Tw^Wh|f_hvJC4dygPAxw|6lgr!oKdcOn%DRBs|th9xAZWd^SbKBpPvt@oi4p4n^m-7BH#T&!dE0YfwmPv zJvr9_xZ&mt8a@SddBG5X^FI&lR@2vs84pvpH}Kr*=JYUg(t6T3t2Vv*z-nBnO6}NE zd7O;h6zmPVa$?uX!^?4*Sy;-w*#D+hP*|`1P)`;;LRIC&r<+@dCU=5$4=m8#=W_95 z9$r6TS8#2ZQPdPShq=FYud1yz-Ugeq!-aNd#NHAyp792bt!@mP??z0FA2Vkw_-1e$ zFc%5V;5y)fhG@XskZJ;5K~{qJfOyyR?QP)%$eys(X!`_~u7!y9`0aNY8C#Pqn;O9) zHV(3XM>dH7)_*;5Za{8E&zB~v(*;JqJMNKpY=6-}Hh^_{2F%S6Fae{5=^|BJ@5~Db z;0P59g7!1|nqyvOS9?e&k39|Qw|(EGD!0KUe^x5=>4YiXF%YJxZn}qQ55!Upy%(K@ z<~L{lgng+3LFW)>Wk^rl5&0K-bTpl5L`;>+E#Q^(V$QsaqM_u^Eyz6-cq3@0gW47Q zgMs~Vq_Bar7K}V#VNjuQ?ySq&@jlx>);I}-OG)PvYaoGb&st}{GXTOlRh~YW`8{XK zCi!O&8%jRv05ItdVe*_@YgZf(29C$6{J#S6FL59%7jaI(AhDDH&{8WCD?)$#0*U1U zif=ejaG`mbg5nn$D88S>9m1==H>n7{S z-m<4;{-#Kz1XZOyO--#9yrgMw?PQ#+F}XR?6Uq7(IU_p z*UZ@^jji`;M$ZZU{z^LEm{a1HU~O|wvH0%FS+3Y}66jWgl5kevkUa$Fb1ZQfV^SBg z)~s7uhAeXr{66iM`zERZg8MVJTQ8v1(eKDRRM39wpb=*f=Yuiz3j0JdaH)}79jJ^bPd-8#dQb7oZ4CAoR2{*B&Yq;uo2y@+8FZ| z&34nQ-JV*`uQN$pq=D`8L=KVU&RjtdF$wI!^$qlh=Qw+LyDFS2pxOY(1!G1jS^{~Dde#<9}X zTh;FEOqiNIfN*GhA@?=5i`;6IJ_CnLzdCeZm;2I%{XJa@R#BtYy#(Fi08_?wT%6?G zN8}q53FEtj9)%%X@jGF|;@92I{Rlhb&r_+EN)QjC6Sr;n9EP5^1?f3rtY%N+B&s8Q?}lkqvyO=}aXDxXS++z+i%7g{o)&7W4e~2kZ8xiz11ICtT@a)-*m*yU3z*{=Nj2(#97} ziWm#jI2HEQwIMUdP)B#a3U7HsY_^}U<6QPH`N6RFKJh_Az5^He)_fo?j;zw zh@gUt2+okp1-!bth#+0e5xU$yV6&)&Ps#-YBe`H;R`bHC_W$92fq$`YA~b*Ib^&%F zE>!r`?E){8MTpQlJRni6ajSa4eYlkuxm}>fdS;i%iRaJzu` zVoHGjGV8n4Qnw3;Kxs9QN|dA@uvYS-CyNe3N`qGm&={u?;>Uo9I@p-VH65YTZICi} zv%tkpyYUL^T;4+5EO0h%kkdNyRjEnVspJk^EHGRpP8A3?|BsqLp_1yMJD&4*Matnt zEF})9GZ#)x%iJsQC@{dU(;I~T8|sCze8 zyG1AOj?}ipd5hImMY>ma&++yK-CC@WV^ufTU+RxU-Cfa&ZQMofY!^9?!vuk08i8-X z!H3;e0@8Arm(o~<@<_EKL~0Rf_nJq|Lj*lNz@F4CYw!}rE4LjkRbiCiR@v?34oJWG zQpoHQk>Cdit{Gem*+P}w0L6@Rhf`1;E(NGG$tfH&5ybcVbQndp_T|1j6XbW!L{L z5{)Z8}}E{XmeqjG2}{hcnqYd6KY8b0_hg z==3`dGPXA}I?Psdn8MBJeAdt7-HbEn^~c8I9Jv$g4tHbS&8T1>TH}X8vj{AB8kt=EsIb%i8orF&A`kcVoopxh&F_8Wyi|68R+Du~Bt( zb?es2VHdX>%N@iYi|=tk^C42IYA$M>dxn28V4+DGYHJ2m)ms_?Q`QmPV9OA-g=r$63(u%WQjm72$7 ze0Ht*G8#Mw+($ej>mYBcEOevu~(tx*WziE6D$ESpc{vf+36xm6@}2>cse zIlMZgm2b_sODzAo8N^7&sr4?a^S{NB;0ipkzgCP?*q_f)!xi4F-BV2~rw=afrTkX> zMyc>4D#&IrLlOydA|~`vLP_yH{^J=CSHj2YcmO0l7;c>Yn&|Iv?+l z>vkfjt)1;H{nm_c#XZ`_yGx4JJg6=*iBF(6Z_Ec&+{x-f=vUE9TBt1{aBB9|UhPTc zPM6TqWAG(!HF}DT*5ct;lo+>qhujjDJ^YmQ4HGKH`Pw_5EA~aH8T?~>3-sDHt~}`s z_dt|(V$s{e^~YItTQS?&iArlGFPV!AwhUv_ve~YhALlLLS&Po88ISOe#h9QEBIf@3 z0M`O@!p0Spjmg(R%Tr-_{P2I?6 zE)41(~C3dM|P)!0etmm?S)~ig9%2R3(F^1wW{Mn8njlaS1+%r9>fqN3|z(K z{=R=hJz-d{-7od_&M_O+kYKyz)!77>&jwoxgh)c=(0e0?hOV{I^5MZtIXFTc6&riw zw|NGeM`r5;xl}diekGFpYEC%0xG&TkDjyzhJP^A%TYv_tXdreCUTrna1=(!s==Nr+ z^h=ehU<3NY`Pq-uxm4;*qRzO%I!=WnRFyiHW~T*j^4D-fM1-5JtoF9gen2=YQAFTa zubuxI(M-*&d8bgITl>y8c*QKbdo?S@{T7|}%k0Xa8??rY_y{z)TH`}VQ_NRUu;I%E zVp=Kp=A}IiOUk{+BDK$8)R8}k=I+oFVM_(da~(Hk<03&1#-SPGwZ`}5{nBS*Mar2J zqflxGImm35Zg+7SuwrZ^8P1VQ5DC}WlAC^j!+_MUD8k4TNHQ`+y9F{dCsvzAGGm;e z#u(=gkngQl`$%2Y{jbGtVq8b=v+bdS(qrQr?q5(4J3Z7qIotBu@Pg*h^x^41gumG~ zLO#bm9qxj383g0>q;AW-ZYj=ae5BQ1(P~VS74Lb3SK7isHX69o(!N#5GDx#Z2Ju+! z;43#hTyUX=A2Roa%ie9ce=#0PyTPnjw;JVq8-LAScSGDubE!Wwcy+pv){LWh4~_-8 z`co)iZ`Pi4&#L^pYxy-?9`v^Mj?mr6@zd()%APv0vU4At(j zlsp@LJ8IrJH(2)iZVPwX8nZ(rQU08rcoxcEdcl^v<(t9}dPH=#eLW;#(FgD=6>zsf zIDvL^Q4b2+%x~KEl^H~G;ZtYW{dQt?xt{t@$~5iSD2p>zgd_f`|0_W*Rs?y=AVG4t z%HK8XhbGS_vo08TCdL7=8yzxNC@&@Q3Us*`VdbO{=6DE`KPprlAI|5z)PK>f(B?mR zX0er_&Akq7f^qc0Ex8%ueBeGsk|S;3$M?#c*7PF^K%kCr0}ai)_p?MAP@}7>n!lI7 zdO=|4+Av(oSqDO@Yr`)ONmgZNw0U0nrRk_paq&R?IB`{@)0Z$+dgo@@3t)h5>$|r= zTY^A(e{mIo3DVQ4>B4N@X33L)Qjh{&FV?;#!cF?jY)`@;2I#sF-*HgtpwJ<0CQ!(r zCh$qj8$mw%=D#z&$4+AIcnuGmuiL)VD#)|n6Q5xHmBSKeC$hTKE1cSu3SyTv`tOYA znQx^32l{xHPpNas#I7*jdXyA<%&Nhv(|=2ObuHwAfkV6-uFu@zi&%j9K{m?4T@p<{ zDBIin-1uqOvNv8yYZb2&czwn|v#CwMQt_(njX&otF!Qc=WpCs_0}^;IYWB$`tI_1l z6=V|_hAi+lcTDE>u^^*V8{WZjl>Hmc~ zud4Qj{MbT9;iS(A8eio8K7#Ij)>>6V0jP_R@5p5JLX8(S|R^)bin<3&Qf2Q-fdM;3B zw|UX(z7!dZ8;RvQ^HOdplAFr5@OL~{6k5CSHg&GO+N5IX1s-JNK|#jR1+l7Cqko|# z8Q)Yv(Y7l+#lF(J3MahWW>{jb_GDYyt8Ln9O~y)rxE9YF?oQ|0EL|rSp781D7ulSM zx@KVJE7fbc&mV907pvDkYj3xjm=@zQECfxjKKNb+r~yl|V>ud-TmRo;y1(qibYB=; zJ0zrgB;B%g(R2J1iRd2X*q#4;ne{PijDW7)|A%mHWz)&}hbyr!`G?YS>T@pKEgOmH z>1g3m!MSi#7aUD2{VJY&xk!ymv8psU0p0NDB{<#kSTGRF9VNAp|L0lZA7gh`7jv*A0o~-iX{SMpf8n=K!@o0r=sbuuu`oJEe|29ViRx#awqL9&lx8u_+ z@!Yj4o;zRoQGeXIi`3{}r8TwFP|I1APS3TwFd@mG$H9KYK0?Iyc76Aev>!wW0@k!E ze5MQRt`L7kCm+3^Qisd7v+L=p`)DT{)O}zesC$VM)QyI6@4~!mh@_fZ9!y?yn2`8u z(pP5#xewf19UhTJHg;kbtv{WcK^UYUo;1B%{6j;x6$VrC2PFkTPUyBduQZwo+P32P zLLY@I24c6*S5qskaR29)fq?C?PQZ4t${P}}t2&wPgk`pVIM41Y*2O-h)C~|XSs)#>ramEx4ajCWvW0r@? zme6R~dlbpWX){LLlK$+s`iXI78+uHIHOn%e%O{D`4wd??3y`I#f>bf<52 z4x;$**dbn0)ln)#D3V@-my3;s=YC4t$DD5SPBmf>P&mty~Xa~TEJa`D33TGJJrR1s&Z z_V1c?L*r~ka1bY=zdj^L{aLA>bxoYD2pEG>_M&#^BND6RcWLZwewT@v;P}e;ql%TM z9|<;8E{hkiHA=cL-3(_aPJfGEzq&>$xK{Rz1KNy>yCkG(g6kFvTN|L83hX(Ot6G8mRfCXYg@Ff(rQ~?S8!`sgy0Ie;ZjYlZJ!vmu~op0{J-bk z=b21Gu=ag_{q^(y{vEhE=ehemcR%;sa~WJG3uH(gFOV^Gq`*~lOM&Q4@c?B8DwJ03 z^E~v7o{p^5r?NCU4B22Yb6441;okU+RW3_dY|64Xj)v8u*Gzi8M>!<(SESc-@M_mV z+jm)kQTEeDaavkCyd7 zcv*PIk9h4jBY0cePdGc}9;KX&9d}2j_*L`%%+uBrKZV?~qEEJdrX%T#f3_~|^BKsH zQV}5)#C$R<7*~#pKO~Jr#z4;bWzeO`-$S@|jy#?gxeMg?IOlfW1F~Q5t1EH4zcAZ{>yl zn!Do*d3B%=tMID>F(0rYOw}909JXxPlvXx-9~{;XHOO9%?u>)z2w<-_*!s!+;Z5=V zpd@TId-oBN?HBrAjja{z@;FKM*v@W`?Tb++FFIgPyuTW3Z5a(G+DOFj2*%c!I6gm&sPu)rv`%3$%p8J;WdZ_xb#PsWZ%U97u#ii?3=^c9SA|t1)zbi1= zR^vw6lx8C(oErmNGnh9hBVC$heh%Td?&{Hy~(g(7P z8mdwFWBuQZSWDA|mt;46eN?WafeJ?JQQEO6R*2L+!KbW-h*{wX@CWN9fnspe^& zRJUt)wh5y_vN-|E*1B6{0Z`#tf0^t{v<|1qFnJhi-a&`c;TV{342w&{bAMY3u03^G z&2aV@={iOUoKQQM{YG|E)r&unHz=}gWmfIq5lvQ%P%<)Qi&VsjV%Z9_E}1aa-q{^( zyPU=vsV54_PIQc(K$q15N<-_hby=n8*ksv%(@YT z`^ywm-NQ`d>}6~PRc0SUpRayGHsLu<<+89@y+-s?!Nsf?yHxfyLf)^pU+HXY-dTN- z_MM&ZXLzQO3aXwRX;akGP)Cbpp3RC-QWb}isyJ5S70^JnZKBf%Da}qtN9cQ;J*{Gi z;B0#SJ({Zeil(Z}W1e|DJ`xyP-J7DSZkr#J9`vH9iree9rm7dTG9Z6gRh6g=)2gbn z*Z-OJ&t6a_;_QqG=n~+Ag9_ACWp9|!_VH(7Jyqx0daAxp9cCUiYN|Z*j?(-6J+xFk z{vuI0TB^$MuD3vd;ma1=P zPcKAz(&N%`TB^30#)O8d_E<9(%Ba}(?x&0d-L+LMZTr+%Mrx~CYP415X>C<`+q|?a zsZPBQ>P=gf-pssg&1R#+u+gQh3iVduUC<&p#-!bgwkkVx4539>@kFYs3cIPQdI(tp zVVCt#RaL0h(pDWilrB|O!u4I%K2ZY>OJy2u9}~`~PTr`ik{!^m@6}T`Jt=Gb!Bv-Q zbyb(>ZPj+6gPqyMB%qrnc`!<-Bmi;BZphQHfB`{vL`T=La-#J}PMN@&uEm?JwQ4$^ zB6MA~?~pnBOI29)Cj@iQdkJlEV4@AmC`Rfhv%febwtc_=!O)Q0_9qZgVRc9>aPo+j zs$NxCJ%o=Fs<8S2ju9%XHp*u?bTCS(zA2w<%I!}Xow}>Ax*VG(pV#=F&xd5%=$({_ zQj0gOGW#E+!b)=~tY&sM(5&q_hI6BBimj{O+UNp1>Z=g(^E4t|tU|{)Yw>F#jqcj3 z{B5j=S-a>hj=$|`omEkX)vNX@z1v|SC=@i>tCqCM5lnc~gH|kO(^Dtj{u%96i;2|T zevw4oK9|3)_AIHFI9M{Gy=tnXx~f75<7{}|HYGEQieza@v>`1RCd))kj4stxM}=w# zsrF&j78jg#ycVmS{w^(6i`GhKz5PU5tgP>F=3=i{&%a4(v@<*Xu3alFDHqJ@ygTo2yml~HLyoN zi`qP4NBeo%JU|@U`-m$U#u|4IzHmkPN+?rb4zm^~w@>OpvOs|-EHhf}gz zVR>kJ5Cm<`uy(rWkvHKW?JZ`&@x_imzSujX5WtEk_LEMrO~l0BmQCN{9-HT3WUA!l zn1jKO{D^#Ur>(O^;^oMCeRPs=HaFl82l+K3mKgzOurL9Q@horcg_$yhIQ#Isxp zle>zYDHmUguVSBeTdmXpNL@+6XqXZI93pA@MAEIZ{^duL_x(md=SX3igA4Y&y^N2zwh!*J33~ ziMY+t82jA)*pPFs297w$X+3=NF@XgV!EG{zp;Er7+7+1OFaAK&LS)UKe@4g=C!ye$ z!oqw>ri>52ujQgIlABaW$@`mz&yl!-4-m1|Pf3(_ApVipIPMD4;qjrpv87L$JEw*+ zS-s1~cHI}uYoxZU{f#258cG^O&aHVSMmKodVKQvjKT>+(Ge}`ibf%m`1);yqTqMj} zK4T;YveJBJqy~>T$OjYlV&yNkq?F}P3yC_Ul$<%DCWfiD#Tqg~8WFd$xb5@DuL(~1 z^#Sd1XQ4J9fyanAOAL(WDuY|}V&^7XKfI>16UEp^Sn5%7Bmo-dBqN|nn~+=h(%<|c z*SZY-AjX9HRjDz-aiJ{lEHCQC11Ymc3FtR#w1Bu-D(eRb_FI49+~XM{lkO)pkT}pC zKu_mB&?WjnQ};|G!{3cITyWwR?46IxSc$y9Tq;6>i7C$?+O%2POX#T?Gq{h~bbYgY z@!o}8@_Wzu=H=!X+@nR9SoYa6S>}a&Zdd_mALaw;%-CR3USqBsb!wk$Fd?$c(z*ZgJO4CKn1LyvCd zE9lu1~A_lJqhsi*}FsNpRhl#m^Aa2vrXxGMQ6#e}ra*+570)b|b_`z@SL`P^QwqFoi zU8V{Y$Qa=!bX~*{L2XiF&sz6NP%}i-b`23%jn;G215qjF~p89@W=ICI5n5pk)Jv7>LOEX)$ zki~kaGY5aXoV_u6L!7^Jujiqu;_{sJQm&pI2KMxTYgWVIz%X_Xzs{;V<_+}WZ{Oe@ z5=q}Z=ONMoPvq&Thar=v;g95^E|c@ay3D>o9!uNR{-L&)wV~V$;dP&xVag&`kP$ z_QWlv43cHmF747h0`quh**()6IB#a(z#Is2mgfof3VxwZC#B$#o{eO9moB^nwCT{E zfD;7SC3czy2<%-V)nU>>kWZ)6HV8X?$%RW%WATY@# zgvUbDp9A9=t(>>9Trv0TWoUb4PwYncChS);7D;;>F$&-Q##yfk4;6t?D2uLk7}N4b zlwa?i;HJY4bxxTcm#uYifH@l`u>OtoXMR|_)L+cGu^*K~wHKil|3iP~ff}ayr>t>L z;@?a;8F@{-AsdcYPbc=-)e2(G)&*^xHIl6OsPg9Q#t|Oy_Gr4SP=W3y8(H1xPrNqB z;(e%vdTC&i^)%?76gtFI%$cz)EA^y&IE=j~lWGP6iUQO92R_p)p={nyL30CEX?oJ_ zOzB6o%#2jzMbg19KmyU89ep|m9bAI3G}UXPityU#g$26XC&=a9pVo@7%13(s{2BIK zHE73y+4NSv%qT}uD;yClb`E6}I!o@z$lN8>?B#CTw*rK1npFqrU9X6ql$lUjzea|; z+=N^56~mcZc>YlA-M5e)V@kbr|-c!U+6=&ZF_U9RBW=FR=671 z9?IIVc8R}nZAVVSvjKPG+M~XQliTC68%vL7Z)9x9KV&^JR~n{g{i(3}waCT#j$rbU zJt`}XA!J6*p+Iy_{1>6;jQ$MR*s9q#W*({j_BWW z*U8zFY*btD&oOWvAo3VEJJiuWH0$slcfd`OiX`9ni2!9*J8~Hvq5MLgL2C9rP8IR? zRdQgW{23#EhRPpL{U=$$hMdff&?}x>c5?n7I)HZC&`a%coQ<_dgF19Xj+6|+v?ogovVvn4w9_vgQoKGHGtTB|qdh>e}B%|#|&{rSa#^c6@@d6V~_LoKT zJllS5)g7{4BMwU6+L`hWR;=}YX?+W;y()>)wBPQ_d@|U_SND8YdtXuU5CiJ=hZePl z60AXWgwz>+jXk8vuq~#}Tk|>bM5XB7Fy_6}V&bM*zSpSBc{hsx* z49{tR#q|rCny=yGKrob$gF=j_I<4^t>NMuGNUaXF`jEkO8R9#TPewX9fozitWN52u zTJ)mH!}7+pFIql!oDgKl^7^$eo)k>xVnz%8zndlJDxHDd#4gjc^;9d24J__AL3I{J zlZ8j5M{ienU;npYQYh!pn4Q6xgb&-J5;~~#oiz73vt*SSIF;=bU^HJ*x;tb6M)4J+ z^j0fI1xI9W$XU`pWV^g+XSbMmZs06wkCEZV^kjs+XhS|8pUV!dZEjrK;#vPwu|PtP zvNn&|L5wQP(;#Akg4PA9IrdpEOi6vWp+=C*KV6mVtN%Ras)_uKY_0zn>GhUb$C#XgCs79%uo<^bz9l^Fg+6P0 zkzCA@`~*kpv>BDG^tbF3Qb<9_rMF{F)&>~Y_F0rZu!@pzK|h&4)t8 znnHOR{%$OFt#?c}1q+_jCK|6GhUD7!xD+jvkXyW)u-rh5ZONIi+sZsuw;49LvgnF# z&B=W4y4Tv#WxlrAZu7+n*&9naF_1Ryt9$1`PHihPR$HW4OMwAJ^|yYtp<*SF4w>HypQ?1Xw6K*2b{e%eZ(gGp%9@*K#HV|)tS9v38 z6?#p5M|NCC1S!lD|lnbb=G&6jm9m2FO z|1J4Hi0IFlx*AaeiTaCu510{lIxBQ*GfpBn4s+^x>$~C)sY&~WX9J%sWt|(I z`O(AQXphbd{hr&M8Dp=T$(1-6>m=aUbS#|#9c6xGlv&-QJmbrwr)avT&b;tHG?u8DGWYjHP3}*Pi2Vsu(+#OQ@>`a~W0csd14u&hrowoz1X4+WRq3 zleJf@EnEf(wTLd-$C35yd@_^JYxa5`-qW7tFPd>+=# z$Mg-{RW#$c<&Ek7`Z(CQdZ+XX*|W}=DJ7@*i@0HSi4;;R=HpEsvsrT9vJUT;e)~OS zni0MsSORjdIUxE55;=Z8*e=0IM63T0*6Q|e>AhI}K9_$+QVFX&dLe6Bn|IQs>wJ-| zBotP(xeKGU&>Rd56gi-N*)SN!(YXULh!u=7d%Hr}#+K>PArA>v$u1f?S&g^KiAn5o zIWf7cHD^Zgpx_wUlK1gE1OcM6GfI!@3lkmoA%Z+hlDhBNvOp%jXDb@>}V@1N_D7B(R?s zdU<|rg)86f-V+^Gk0$Gi}*&?0`6a2LTD zJI}x4-DL0?;FE296!;Kh9p7*`xE-d7i_XR0WBTtG`tRrZ?`Qh&r~2yHO~#8%uPK1HsL%_q6bS${OZwaRKaA&}0M`Jw0AF+etMWz42&;qb&| zAE{LkPg^VWqTnk`!Tm>ITv2co4(6SioSWHlHIH(eLdW~Vgwkby^HIC(!a$UHo&iwp zjdsdkEMuk|bp-l3<=>SI=izl3bSfir6Fy=^e=-CRHJ*W)p`2=RM8;v@a2N}ZiNTm! zOOUeYt+begR$1P3&}{+ye^Atu?V5*E8p#(`m9y< zb;&1akruWdkk}f=%1SC5Rzx#UJ7+W8 zWRbxP9OV!KG~Exr1w7AiJJa~w%%`X*dl`4H)&cJVs0qWhQ%12|Oi_Q6urY=k4K4ZstiwB^m>oh`)LT*Z%PWU>!~~LzRg8X%B}UY>>}ZP(USyDH zc-Od#!V+6$3(r@!#>sM<8`HbAz82EZ35W)lzl$XbT;%5&$#BjO)Y0eSWpzDUBFqad zjF(lI*Wc)C%@Z{)q3n3>IWL6kA$nbW9atU>zDQyt+rGgl92wsx&LZWpw3-LE5ux&= z#>9J4v*WY;>vq)fO*UXrwuz5zS$yY(5>0w}o?U%0GXLkrCre_feC8&LU8>l5#V(C( zWr=;O*jr+6GKK;OY&*pEXz*9L>nuqD=@S8-ddZ~GB(t5$Jih$UU{h{1igCJEkiT=E zQ%Aaj{Pk^75tXDX2)meYB{>yT&{aY8ZEm5dCY&o6uAn$mK^*dgllY4DlO2ClDA7T} zQbDQIMY2>7gd1d%@gdCEKlqZa9v1iA%d6{$+4E{sKh%X(OSqa${p^USpFBG~q3=br=F%riMN739XU|CiOzBh-&#iTr zmeq48*KJ+%HR=5qBwODwNUBw45U+K)LDH;?4U%rtyF`QSssIASbYpqZGCZxPJEU1kw!v7Gs`mg2EpGj_$I;k8(hX0Yq!BS3%7<|9r)doK#c!|MV1z%!tOYl5{cL<(k@S}oH zGq`Yrtu%wX1s`s3{Qyj|!BfRP#^7GTk1i1+m?vf4Gq`@yrPbgW;^#$!%fj1gF}U1; zwH`CLJP2cLHF&k)KR5U)!EZBoo!~bbe1qV12Hzxjz~HwDUS{wz!Iv6*i{J$Y-zs>v z!M6#XVen?bPd9jr;9i687krSxHw*4I_#weRU#!dCDtL#%Ey3S0c!%JJ41QGbXABO< zR9VdimuI`J2MnGp_!fhw3Vyr6y@GEtc$(l122U4!mBBLvuP`{QSY;I&+%Nb-gBJ+y zH~134XBxav@N|Qh2|m`~)q#8tO_fHx-Y=jmH!d)QimkV-sy`(y(zG zn-3RBu`l2S!K7n1=xn}aY%;L<$k;q-j?C1ieG>kSq|d7-Cd4K!?{Yxc%Leb3$*yqKHjM77v|WJerfgMZ%CwH-dc zX;9zg>)!74EMNEOQP0&+vj|3sBTZyy@OQb7INRsE=!5?H4hn|mx~V&J*Y67KZTI+x zvEe(^xeLytta8{ek7tuS#@;XwlMS}Dio_aWRp#ELByibxJkiatelP`ak)V~`YSWy3NOkh&|yL|$KJD&j$KjJV1E{YqKx(^^OzN!8*cc6d$ zX9M8|1H0p*>bEuoQ~p zj8IY|M?0Yd@EE+I*mdC1Etv<_p2nk!T2u24n+brBN{gG97m>yHhLV=xsr?1(RnC8M z8)L?jvp8~g5`x>mbK^PlEsjIKCuxPAM@MjbY=~<}FJ->P!&PLtFIo1iPo)XvHR}9k zzU9$u$?Qg*%eF6M19?>Mfc>7?`~A`TQ2|)fU;JD|-i1}v96U+$jG8WH8hyDYSKOvcxr9gL-+`{B zrr}5Rk^b`&iM26S6l0;`t20F|H~HbfH}T?H%6-PMSUbKcFR z81cflrNl=)>t7PGG$sAaFZ9dT^pfu7Y51;mt)`S~aL}c>LozH5*XTaSUGu-5u6_8m z4>)+S*Ai)G$|~_FchR3W?#W^I<=TCTohiwVzZDWsV{9s(&}|)x^$5}rqz?!>{o^Dwa$C!grV3o9vo=$Lgp%IBNkB(u z%IP|(R#C|{QxZC>^JM|BSK;yb^eb?3@h3yG`C#LJOf0_67x5Bzm^%VUW1|%yg#(^Y z(mIJV^ZCFu-pvw$G5nm0T(4m~j>JQm?O|YN%7eBC_R#YB7=A)YBI4Yc@*~?NnQI5I znNW15z0gjY9ahiv48usxvYph53A*~8(9C(zhxUuAG_s-p91ME#!0Q$JSe%fv0pf`Iy`k-vUY&tiPqL?X zvbdHFYS-%QRTNw0a;_E}ofZE#A@+KUZ!$4dp*1|c4o(ssj&>wkjNm~aX$iNMcV14@ZI|{H zteO#9yn&@U{r+j|$KTficN6^epS51~xY&fSu_`(9-m4Oc$sEe1%lMrkgUjW+tc!5e zgK{8^X`#jX1dbAKLcU~WI1ZN@hgR(%0-TSU^Zzg(+AFW7aED6TPGE$v?$2xWANhN3 zW^=8_`jB8w;_b6g-wYRiU%+k67$s$3wB$Xs=d4%s)FPu#V6f=L>+hd{RBmFN6nK~Q zA^ONfNwq$`Yr+CA|pKr0h>E5yX|AZ((`Y_fSPl*yW&O<`6hpr$o84=fePl5_C zaAEblI|_9p=={%tjKW&}Qy)B05hJb3$n&TS>r9<>y=?g_8$~(U+kv0F5JIzmL=C|Y zZ)J4f@p-JT{x2itfeVp|Ey%yJbBS+bz>^`fePLGA;jI0~kn)bwvfi#>U*yiT&fXvT z4rhDNs-1*Z?WeU??I8oHfTyh&-;zr7G(5#-l0>GH$oZj|R=mf_>Gl0sTV>q8Vl3wn zdnv2JW@#f$u?hH`amgUb2{IfW&n>$;Q@%~zNn~pY1t+^N;^&?Q*%BichZ7V)-sAVM z`bpKsGH=pT&i!vuH0x=%)GL8)31qNbEr*FT7eaVPc5%> zpSU6JKHQejp@j%9+xp|%wukSC2Lw+t^xt&FptzLtz_Eqqf~G!ooqABDH)4e{92UxX zMrX>|0LWzQKOtB?ny+XZb^=4+M+5=f4>c;9Ej z7tu5vdBuH+=f+sr}mV#cafb!(7!3=m#mFD z_fnX*eH*epc{IzneS5Rx3ZQ|aZ|1dqqFdH!WBEMP_8uSFwjBftUrA^ogl_n>2W*^$!WUD&UoL(n6bH?yJyA+6E+Oy7Cl-d z*t+q5LmxrcebPxks(H>oiW7E!(|QSy3YqK)OrF`)cT>_IS*7|zi958qAz7j8nwEO^ z`gOEPNKGP&=L73boh(8E8x%Eb4b zzCsCqKgN_WpON=OB|MFS^ekbfl(0Vzx?I)bW1CPw`Y4B_T@^LCdx;WhZE~8UMWaMK z%03I?P-P1wuh|pXqop@jPoOUXq#rLL1;pD$P4W*WphWe+QQnqt>cn*J%P0?e1f6Rp^+8hqunvz;&Sx6HQKa3hu^Pxm{_Jlp?Umh)V2_!_b2+z(u zcHOpiR_segNsE@x6z*V}0y7Ty&>(SrGz8JD28qn_-zOuCpD~#2Ct1kRYrW2tIXVZ7^q;c=qU}w6z5VCR3nEV6wuJZbuMb_Fh^uaF_0jc?m?bbGyY)f%N3*m#X-rb81yl(n$b5OyH4h^jj z?;S>*F8#NTsyxwu`zS6w^xr;oqkHS{Nd33A(yL}}@yzu+)X;Z7uD%@>8n5(9>nI8; zWWMo*T3Et*8j8u8h>G9nHgK8^|8CpAX~WxX*gzIUq%yV^w8t3upxNUace9#R_-3US>Dy7DPR zH-)(8{clrsI!>Z{|SY-y7{zE zl2~;tT?%o}JK8P^aRFh4xZp84q4Rh&3#GaLe^7{f&ql_}6Dq_-9x>@zw!oTrkqU9s zhtdxIM+$LoB3j;6PL+6iQ;54@oX!^J)DhX;)xaF))?PH z#uF>V{p6=%Li-~X;(l_LPRdb;YgD_+(m1RU_xThA%r=hJ8gZwykYvIM#QW-x#-WCr zrP-G&$h~>GS!8~hg4|gsU@Z$w;;*A1cN5oL-cM+6tUJ4cI~AQfkN}=GnIX}UEB2_!we3-nJ4x(IQ1C9W+|zKfKvd)o z7Kn=6egaXE+eaX(9OYh;s5dHBKPasgRLU>A}1PDexrbo}5QDqzeS^fby<-qp+v|cr^tiSI#wx0<1w^RUtBPDx8gX9O_ES7s zPhJ*YIbNG>tH}N4;mG?&EYL;JRWuG~upaoiA1cE%;+@V$9agpqUSN2^Q-L6iU zbJBmXKT0Ncwkei{jHg-6x4{Sz-MCj}&dMaM+RARaakH`NZGR*eT+%3S#Qtc2eh0L$EcL`h|cCwTyo7meir45qW_ypeM~7y_JZ z!o4-OO5no44Mw7whm8*g&6N^i6-SLi^G4f7iHoo3`o5hAKhi0$yDG)Hg>ww&z#wln z-Dp=k3PBe!lIOQtcTY99OMLa;9Hcz!g{{VA#ti*NEh@III$w@_28a+m&$Pf=7e4g2 zzD+Ychgi++4r?lC-P)rnq~tnE_!fw4nd>A+^}7o%mwhrZr4v)|RLez(rprgOeS6d= zO?WMLNMwkL2;H`bZ@5+L_4@3MX8XmI5|qfxsj}$AfKM?%H|l})Yttw(<>zSf^}rqQ^MA}coYYVK(Q7>GhiUuc z${xCjvd`w&MIU}pfKRhb;XMsMXINmy2i-}^sUw=|1pn$$98FRi2rB9+R;a;6~fxl?~TJ;rMl$xRda5T${3Oy zd3HcHr@kNhl%wU)@8x_Z#hQLecs%;xTy`Fx5_w)|6e>%MdX`6KVIhaWG3nCOEP4Zc zd-0UnYP0|^pHUX&4^3ZECd?_G@4IEMKXdwgzJgU;s0@9;twqtX(*89#du}e1&FB~W zxU)H|w`<`#p%2|cPDbPn;=b1QYjjo68JYvb{1g7l*k-L~rzh%nWP=ro;f$?0Xia_J z-#8hPuJSide|3d)9@zT7Aa5Lph|XG?eXhijZ9Vz`F*e5TE`nKf_5H%GU%lG8>pso5 zueQ!u;?O`358-y-b@osD&mp!Lj`!Y@q{lS*-PTEUI?{PM<>mmKq%`PIU@{W)YAs0C z$Jc33XWO2BVmwWd&(H_br*8Cz`s7b|&mTILd*BOsAgwyT7?G^zK+Y3F`h3yTwO=aW zy#Hbv=Bh?;sNA5NJ!4v#r{NBKfF^>lzq zb$pN|ZU^7_g)Bk$*;kFFs=e0BnN0oS?Gody?T2{karT%c2aoy=41CE?U`<+E@hn+O zlbdqBhBeV6f+J~4DPrg4v@DAOSKpi)vqz59DP*iZW$o<_9b-s=3?DLb$R**>0pE6R zH?fFs=9V4@q$r^4b<9J@lzrO!?$l0sSMxj<5-Zb>m|=n?NT2|_D0xvAH7I0QtdNQO zJ(_tKvOPELAeGLPRQL_P-^s+nJ=g@#ux^GYXpUE{ZwY%4mtMy` zdD-kT#=b{X9jwOZtT&0DvoK!6%*}kuA9^XrlfM`1d(0Ud7u{|%Ik|RN`|DOdG1q6r z1{16?I=LhQ`+2%b^zuJvamYnhSH{cONPldZdayI)YQEYRt-cIG5jmdDW*H}iH2NvA zXgf!$iFMgbydF8^ABJ4ZTij0d*P{@5ob|{8DVHQnpw}3AsEltK@!{1nR%n)CuKi>d2T@PY-k9ymfU~yL<&J9ht@~pg zsbzbf*zY^=DK|Z`I8|Q)#5N!|KM<`AqzObvgjXQiA^fxJ@?7pZ4#J-1X1&T-$G6IG zwWs&6zh2u%wWs3C<-V>x*>NWm*ksh9a3>h2b<*&_(vjDOHIGxx3MDOMLMqg4%m2u< zG{pMJd}m0u7SG_YTUf2_@uAq!aCI78P`uu`56<9JF*em1t$8(4-nZr^QMU)K7yX6e z$OG3;c^em`w#}qp_VU1WdywMw^1$`3MHICA1J`3eavIco(vn!eGQfG;himmbayZOd zF+21mmL+5T*2{mEFA5+U{qO65&=u9G-(S%t(!U9u$k=_u#4Agc&UD^ zGa+fiXkX27H zll;60td$0~ShuqcVcI}V-QM<8lXBOjVC{hjqV&=bm-9K2MXRc$TmK#(B`Ad84-00! zBIKOUPopJ*M<^S2;j|FIWpNa_G4`${Qu5t?qnCl{`BrVg&HY3nNT5$=N+?!)N!!&q z&I0Wm_pbgc>~fOi&LgRM{h@bR*%w$JOb}s2b~jwpjC9GeUhL@tStLxM^@#0~9vNmk z!=bWPtm!2>Ct{ZaWhL_dg=sbxtI`?UY(s{cWdi36hm`YjV#_nu1YR2SRS^ z!Fzhk4da8dp7>^OPI}yycYu#0iI%6cHuUPGL#>Q(>QOw_6w1nva1Rr@{_#58*rSS#BR!2%5`H^JUW8LYM5t6CBi-t*er=)B!pCRzmQ8EXmAzy>l%Hj7up{f%TBR9RMK}mW|MUBQmIAG3NCQ{u z0~@L-=DVK_(`hN3LD;F!`p258yoJnVXF-f+t5AL#Gh)z(``7@hIuwzYQrmR zc)bmOXu~vFnD85H!#*~A?<`~gk?l`SGvA3e9BadwHoVY=SJ-fa4R5#MRvSKL!#8dC zfenw@aKLnv&M7v$(1wLJth8Z+4R5yLW*gpX!-s6R(}pkF@NFA**zi*u#-C}@_1f@s z8=hms`8NEz4XbUq!G@b`xY>sH+VBY*9d$J8PZ0NV)*KN4UhBw&odp7*J z4Ii-K9vi-9!)bOs>dNKMGj=^bWWz&Fy*eIF05^{lrEW?MDl)L}pn=caZD7w}?$3;U z-6_4hNBVaqeXvZvWhs-7X+5lf9K$B+5tt0KOO70fdIn~UFN*aWqGWIRR0(`9SQqm;?N zf}WCJu0`s6O4%h}PJRrmb5 z_^R#UZ!!5O(IxNhvJl^;5x(=Gab-l<1-N(rmV7wrDq5MOr<93bz9l{>hr}cKmhh~6 z{AaIRd3J5ML6z`3-J8$PE68eo_##~X9U$&QBAml&o8Rf zpQNiuOA)`st%y_N!&DM}wIVKwN6jr=rU;`J6a|7cB{=Y#TT^ah(4{O`Qycz*UZo|K zr4bejgXSy0s#5z}5VT=YK;n_`5=P-q;YZ;vNhnuTbWCiYICtOpgv6wNp5*=m1`bLY zJS27KNyCPZIC-RZ)aWr|$DJ}h?bOpIoIY{Vz5Z6Eh{c5UB05M{E90pR#sM3f1{>0 z5WMQ@RjaT0=9;zFUZ>_%)#R)y4;0i?6_-lwuB0s$Q};Erf>Je!mQ1^kQj$ap5>jf{=b z56da_3cf0J|1H;JTV!0~UQU|jxL5G^8rz@ro_O86O#I@n1ovX?Ek%|D6Jgeb?QlKSvM87ZZSbtSekQhK$|E6Kmfdw^aorI%W)CB_Qvr%Ely zPU4d~bxJ1VQx}~kYC5eXZ5dN#%<-x;W`ttCYSgKGEhoN8zNO5PC$W*1AoP?H9Z#uB zokwXwW)6_@Nehb%nXU6Aqp9R;lCE88PfmSL3DqbeZN0_i)ooDPv6H7R z`c6@2h2wMb^VRC}YSQXG#op`G&|wOrhLiuVo}Tn9>9hZx^rnZ?tEP>bHgFYj)extw zIx3*r@jc1un_U!h@;@yc-&fE7<>Xw}N~=gWKpz$gIbYHuom%Wl&8hD*)QoU?z14RW zwJP;xMndV|ReH3LQL~gWQbw&(9fQ-39B9gOMvwL+xsn)Vd@y5MC@_T%IE1|lKfkF|&gSBdxJJjbsld zzrtj*-;$G6{j?eC%Xx7YqY$^PD&X#8`vLjSVtZ@HWyzm5ds&J_Ut+hTu@w7*;9jl0+WuC~8N z+23_;()`k9?#x3GPbjc&-~JeK}L)U`k?&MDuWdjps?}#aHhxMYIGmf zCn`B6CnqOXe$&&5OFVir3YNsV)miE3iwoeNd%e1exeLn*`6;!kdKEu6K6rV-?FP8{ zC!hcMK>_b^|I!!-&A;Q_j<@ksGhgz_+~wSSQ@T(7$RMZxp=D*v4D z-v6|L>tB@XtNnArAK#+?S(|^<10RkcF}imB>egLf-?09MZ*6GY7`n0Prf+Zh&duMw z<<{?g|F$3e@JF}*_$NQze8-(X`}r^Kx_iqne|68jzy8f{xBl0C_doF9Ll1A;{>Y<` zJ^sY+ns@Bnwfo6Edt3HB_4G5(KKK0o0|#Gt@uinvIrQplufOs8H{WXg!`pv+=TCqB zi`DjS`+M(y@YjwH|MvHfK0bWp=qI0k_BpC+{>KcO6Ek4G5`*U7UH*S}`u}74|04$3 ziQP4W?B8AfSk8mxfZq9y;9F$LoF6iZ-M*Xnj$BLJ)Z?4mzunw7_4wuvcsKW(dwhSl z$G1FL8JV6uYZ>`1(kHT}ZpO$-{CTAguW@mCWl7c53j#%fa`>UxFRCrAnYZkU(&9jF z*`q0Mc+_&!}WE8Vq;m+tzW+$!l$R#71V7|Zk0AZqhN6z z>opd21qB-j>P@TLP)8`mvaYPG%X6^@^t?zN?XK!meeS#+g*)&@!_eR(BCFW1F#!gsk>1p~c#u=CgD4_bbS zzeUuG!zXcg%f-};a3_RUA-hr8K?uJ?ILLQ+pNIj<;)4aPup!stnXrRd~ya zDoZL#YrH+n*;RilN&{41dB9s-RZ{A$TJEiOc=Zy~B+^}laek9&Kegm&GVMTeF&Q`6 z)jPkORn>Gb(=trW6Yt8E6X0`$Usb$wOqb8}>qxrm+(r5?Db-CO(vLS-D}-6JaPCBN zVjSsTr#yblcyEzi3TZ`=p-JI*|D(o3+KP&*t0iIy-J>}eq8%5mdyV!;rI&PyYE}fL z!fU;0rB^Xhl`r>}uB;BMKJ_1`w~VG{4`M}Rw77`Y;524wu-=uWE351y!O?b49IZ!G z>4#o*ydC_r1=$O3T{GeF-?yBX^Mk`lj~;vLYw0eEI_K=AGC$QWy_iP0dMW2+GEvno ztu0?!T~T_uGY&5;DX$GI4V*b`Qgw+Lhz*%e_*dfYKhUiPmL#fy(-PFc`JVkr%?Z_S z%rWu;cY2k25|bqY{rsNtD)lDD`R;#Gj5=w`;OdmZLFp1k;@dY$slQ{sW`}VNjaNeh zNopu*3|*L@hEC(VCZ&1k#H8sXcYD;ZKtDC4B#HDBm1k;vO`q17{ZYcqSi>9$aK*={ zc*5XP?MiT|1WM)_6t4zN^Qb{nk~{jfChm`Kc2~z0_9^HuY3(MB0I;MlX}Q(V`6>II zytSOJ)E_VbCvUv(5kq|ahsUbnvs0T*NtAN@Z|uz2brSq&?pKBo0k!)_k5e?W6`fh#p$rBZLH)LSZbkUC%6 zSN9*(M-3`*QwMQU2fDpTxpHSJwFDC`SDz@=XMWU|){ErtGH%9vgn7r#PZaF4AsFYo zHyRe7%Xu-zNvnVVKB_-?>_0_XaD1Udt9!DPdLHxFFGz@AU)`Sis`&YR!uj6j<4k?F zQbRvC(1o6)L|1?1@+K;8Nq^;Cn5?|e#alDHMYWcpDQj(#kqc@`;E{~o8&%x%-G@%@t4 zZify%esd{8`b!yWoIFS!)kLKa9qA@b_Tn{N{Ym@RUni3*Pi z*Oe%BD`usgrpcG-A5I&c%QB(>v%&UL3NH6Iw?yW13TrdLxd&{Xi z1Z14Bavf_KCLDG^j2bX4Ne#F;p}?j4qutMj$D2B&Zim-&)t^JF*RMb`(3L2N?VgA9 zp%WA6D;KF@3k&Ek^VBfc`O4HhnOVblL8e^86V&iPD(zzk?PIVS?i!#>uf$D{iS%#k zb13y`_wVNZCuldnLJs9*1ZA9dWBNP&yu=<)=cjZ;_V?v1xqgNDi=FR@;JYwG>^|U1 zajO)@mK4U86xveCl>W{AkGI?J(BWq=>i>Y5;)K`vC+!l(*@fY8w%OGq|1KF{Ih1e> zaWlsERYMj6skoRm1Nj|E>M^dzzD~6AKg4<7vbFWlUo18OFRcY|4-h zLpxLF(oeRs6M7rtJ|-~{mmaGaqsUL{G`C8fV)sQU7jaO=Rx`VGjSWBk9%BQhD-Oa@ zC#lp)Ds&-^>Y?cgYUH%L)JWIus{3q1qSW>N7}6djeX}2ZGl{;Ls0Q7fT&-!bFrG1h zaey(v_+j26e}l;1p!v2R>d?curTyss>el_Wuh5P$$*F_ITTyR_DWDDny2i$Lh+95aM;2Ttu*(=%LpIGl%Y{gmgvglZ>USHCFLZ%Vv)(e0)u>`AZ3pI2%J zM%s$N{zKwvgRC_e2Zqca*x|GWhenGIDD_9oqc)99AB$K=F#kGzOyb;gkn!mSrCxPt zdNO1E%?Yi2_s2EIR>u@Z7eu8CO}l8(HNOu%GeM1;_KoOquI16awJGl~^7|$2_6My> zJ&keN?TO~TEB~O>Z!yl?XWDWJZTV}xw&fPatuIS=`}<10k8#pVm~)T#81>lyP;k5VVO8qHdferUe&1l`l!_)F}g66srs z^UeCuH8N3+4D?qcOOol+{nW^=G2dS6bQ?cfSp%IYudR~Tp;Hso=s>A!bV-S8^t58v zXxGz7)@6QM zrV8#-&5pb~Ulw+oqq_XqUN!iSe7vE{f8^s09sak;$B%SHii0+};JeN-{GmK{)Qi=G zm<6T6AS@^flr2`*@)gOgg?nc>xN3`{{{b*X*tc{w}+L*u_QVfw@&R z3t%)y6x>0Nv!l^KXP`BFU4aekD>Pi!;#1xt_TfT*hog?g9rEU?5EC__%Kb0~_J{PX8 zE>)T0I;X0#wyL6ZPN1g3#8RU!)%L-f8ki>83 zj#*S$rkg}b&Z=TWzX=Zkh*YWjrJN^pj*8B$%`ROQT(P3Grl6*@7GkJVV&(@bE-t5% ziYgXW!nb0-Gg9pGs;aIGR?mf1E(wrnVG5;+%bcQWO89(N@`42punm8KtTHlJ;YI8{#E8#scxLDh2n=VTL+@7t?@rvs7y&4dY@6qz+O86{UfmROHZWK}9L@ z{F9^e=HwSu(~4eHm z>RPTqEG#FTT1inb^=*565sSsj7oAsCRFYS|tcEKOl=?N@2IiLO_3<~_LlMN!&ee&RkDtBlgoV z^39a1zd26P-%M*d%zWE^femGLk@zpcNZKrZb-0y4FNUc}4acy+)cKcki2pi_M`QpfRX$lAEPCLe`0^%0hIjx93$!7jS+tjW28*aVZ{9vjJT&l6rqn8q07Ja zmwdvXN!NSA-@i6r|F>d4vGASA!HI>x{%_^*U!Tqin}9t_pRfsd|MhwMH>B{tyh#+~ znDv({Dn<_=`)vOY;s5zN-?{T7^`|?nJ2~j=@e9X)?HxMAMNB9cz4rCjyz27Tu6S)q z58sT(FC2Qa^%JGexYmS3RaWPm2w#5t-buC%vurrih8Z@TX2WzFrrFSI!&Do(ZFsbg zq4Rq-Y_;JVHauj*7j3xThR@ir#fH0W*lfecY`D#a57=<44Y%0vHXGh(!v-5V@vpJJ z12(L%VWAC|*wAmo3>&7~@N^q`ZRob)(O6UNzD)S82s(Gz_LdD>ZFtCr`)$}_!)6<9 zwc%zPZnEJj8y4EIz=jz%Ot)d04ZSu@wPCUi-8NJ67^?HGPnht$A)*?=`K|O{LVnuoY>z2TssI^0Ps5CKFk~7 z&j6E9R9ctjQiFiYFk8mDR0%L`2)ujz2%N`-=uO}Sz@=>5mx2pCG*YPtzy-dIkvNr? z^BzpW7?<(_zrZX6SED%3!bn;HVC-n(#NG|e!PJqi==^LH96vV#Cyp_AI&kh-(!#$V z*ou*~1b%OvDeq<=dcbs8fp=rX&lX_9cw?UkoMq!J!23@{R~d0W0PMtkB>6c_snalu z{G1LfJ{=x`&;*z;k>Y_T0#C&hh#%nBXaq~ZmjZWUq%6CE?_wkm9|6xzM=lThEZ{dW zLgzKWUt`42R^Z4plzNPp8@<4DFcNWNV zux2J@!A}4;->+am1XP&M*H9i5q}Ku zo3qhD1il7%6GrmC3HTbDjxy{;R_WCo@+mlQyB`@O@W+4y&nHgsrNA{92`lh+8yEOC zM)IaEpqerJ@t+R#V-A5A058J40bU3!!nA^y0H^06j|-jwtipT*UJZ=TC;!x4B9Lo1 zDj+X#0x!l$9+m+AhLL*z2v`SmOz0`F`cmq0Jn;ZeTS`9#KOOiOW+Ax1GcKp!flmVt zDB_F}96fnzCPw0~SfPi2)u3u>axM>fUYuQ9|L?9lY#vkz?5=hp9-90<9=Ys#%~1v4wH@lX5c3np~L6E zd#*6}y}-;0+8cfXz#n2H4=uoPRkSzoG~ksO$$tQNH%9zy0bT<$@m}yXz)vwP;GYAp zt2KBXFg9RtH*gb1>Pz6+LFyO(Gl36cWc=I)jJe7#FR%mSK9xAd?rPc!xWKqorXIb( zKC7uC?A^dTjFeH}6cji}|C$C|^G(WvAAvu_NdLMW*ol#{h`iJYjFiy}T#MO^|E<7d zn62PyEn4NTC7csuorkQM#|U%Z2AS?*lz+pd6%J23o!p~L)!x2w=fd_2H-x7ghel;ddJ2E zKJZK9U*J2xGGnR0`|mYl<^#ZA{Tf=4*1f>ZzcF))z(W|RFM-LwHMqcCm{$B3Y^7Y7 z_rPxf&fEt7cmiz(*l#=I2zWAZHb&~S8u&a$^0{B|M`<(o*$?dVn2FyDy!CNTeX-vR z{1Zm{y9J#5gu%0b7N!nA0`J=a9~}Gv;Q2eD8+ab@SGy=L_`Sf>c2j=vEMQI>x7rku!F9D8!#o%ec zGK}~an0d&w!A)nZ<0X~Kidx0O@_)*|RpHd&#F9hzx$e8d9Fzz$z2zzv)s?#tM zR_^J@y`#@*O9JJdkKh93uFO`(B7t%bM(hRdwsE-&Blk_jUZC775&r^*es1gqiVVK^ z5h(W^1Q#fG8w3|9_YedZ_%j=qy9jcRK4*h{2a#nJvb@yloP3GDZuz`pea_8lj%S3(5)7nyGI3GBTmuut#BUii0J*caT% z*bRKgB%m^W!5Bk+obSTB7)#w<-|pWs#!(55d-VgjkL&tQeT{D_*>P`v7yrcVe5d`D zZ_4C+Z{picB|G1@{f%)UBKsz;prkm90|EjnD2jRYTG~CKf}(;9j-`S_W#v`5w_A=WU}l_y z;cRK5W@TpKRGAH!X*pz2PL$O8e!tDxcpy}=`~N@R_dIv&W}o+c_psL5YpuQZTKl|b z$`mXexXhD+;?%o~r%ahXofQh?KyjWNxQfT`;o21!1!#-wDN}fTCJ(Ru*Vni!$`yYK zq&P-DpEAAB^?NjycOBPL@?4A9J|+hJGo`TpfxMWQJdCfeb^kFj<8dE#QrLeno-?72 zuFv*N!J_yR(skH=(>E4cHakemm)O3Ti4G!@KNSp4-^&WrS9T2=p(-l&Mhj| zk69n|J-fU}^kYqbc5$ZUG3)E!UN~0rSog1CW9RH|o+Itjm#WxfSIUVhsR5}lN1bvN ze0D+BtZ{>q&sop89s8*$r{KP%Di`D`$kmZ+BYOz>+zr`+d@DZh(4hm~ql`D#4jQDM zc;X2So8j?Aix#P@tSq%@(A`~s=f?EwU_)sg7oIpk zWV2OfKI@e`Y_L_ONM`Ql!R+r1{P15PuI0kjt2s(@@smo;^zab3>K>x1I5Kbt=_<~q z4(l`5E};`?6sYM0#dMR@tXVVV=o5`+JE&QY^}6t%ff)I0CIquzi2ef8moAF4x#4ymI@ zkE-LxkE<`g{8AO1KBkWRBPC~Hp_Z-R1O(^s_sL3&q%EQ39#K4 zVc(g9b2n%|x)8R2)KSl&{c^NlhxWVB{xI5~a%$fk?E@oWFDAfFTm<_t2X-Oa?)Xc8?--*_62Bv#i_k-bF6PcB|Z%9rE?ZwuY>(p;oA_x!*!G#YpdkTfl5v# zDmj;?q;S2GD~C$q_q`A8J<+}mY?dBqAA$D6&^{LJr`1vNTw5h81}b?yQOWK!^toQi z=|k7sV_l1^iuQM+eGRm)iT2Pxg)oN1a<7seZIuiksN{)6B`>8Z*|J{A(L>kUt6F$} zNLwk&w_66FXM*G6j_7CChAbrt8EP8kbJ-mV*_M(Tc(L-S! zQ9rd6b!nieD~Y16riuD_y{K!4q_q75Xx|F$1JM3qv>%D~lhJ-2+ApaiYE4^Fn+A&7 zlPD@LP1G0bMV&u%y}c{ilLGgn{R3#<810**{X=NqqmHPdZAB#x6g58)eWi)YT`%hR zq3i9-ss?LGa=sZH+%u?WP>8KPsu77`L391su`Vr}K^<<+cd z@7_(UjtU`Rc#QvH_z)6e^=ZKhy?a|6&%+~vL&F2meQ0n{K+hoSL#)uOnP+3i^RS4( zpzxsJkYKbYWUU|a@#*1!kyXMY!cFS+fO|b_YgAwlO+6c1ngGZ!_8%S|77%W2RJXo6 zo}+@dqe62l+6M%&gWz!M19j`xFL|zEZ0czNkYT0$TkpTGZr#5Ax&t4LWn(-C{2^=~ z6prWj)os+*0b9S81Oe@>0YMS{!^1i&C1goKAighcduu)+O~Auxiqg?Fodz@PmG>mdx) ztlixK06yXn2H1Lc?h25BRMiZ378HctnJ?L9bqQ>(&JlrO&OM?z9H_ zhGK~Rc>Z9oUK-1WrO$(FSF7z;t4(NlNCc?d%R^(?sF#C10=fnTwX0IQTAKiCgf${8 zG@=AbsF0Er7Tlp{uy0kDn*ALQNUN|As4K^FYZqU)8h5(5)ae5V31UQe02t}|^PtWh z+qADy#r57WJPhs$UhdE#IQaT=tF>p)fNr&_SL=ikg01X5Eadw5)?o78fNuBPQ9Y2) zg9E^>;ift0NBV%l0`IC*Yk)O4BA^?1t@JUTo7BgxPM3%v&@s&EG3g(oYv6w8_QBmC zD$b8}|CrzK5HMm`XvePQ9y|4qi4F_uTDCRhqil*O(>e63>`E!Bof>cg#>Fn=)RO#X z?70;5Q9esWwp^+TC}+>z+)gp}+`|t)tnHDR(Y;G;k5{MmQ1fC#)#9XJwP|*!%2_&7 zb>0V?0(!2`r=o_ycA(8L73GUBzNnThTc%d5SfSF>)79$LtF^7LapOj955D>4n~Ju= z$Gcut%V2Nhz*gA1cdz>BqmQ(0aOz~fI(hP>I(6!lI)DDW`u_Xx)lWYbXxrfO<;&{V zUw>6;S4FLdt#Ig?lb-c$jD@Z<7COk~qLEnWreUF5h=p#Asw10JTiK}w%087SM^u`8 zq1MZ3tl!ty8Rd%wG149FvDhwp2<DV5F+?7>+^2|_h!DnzAgF5(x8ERgT^gA+-lW&5cY6=Pw!^U zyqfveajV_p9$YtQ+{DWZb?dl!`1<(zsD_>`y?qKG;pui()TV`*P1n|RlB=I?b;2h-r1mKqq|&d@`kUQ$Nkk^ zs(YfYuWzFo)!6@E-0!H1tGk-itWm=U`+&yH9;ng~Ptl|it}(t(!~49N`1tzzcmaNl z-_pmYsgF-%AB|r_BnwjG#`;pF)P^s5B3Yh2Waby=| z!QVfX?m%l;?E32qu-bK54JAkZ|B-Hl{msTEymukr@8RLm7$tL4=(P6N+r_P4Vmn3S-EoM->~j%FDNMZ_J<#S_zr8&$qN@Q zoJN^{>C&YF>`PC5@x>SG-+S-9=W$-hr+fGA;Czh}77g7Gc2|1d{s{dz*IbzIZQi_j z6O^PC-PDyy?!$%+3+RM|$`8PgxDfkj$<57`@4ov^u$~J1E_6^yety1S|1EqD90Yst zFVCDga~8OMefaRB#&{8uQs-kQ*V_gRi1} zQBe`1E!Y-S9{&2EcOGQ01N-RQxpQ;E2e>r+`}gk`_(%ot$+m6VG~DnZ$+zErD+doA z6!;W$J+^_3!274rX65?z>t}4;x^)K3zR)>y<_t(kNC;W5U_m78YQok6*{5E;dhOxc zc(h%+cBGwqNJxk``^D!!1pmd07X`LVT9YPCI)a7^&Ye3a@adM)IWI3yKKtx54JYPD z!_K)mapHu0{`u$nnmCyZn@4{J&t3ZQ#~+Kpch}J8nO}bSMSl9}r;8td{P7##eDh5{ z#`>9Ufakv;Zyf={0=E65@WY-Gj8&QmzsaPl$($3D2|sDY(tPGMv%!NU;V~I7i^-UC zG1=TR*s){!GjX5}Aiq&gI7ftS%MT*a z$3+4@66x^1i0?ZhKD$L)?-F@13^Z(UXU+K)xq&ewLc*zUIg>aMp=ny1@nC z88L(Q`+&n`6Y??WPaH4@ojwq02OQ!*MZBXJ{_aB}ZFXw5f@iT(sXqLqnlE>jK+S&p4+cry!%m;Y*P);1CKNaxOU6xZ&p?@nq=@kg5Ok&p!*+ znKGF;m~8NBK-hUAK*|b&&0tY&#kGGpi`Sc zCp88R9@|QEE4rCg4hIdtr>AGB{>K{6b^dqrLHXr+VCIAJz-uEXgdciOcD*!8Y}2~S znkoL00UTBU2c{_K&*8vfD0KW^Pl^x+BhSRa$TM|P+xI~Cw?$eI2hiX0hR6WuDWs1o zf*<-`^FL&50qr5moWaB3!a3l2;5;V=7nX#ddUEruAX)chH^~AHuKLl;&8h)H4DwFC{&bYq7&MM8s!Q}FI7+G<~ zgP(~5)7F$OvSE5xS@%RYd2K3inCvf?3(v`?5NG0G1rCU@$={shnL3H`90XaQPV$3J z;`(fKl2@)slM?vL>m?08?ECp0J9hMhjWU;bP%pA1&kzsVJS>R|?{Td)b{U^>y`{~- z^k!-o*)p?>tOpL+z+o+L*s)}STq*$bR3e=Po^hBHKd#Tf!PppHJ4+gup+59K^@ZX8 zu3ft}=UkXfc_rM0jj$VBsQan!xv!z#KY0T)ImunjnDngJXj3zTQ#KzXK4S^~XF92OCWu|4IP zF~A`vL}rc()i@AN+8A7)jg8R}>l@PV_@6q1YcTBy=Cy0rip^#d+NCC&`Cxf7Ie}kR z4!#^EyXSP49kYM~WMLz4c-!#RfV`1tt!gq8A08JIb9 zrpCdUzsloMeolBzoj_T*Rr+-Ax>6g1^862nJeO;CQ~QVTbME`}>BD)D*Is)~+g`M3 z;2)65lP8zra5Fq^HYbFKI%yMi(yinfI51T}>xypGC!KKCW;b-FczJn=)oK--%@Epk z$;rtwapFXoKYzZKYq)&0-k~j25iX=X*9Ph;+CsEPxgTJnJm;;MB*njAzmYjvug}gl z#uA4-FM=#ERRpZL2VX^P*!q9PwT8b{t5%(Gv?m@v)qWm!w4R5Ij0_ntV1VFkk^JKy z{}9BKuK2ccf>guJ7ie!Oz_)@0+|qA_wCzP+dViB zqxC0YC#;+!;$SlGk>2Dz?sI6{aV{tilxZ!`mGm3&UNdljiv}n;nU|VZg2nX#Y=S&<-Hh8dPf6U}5(>HbNhbu_LMFCW2tSK9x8vy?J-?%azwqa?Fu&%XTVqmRn? z@#8h!v9Hv;L>wp=^!pGt;!z$K`XIOtlkaIGQC8@W$i;q=_h=(=oggk1B#_|euQ*?S z)c?c2AiJyJqs)Ku$tQm!oH#eCGiBP~(6VJq!PzxE4_q_I^R#^o9)y?hljn&i_XaE} z50nwggP9YSEIS0hSWa41{f3YajYCval=SP@PxBUH@)~~9 znlvXagqw0e{loj*BanY+Yv)1^C>LE3hqis#wbd=3BsTng_T>kVdePuwa9~M#Q#WDl*82qV6xUj3`Vv-N6MoK(u^|}O zA>WgiNOQ^%bjD)9aXYY=!tc!g)Dh0}QPG?bFZ$Sr4<9Z#m#g{P(A>FXJ@OKHo%gv0 z5OxzE!5(SFpMYJ%&oRvY7i-S~@+H?G;z1cCov1&k>$n~{&k1R}Wy=;>x^$`LXVTkb z;!WP6?9guFHF<{paV>z(d=;-?xLvvvb|&qAa9$S)zsbbGL3+W!Oj()(Ym-*brz>Ce*O!P4Ntk~F1$p)4?SZg`LL zK;20><(yF$F_yp>6mcQlF~;rand=aj{iNYH{1029IqAfFvpJ!CPy2^>F)qhU-eURg zyYFhfOBzsK$?t@pasYl@jp6?+&p2aHpY$~TPw0tK{||BC+DZF``j33T^@wXHY0Nz* zY0r6}Enwz>*VOscwbWzSQ|Pz`aQ;ujPCPXKJM%_)J~!7!Zk-bdppoF1b^t6szj;Tfmel zC-D88!jcl7^!0zPtm5d;#ZlthFBlK$PnM=X)4vnksq7xtyBy_-KU1bS;FOZm#Zgvq zl+_(&ZAav1f^D$ohet<84}`rR0-t{n?zcgv&3!*|DW9_MFN;JbBi7UBlad%4 zeZh<=v_ouXIDEsO{G>s;84Sd=VYO=2I%98_2$?RTK7-A98T$4t)`IV$myW|e&w!t7 zENtKq)W@4xlqc+sg3*X^Fiu8aXdwKzjCnI&eDtD79=vk+J`&RpsDGo>2s%6yV-`_= zQb%&XL>QP&48vjb{|3E!5jyt>^yZuFm@!kv8yV+fe1$%7#)m$>ByteAS0dKyK|Kw> zLKJ*YLkI(HC++ca$HOniOuW4{?# z*n)m#`T{fI&t%LO-=&jTkTGW%$d5zH@gvUfYnk9#8uDYgIGqQ_M?578euW{sbN=nLD+Mvt^ z4*E?<3nu#48EayUgRwHknHb+-oQyFh#x@uuV|;1)$ddfx>{uZTnjaB&HL-8@=R7`j z05feHW@k7E3+I3LGXcVQ*9O2qpC*0djIod(8Ruerg|S8*<8q8YV^l8=rDekKqd(|? zR675(%@Bu(!uKwRP#18&Py3vB6Bg2d<>Aa&9XDjWim_Y93>kY~>4+CnCKw-KtS}WZ zDnkPf=B%R`yG1JHf65lV+Zzx4KbEpcy~BM0_jaTSbr0!r>C9)c|6h?hPC)-O`H?Yv z`lgLcFwVqSALC>x4j4FCWPV8;e>lee;wtZy>L3r=m*hdpA#Hw^goBy%U?v=-6X)S( z>Byefb0krC-=-4#gsUXii_nh9}5hm(I z$^qdZ4LCof&BZgv<)id*^2VGXeHOs*W5%>zvfABSyT3?{CYvapSZf7-uB4@51bTNORJI^ymJLyg+&o{_=@^qTXTfmh)O$W_(^C zE;bl)ulIpm>lv3}YzcOj;M}*y+w8vx2Q%qGILglr;p4hR-NAh+y6=Qc`|f7RgL_ce zALkfLWLyH@GZp$@2}5FHqNY3b0oOtHZFqoVQ7;%-#X3=pxXftqax!cryk$o5%+sy* zR34-iXuoL8m@)4%#zP-HzVI)c3&z*5F6(h<50LI0pP4opZ8TG2t@;J@d=h=FM7-hx z#^zSwR!Q?8WsrT*wj;gi%Owxem(I8b<%4h<-xcS8dV%c-3u!^UO`SG2IeldjB!gPG%TJ;FN@v~J*8RpL&b<9w0U#G5)9zLzzBaBMw3$APZV`oAJLj17$M z903+ZzrKmLia_4vfstiR+X;>$jnsq-jvlx^Zk9^?5Pp1r{L0f

NjdKxu zuw{UmeQ+<%y%&8E-1l+c>4$wMeblt62siZ>6ZbIOLv8$bqOcF=bRH^}J{|fIxMw5% zI8R)IXa_NEe>zm#Tikze@4>wfZC&m+?8~1O_Q8F4AHc!+!@Fm6oSQm~bU3j$UACn9 z>-{8cciQdTH*oJXBf7U9V^F@xTI_4_J#R@#NzrFS7+2$5l3xt{b!GfHxI0JN{WGxl zaU>$Une@ZDp!F7Gay&o4wua}{&+H-RP8`wnU!!O&LzIPu4zhL z&AI$)Pljxm-CLjMUNN<=?0@rB4!_y7$9Gq>tw#MtzMS@Bwen<;E*Yd=BSA))Vb49)1u$HRvqtFqq#Rk4N_M84aIv{2I1{715|%phv9E8 zd}ey9Y5{CR7yXY4iJw>o@F7xF`~z5JQVgs}7xXs~zm^rN;?-#M7K>lhibl`zDn?B} z?Vh?$8|4RgX&2-Ucn=MLSK%;6TvRaX#Nq8OQK&x+&_v-XT8}XZJ@E;9v*7(TQEC$E zjKsU+qA{994OYqe6D$86Uu|_M9XkZ~lTni}B>=Wqj1ga{K11<-oM^P1gjP|2lVdZr z!!LbZ?|+CMH`%G*ws_Mza~oZf-gFMTq7CUVQP+=0kK>&%oQQ2e2h@+&W8%HostM}% z!}VxglaxsSO^C&4U_B9i{{Ef?#&j^gSZTp5RGwab8nywBzBzM*&CDIIN@k%vjDau- z@TN<&o1jL4N+SVP<>!nWAJVaG`|{MRh?aD2yMTM6FtUO2I)~8-Pjln1cuGD>1};&6 z?E1XdsG3u?dsK3AbliyX(=1ct#>Xf7drnG<@0vVvOmtjSa;v!5kx3JhC&Wx_HF82+ z*Qn&U)|1POV`HL|%`fTtd$w)e#)+h1WN+=79h859x{*1z}Aq4>a=;+L|IWt_n8I~8WQX1ZlYW)96%Zg|Hu?(bW5Y}J`nMXOxX z-P66({nGoS4^1DFJ~e$_`r`Dp>D$uxr5{T_lU|hWn&F{QuB?4o2eXc4oyaOlCsn)XdaO66U|@ zzZCf2MS&)bL%I%!lPL0$;c-zDqgzFdj2aakH*I*rv{noNnYNqk~I>;0nn7`!-+$SfTms#S-g;)+O#O`BzkJ>L~oAh-J%7Mb-Cj{{az0I gC_x42YC7Ogs9@yji=)ViPoU297H58MhmzyJUM literal 0 HcmV?d00001 diff --git a/venv/Scripts/coverage3.exe b/venv/Scripts/coverage3.exe new file mode 100644 index 0000000000000000000000000000000000000000..dc07cfa8de764ec8f315f8d8c9cba49d651be744 GIT binary patch literal 108416 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

NjdKxu zuw{UmeQ+<%y%&8E-1l+c>4$wMeblt62siZ>6ZbIOLv8$bqOcF=bRH^}J{|fIxMw5% zI8R)IXa_NEe>zm#Tikze@4>wfZC&m+?8~1O_Q8F4AHc!+!@Fm6oSQm~bU3j$UACn9 z>-{8cciQdTH*oJXBf7U9V^F@xTI_4_J#R@#NzrFS7+2$5l3xt{b!GfHxI0JN{WGxl zaU>$Une@ZDp!F7Gay&o4wua}{&+H-RP8`wnU!!O&LzIPu4zhL z&AI$)Pljxm-CLjMUNN<=?0@rB4!_y7$9Gq>tw#MtzMS@Bwen<;E*Yd=BSA))Vb49)1u$HRvqtFqq#Rk4N_M84aIv{2I1{715|%phv9E8 zd}ey9Y5{CR7yXY4iJw>o@F7xF`~z5JQVgs}7xXs~zm^rN;?-#M7K>lhibl`zDn?B} z?Vh?$8|4RgX&2-Ucn=MLSK%;6TvRaX#Nq8OQK&x+&_v-XT8}XZJ@E;9v*7(TQEC$E zjKsU+qA{994OYqe6D$86Uu|_M9XkZ~lTni}B>=Wqj1ga{K11<-oM^P1gjP|2lVdZr z!!LbZ?|+CMH`%G*ws_Mza~oZf-gFMTq7CUVQP+=0kK>&%oQQ2e2h@+&W8%HostM}% z!}VxglaxsSO^C&4U_B9i{{Ef?#&j^gSZTp5RGwab8nywBzBzM*&CDIIN@k%vjDau- z@TN<&o1jL4N+SVP<>!nWAJVaG`|{MRh?aD2yMTM6FtUO2I)~8-Pjln1cuGD>1};&6 z?E1XdsG3u?dsK3AbliyX(=1ct#>Xf7drnG<@0vVvOmtjSa;v!5kx3JhC&Wx_HF82+ z*Qn&U)|1POV`HL|%`fTtd$w)e#)+h1WN+=79h859x{*1z}Aq4>a=;+L|IWt_n8I~8WQX1ZlYW)96%Zg|Hu?(bW5Y}J`nMXOxX z-P66({nGoS4^1DFJ~e$_`r`Dp>D$uxr5{T_lU|hWn&F{QuB?4o2eXc4oyaOlCsn)XdaO66U|@ zzZCf2MS&)bL%I%!lPL0$;c-zDqgzFdj2aakH*I*rv{noNnYNqk~I>;0nn7`!-+$SfTms#S-g;)+O#O`BzkJ>L~oAh-J%7Mb-Cj{{az0I gC_x42YC7Ogs9@yji=)ViPoU297H58MhmzyJUM literal 0 HcmV?d00001 diff --git a/venv/Scripts/deactivate.bat b/venv/Scripts/deactivate.bat new file mode 100644 index 0000000..62a39a7 --- /dev/null +++ b/venv/Scripts/deactivate.bat @@ -0,0 +1,22 @@ +@echo off + +if defined _OLD_VIRTUAL_PROMPT ( + set "PROMPT=%_OLD_VIRTUAL_PROMPT%" +) +set _OLD_VIRTUAL_PROMPT= + +if defined _OLD_VIRTUAL_PYTHONHOME ( + set "PYTHONHOME=%_OLD_VIRTUAL_PYTHONHOME%" + set _OLD_VIRTUAL_PYTHONHOME= +) + +if defined _OLD_VIRTUAL_PATH ( + set "PATH=%_OLD_VIRTUAL_PATH%" +) + +set _OLD_VIRTUAL_PATH= + +set VIRTUAL_ENV= +set VIRTUAL_ENV_PROMPT= + +:END diff --git a/venv/Scripts/pip.exe b/venv/Scripts/pip.exe new file mode 100644 index 0000000000000000000000000000000000000000..fb9412e96f74d61db3503b0d5915a82d62764544 GIT binary patch literal 108422 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 literal 0 HcmV?d00001 diff --git a/venv/Scripts/pip3.12.exe b/venv/Scripts/pip3.12.exe new file mode 100644 index 0000000000000000000000000000000000000000..fb9412e96f74d61db3503b0d5915a82d62764544 GIT binary patch literal 108422 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 literal 0 HcmV?d00001 diff --git a/venv/Scripts/pip3.exe b/venv/Scripts/pip3.exe new file mode 100644 index 0000000000000000000000000000000000000000..fb9412e96f74d61db3503b0d5915a82d62764544 GIT binary patch literal 108422 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 literal 0 HcmV?d00001 diff --git a/venv/Scripts/py.test.exe b/venv/Scripts/py.test.exe new file mode 100644 index 0000000000000000000000000000000000000000..dc0bcb23ded0570d6904fd3777ded73870fac4ba GIT binary patch literal 108422 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

DV;hsUS~0@q}LA5hvP3 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 c*4)6KbnSx~)X-fmuXRI(bGqjL02F0C!2kdN literal 0 HcmV?d00001 diff --git a/venv/Scripts/pygmentize.exe b/venv/Scripts/pygmentize.exe new file mode 100644 index 0000000000000000000000000000000000000000..96d824f4057975476f731a53c3c24d1cee01dc62 GIT binary patch literal 108416 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

NjdKxu zuw{UmeQ+<%y%&8E-1l+c>4$wMeblt62siZ>6ZbIOLv8$bqOcF=bRH^}J{|fIxMw5% zI8R)IXa_NEe>zm#Tikze@4>wfZC&m+?8~1O_Q8F4AHc!+!@Fm6oSQm~bU3j$UACn9 z>-{8cciQdTH*oJXBf7U9V^F@xTI_4_J#R@#NzrFS7+2$5l3xt{b!GfHxI0JN{WGxl zaU>$Une@ZDp!F7Gay&o4wua}{&+H-RP8`wnU!!O&LzIPu4zhL z&AI$)Pljxm-CLjMUNN<=?0@rB4!_y7$9Gq>tw#MtzMS@Bwen<;E*Yd=BSA))Vb49)1u$HRvqtFqq#Rk4N_M84aIv{2I1{715|%phv9E8 zd}ey9Y5{CR7yXY4iJw>o@F7xF`~z5JQVgs}7xXs~zm^rN;?-#M7K>lhibl`zDn?B} z?Vh?$8|4RgX&2-Ucn=MLSK%;6TvRaX#Nq8OQK&x+&_v-XT8}XZJ@E;9v*7(TQEC$E zjKsU+qA{994OYqe6D$86Uu|_M9XkZ~lTni}B>=Wqj1ga{K11<-oM^P1gjP|2lVdZr z!!LbZ?|+CMH`%G*ws_Mza~oZf-gFMTq7CUVQP+=0kK>&%oQQ2e2h@+&W8%HostM}% z!}VxglaxsSO^C&4U_B9i{{Ef?#&j^gSZTp5RGwab8nywBzBzM*&CDIIN@k%vjDau- z@TN<&o1jL4N+SVP<>!nWAJVaG`|{MRh?aD2yMTM6FtUO2I)~8-Pjln1cuGD>1};&6 z?E1XdsG3u?dsK3AbliyX(=1ct#>Xf7drnG<@0vVvOmtjSa;v!5kx3JhC&Wx_HF82+ z*Qn&U)|1POV`HL|%`fTtd$w)e#)+h1WN+=79h859x{*1z}Aq4>a=;+L|IWt_n8I~8WQX1ZlYW)96%Zg|Hu?(bW5Y}J`nMXOxX z-P66({nGoS4^1DFJ~e$_`r`Dp>D$uxr5{T_lU|hWn&F{QuB?4o2eXc4oyaOlCsn)XdaO66U|@ zzZCf2MS&)bL%I%!lPL0$;c-zDqgzFdj2aakH*I*rv{nw#G%==?AMezRjZ2u2G|`e2Z9XMWORgD{G$GECFl}@k zehNOh^~ktUgu>#e$}Vfh##pS@c>I31)oStgw|JS6yt+c?TP*0LbyU*m$-~+_X7NYo zt&=B>@J{mbZk4Q&7!H7@Odn1hS_CxpddT91?9`@3X(!QBV<&oZMDG?YfUL_M_v!a? h;H8KapsVSCKdIXKG9Y2BNXIX?&{FGy3UsY0^*=4}JevRj literal 0 HcmV?d00001 diff --git a/venv/Scripts/pytest.exe b/venv/Scripts/pytest.exe new file mode 100644 index 0000000000000000000000000000000000000000..dc0bcb23ded0570d6904fd3777ded73870fac4ba GIT binary patch literal 108422 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

DV;hsUS~0@q}LA5hvP3 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 c*4)6KbnSx~)X-fmuXRI(bGqjL02F0C!2kdN literal 0 HcmV?d00001 diff --git a/venv/Scripts/python.exe b/venv/Scripts/python.exe new file mode 100644 index 0000000000000000000000000000000000000000..53121aefedc530b3cd70e9c10ed79327a69cf441 GIT binary patch literal 270104 zcmeFa4SZC^x%j`E?2-i%&SHrM#k$JUMnMgR)`W$+2X^(WY%~f8Dhf6bu~?0;3s?{W zn^ZQ3RchON`(kgYt-X5t;!CZ(Xf+|27hdGWs-U$++u9Qs+o&}JtnB~$%-Ou4Xzjh; z{`$Fp;gdac=FB|v%rnnC^UO2P%v4;p&XMbII9&W?G7iT^p7JlKe*eyo)8QC$+U6mS zM+WXZeWNqF^Ym%+Z&>83S$OmH3$OdKuj;y+Zn`<<`{F#`!uUibh;-{kqW=c$tK@_cpej*> zG@D|#)6ogOAN22>*u14NKGyt_G6f;2yD&w+e?Es}&N&O`UKhL0;W)9C55RP^@!ZU_ z-@hR2bB>+NF@Fc&)>Cjk305DA0O=afsj(BPI#O3h5$W!DU%KEq3l}Y{;*+8Yv~f%V z?vIa`Zr+VIlTgtJS~)yqvg|e_r2YlToelo~{$ZrtSI%-dns;RX8OdpWZ!{V57e$j% zzi;XlGmON+62m-Vq+OS8Fg@0=A2}RGVr$9lYp?Ew zB_NIDEWbx!1S95t!#r%6WH-!d{u+Iw@RyVa_~(t*kiS@317MQJXS6nxG7~koTaF() z9QBQw(QKHr{FRgvoZVZ7jpg(!X6RSgg>*zykNDRCC}Qptw9PlHB7p?L!N1^_l zk-U+j!2j-hrHoBdq-Hcp8Tz%euf68j%BcE8{UCTOk)&iJF>%As;YUamJ4c99CKQ`# zefOx;xA1PhLA9vatOzGXx`kw>B&4*Sf&gU*jwM2iklCcj0-f|tJ=F4Ta-86Bl~-PS#9_RR7}AQX3_*OYG?FDz8zsCz>kww=jhW zyhv-Kkkm-!Uuj(i$W78?t}{OjW%!+Zh$ z$rOI)S58N1yO2m=8Og!4s!&N}P}FP`t`&YV+cJg!Nm6rPePjImOyMsGC%Om3M;NJs z=cT~&Kxd<5>c>^)ZoUq`kgvn|N?W~;;Z%-(1Q-0gMsm5oXu6TQ(O+w%(2z?Kd zToF241Kx(Y2hc~Q;7!!DbZ=>Urto}l(l@OW#8cq|hPiL^K*0yHR2s0Vv_+=yuE(9i z{dI2ucGYr!r6kwte=mj9zo^x{Akh__juIP)4bFyRZqF1(Rqpr+kl9JSHq9fq^*k8t z&}KAB6V;S9YC}W*8Y3Crtql#=^0$t^_53}^Ut^~5@MBKLrY>p5!p{H=${MNZjn)^T z5Tq5zf1+BO<_hH71oEc0Rbt6!CvLMo*O&Os-o%wE@n2Qq9leQnTb{ne@Af7hDT&oU z105HWB2ze@JXs71dh7Pi-$*AUoY@-wsY9p)84VFw#MOjL8|6I+hmJ zQq%mEnZhe=phB>&UWieF2*65eRV(w?CeGS>)^Q~nz}F}%wJ5OKLPA*$!$Jom^-a69udK} z(|X|;tb=6w5vW7JE=MmaKwh%Xe5OKN%wE>6A~5dBC&Pku*kE5w

Y`T`{`vgv2kKpi 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 C;tFX1 literal 0 HcmV?d00001 diff --git a/venv/Scripts/pythonw.exe b/venv/Scripts/pythonw.exe new file mode 100644 index 0000000000000000000000000000000000000000..a09f6e9d7f2982d1ce6f3d247747fedfaf111a91 GIT binary patch literal 258840 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 z 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 z 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 z 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

Date: Mon, 8 Dec 2025 17:35:34 -0600 Subject: [PATCH 2/2] Solution --- app/main.py | 0 src/app/main.py | 1 - 2 files changed, 1 deletion(-) create mode 100644 app/main.py delete mode 100644 src/app/main.py diff --git a/app/main.py b/app/main.py new file mode 100644 index 0000000..e69de29 diff --git a/src/app/main.py b/src/app/main.py deleted file mode 100644 index f5dc8c4..0000000 --- a/src/app/main.py +++ /dev/null @@ -1 +0,0 @@ -print (1 + 1) \ No newline at end of file

$!jHKq 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

Cl>D-DFDq4>^eTE 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 X(|;RI@mxcWAOw9em_B9%Hu(Pn(sxO8 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/_pytest/_io/__pycache__/wcwidth.cpython-312.pyc b/venv/Lib/site-packages/_pytest/_io/__pycache__/wcwidth.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d040811c521698d83fb2a159be6de0d23fa5de33 GIT binary patch literal 1686 zcmbVML2nyH6rR~#du^{bc9K?2i%KVn6k?R5X=yp23Mp!+DikUTq6o{#Om`;9HtSt8 zyH4215tit|Z4vSXm17SaihAIOa7tP!VJT82%E33c$*Gs}X6+_12njLLe*50M_vXDf z@4flOG&2a+_b<=d=>$T5Naod2y9C~SABek%A%-1P!9|QEtvE_WEvhlCI$BXv5LcP@ z0C%mJ&Js7Z2fee336^5Xn_4j$s)cl0?iO%lP}lVW8rYue1Dke4t!z)2AhQdUkNBoa(nQQ)tH5M&)QZDkT6f08Bobdpln zKk3S(^gc-{mgHkex=)gc&(}f?BNvBS(7PyvZfJ3=eQ+o~rLHhTMj>#R1=?Jd)M5K% zpJ~h-*t3CHKL=NsU;lZY2V7Kamy>00jZ~-`63cUH71t+JaN=^#I3s~anC({`8WKvZ zWh$rzX2f=Z?b=gkCX*qsKH@=5fc5klVlR?lnIG2o2~>(yh3y9Py2EGsEM}+38R7|Y zmMj8#U@8u5{jP4CKsN2KCCJhVs0^|V0+5q1DPcy!7ZNOhNV#aWU&1OVk@5kD&jku! zitwjxX z3aS`_UzBz|QK62#&f|(hZ(=`6#O)xCN(E)j^5sQB&k=D`|EmFA;~By;t7vb7-A5d1i2WMFeYJKs_RjvTwbifL%_>rA!j5? zFV;*4ITE>t{LbNIXB4G_sO_oo0lD#rMCH1;Pt^&1*` emPCrteCwOnpDW|o+*Og1??`HI0ULNQKJ722c%Yd8 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/_pytest/_io/pprint.py b/venv/Lib/site-packages/_pytest/_io/pprint.py new file mode 100644 index 0000000..28f0690 --- /dev/null +++ b/venv/Lib/site-packages/_pytest/_io/pprint.py @@ -0,0 +1,673 @@ +# mypy: allow-untyped-defs +# This module was imported from the cpython standard library +# (https://github.com/python/cpython/) at commit +# c5140945c723ae6c4b7ee81ff720ac8ea4b52cfd (python3.12). +# +# +# Original Author: Fred L. Drake, Jr. +# fdrake@acm.org +# +# This is a simple little module I wrote to make life easier. I didn't +# see anything quite like it in the library, though I may have overlooked +# something. I wrote this when I was trying to read some heavily nested +# tuples with fairly non-descriptive content. This is modeled very much +# after Lisp/Scheme - style pretty-printing of lists. If you find it +# useful, thank small children who sleep at night. +from __future__ import annotations + +import collections as _collections +from collections.abc import Callable +from collections.abc import Iterator +import dataclasses as _dataclasses +from io import StringIO as _StringIO +import re +import types as _types +from typing import Any +from typing import IO + + +class _safe_key: + """Helper function for key functions when sorting unorderable objects. + + The wrapped-object will fallback to a Py2.x style comparison for + unorderable types (sorting first comparing the type name and then by + the obj ids). Does not work recursively, so dict.items() must have + _safe_key applied to both the key and the value. + + """ + + __slots__ = ["obj"] + + def __init__(self, obj): + self.obj = obj + + def __lt__(self, other): + try: + return self.obj < other.obj + except TypeError: + return (str(type(self.obj)), id(self.obj)) < ( + str(type(other.obj)), + id(other.obj), + ) + + +def _safe_tuple(t): + """Helper function for comparing 2-tuples""" + return _safe_key(t[0]), _safe_key(t[1]) + + +class PrettyPrinter: + def __init__( + self, + indent: int = 4, + width: int = 80, + depth: int | None = None, + ) -> None: + """Handle pretty printing operations onto a stream using a set of + configured parameters. + + indent + Number of spaces to indent for each level of nesting. + + width + Attempted maximum number of columns in the output. + + depth + The maximum depth to print out nested structures. + + """ + if indent < 0: + raise ValueError("indent must be >= 0") + if depth is not None and depth <= 0: + raise ValueError("depth must be > 0") + if not width: + raise ValueError("width must be != 0") + self._depth = depth + self._indent_per_level = indent + self._width = width + + def pformat(self, object: Any) -> str: + sio = _StringIO() + self._format(object, sio, 0, 0, set(), 0) + return sio.getvalue() + + def _format( + self, + object: Any, + stream: IO[str], + indent: int, + allowance: int, + context: set[int], + level: int, + ) -> None: + objid = id(object) + if objid in context: + stream.write(_recursion(object)) + return + + p = self._dispatch.get(type(object).__repr__, None) + if p is not None: + context.add(objid) + p(self, object, stream, indent, allowance, context, level + 1) + context.remove(objid) + elif ( + _dataclasses.is_dataclass(object) + and not isinstance(object, type) + and object.__dataclass_params__.repr # type:ignore[attr-defined] + and + # Check dataclass has generated repr method. + hasattr(object.__repr__, "__wrapped__") + and "__create_fn__" in object.__repr__.__wrapped__.__qualname__ + ): + context.add(objid) + self._pprint_dataclass( + object, stream, indent, allowance, context, level + 1 + ) + context.remove(objid) + else: + stream.write(self._repr(object, context, level)) + + def _pprint_dataclass( + self, + object: Any, + stream: IO[str], + indent: int, + allowance: int, + context: set[int], + level: int, + ) -> None: + cls_name = object.__class__.__name__ + items = [ + (f.name, getattr(object, f.name)) + for f in _dataclasses.fields(object) + if f.repr + ] + stream.write(cls_name + "(") + self._format_namespace_items(items, stream, indent, allowance, context, level) + stream.write(")") + + _dispatch: dict[ + Callable[..., str], + Callable[[PrettyPrinter, Any, IO[str], int, int, set[int], int], None], + ] = {} + + def _pprint_dict( + self, + object: Any, + stream: IO[str], + indent: int, + allowance: int, + context: set[int], + level: int, + ) -> None: + write = stream.write + write("{") + items = sorted(object.items(), key=_safe_tuple) + self._format_dict_items(items, stream, indent, allowance, context, level) + write("}") + + _dispatch[dict.__repr__] = _pprint_dict + + def _pprint_ordered_dict( + self, + object: Any, + stream: IO[str], + indent: int, + allowance: int, + context: set[int], + level: int, + ) -> None: + if not len(object): + stream.write(repr(object)) + return + cls = object.__class__ + stream.write(cls.__name__ + "(") + self._pprint_dict(object, stream, indent, allowance, context, level) + stream.write(")") + + _dispatch[_collections.OrderedDict.__repr__] = _pprint_ordered_dict + + def _pprint_list( + self, + object: Any, + stream: IO[str], + indent: int, + allowance: int, + context: set[int], + level: int, + ) -> None: + stream.write("[") + self._format_items(object, stream, indent, allowance, context, level) + stream.write("]") + + _dispatch[list.__repr__] = _pprint_list + + def _pprint_tuple( + self, + object: Any, + stream: IO[str], + indent: int, + allowance: int, + context: set[int], + level: int, + ) -> None: + stream.write("(") + self._format_items(object, stream, indent, allowance, context, level) + stream.write(")") + + _dispatch[tuple.__repr__] = _pprint_tuple + + def _pprint_set( + self, + object: Any, + stream: IO[str], + indent: int, + allowance: int, + context: set[int], + level: int, + ) -> None: + if not len(object): + stream.write(repr(object)) + return + typ = object.__class__ + if typ is set: + stream.write("{") + endchar = "}" + else: + stream.write(typ.__name__ + "({") + endchar = "})" + object = sorted(object, key=_safe_key) + self._format_items(object, stream, indent, allowance, context, level) + stream.write(endchar) + + _dispatch[set.__repr__] = _pprint_set + _dispatch[frozenset.__repr__] = _pprint_set + + def _pprint_str( + self, + object: Any, + stream: IO[str], + indent: int, + allowance: int, + context: set[int], + level: int, + ) -> None: + write = stream.write + if not len(object): + write(repr(object)) + return + chunks = [] + lines = object.splitlines(True) + if level == 1: + indent += 1 + allowance += 1 + max_width1 = max_width = self._width - indent + for i, line in enumerate(lines): + rep = repr(line) + if i == len(lines) - 1: + max_width1 -= allowance + if len(rep) <= max_width1: + chunks.append(rep) + else: + # A list of alternating (non-space, space) strings + parts = re.findall(r"\S*\s*", line) + assert parts + assert not parts[-1] + parts.pop() # drop empty last part + max_width2 = max_width + current = "" + for j, part in enumerate(parts): + candidate = current + part + if j == len(parts) - 1 and i == len(lines) - 1: + max_width2 -= allowance + if len(repr(candidate)) > max_width2: + if current: + chunks.append(repr(current)) + current = part + else: + current = candidate + if current: + chunks.append(repr(current)) + if len(chunks) == 1: + write(rep) + return + if level == 1: + write("(") + for i, rep in enumerate(chunks): + if i > 0: + write("\n" + " " * indent) + write(rep) + if level == 1: + write(")") + + _dispatch[str.__repr__] = _pprint_str + + def _pprint_bytes( + self, + object: Any, + stream: IO[str], + indent: int, + allowance: int, + context: set[int], + level: int, + ) -> None: + write = stream.write + if len(object) <= 4: + write(repr(object)) + return + parens = level == 1 + if parens: + indent += 1 + allowance += 1 + write("(") + delim = "" + for rep in _wrap_bytes_repr(object, self._width - indent, allowance): + write(delim) + write(rep) + if not delim: + delim = "\n" + " " * indent + if parens: + write(")") + + _dispatch[bytes.__repr__] = _pprint_bytes + + def _pprint_bytearray( + self, + object: Any, + stream: IO[str], + indent: int, + allowance: int, + context: set[int], + level: int, + ) -> None: + write = stream.write + write("bytearray(") + self._pprint_bytes( + bytes(object), stream, indent + 10, allowance + 1, context, level + 1 + ) + write(")") + + _dispatch[bytearray.__repr__] = _pprint_bytearray + + def _pprint_mappingproxy( + self, + object: Any, + stream: IO[str], + indent: int, + allowance: int, + context: set[int], + level: int, + ) -> None: + stream.write("mappingproxy(") + self._format(object.copy(), stream, indent, allowance, context, level) + stream.write(")") + + _dispatch[_types.MappingProxyType.__repr__] = _pprint_mappingproxy + + def _pprint_simplenamespace( + self, + object: Any, + stream: IO[str], + indent: int, + allowance: int, + context: set[int], + level: int, + ) -> None: + if type(object) is _types.SimpleNamespace: + # The SimpleNamespace repr is "namespace" instead of the class + # name, so we do the same here. For subclasses; use the class name. + cls_name = "namespace" + else: + cls_name = object.__class__.__name__ + items = object.__dict__.items() + stream.write(cls_name + "(") + self._format_namespace_items(items, stream, indent, allowance, context, level) + stream.write(")") + + _dispatch[_types.SimpleNamespace.__repr__] = _pprint_simplenamespace + + def _format_dict_items( + self, + items: list[tuple[Any, Any]], + stream: IO[str], + indent: int, + allowance: int, + context: set[int], + level: int, + ) -> None: + if not items: + return + + write = stream.write + item_indent = indent + self._indent_per_level + delimnl = "\n" + " " * item_indent + for key, ent in items: + write(delimnl) + write(self._repr(key, context, level)) + write(": ") + self._format(ent, stream, item_indent, 1, context, level) + write(",") + + write("\n" + " " * indent) + + def _format_namespace_items( + self, + items: list[tuple[Any, Any]], + stream: IO[str], + indent: int, + allowance: int, + context: set[int], + level: int, + ) -> None: + if not items: + return + + write = stream.write + item_indent = indent + self._indent_per_level + delimnl = "\n" + " " * item_indent + for key, ent in items: + write(delimnl) + write(key) + write("=") + if id(ent) in context: + # Special-case representation of recursion to match standard + # recursive dataclass repr. + write("...") + else: + self._format( + ent, + stream, + item_indent + len(key) + 1, + 1, + context, + level, + ) + + write(",") + + write("\n" + " " * indent) + + def _format_items( + self, + items: list[Any], + stream: IO[str], + indent: int, + allowance: int, + context: set[int], + level: int, + ) -> None: + if not items: + return + + write = stream.write + item_indent = indent + self._indent_per_level + delimnl = "\n" + " " * item_indent + + for item in items: + write(delimnl) + self._format(item, stream, item_indent, 1, context, level) + write(",") + + write("\n" + " " * indent) + + def _repr(self, object: Any, context: set[int], level: int) -> str: + return self._safe_repr(object, context.copy(), self._depth, level) + + def _pprint_default_dict( + self, + object: Any, + stream: IO[str], + indent: int, + allowance: int, + context: set[int], + level: int, + ) -> None: + rdf = self._repr(object.default_factory, context, level) + stream.write(f"{object.__class__.__name__}({rdf}, ") + self._pprint_dict(object, stream, indent, allowance, context, level) + stream.write(")") + + _dispatch[_collections.defaultdict.__repr__] = _pprint_default_dict + + def _pprint_counter( + self, + object: Any, + stream: IO[str], + indent: int, + allowance: int, + context: set[int], + level: int, + ) -> None: + stream.write(object.__class__.__name__ + "(") + + if object: + stream.write("{") + items = object.most_common() + self._format_dict_items(items, stream, indent, allowance, context, level) + stream.write("}") + + stream.write(")") + + _dispatch[_collections.Counter.__repr__] = _pprint_counter + + def _pprint_chain_map( + self, + object: Any, + stream: IO[str], + indent: int, + allowance: int, + context: set[int], + level: int, + ) -> None: + if not len(object.maps) or (len(object.maps) == 1 and not len(object.maps[0])): + stream.write(repr(object)) + return + + stream.write(object.__class__.__name__ + "(") + self._format_items(object.maps, stream, indent, allowance, context, level) + stream.write(")") + + _dispatch[_collections.ChainMap.__repr__] = _pprint_chain_map + + def _pprint_deque( + self, + object: Any, + stream: IO[str], + indent: int, + allowance: int, + context: set[int], + level: int, + ) -> None: + stream.write(object.__class__.__name__ + "(") + if object.maxlen is not None: + stream.write(f"maxlen={object.maxlen}, ") + stream.write("[") + + self._format_items(object, stream, indent, allowance + 1, context, level) + stream.write("])") + + _dispatch[_collections.deque.__repr__] = _pprint_deque + + def _pprint_user_dict( + self, + object: Any, + stream: IO[str], + indent: int, + allowance: int, + context: set[int], + level: int, + ) -> None: + self._format(object.data, stream, indent, allowance, context, level - 1) + + _dispatch[_collections.UserDict.__repr__] = _pprint_user_dict + + def _pprint_user_list( + self, + object: Any, + stream: IO[str], + indent: int, + allowance: int, + context: set[int], + level: int, + ) -> None: + self._format(object.data, stream, indent, allowance, context, level - 1) + + _dispatch[_collections.UserList.__repr__] = _pprint_user_list + + def _pprint_user_string( + self, + object: Any, + stream: IO[str], + indent: int, + allowance: int, + context: set[int], + level: int, + ) -> None: + self._format(object.data, stream, indent, allowance, context, level - 1) + + _dispatch[_collections.UserString.__repr__] = _pprint_user_string + + def _safe_repr( + self, object: Any, context: set[int], maxlevels: int | None, level: int + ) -> str: + typ = type(object) + if typ in _builtin_scalars: + return repr(object) + + r = getattr(typ, "__repr__", None) + + if issubclass(typ, dict) and r is dict.__repr__: + if not object: + return "{}" + objid = id(object) + if maxlevels and level >= maxlevels: + return "{...}" + if objid in context: + return _recursion(object) + context.add(objid) + components: list[str] = [] + append = components.append + level += 1 + for k, v in sorted(object.items(), key=_safe_tuple): + krepr = self._safe_repr(k, context, maxlevels, level) + vrepr = self._safe_repr(v, context, maxlevels, level) + append(f"{krepr}: {vrepr}") + context.remove(objid) + return "{{{}}}".format(", ".join(components)) + + if (issubclass(typ, list) and r is list.__repr__) or ( + issubclass(typ, tuple) and r is tuple.__repr__ + ): + if issubclass(typ, list): + if not object: + return "[]" + format = "[%s]" + elif len(object) == 1: + format = "(%s,)" + else: + if not object: + return "()" + format = "(%s)" + objid = id(object) + if maxlevels and level >= maxlevels: + return format % "..." + if objid in context: + return _recursion(object) + context.add(objid) + components = [] + append = components.append + level += 1 + for o in object: + orepr = self._safe_repr(o, context, maxlevels, level) + append(orepr) + context.remove(objid) + return format % ", ".join(components) + + return repr(object) + + +_builtin_scalars = frozenset( + {str, bytes, bytearray, float, complex, bool, type(None), int} +) + + +def _recursion(object: Any) -> str: + return f"" + + +def _wrap_bytes_repr(object: Any, width: int, allowance: int) -> Iterator[str]: + current = b"" + last = len(object) // 4 * 4 + for i in range(0, len(object), 4): + part = object[i : i + 4] + candidate = current + part + if i == last: + width -= allowance + if len(repr(candidate)) > width: + if current: + yield repr(current) + current = part + else: + current = candidate + if current: + yield repr(current) diff --git a/venv/Lib/site-packages/_pytest/_io/saferepr.py b/venv/Lib/site-packages/_pytest/_io/saferepr.py new file mode 100644 index 0000000..cee70e3 --- /dev/null +++ b/venv/Lib/site-packages/_pytest/_io/saferepr.py @@ -0,0 +1,130 @@ +from __future__ import annotations + +import pprint +import reprlib + + +def _try_repr_or_str(obj: object) -> str: + try: + return repr(obj) + except (KeyboardInterrupt, SystemExit): + raise + except BaseException: + return f'{type(obj).__name__}("{obj}")' + + +def _format_repr_exception(exc: BaseException, obj: object) -> str: + try: + exc_info = _try_repr_or_str(exc) + except (KeyboardInterrupt, SystemExit): + raise + except BaseException as inner_exc: + exc_info = f"unpresentable exception ({_try_repr_or_str(inner_exc)})" + return ( + f"<[{exc_info} raised in repr()] {type(obj).__name__} object at 0x{id(obj):x}>" + ) + + +def _ellipsize(s: str, maxsize: int) -> str: + if len(s) > maxsize: + i = max(0, (maxsize - 3) // 2) + j = max(0, maxsize - 3 - i) + return s[:i] + "..." + s[len(s) - j :] + return s + + +class SafeRepr(reprlib.Repr): + """ + repr.Repr that limits the resulting size of repr() and includes + information on exceptions raised during the call. + """ + + def __init__(self, maxsize: int | None, use_ascii: bool = False) -> None: + """ + :param maxsize: + If not None, will truncate the resulting repr to that specific size, using ellipsis + somewhere in the middle to hide the extra text. + If None, will not impose any size limits on the returning repr. + """ + super().__init__() + # ``maxstring`` is used by the superclass, and needs to be an int; using a + # very large number in case maxsize is None, meaning we want to disable + # truncation. + self.maxstring = maxsize if maxsize is not None else 1_000_000_000 + self.maxsize = maxsize + self.use_ascii = use_ascii + + def repr(self, x: object) -> str: + try: + if self.use_ascii: + s = ascii(x) + else: + s = super().repr(x) + except (KeyboardInterrupt, SystemExit): + raise + except BaseException as exc: + s = _format_repr_exception(exc, x) + if self.maxsize is not None: + s = _ellipsize(s, self.maxsize) + return s + + def repr_instance(self, x: object, level: int) -> str: + try: + s = repr(x) + except (KeyboardInterrupt, SystemExit): + raise + except BaseException as exc: + s = _format_repr_exception(exc, x) + if self.maxsize is not None: + s = _ellipsize(s, self.maxsize) + return s + + +def safeformat(obj: object) -> str: + """Return a pretty printed string for the given object. + + Failing __repr__ functions of user instances will be represented + with a short exception info. + """ + try: + return pprint.pformat(obj) + except Exception as exc: + return _format_repr_exception(exc, obj) + + +# Maximum size of overall repr of objects to display during assertion errors. +DEFAULT_REPR_MAX_SIZE = 240 + + +def saferepr( + obj: object, maxsize: int | None = DEFAULT_REPR_MAX_SIZE, use_ascii: bool = False +) -> str: + """Return a size-limited safe repr-string for the given object. + + Failing __repr__ functions of user instances will be represented + with a short exception info and 'saferepr' generally takes + care to never raise exceptions itself. + + This function is a wrapper around the Repr/reprlib functionality of the + stdlib. + """ + return SafeRepr(maxsize, use_ascii).repr(obj) + + +def saferepr_unlimited(obj: object, use_ascii: bool = True) -> str: + """Return an unlimited-size safe repr-string for the given object. + + As with saferepr, failing __repr__ functions of user instances + will be represented with a short exception info. + + This function is a wrapper around simple repr. + + Note: a cleaner solution would be to alter ``saferepr``this way + when maxsize=None, but that might affect some other code. + """ + try: + if use_ascii: + return ascii(obj) + return repr(obj) + except Exception as exc: + return _format_repr_exception(exc, obj) diff --git a/venv/Lib/site-packages/_pytest/_io/terminalwriter.py b/venv/Lib/site-packages/_pytest/_io/terminalwriter.py new file mode 100644 index 0000000..9191b4e --- /dev/null +++ b/venv/Lib/site-packages/_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/venv/Lib/site-packages/_pytest/_io/wcwidth.py b/venv/Lib/site-packages/_pytest/_io/wcwidth.py new file mode 100644 index 0000000..23886ff --- /dev/null +++ b/venv/Lib/site-packages/_pytest/_io/wcwidth.py @@ -0,0 +1,57 @@ +from __future__ import annotations + +from functools import lru_cache +import unicodedata + + +@lru_cache(100) +def wcwidth(c: str) -> int: + """Determine how many columns are needed to display a character in a terminal. + + Returns -1 if the character is not printable. + Returns 0, 1 or 2 for other characters. + """ + o = ord(c) + + # ASCII fast path. + if 0x20 <= o < 0x07F: + return 1 + + # Some Cf/Zp/Zl characters which should be zero-width. + if ( + o == 0x0000 + or 0x200B <= o <= 0x200F + or 0x2028 <= o <= 0x202E + or 0x2060 <= o <= 0x2063 + ): + return 0 + + category = unicodedata.category(c) + + # Control characters. + if category == "Cc": + return -1 + + # Combining characters with zero width. + if category in ("Me", "Mn"): + return 0 + + # Full/Wide east asian characters. + if unicodedata.east_asian_width(c) in ("F", "W"): + return 2 + + return 1 + + +def wcswidth(s: str) -> int: + """Determine how many columns are needed to display a string in a terminal. + + Returns -1 if the string contains non-printable characters. + """ + width = 0 + for c in unicodedata.normalize("NFC", s): + wc = wcwidth(c) + if wc < 0: + return -1 + width += wc + return width diff --git a/venv/Lib/site-packages/_pytest/_py/__init__.py b/venv/Lib/site-packages/_pytest/_py/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/venv/Lib/site-packages/_pytest/_py/__pycache__/__init__.cpython-312.pyc b/venv/Lib/site-packages/_pytest/_py/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..be12f1b2180d96e07a848eedffbaf008b64c01fe GIT binary patch literal 183 zcmX@j%ge<81XKSTWrFC(AOanHW&w&!XQ*V*Wb|9fP{ah}eFmxd73N|UV_;wq5E+x3 zSdyxnn4FlBnp+uDP^nvzSe&h!UYePbnv!4O1ma>4<0CU8BV!RWkOcq= C^Dw#q literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/_pytest/_py/__pycache__/error.cpython-312.pyc b/venv/Lib/site-packages/_pytest/_py/__pycache__/error.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7ddf62bf20180acf3305f80bc79fb1884ea0073e GIT binary patch literal 4951 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 UR^FW`RPz;Zq-$_IDX=m5Zz$RdJOBUy literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/_pytest/_py/error.py b/venv/Lib/site-packages/_pytest/_py/error.py new file mode 100644 index 0000000..dace237 --- /dev/null +++ b/venv/Lib/site-packages/_pytest/_py/error.py @@ -0,0 +1,119 @@ +"""create errno-specific classes for IO or os calls.""" + +from __future__ import annotations + +from collections.abc import Callable +import errno +import os +import sys +from typing import TYPE_CHECKING +from typing import TypeVar + + +if TYPE_CHECKING: + from typing_extensions import ParamSpec + + P = ParamSpec("P") + +R = TypeVar("R") + + +class Error(EnvironmentError): + def __repr__(self) -> str: + return "{}.{} {!r}: {} ".format( + self.__class__.__module__, + self.__class__.__name__, + self.__class__.__doc__, + " ".join(map(str, self.args)), + # repr(self.args) + ) + + def __str__(self) -> str: + s = "[{}]: {}".format( + self.__class__.__doc__, + " ".join(map(str, self.args)), + ) + return s + + +_winerrnomap = { + 2: errno.ENOENT, + 3: errno.ENOENT, + 17: errno.EEXIST, + 18: errno.EXDEV, + 13: errno.EBUSY, # empty cd drive, but ENOMEDIUM seems unavailable + 22: errno.ENOTDIR, + 20: errno.ENOTDIR, + 267: errno.ENOTDIR, + 5: errno.EACCES, # anything better? +} + + +class ErrorMaker: + """lazily provides Exception classes for each possible POSIX errno + (as defined per the 'errno' module). All such instances + subclass EnvironmentError. + """ + + _errno2class: dict[int, type[Error]] = {} + + def __getattr__(self, name: str) -> type[Error]: + if name[0] == "_": + raise AttributeError(name) + eno = getattr(errno, name) + cls = self._geterrnoclass(eno) + setattr(self, name, cls) + return cls + + def _geterrnoclass(self, eno: int) -> type[Error]: + try: + return self._errno2class[eno] + except KeyError: + clsname = errno.errorcode.get(eno, f"UnknownErrno{eno}") + errorcls = type( + clsname, + (Error,), + {"__module__": "py.error", "__doc__": os.strerror(eno)}, + ) + self._errno2class[eno] = errorcls + return errorcls + + def checked_call( + self, func: Callable[P, R], *args: P.args, **kwargs: P.kwargs + ) -> R: + """Call a function and raise an errno-exception if applicable.""" + __tracebackhide__ = True + try: + return func(*args, **kwargs) + except Error: + raise + except OSError as value: + if not hasattr(value, "errno"): + raise + if sys.platform == "win32": + try: + # error: Invalid index type "Optional[int]" for "dict[int, int]"; expected type "int" [index] + # OK to ignore because we catch the KeyError below. + cls = self._geterrnoclass(_winerrnomap[value.errno]) # type:ignore[index] + except KeyError: + raise value + else: + # we are not on Windows, or we got a proper OSError + if value.errno is None: + cls = type( + "UnknownErrnoNone", + (Error,), + {"__module__": "py.error", "__doc__": None}, + ) + else: + cls = self._geterrnoclass(value.errno) + + raise cls(f"{func.__name__}{args!r}") + + +_error_maker = ErrorMaker() +checked_call = _error_maker.checked_call + + +def __getattr__(attr: str) -> type[Error]: + return getattr(_error_maker, attr) # type: ignore[no-any-return] diff --git a/venv/Lib/site-packages/_pytest/_py/path.py b/venv/Lib/site-packages/_pytest/_py/path.py new file mode 100644 index 0000000..b7131b0 --- /dev/null +++ b/venv/Lib/site-packages/_pytest/_py/path.py @@ -0,0 +1,1475 @@ +# mypy: allow-untyped-defs +"""local path implementation.""" + +from __future__ import annotations + +import atexit +from collections.abc import Callable +from contextlib import contextmanager +import fnmatch +import importlib.util +import io +import os +from os.path import abspath +from os.path import dirname +from os.path import exists +from os.path import isabs +from os.path import isdir +from os.path import isfile +from os.path import islink +from os.path import normpath +import posixpath +from stat import S_ISDIR +from stat import S_ISLNK +from stat import S_ISREG +import sys +from typing import Any +from typing import cast +from typing import Literal +from typing import overload +from typing import TYPE_CHECKING +import uuid +import warnings + +from . import error + + +# Moved from local.py. +iswin32 = sys.platform == "win32" or (getattr(os, "_name", False) == "nt") + + +class Checkers: + _depend_on_existence = "exists", "link", "dir", "file" + + def __init__(self, path): + self.path = path + + def dotfile(self): + return self.path.basename.startswith(".") + + def ext(self, arg): + if not arg.startswith("."): + arg = "." + arg + return self.path.ext == arg + + def basename(self, arg): + return self.path.basename == arg + + def basestarts(self, arg): + return self.path.basename.startswith(arg) + + def relto(self, arg): + return self.path.relto(arg) + + def fnmatch(self, arg): + return self.path.fnmatch(arg) + + def endswith(self, arg): + return str(self.path).endswith(arg) + + def _evaluate(self, kw): + from .._code.source import getrawcode + + for name, value in kw.items(): + invert = False + meth = None + try: + meth = getattr(self, name) + except AttributeError: + if name[:3] == "not": + invert = True + try: + meth = getattr(self, name[3:]) + except AttributeError: + pass + if meth is None: + raise TypeError(f"no {name!r} checker available for {self.path!r}") + try: + if getrawcode(meth).co_argcount > 1: + if (not meth(value)) ^ invert: + return False + else: + if bool(value) ^ bool(meth()) ^ invert: + return False + except (error.ENOENT, error.ENOTDIR, error.EBUSY): + # EBUSY feels not entirely correct, + # but its kind of necessary since ENOMEDIUM + # is not accessible in python + for name in self._depend_on_existence: + if name in kw: + if kw.get(name): + return False + name = "not" + name + if name in kw: + if not kw.get(name): + return False + return True + + _statcache: Stat + + def _stat(self) -> Stat: + try: + return self._statcache + except AttributeError: + try: + self._statcache = self.path.stat() + except error.ELOOP: + self._statcache = self.path.lstat() + return self._statcache + + def dir(self): + return S_ISDIR(self._stat().mode) + + def file(self): + return S_ISREG(self._stat().mode) + + def exists(self): + return self._stat() + + def link(self): + st = self.path.lstat() + return S_ISLNK(st.mode) + + +class NeverRaised(Exception): + pass + + +class Visitor: + def __init__(self, fil, rec, ignore, bf, sort): + if isinstance(fil, str): + fil = FNMatcher(fil) + if isinstance(rec, str): + self.rec: Callable[[LocalPath], bool] = FNMatcher(rec) + elif not hasattr(rec, "__call__") and rec: + self.rec = lambda path: True + else: + self.rec = rec + self.fil = fil + self.ignore = ignore + self.breadthfirst = bf + self.optsort = cast(Callable[[Any], Any], sorted) if sort else (lambda x: x) + + def gen(self, path): + try: + entries = path.listdir() + except self.ignore: + return + rec = self.rec + dirs = self.optsort( + [p for p in entries if p.check(dir=1) and (rec is None or rec(p))] + ) + if not self.breadthfirst: + for subdir in dirs: + yield from self.gen(subdir) + for p in self.optsort(entries): + if self.fil is None or self.fil(p): + yield p + if self.breadthfirst: + for subdir in dirs: + yield from self.gen(subdir) + + +class FNMatcher: + def __init__(self, pattern): + self.pattern = pattern + + def __call__(self, path): + pattern = self.pattern + + if ( + pattern.find(path.sep) == -1 + and iswin32 + and pattern.find(posixpath.sep) != -1 + ): + # Running on Windows, the pattern has no Windows path separators, + # and the pattern has one or more Posix path separators. Replace + # the Posix path separators with the Windows path separator. + pattern = pattern.replace(posixpath.sep, path.sep) + + if pattern.find(path.sep) == -1: + name = path.basename + else: + name = str(path) # path.strpath # XXX svn? + if not os.path.isabs(pattern): + pattern = "*" + path.sep + pattern + return fnmatch.fnmatch(name, pattern) + + +def map_as_list(func, iter): + return list(map(func, iter)) + + +class Stat: + if TYPE_CHECKING: + + @property + def size(self) -> int: ... + + @property + def mtime(self) -> float: ... + + def __getattr__(self, name: str) -> Any: + return getattr(self._osstatresult, "st_" + name) + + def __init__(self, path, osstatresult): + self.path = path + self._osstatresult = osstatresult + + @property + def owner(self): + if iswin32: + raise NotImplementedError("XXX win32") + import pwd + + entry = error.checked_call(pwd.getpwuid, self.uid) # type:ignore[attr-defined,unused-ignore] + return entry[0] + + @property + def group(self): + """Return group name of file.""" + if iswin32: + raise NotImplementedError("XXX win32") + import grp + + entry = error.checked_call(grp.getgrgid, self.gid) # type:ignore[attr-defined,unused-ignore] + return entry[0] + + def isdir(self): + return S_ISDIR(self._osstatresult.st_mode) + + def isfile(self): + return S_ISREG(self._osstatresult.st_mode) + + def islink(self): + self.path.lstat() + return S_ISLNK(self._osstatresult.st_mode) + + +def getuserid(user): + import pwd + + if not isinstance(user, int): + user = pwd.getpwnam(user)[2] # type:ignore[attr-defined,unused-ignore] + return user + + +def getgroupid(group): + import grp + + if not isinstance(group, int): + group = grp.getgrnam(group)[2] # type:ignore[attr-defined,unused-ignore] + return group + + +class LocalPath: + """Object oriented interface to os.path and other local filesystem + related information. + """ + + class ImportMismatchError(ImportError): + """raised on pyimport() if there is a mismatch of __file__'s""" + + sep = os.sep + + def __init__(self, path=None, expanduser=False): + """Initialize and return a local Path instance. + + Path can be relative to the current directory. + If path is None it defaults to the current working directory. + If expanduser is True, tilde-expansion is performed. + Note that Path instances always carry an absolute path. + Note also that passing in a local path object will simply return + the exact same path object. Use new() to get a new copy. + """ + if path is None: + self.strpath = error.checked_call(os.getcwd) + else: + try: + path = os.fspath(path) + except TypeError: + raise ValueError( + "can only pass None, Path instances " + "or non-empty strings to LocalPath" + ) + if expanduser: + path = os.path.expanduser(path) + self.strpath = abspath(path) + + if sys.platform != "win32": + + def chown(self, user, group, rec=0): + """Change ownership to the given user and group. + user and group may be specified by a number or + by a name. if rec is True change ownership + recursively. + """ + uid = getuserid(user) + gid = getgroupid(group) + if rec: + for x in self.visit(rec=lambda x: x.check(link=0)): + if x.check(link=0): + error.checked_call(os.chown, str(x), uid, gid) + error.checked_call(os.chown, str(self), uid, gid) + + def readlink(self) -> str: + """Return value of a symbolic link.""" + # https://github.com/python/mypy/issues/12278 + return error.checked_call(os.readlink, self.strpath) # type: ignore[arg-type,return-value,unused-ignore] + + def mklinkto(self, oldname): + """Posix style hard link to another name.""" + error.checked_call(os.link, str(oldname), str(self)) + + def mksymlinkto(self, value, absolute=1): + """Create a symbolic link with the given value (pointing to another name).""" + if absolute: + error.checked_call(os.symlink, str(value), self.strpath) + else: + base = self.common(value) + # with posix local paths '/' is always a common base + relsource = self.__class__(value).relto(base) + reldest = self.relto(base) + n = reldest.count(self.sep) + target = self.sep.join(("..",) * n + (relsource,)) + error.checked_call(os.symlink, target, self.strpath) + + def __div__(self, other): + return self.join(os.fspath(other)) + + __truediv__ = __div__ # py3k + + @property + def basename(self): + """Basename part of path.""" + return self._getbyspec("basename")[0] + + @property + def dirname(self): + """Dirname part of path.""" + return self._getbyspec("dirname")[0] + + @property + def purebasename(self): + """Pure base name of the path.""" + return self._getbyspec("purebasename")[0] + + @property + def ext(self): + """Extension of the path (including the '.').""" + return self._getbyspec("ext")[0] + + def read_binary(self): + """Read and return a bytestring from reading the path.""" + with self.open("rb") as f: + return f.read() + + def read_text(self, encoding): + """Read and return a Unicode string from reading the path.""" + with self.open("r", encoding=encoding) as f: + return f.read() + + def read(self, mode="r"): + """Read and return a bytestring from reading the path.""" + with self.open(mode) as f: + return f.read() + + def readlines(self, cr=1): + """Read and return a list of lines from the path. if cr is False, the + newline will be removed from the end of each line.""" + mode = "r" + + if not cr: + content = self.read(mode) + return content.split("\n") + else: + f = self.open(mode) + try: + return f.readlines() + finally: + f.close() + + def load(self): + """(deprecated) return object unpickled from self.read()""" + f = self.open("rb") + try: + import pickle + + return error.checked_call(pickle.load, f) + finally: + f.close() + + def move(self, target): + """Move this path to target.""" + if target.relto(self): + raise error.EINVAL(target, "cannot move path into a subdirectory of itself") + try: + self.rename(target) + except error.EXDEV: # invalid cross-device link + self.copy(target) + self.remove() + + def fnmatch(self, pattern): + """Return true if the basename/fullname matches the glob-'pattern'. + + valid pattern characters:: + + * matches everything + ? matches any single character + [seq] matches any character in seq + [!seq] matches any char not in seq + + If the pattern contains a path-separator then the full path + is used for pattern matching and a '*' is prepended to the + pattern. + + if the pattern doesn't contain a path-separator the pattern + is only matched against the basename. + """ + return FNMatcher(pattern)(self) + + def relto(self, relpath): + """Return a string which is the relative part of the path + to the given 'relpath'. + """ + if not isinstance(relpath, str | LocalPath): + raise TypeError(f"{relpath!r}: not a string or path object") + strrelpath = str(relpath) + if strrelpath and strrelpath[-1] != self.sep: + strrelpath += self.sep + # assert strrelpath[-1] == self.sep + # assert strrelpath[-2] != self.sep + strself = self.strpath + if sys.platform == "win32" or getattr(os, "_name", None) == "nt": + if os.path.normcase(strself).startswith(os.path.normcase(strrelpath)): + return strself[len(strrelpath) :] + elif strself.startswith(strrelpath): + return strself[len(strrelpath) :] + return "" + + def ensure_dir(self, *args): + """Ensure the path joined with args is a directory.""" + return self.ensure(*args, dir=True) + + def bestrelpath(self, dest): + """Return a string which is a relative path from self + (assumed to be a directory) to dest such that + self.join(bestrelpath) == dest and if not such + path can be determined return dest. + """ + try: + if self == dest: + return os.curdir + base = self.common(dest) + if not base: # can be the case on windows + return str(dest) + self2base = self.relto(base) + reldest = dest.relto(base) + if self2base: + n = self2base.count(self.sep) + 1 + else: + n = 0 + lst = [os.pardir] * n + if reldest: + lst.append(reldest) + target = dest.sep.join(lst) + return target + except AttributeError: + return str(dest) + + def exists(self): + return self.check() + + def isdir(self): + return self.check(dir=1) + + def isfile(self): + return self.check(file=1) + + def parts(self, reverse=False): + """Return a root-first list of all ancestor directories + plus the path itself. + """ + current = self + lst = [self] + while 1: + last = current + current = current.dirpath() + if last == current: + break + lst.append(current) + if not reverse: + lst.reverse() + return lst + + def common(self, other): + """Return the common part shared with the other path + or None if there is no common part. + """ + last = None + for x, y in zip(self.parts(), other.parts()): + if x != y: + return last + last = x + return last + + def __add__(self, other): + """Return new path object with 'other' added to the basename""" + return self.new(basename=self.basename + str(other)) + + def visit(self, fil=None, rec=None, ignore=NeverRaised, bf=False, sort=False): + """Yields all paths below the current one + + fil is a filter (glob pattern or callable), if not matching the + path will not be yielded, defaulting to None (everything is + returned) + + rec is a filter (glob pattern or callable) that controls whether + a node is descended, defaulting to None + + ignore is an Exception class that is ignoredwhen calling dirlist() + on any of the paths (by default, all exceptions are reported) + + bf if True will cause a breadthfirst search instead of the + default depthfirst. Default: False + + sort if True will sort entries within each directory level. + """ + yield from Visitor(fil, rec, ignore, bf, sort).gen(self) + + def _sortlist(self, res, sort): + if sort: + if hasattr(sort, "__call__"): + warnings.warn( + DeprecationWarning( + "listdir(sort=callable) is deprecated and breaks on python3" + ), + stacklevel=3, + ) + res.sort(sort) + else: + res.sort() + + def __fspath__(self): + return self.strpath + + def __hash__(self): + s = self.strpath + if iswin32: + s = s.lower() + return hash(s) + + def __eq__(self, other): + s1 = os.fspath(self) + try: + s2 = os.fspath(other) + except TypeError: + return False + if iswin32: + s1 = s1.lower() + try: + s2 = s2.lower() + except AttributeError: + return False + return s1 == s2 + + def __ne__(self, other): + return not (self == other) + + def __lt__(self, other): + return os.fspath(self) < os.fspath(other) + + def __gt__(self, other): + return os.fspath(self) > os.fspath(other) + + def samefile(self, other): + """Return True if 'other' references the same file as 'self'.""" + other = os.fspath(other) + if not isabs(other): + other = abspath(other) + if self == other: + return True + if not hasattr(os.path, "samefile"): + return False + return error.checked_call(os.path.samefile, self.strpath, other) + + def remove(self, rec=1, ignore_errors=False): + """Remove a file or directory (or a directory tree if rec=1). + if ignore_errors is True, errors while removing directories will + be ignored. + """ + if self.check(dir=1, link=0): + if rec: + # force remove of readonly files on windows + if iswin32: + self.chmod(0o700, rec=1) + import shutil + + error.checked_call( + shutil.rmtree, self.strpath, ignore_errors=ignore_errors + ) + else: + error.checked_call(os.rmdir, self.strpath) + else: + if iswin32: + self.chmod(0o700) + error.checked_call(os.remove, self.strpath) + + def computehash(self, hashtype="md5", chunksize=524288): + """Return hexdigest of hashvalue for this file.""" + try: + try: + import hashlib as mod + except ImportError: + if hashtype == "sha1": + hashtype = "sha" + mod = __import__(hashtype) + hash = getattr(mod, hashtype)() + except (AttributeError, ImportError): + raise ValueError(f"Don't know how to compute {hashtype!r} hash") + f = self.open("rb") + try: + while 1: + buf = f.read(chunksize) + if not buf: + return hash.hexdigest() + hash.update(buf) + finally: + f.close() + + def new(self, **kw): + """Create a modified version of this path. + the following keyword arguments modify various path parts:: + + a:/some/path/to/a/file.ext + xx drive + xxxxxxxxxxxxxxxxx dirname + xxxxxxxx basename + xxxx purebasename + xxx ext + """ + obj = object.__new__(self.__class__) + if not kw: + obj.strpath = self.strpath + return obj + drive, dirname, _basename, purebasename, ext = self._getbyspec( + "drive,dirname,basename,purebasename,ext" + ) + if "basename" in kw: + if "purebasename" in kw or "ext" in kw: + raise ValueError(f"invalid specification {kw!r}") + else: + pb = kw.setdefault("purebasename", purebasename) + try: + ext = kw["ext"] + except KeyError: + pass + else: + if ext and not ext.startswith("."): + ext = "." + ext + kw["basename"] = pb + ext + + if "dirname" in kw and not kw["dirname"]: + kw["dirname"] = drive + else: + kw.setdefault("dirname", dirname) + kw.setdefault("sep", self.sep) + obj.strpath = normpath("{dirname}{sep}{basename}".format(**kw)) + return obj + + def _getbyspec(self, spec: str) -> list[str]: + """See new for what 'spec' can be.""" + res = [] + parts = self.strpath.split(self.sep) + + args = filter(None, spec.split(",")) + for name in args: + if name == "drive": + res.append(parts[0]) + elif name == "dirname": + res.append(self.sep.join(parts[:-1])) + else: + basename = parts[-1] + if name == "basename": + res.append(basename) + else: + i = basename.rfind(".") + if i == -1: + purebasename, ext = basename, "" + else: + purebasename, ext = basename[:i], basename[i:] + if name == "purebasename": + res.append(purebasename) + elif name == "ext": + res.append(ext) + else: + raise ValueError(f"invalid part specification {name!r}") + return res + + def dirpath(self, *args, **kwargs): + """Return the directory path joined with any given path arguments.""" + if not kwargs: + path = object.__new__(self.__class__) + path.strpath = dirname(self.strpath) + if args: + path = path.join(*args) + return path + return self.new(basename="").join(*args, **kwargs) + + def join(self, *args: os.PathLike[str], abs: bool = False) -> LocalPath: + """Return a new path by appending all 'args' as path + components. if abs=1 is used restart from root if any + of the args is an absolute path. + """ + sep = self.sep + strargs = [os.fspath(arg) for arg in args] + strpath = self.strpath + if abs: + newargs: list[str] = [] + for arg in reversed(strargs): + if isabs(arg): + strpath = arg + strargs = newargs + break + newargs.insert(0, arg) + # special case for when we have e.g. strpath == "/" + actual_sep = "" if strpath.endswith(sep) else sep + for arg in strargs: + arg = arg.strip(sep) + if iswin32: + # allow unix style paths even on windows. + arg = arg.strip("/") + arg = arg.replace("/", sep) + strpath = strpath + actual_sep + arg + actual_sep = sep + obj = object.__new__(self.__class__) + obj.strpath = normpath(strpath) + return obj + + def open(self, mode="r", ensure=False, encoding=None): + """Return an opened file with the given mode. + + If ensure is True, create parent directories if needed. + """ + if ensure: + self.dirpath().ensure(dir=1) + if encoding: + return error.checked_call( + io.open, + self.strpath, + mode, + encoding=encoding, + ) + return error.checked_call(open, self.strpath, mode) + + def _fastjoin(self, name): + child = object.__new__(self.__class__) + child.strpath = self.strpath + self.sep + name + return child + + def islink(self): + return islink(self.strpath) + + def check(self, **kw): + """Check a path for existence and properties. + + Without arguments, return True if the path exists, otherwise False. + + valid checkers:: + + file = 1 # is a file + file = 0 # is not a file (may not even exist) + dir = 1 # is a dir + link = 1 # is a link + exists = 1 # exists + + You can specify multiple checker definitions, for example:: + + path.check(file=1, link=1) # a link pointing to a file + """ + if not kw: + return exists(self.strpath) + if len(kw) == 1: + if "dir" in kw: + return not kw["dir"] ^ isdir(self.strpath) + if "file" in kw: + return not kw["file"] ^ isfile(self.strpath) + if not kw: + kw = {"exists": 1} + return Checkers(self)._evaluate(kw) + + _patternchars = set("*?[" + os.sep) + + def listdir(self, fil=None, sort=None): + """List directory contents, possibly filter by the given fil func + and possibly sorted. + """ + if fil is None and sort is None: + names = error.checked_call(os.listdir, self.strpath) + return map_as_list(self._fastjoin, names) + if isinstance(fil, str): + if not self._patternchars.intersection(fil): + child = self._fastjoin(fil) + if exists(child.strpath): + return [child] + return [] + fil = FNMatcher(fil) + names = error.checked_call(os.listdir, self.strpath) + res = [] + for name in names: + child = self._fastjoin(name) + if fil is None or fil(child): + res.append(child) + self._sortlist(res, sort) + return res + + def size(self) -> int: + """Return size of the underlying file object""" + return self.stat().size + + def mtime(self) -> float: + """Return last modification time of the path.""" + return self.stat().mtime + + def copy(self, target, mode=False, stat=False): + """Copy path to target. + + If mode is True, will copy permission from path to target. + If stat is True, copy permission, last modification + time, last access time, and flags from path to target. + """ + if self.check(file=1): + if target.check(dir=1): + target = target.join(self.basename) + assert self != target + copychunked(self, target) + if mode: + copymode(self.strpath, target.strpath) + if stat: + copystat(self, target) + else: + + def rec(p): + return p.check(link=0) + + for x in self.visit(rec=rec): + relpath = x.relto(self) + newx = target.join(relpath) + newx.dirpath().ensure(dir=1) + if x.check(link=1): + newx.mksymlinkto(x.readlink()) + continue + elif x.check(file=1): + copychunked(x, newx) + elif x.check(dir=1): + newx.ensure(dir=1) + if mode: + copymode(x.strpath, newx.strpath) + if stat: + copystat(x, newx) + + def rename(self, target): + """Rename this path to target.""" + target = os.fspath(target) + return error.checked_call(os.rename, self.strpath, target) + + def dump(self, obj, bin=1): + """Pickle object into path location""" + f = self.open("wb") + import pickle + + try: + error.checked_call(pickle.dump, obj, f, bin) + finally: + f.close() + + def mkdir(self, *args): + """Create & return the directory joined with args.""" + p = self.join(*args) + error.checked_call(os.mkdir, os.fspath(p)) + return p + + def write_binary(self, data, ensure=False): + """Write binary data into path. If ensure is True create + missing parent directories. + """ + if ensure: + self.dirpath().ensure(dir=1) + with self.open("wb") as f: + f.write(data) + + def write_text(self, data, encoding, ensure=False): + """Write text data into path using the specified encoding. + If ensure is True create missing parent directories. + """ + if ensure: + self.dirpath().ensure(dir=1) + with self.open("w", encoding=encoding) as f: + f.write(data) + + def write(self, data, mode="w", ensure=False): + """Write data into path. If ensure is True create + missing parent directories. + """ + if ensure: + self.dirpath().ensure(dir=1) + if "b" in mode: + if not isinstance(data, bytes): + raise ValueError("can only process bytes") + else: + if not isinstance(data, str): + if not isinstance(data, bytes): + data = str(data) + else: + data = data.decode(sys.getdefaultencoding()) + f = self.open(mode) + try: + f.write(data) + finally: + f.close() + + def _ensuredirs(self): + parent = self.dirpath() + if parent == self: + return self + if parent.check(dir=0): + parent._ensuredirs() + if self.check(dir=0): + try: + self.mkdir() + except error.EEXIST: + # race condition: file/dir created by another thread/process. + # complain if it is not a dir + if self.check(dir=0): + raise + return self + + def ensure(self, *args, **kwargs): + """Ensure that an args-joined path exists (by default as + a file). if you specify a keyword argument 'dir=True' + then the path is forced to be a directory path. + """ + p = self.join(*args) + if kwargs.get("dir", 0): + return p._ensuredirs() + else: + p.dirpath()._ensuredirs() + if not p.check(file=1): + p.open("wb").close() + return p + + @overload + def stat(self, raising: Literal[True] = ...) -> Stat: ... + + @overload + def stat(self, raising: Literal[False]) -> Stat | None: ... + + def stat(self, raising: bool = True) -> Stat | None: + """Return an os.stat() tuple.""" + if raising: + return Stat(self, error.checked_call(os.stat, self.strpath)) + try: + return Stat(self, os.stat(self.strpath)) + except KeyboardInterrupt: + raise + except Exception: + return None + + def lstat(self) -> Stat: + """Return an os.lstat() tuple.""" + return Stat(self, error.checked_call(os.lstat, self.strpath)) + + def setmtime(self, mtime=None): + """Set modification time for the given path. if 'mtime' is None + (the default) then the file's mtime is set to current time. + + Note that the resolution for 'mtime' is platform dependent. + """ + if mtime is None: + return error.checked_call(os.utime, self.strpath, mtime) + try: + return error.checked_call(os.utime, self.strpath, (-1, mtime)) + except error.EINVAL: + return error.checked_call(os.utime, self.strpath, (self.atime(), mtime)) + + def chdir(self): + """Change directory to self and return old current directory""" + try: + old = self.__class__() + except error.ENOENT: + old = None + error.checked_call(os.chdir, self.strpath) + return old + + @contextmanager + def as_cwd(self): + """ + Return a context manager, which changes to the path's dir during the + managed "with" context. + On __enter__ it returns the old dir, which might be ``None``. + """ + old = self.chdir() + try: + yield old + finally: + if old is not None: + old.chdir() + + def realpath(self): + """Return a new path which contains no symbolic links.""" + return self.__class__(os.path.realpath(self.strpath)) + + def atime(self): + """Return last access time of the path.""" + return self.stat().atime + + def __repr__(self): + return f"local({self.strpath!r})" + + def __str__(self): + """Return string representation of the Path.""" + return self.strpath + + def chmod(self, mode, rec=0): + """Change permissions to the given mode. If mode is an + integer it directly encodes the os-specific modes. + if rec is True perform recursively. + """ + if not isinstance(mode, int): + raise TypeError(f"mode {mode!r} must be an integer") + if rec: + for x in self.visit(rec=rec): + error.checked_call(os.chmod, str(x), mode) + error.checked_call(os.chmod, self.strpath, mode) + + def pypkgpath(self): + """Return the Python package path by looking for the last + directory upwards which still contains an __init__.py. + Return None if a pkgpath cannot be determined. + """ + pkgpath = None + for parent in self.parts(reverse=True): + if parent.isdir(): + if not parent.join("__init__.py").exists(): + break + if not isimportable(parent.basename): + break + pkgpath = parent + return pkgpath + + def _ensuresyspath(self, ensuremode, path): + if ensuremode: + s = str(path) + if ensuremode == "append": + if s not in sys.path: + sys.path.append(s) + else: + if s != sys.path[0]: + sys.path.insert(0, s) + + def pyimport(self, modname=None, ensuresyspath=True): + """Return path as an imported python module. + + If modname is None, look for the containing package + and construct an according module name. + The module will be put/looked up in sys.modules. + if ensuresyspath is True then the root dir for importing + the file (taking __init__.py files into account) will + be prepended to sys.path if it isn't there already. + If ensuresyspath=="append" the root dir will be appended + if it isn't already contained in sys.path. + if ensuresyspath is False no modification of syspath happens. + + Special value of ensuresyspath=="importlib" is intended + purely for using in pytest, it is capable only of importing + separate .py files outside packages, e.g. for test suite + without any __init__.py file. It effectively allows having + same-named test modules in different places and offers + mild opt-in via this option. Note that it works only in + recent versions of python. + """ + if not self.check(): + raise error.ENOENT(self) + + if ensuresyspath == "importlib": + if modname is None: + modname = self.purebasename + spec = importlib.util.spec_from_file_location(modname, str(self)) + if spec is None or spec.loader is None: + raise ImportError(f"Can't find module {modname} at location {self!s}") + mod = importlib.util.module_from_spec(spec) + spec.loader.exec_module(mod) + return mod + + pkgpath = None + if modname is None: + pkgpath = self.pypkgpath() + if pkgpath is not None: + pkgroot = pkgpath.dirpath() + names = self.new(ext="").relto(pkgroot).split(self.sep) + if names[-1] == "__init__": + names.pop() + modname = ".".join(names) + else: + pkgroot = self.dirpath() + modname = self.purebasename + + self._ensuresyspath(ensuresyspath, pkgroot) + __import__(modname) + mod = sys.modules[modname] + if self.basename == "__init__.py": + return mod # we don't check anything as we might + # be in a namespace package ... too icky to check + modfile = mod.__file__ + assert modfile is not None + if modfile[-4:] in (".pyc", ".pyo"): + modfile = modfile[:-1] + elif modfile.endswith("$py.class"): + modfile = modfile[:-9] + ".py" + if modfile.endswith(os.sep + "__init__.py"): + if self.basename != "__init__.py": + modfile = modfile[:-12] + try: + issame = self.samefile(modfile) + except error.ENOENT: + issame = False + if not issame: + ignore = os.getenv("PY_IGNORE_IMPORTMISMATCH") + if ignore != "1": + raise self.ImportMismatchError(modname, modfile, self) + return mod + else: + try: + return sys.modules[modname] + except KeyError: + # we have a custom modname, do a pseudo-import + import types + + mod = types.ModuleType(modname) + mod.__file__ = str(self) + sys.modules[modname] = mod + try: + with open(str(self), "rb") as f: + exec(f.read(), mod.__dict__) + except BaseException: + del sys.modules[modname] + raise + return mod + + def sysexec(self, *argv: os.PathLike[str], **popen_opts: Any) -> str: + """Return stdout text from executing a system child process, + where the 'self' path points to executable. + The process is directly invoked and not through a system shell. + """ + from subprocess import PIPE + from subprocess import Popen + + popen_opts.pop("stdout", None) + popen_opts.pop("stderr", None) + proc = Popen( + [str(self)] + [str(arg) for arg in argv], + **popen_opts, + stdout=PIPE, + stderr=PIPE, + ) + stdout: str | bytes + stdout, stderr = proc.communicate() + ret = proc.wait() + if isinstance(stdout, bytes): + stdout = stdout.decode(sys.getdefaultencoding()) + if ret != 0: + if isinstance(stderr, bytes): + stderr = stderr.decode(sys.getdefaultencoding()) + raise RuntimeError( + ret, + ret, + str(self), + stdout, + stderr, + ) + return stdout + + @classmethod + def sysfind(cls, name, checker=None, paths=None): + """Return a path object found by looking at the systems + underlying PATH specification. If the checker is not None + it will be invoked to filter matching paths. If a binary + cannot be found, None is returned + Note: This is probably not working on plain win32 systems + but may work on cygwin. + """ + if isabs(name): + p = local(name) + if p.check(file=1): + return p + else: + if paths is None: + if iswin32: + paths = os.environ["Path"].split(";") + if "" not in paths and "." not in paths: + paths.append(".") + try: + systemroot = os.environ["SYSTEMROOT"] + except KeyError: + pass + else: + paths = [ + path.replace("%SystemRoot%", systemroot) for path in paths + ] + else: + paths = os.environ["PATH"].split(":") + tryadd = [] + if iswin32: + tryadd += os.environ["PATHEXT"].split(os.pathsep) + tryadd.append("") + + for x in paths: + for addext in tryadd: + p = local(x).join(name, abs=True) + addext + try: + if p.check(file=1): + if checker: + if not checker(p): + continue + return p + except error.EACCES: + pass + return None + + @classmethod + def _gethomedir(cls): + try: + x = os.environ["HOME"] + except KeyError: + try: + x = os.environ["HOMEDRIVE"] + os.environ["HOMEPATH"] + except KeyError: + return None + return cls(x) + + # """ + # special class constructors for local filesystem paths + # """ + @classmethod + def get_temproot(cls): + """Return the system's temporary directory + (where tempfiles are usually created in) + """ + import tempfile + + return local(tempfile.gettempdir()) + + @classmethod + def mkdtemp(cls, rootdir=None): + """Return a Path object pointing to a fresh new temporary directory + (which we created ourselves). + """ + import tempfile + + if rootdir is None: + rootdir = cls.get_temproot() + path = error.checked_call(tempfile.mkdtemp, dir=str(rootdir)) + return cls(path) + + @classmethod + def make_numbered_dir( + cls, prefix="session-", rootdir=None, keep=3, lock_timeout=172800 + ): # two days + """Return unique directory with a number greater than the current + maximum one. The number is assumed to start directly after prefix. + if keep is true directories with a number less than (maxnum-keep) + will be removed. If .lock files are used (lock_timeout non-zero), + algorithm is multi-process safe. + """ + if rootdir is None: + rootdir = cls.get_temproot() + + nprefix = prefix.lower() + + def parse_num(path): + """Parse the number out of a path (if it matches the prefix)""" + nbasename = path.basename.lower() + if nbasename.startswith(nprefix): + try: + return int(nbasename[len(nprefix) :]) + except ValueError: + pass + + def create_lockfile(path): + """Exclusively create lockfile. Throws when failed""" + mypid = os.getpid() + lockfile = path.join(".lock") + if hasattr(lockfile, "mksymlinkto"): + lockfile.mksymlinkto(str(mypid)) + else: + fd = error.checked_call( + os.open, str(lockfile), os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o644 + ) + with os.fdopen(fd, "w") as f: + f.write(str(mypid)) + return lockfile + + def atexit_remove_lockfile(lockfile): + """Ensure lockfile is removed at process exit""" + mypid = os.getpid() + + def try_remove_lockfile(): + # in a fork() situation, only the last process should + # remove the .lock, otherwise the other processes run the + # risk of seeing their temporary dir disappear. For now + # we remove the .lock in the parent only (i.e. we assume + # that the children finish before the parent). + if os.getpid() != mypid: + return + try: + lockfile.remove() + except error.Error: + pass + + atexit.register(try_remove_lockfile) + + # compute the maximum number currently in use with the prefix + lastmax = None + while True: + maxnum = -1 + for path in rootdir.listdir(): + num = parse_num(path) + if num is not None: + maxnum = max(maxnum, num) + + # make the new directory + try: + udir = rootdir.mkdir(prefix + str(maxnum + 1)) + if lock_timeout: + lockfile = create_lockfile(udir) + atexit_remove_lockfile(lockfile) + except (error.EEXIST, error.ENOENT, error.EBUSY): + # race condition (1): another thread/process created the dir + # in the meantime - try again + # race condition (2): another thread/process spuriously acquired + # lock treating empty directory as candidate + # for removal - try again + # race condition (3): another thread/process tried to create the lock at + # the same time (happened in Python 3.3 on Windows) + # https://ci.appveyor.com/project/pytestbot/py/build/1.0.21/job/ffi85j4c0lqwsfwa + if lastmax == maxnum: + raise + lastmax = maxnum + continue + break + + def get_mtime(path): + """Read file modification time""" + try: + return path.lstat().mtime + except error.Error: + pass + + garbage_prefix = prefix + "garbage-" + + def is_garbage(path): + """Check if path denotes directory scheduled for removal""" + bn = path.basename + return bn.startswith(garbage_prefix) + + # prune old directories + udir_time = get_mtime(udir) + if keep and udir_time: + for path in rootdir.listdir(): + num = parse_num(path) + if num is not None and num <= (maxnum - keep): + try: + # try acquiring lock to remove directory as exclusive user + if lock_timeout: + create_lockfile(path) + except (error.EEXIST, error.ENOENT, error.EBUSY): + path_time = get_mtime(path) + if not path_time: + # assume directory doesn't exist now + continue + if abs(udir_time - path_time) < lock_timeout: + # assume directory with lockfile exists + # and lock timeout hasn't expired yet + continue + + # path dir locked for exclusive use + # and scheduled for removal to avoid another thread/process + # treating it as a new directory or removal candidate + garbage_path = rootdir.join(garbage_prefix + str(uuid.uuid4())) + try: + path.rename(garbage_path) + garbage_path.remove(rec=1) + except KeyboardInterrupt: + raise + except Exception: # this might be error.Error, WindowsError ... + pass + if is_garbage(path): + try: + path.remove(rec=1) + except KeyboardInterrupt: + raise + except Exception: # this might be error.Error, WindowsError ... + pass + + # make link... + try: + username = os.environ["USER"] # linux, et al + except KeyError: + try: + username = os.environ["USERNAME"] # windows + except KeyError: + username = "current" + + src = str(udir) + dest = src[: src.rfind("-")] + "-" + username + try: + os.unlink(dest) + except OSError: + pass + try: + os.symlink(src, dest) + except (OSError, AttributeError, NotImplementedError): + pass + + return udir + + +def copymode(src, dest): + """Copy permission from src to dst.""" + import shutil + + shutil.copymode(src, dest) + + +def copystat(src, dest): + """Copy permission, last modification time, + last access time, and flags from src to dst.""" + import shutil + + shutil.copystat(str(src), str(dest)) + + +def copychunked(src, dest): + chunksize = 524288 # half a meg of bytes + fsrc = src.open("rb") + try: + fdest = dest.open("wb") + try: + while 1: + buf = fsrc.read(chunksize) + if not buf: + break + fdest.write(buf) + finally: + fdest.close() + finally: + fsrc.close() + + +def isimportable(name): + if name and (name[0].isalpha() or name[0] == "_"): + name = name.replace("_", "") + return not name or name.isalnum() + + +local = LocalPath diff --git a/venv/Lib/site-packages/_pytest/_version.py b/venv/Lib/site-packages/_pytest/_version.py new file mode 100644 index 0000000..25a26b4 --- /dev/null +++ b/venv/Lib/site-packages/_pytest/_version.py @@ -0,0 +1,34 @@ +# file generated by setuptools-scm +# don't change, don't track in version control + +__all__ = [ + "__version__", + "__version_tuple__", + "version", + "version_tuple", + "__commit_id__", + "commit_id", +] + +TYPE_CHECKING = False +if TYPE_CHECKING: + from typing import Tuple + from typing import Union + + VERSION_TUPLE = Tuple[Union[int, str], ...] + COMMIT_ID = Union[str, None] +else: + VERSION_TUPLE = object + COMMIT_ID = object + +version: str +__version__: str +__version_tuple__: VERSION_TUPLE +version_tuple: VERSION_TUPLE +commit_id: COMMIT_ID +__commit_id__: COMMIT_ID + +__version__ = version = '9.0.1' +__version_tuple__ = version_tuple = (9, 0, 1) + +__commit_id__ = commit_id = None diff --git a/venv/Lib/site-packages/_pytest/assertion/__init__.py b/venv/Lib/site-packages/_pytest/assertion/__init__.py new file mode 100644 index 0000000..22f3ca8 --- /dev/null +++ b/venv/Lib/site-packages/_pytest/assertion/__init__.py @@ -0,0 +1,208 @@ +# mypy: allow-untyped-defs +"""Support for presenting detailed information in failing assertions.""" + +from __future__ import annotations + +from collections.abc import Generator +import sys +from typing import Any +from typing import Protocol +from typing import TYPE_CHECKING + +from _pytest.assertion import rewrite +from _pytest.assertion import truncate +from _pytest.assertion import util +from _pytest.assertion.rewrite import assertstate_key +from _pytest.config import Config +from _pytest.config import hookimpl +from _pytest.config.argparsing import Parser +from _pytest.nodes import Item + + +if TYPE_CHECKING: + from _pytest.main import Session + + +def pytest_addoption(parser: Parser) -> None: + group = parser.getgroup("debugconfig") + group.addoption( + "--assert", + action="store", + dest="assertmode", + choices=("rewrite", "plain"), + default="rewrite", + metavar="MODE", + help=( + "Control assertion debugging tools.\n" + "'plain' performs no assertion debugging.\n" + "'rewrite' (the default) rewrites assert statements in test modules" + " on import to provide assert expression information." + ), + ) + parser.addini( + "enable_assertion_pass_hook", + type="bool", + default=False, + help="Enables the pytest_assertion_pass hook. " + "Make sure to delete any previously generated pyc cache files.", + ) + + parser.addini( + "truncation_limit_lines", + default=None, + help="Set threshold of LINES after which truncation will take effect", + ) + parser.addini( + "truncation_limit_chars", + default=None, + help=("Set threshold of CHARS after which truncation will take effect"), + ) + + Config._add_verbosity_ini( + parser, + Config.VERBOSITY_ASSERTIONS, + help=( + "Specify a verbosity level for assertions, overriding the main level. " + "Higher levels will provide more detailed explanation when an assertion fails." + ), + ) + + +def register_assert_rewrite(*names: str) -> None: + """Register one or more module names to be rewritten on import. + + This function will make sure that this module or all modules inside + the package will get their assert statements rewritten. + Thus you should make sure to call this before the module is + actually imported, usually in your __init__.py if you are a plugin + using a package. + + :param names: The module names to register. + """ + for name in names: + if not isinstance(name, str): + msg = "expected module names as *args, got {0} instead" # type: ignore[unreachable] + raise TypeError(msg.format(repr(names))) + rewrite_hook: RewriteHook + for hook in sys.meta_path: + if isinstance(hook, rewrite.AssertionRewritingHook): + rewrite_hook = hook + break + else: + rewrite_hook = DummyRewriteHook() + rewrite_hook.mark_rewrite(*names) + + +class RewriteHook(Protocol): + def mark_rewrite(self, *names: str) -> None: ... + + +class DummyRewriteHook: + """A no-op import hook for when rewriting is disabled.""" + + def mark_rewrite(self, *names: str) -> None: + pass + + +class AssertionState: + """State for the assertion plugin.""" + + def __init__(self, config: Config, mode) -> None: + self.mode = mode + self.trace = config.trace.root.get("assertion") + self.hook: rewrite.AssertionRewritingHook | None = None + + +def install_importhook(config: Config) -> rewrite.AssertionRewritingHook: + """Try to install the rewrite hook, raise SystemError if it fails.""" + config.stash[assertstate_key] = AssertionState(config, "rewrite") + config.stash[assertstate_key].hook = hook = rewrite.AssertionRewritingHook(config) + sys.meta_path.insert(0, hook) + config.stash[assertstate_key].trace("installed rewrite import hook") + + def undo() -> None: + hook = config.stash[assertstate_key].hook + if hook is not None and hook in sys.meta_path: + sys.meta_path.remove(hook) + + config.add_cleanup(undo) + return hook + + +def pytest_collection(session: Session) -> None: + # This hook is only called when test modules are collected + # so for example not in the managing process of pytest-xdist + # (which does not collect test modules). + assertstate = session.config.stash.get(assertstate_key, None) + if assertstate: + if assertstate.hook is not None: + assertstate.hook.set_session(session) + + +@hookimpl(wrapper=True, tryfirst=True) +def pytest_runtest_protocol(item: Item) -> Generator[None, object, object]: + """Setup the pytest_assertrepr_compare and pytest_assertion_pass hooks. + + The rewrite module will use util._reprcompare if it exists to use custom + reporting via the pytest_assertrepr_compare hook. This sets up this custom + comparison for the test. + """ + ihook = item.ihook + + def callbinrepr(op, left: object, right: object) -> str | None: + """Call the pytest_assertrepr_compare hook and prepare the result. + + This uses the first result from the hook and then ensures the + following: + * Overly verbose explanations are truncated unless configured otherwise + (eg. if running in verbose mode). + * Embedded newlines are escaped to help util.format_explanation() + later. + * If the rewrite mode is used embedded %-characters are replaced + to protect later % formatting. + + The result can be formatted by util.format_explanation() for + pretty printing. + """ + hook_result = ihook.pytest_assertrepr_compare( + config=item.config, op=op, left=left, right=right + ) + for new_expl in hook_result: + if new_expl: + new_expl = truncate.truncate_if_required(new_expl, item) + new_expl = [line.replace("\n", "\\n") for line in new_expl] + res = "\n~".join(new_expl) + if item.config.getvalue("assertmode") == "rewrite": + res = res.replace("%", "%%") + return res + return None + + saved_assert_hooks = util._reprcompare, util._assertion_pass + util._reprcompare = callbinrepr + util._config = item.config + + if ihook.pytest_assertion_pass.get_hookimpls(): + + def call_assertion_pass_hook(lineno: int, orig: str, expl: str) -> None: + ihook.pytest_assertion_pass(item=item, lineno=lineno, orig=orig, expl=expl) + + util._assertion_pass = call_assertion_pass_hook + + try: + return (yield) + finally: + util._reprcompare, util._assertion_pass = saved_assert_hooks + util._config = None + + +def pytest_sessionfinish(session: Session) -> None: + assertstate = session.config.stash.get(assertstate_key, None) + if assertstate: + if assertstate.hook is not None: + assertstate.hook.set_session(None) + + +def pytest_assertrepr_compare( + config: Config, op: str, left: Any, right: Any +) -> list[str] | None: + return util.assertrepr_compare(config=config, op=op, left=left, right=right) diff --git a/venv/Lib/site-packages/_pytest/assertion/__pycache__/__init__.cpython-312.pyc b/venv/Lib/site-packages/_pytest/assertion/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e61889f92ed351aa1b06ebea071dab1b55bd0b0f GIT binary patch literal 10118 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 OpLjYRd#FbU`Tqbb7?+L! literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/_pytest/assertion/__pycache__/rewrite.cpython-312.pyc b/venv/Lib/site-packages/_pytest/assertion/__pycache__/rewrite.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a3f6a805eb8c411a6c34c8e303ed2a838df41020 GIT binary patch literal 61295 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 JPq5|U{{W>5SCaq$ literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/_pytest/assertion/__pycache__/truncate.cpython-312.pyc b/venv/Lib/site-packages/_pytest/assertion/__pycache__/truncate.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..94a811c121b2e81391b4bc28c0ba1a68b4476edf GIT binary patch literal 4192 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/_pytest/assertion/rewrite.py b/venv/Lib/site-packages/_pytest/assertion/rewrite.py new file mode 100644 index 0000000..566549d --- /dev/null +++ b/venv/Lib/site-packages/_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/venv/Lib/site-packages/_pytest/assertion/truncate.py b/venv/Lib/site-packages/_pytest/assertion/truncate.py new file mode 100644 index 0000000..5820e6e --- /dev/null +++ b/venv/Lib/site-packages/_pytest/assertion/truncate.py @@ -0,0 +1,137 @@ +"""Utilities for truncating assertion output. + +Current default behaviour is to truncate assertion explanations at +terminal lines, unless running with an assertions verbosity level of at least 2 or running on CI. +""" + +from __future__ import annotations + +from _pytest.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/venv/Lib/site-packages/_pytest/assertion/util.py b/venv/Lib/site-packages/_pytest/assertion/util.py new file mode 100644 index 0000000..f35d83a --- /dev/null +++ b/venv/Lib/site-packages/_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/venv/Lib/site-packages/_pytest/cacheprovider.py b/venv/Lib/site-packages/_pytest/cacheprovider.py new file mode 100644 index 0000000..4383f10 --- /dev/null +++ b/venv/Lib/site-packages/_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/venv/Lib/site-packages/_pytest/capture.py b/venv/Lib/site-packages/_pytest/capture.py new file mode 100644 index 0000000..6d98676 --- /dev/null +++ b/venv/Lib/site-packages/_pytest/capture.py @@ -0,0 +1,1144 @@ +# mypy: allow-untyped-defs +"""Per-test stdout/stderr capturing mechanism.""" + +from __future__ import annotations + +import abc +import collections +from collections.abc import Generator +from collections.abc import Iterable +from collections.abc import Iterator +import contextlib +import io +from io import UnsupportedOperation +import os +import sys +from tempfile import TemporaryFile +from types import TracebackType +from typing import Any +from typing import AnyStr +from typing import BinaryIO +from typing import cast +from typing import Final +from typing import final +from typing import Generic +from typing import Literal +from typing import NamedTuple +from typing import TextIO +from typing import TYPE_CHECKING + + +if TYPE_CHECKING: + from typing_extensions import Self + +from _pytest.config import Config +from _pytest.config import hookimpl +from _pytest.config.argparsing import Parser +from _pytest.deprecated import check_ispytest +from _pytest.fixtures import fixture +from _pytest.fixtures import SubRequest +from _pytest.nodes import Collector +from _pytest.nodes import File +from _pytest.nodes import Item +from _pytest.reports import CollectReport + + +_CaptureMethod = Literal["fd", "sys", "no", "tee-sys"] + + +def pytest_addoption(parser: Parser) -> None: + group = parser.getgroup("general") + group.addoption( + "--capture", + action="store", + default="fd", + metavar="method", + choices=["fd", "sys", "no", "tee-sys"], + help="Per-test capturing method: one of fd|sys|no|tee-sys", + ) + group._addoption( # private to use reserved lower-case short option + "-s", + action="store_const", + const="no", + dest="capture", + help="Shortcut for --capture=no", + ) + + +def _colorama_workaround() -> None: + """Ensure colorama is imported so that it attaches to the correct stdio + handles on Windows. + + colorama uses the terminal on import time. So if something does the + first import of colorama while I/O capture is active, colorama will + fail in various ways. + """ + if sys.platform.startswith("win32"): + try: + import colorama # noqa: F401 + except ImportError: + pass + + +def _readline_workaround() -> None: + """Ensure readline is imported early so it attaches to the correct stdio handles. + + This isn't a problem with the default GNU readline implementation, but in + some configurations, Python uses libedit instead (on macOS, and for prebuilt + binaries such as used by uv). + + In theory this is only needed if readline.backend == "libedit", but the + workaround consists of importing readline here, so we already worked around + the issue by the time we could check if we need to. + """ + try: + import readline # noqa: F401 + except ImportError: + pass + + +def _windowsconsoleio_workaround(stream: TextIO) -> None: + """Workaround for Windows Unicode console handling. + + Python 3.6 implemented Unicode console handling for Windows. This works + by reading/writing to the raw console handle using + ``{Read,Write}ConsoleW``. + + The problem is that we are going to ``dup2`` over the stdio file + descriptors when doing ``FDCapture`` and this will ``CloseHandle`` the + handles used by Python to write to the console. Though there is still some + weirdness and the console handle seems to only be closed randomly and not + on the first call to ``CloseHandle``, or maybe it gets reopened with the + same handle value when we suspend capturing. + + The workaround in this case will reopen stdio with a different fd which + also means a different handle by replicating the logic in + "Py_lifecycle.c:initstdio/create_stdio". + + :param stream: + In practice ``sys.stdout`` or ``sys.stderr``, but given + here as parameter for unittesting purposes. + + See https://github.com/pytest-dev/py/issues/103. + """ + if not sys.platform.startswith("win32") or hasattr(sys, "pypy_version_info"): + return + + # Bail out if ``stream`` doesn't seem like a proper ``io`` stream (#2666). + if not hasattr(stream, "buffer"): # type: ignore[unreachable,unused-ignore] + return + + raw_stdout = stream.buffer.raw if hasattr(stream.buffer, "raw") else stream.buffer + + if not isinstance(raw_stdout, io._WindowsConsoleIO): # type: ignore[attr-defined,unused-ignore] + return + + def _reopen_stdio(f, mode): + if not hasattr(stream.buffer, "raw") and mode[0] == "w": + buffering = 0 + else: + buffering = -1 + + return io.TextIOWrapper( + open(os.dup(f.fileno()), mode, buffering), + f.encoding, + f.errors, + f.newlines, + f.line_buffering, + ) + + sys.stdin = _reopen_stdio(sys.stdin, "rb") + sys.stdout = _reopen_stdio(sys.stdout, "wb") + sys.stderr = _reopen_stdio(sys.stderr, "wb") + + +@hookimpl(wrapper=True) +def pytest_load_initial_conftests(early_config: Config) -> Generator[None]: + ns = early_config.known_args_namespace + if ns.capture == "fd": + _windowsconsoleio_workaround(sys.stdout) + _colorama_workaround() + _readline_workaround() + pluginmanager = early_config.pluginmanager + capman = CaptureManager(ns.capture) + pluginmanager.register(capman, "capturemanager") + + # Make sure that capturemanager is properly reset at final shutdown. + early_config.add_cleanup(capman.stop_global_capturing) + + # Finally trigger conftest loading but while capturing (issue #93). + capman.start_global_capturing() + try: + try: + yield + finally: + capman.suspend_global_capture() + except BaseException: + out, err = capman.read_global_capture() + sys.stdout.write(out) + sys.stderr.write(err) + raise + + +# IO Helpers. + + +class EncodedFile(io.TextIOWrapper): + __slots__ = () + + @property + def name(self) -> str: + # Ensure that file.name is a string. Workaround for a Python bug + # fixed in >=3.7.4: https://bugs.python.org/issue36015 + return repr(self.buffer) + + @property + def mode(self) -> str: + # TextIOWrapper doesn't expose a mode, but at least some of our + # tests check it. + assert hasattr(self.buffer, "mode") + return cast(str, self.buffer.mode.replace("b", "")) + + +class CaptureIO(io.TextIOWrapper): + def __init__(self) -> None: + super().__init__(io.BytesIO(), encoding="UTF-8", newline="", write_through=True) + + def getvalue(self) -> str: + assert isinstance(self.buffer, io.BytesIO) + return self.buffer.getvalue().decode("UTF-8") + + +class TeeCaptureIO(CaptureIO): + def __init__(self, other: TextIO) -> None: + self._other = other + super().__init__() + + def write(self, s: str) -> int: + super().write(s) + return self._other.write(s) + + +class DontReadFromInput(TextIO): + @property + def encoding(self) -> str: + assert sys.__stdin__ is not None + return sys.__stdin__.encoding + + def read(self, size: int = -1) -> str: + raise OSError( + "pytest: reading from stdin while output is captured! Consider using `-s`." + ) + + readline = read + + def __next__(self) -> str: + return self.readline() + + def readlines(self, hint: int | None = -1) -> list[str]: + raise OSError( + "pytest: reading from stdin while output is captured! Consider using `-s`." + ) + + def __iter__(self) -> Iterator[str]: + return self + + def fileno(self) -> int: + raise UnsupportedOperation("redirected stdin is pseudofile, has no fileno()") + + def flush(self) -> None: + raise UnsupportedOperation("redirected stdin is pseudofile, has no flush()") + + def isatty(self) -> bool: + return False + + def close(self) -> None: + pass + + def readable(self) -> bool: + return False + + def seek(self, offset: int, whence: int = 0) -> int: + raise UnsupportedOperation("redirected stdin is pseudofile, has no seek(int)") + + def seekable(self) -> bool: + return False + + def tell(self) -> int: + raise UnsupportedOperation("redirected stdin is pseudofile, has no tell()") + + def truncate(self, size: int | None = None) -> int: + raise UnsupportedOperation("cannot truncate stdin") + + def write(self, data: str) -> int: + raise UnsupportedOperation("cannot write to stdin") + + def writelines(self, lines: Iterable[str]) -> None: + raise UnsupportedOperation("Cannot write to stdin") + + def writable(self) -> bool: + return False + + def __enter__(self) -> Self: + return self + + def __exit__( + self, + type: type[BaseException] | None, + value: BaseException | None, + traceback: TracebackType | None, + ) -> None: + pass + + @property + def buffer(self) -> BinaryIO: + # The str/bytes doesn't actually matter in this type, so OK to fake. + return self # type: ignore[return-value] + + +# Capture classes. + + +class CaptureBase(abc.ABC, Generic[AnyStr]): + EMPTY_BUFFER: AnyStr + + @abc.abstractmethod + def __init__(self, fd: int) -> None: + raise NotImplementedError() + + @abc.abstractmethod + def start(self) -> None: + raise NotImplementedError() + + @abc.abstractmethod + def done(self) -> None: + raise NotImplementedError() + + @abc.abstractmethod + def suspend(self) -> None: + raise NotImplementedError() + + @abc.abstractmethod + def resume(self) -> None: + raise NotImplementedError() + + @abc.abstractmethod + def writeorg(self, data: AnyStr) -> None: + raise NotImplementedError() + + @abc.abstractmethod + def snap(self) -> AnyStr: + raise NotImplementedError() + + +patchsysdict = {0: "stdin", 1: "stdout", 2: "stderr"} + + +class NoCapture(CaptureBase[str]): + EMPTY_BUFFER = "" + + def __init__(self, fd: int) -> None: + pass + + def start(self) -> None: + pass + + def done(self) -> None: + pass + + def suspend(self) -> None: + pass + + def resume(self) -> None: + pass + + def snap(self) -> str: + return "" + + def writeorg(self, data: str) -> None: + pass + + +class SysCaptureBase(CaptureBase[AnyStr]): + def __init__( + self, fd: int, tmpfile: TextIO | None = None, *, tee: bool = False + ) -> None: + name = patchsysdict[fd] + self._old: TextIO = getattr(sys, name) + self.name = name + if tmpfile is None: + if name == "stdin": + tmpfile = DontReadFromInput() + else: + tmpfile = CaptureIO() if not tee else TeeCaptureIO(self._old) + self.tmpfile = tmpfile + self._state = "initialized" + + def repr(self, class_name: str) -> str: + return "<{} {} _old={} _state={!r} tmpfile={!r}>".format( + class_name, + self.name, + (hasattr(self, "_old") and repr(self._old)) or "", + self._state, + self.tmpfile, + ) + + def __repr__(self) -> str: + return "<{} {} _old={} _state={!r} tmpfile={!r}>".format( + self.__class__.__name__, + self.name, + (hasattr(self, "_old") and repr(self._old)) or "", + self._state, + self.tmpfile, + ) + + def _assert_state(self, op: str, states: tuple[str, ...]) -> None: + assert self._state in states, ( + "cannot {} in state {!r}: expected one of {}".format( + op, self._state, ", ".join(states) + ) + ) + + def start(self) -> None: + self._assert_state("start", ("initialized",)) + setattr(sys, self.name, self.tmpfile) + self._state = "started" + + def done(self) -> None: + self._assert_state("done", ("initialized", "started", "suspended", "done")) + if self._state == "done": + return + setattr(sys, self.name, self._old) + del self._old + self.tmpfile.close() + self._state = "done" + + def suspend(self) -> None: + self._assert_state("suspend", ("started", "suspended")) + setattr(sys, self.name, self._old) + self._state = "suspended" + + def resume(self) -> None: + self._assert_state("resume", ("started", "suspended")) + if self._state == "started": + return + setattr(sys, self.name, self.tmpfile) + self._state = "started" + + +class SysCaptureBinary(SysCaptureBase[bytes]): + EMPTY_BUFFER = b"" + + def snap(self) -> bytes: + self._assert_state("snap", ("started", "suspended")) + self.tmpfile.seek(0) + res = self.tmpfile.buffer.read() + self.tmpfile.seek(0) + self.tmpfile.truncate() + return res + + def writeorg(self, data: bytes) -> None: + self._assert_state("writeorg", ("started", "suspended")) + self._old.flush() + self._old.buffer.write(data) + self._old.buffer.flush() + + +class SysCapture(SysCaptureBase[str]): + EMPTY_BUFFER = "" + + def snap(self) -> str: + self._assert_state("snap", ("started", "suspended")) + assert isinstance(self.tmpfile, CaptureIO) + res = self.tmpfile.getvalue() + self.tmpfile.seek(0) + self.tmpfile.truncate() + return res + + def writeorg(self, data: str) -> None: + self._assert_state("writeorg", ("started", "suspended")) + self._old.write(data) + self._old.flush() + + +class FDCaptureBase(CaptureBase[AnyStr]): + def __init__(self, targetfd: int) -> None: + self.targetfd = targetfd + + try: + os.fstat(targetfd) + except OSError: + # FD capturing is conceptually simple -- create a temporary file, + # redirect the FD to it, redirect back when done. But when the + # target FD is invalid it throws a wrench into this lovely scheme. + # + # Tests themselves shouldn't care if the FD is valid, FD capturing + # should work regardless of external circumstances. So falling back + # to just sys capturing is not a good option. + # + # Further complications are the need to support suspend() and the + # possibility of FD reuse (e.g. the tmpfile getting the very same + # target FD). The following approach is robust, I believe. + self.targetfd_invalid: int | None = os.open(os.devnull, os.O_RDWR) + os.dup2(self.targetfd_invalid, targetfd) + else: + self.targetfd_invalid = None + self.targetfd_save = os.dup(targetfd) + + if targetfd == 0: + self.tmpfile = open(os.devnull, encoding="utf-8") + self.syscapture: CaptureBase[str] = SysCapture(targetfd) + else: + self.tmpfile = EncodedFile( + TemporaryFile(buffering=0), + encoding="utf-8", + errors="replace", + newline="", + write_through=True, + ) + if targetfd in patchsysdict: + self.syscapture = SysCapture(targetfd, self.tmpfile) + else: + self.syscapture = NoCapture(targetfd) + + self._state = "initialized" + + def __repr__(self) -> str: + return ( + f"<{self.__class__.__name__} {self.targetfd} oldfd={self.targetfd_save} " + f"_state={self._state!r} tmpfile={self.tmpfile!r}>" + ) + + def _assert_state(self, op: str, states: tuple[str, ...]) -> None: + assert self._state in states, ( + "cannot {} in state {!r}: expected one of {}".format( + op, self._state, ", ".join(states) + ) + ) + + def start(self) -> None: + """Start capturing on targetfd using memorized tmpfile.""" + self._assert_state("start", ("initialized",)) + os.dup2(self.tmpfile.fileno(), self.targetfd) + self.syscapture.start() + self._state = "started" + + def done(self) -> None: + """Stop capturing, restore streams, return original capture file, + seeked to position zero.""" + self._assert_state("done", ("initialized", "started", "suspended", "done")) + if self._state == "done": + return + os.dup2(self.targetfd_save, self.targetfd) + os.close(self.targetfd_save) + if self.targetfd_invalid is not None: + if self.targetfd_invalid != self.targetfd: + os.close(self.targetfd) + os.close(self.targetfd_invalid) + self.syscapture.done() + self.tmpfile.close() + self._state = "done" + + def suspend(self) -> None: + self._assert_state("suspend", ("started", "suspended")) + if self._state == "suspended": + return + self.syscapture.suspend() + os.dup2(self.targetfd_save, self.targetfd) + self._state = "suspended" + + def resume(self) -> None: + self._assert_state("resume", ("started", "suspended")) + if self._state == "started": + return + self.syscapture.resume() + os.dup2(self.tmpfile.fileno(), self.targetfd) + self._state = "started" + + +class FDCaptureBinary(FDCaptureBase[bytes]): + """Capture IO to/from a given OS-level file descriptor. + + snap() produces `bytes`. + """ + + EMPTY_BUFFER = b"" + + def snap(self) -> bytes: + self._assert_state("snap", ("started", "suspended")) + self.tmpfile.seek(0) + res = self.tmpfile.buffer.read() + self.tmpfile.seek(0) + self.tmpfile.truncate() + return res # type: ignore[return-value] + + def writeorg(self, data: bytes) -> None: + """Write to original file descriptor.""" + self._assert_state("writeorg", ("started", "suspended")) + os.write(self.targetfd_save, data) + + +class FDCapture(FDCaptureBase[str]): + """Capture IO to/from a given OS-level file descriptor. + + snap() produces text. + """ + + EMPTY_BUFFER = "" + + def snap(self) -> str: + self._assert_state("snap", ("started", "suspended")) + self.tmpfile.seek(0) + res = self.tmpfile.read() + self.tmpfile.seek(0) + self.tmpfile.truncate() + return res + + def writeorg(self, data: str) -> None: + """Write to original file descriptor.""" + self._assert_state("writeorg", ("started", "suspended")) + # XXX use encoding of original stream + os.write(self.targetfd_save, data.encode("utf-8")) + + +# MultiCapture + + +# Generic NamedTuple only supported since Python 3.11. +if sys.version_info >= (3, 11) or TYPE_CHECKING: + + @final + class CaptureResult(NamedTuple, Generic[AnyStr]): + """The result of :method:`caplog.readouterr() `.""" + + out: AnyStr + err: AnyStr + +else: + + class CaptureResult( + collections.namedtuple("CaptureResult", ["out", "err"]), # noqa: PYI024 + Generic[AnyStr], + ): + """The result of :method:`caplog.readouterr() `.""" + + __slots__ = () + + +class MultiCapture(Generic[AnyStr]): + _state = None + _in_suspended = False + + def __init__( + self, + in_: CaptureBase[AnyStr] | None, + out: CaptureBase[AnyStr] | None, + err: CaptureBase[AnyStr] | None, + ) -> None: + self.in_: CaptureBase[AnyStr] | None = in_ + self.out: CaptureBase[AnyStr] | None = out + self.err: CaptureBase[AnyStr] | None = err + + def __repr__(self) -> str: + return ( + f"" + ) + + def start_capturing(self) -> None: + self._state = "started" + if self.in_: + self.in_.start() + if self.out: + self.out.start() + if self.err: + self.err.start() + + def pop_outerr_to_orig(self) -> tuple[AnyStr, AnyStr]: + """Pop current snapshot out/err capture and flush to orig streams.""" + out, err = self.readouterr() + if out: + assert self.out is not None + self.out.writeorg(out) + if err: + assert self.err is not None + self.err.writeorg(err) + return out, err + + def suspend_capturing(self, in_: bool = False) -> None: + self._state = "suspended" + if self.out: + self.out.suspend() + if self.err: + self.err.suspend() + if in_ and self.in_: + self.in_.suspend() + self._in_suspended = True + + def resume_capturing(self) -> None: + self._state = "started" + if self.out: + self.out.resume() + if self.err: + self.err.resume() + if self._in_suspended: + assert self.in_ is not None + self.in_.resume() + self._in_suspended = False + + def stop_capturing(self) -> None: + """Stop capturing and reset capturing streams.""" + if self._state == "stopped": + raise ValueError("was already stopped") + self._state = "stopped" + if self.out: + self.out.done() + if self.err: + self.err.done() + if self.in_: + self.in_.done() + + def is_started(self) -> bool: + """Whether actively capturing -- not suspended or stopped.""" + return self._state == "started" + + def readouterr(self) -> CaptureResult[AnyStr]: + out = self.out.snap() if self.out else "" + err = self.err.snap() if self.err else "" + # TODO: This type error is real, need to fix. + return CaptureResult(out, err) # type: ignore[arg-type] + + +def _get_multicapture(method: _CaptureMethod) -> MultiCapture[str]: + if method == "fd": + return MultiCapture(in_=FDCapture(0), out=FDCapture(1), err=FDCapture(2)) + elif method == "sys": + return MultiCapture(in_=SysCapture(0), out=SysCapture(1), err=SysCapture(2)) + elif method == "no": + return MultiCapture(in_=None, out=None, err=None) + elif method == "tee-sys": + return MultiCapture( + in_=None, out=SysCapture(1, tee=True), err=SysCapture(2, tee=True) + ) + raise ValueError(f"unknown capturing method: {method!r}") + + +# CaptureManager and CaptureFixture + + +class CaptureManager: + """The capture plugin. + + Manages that the appropriate capture method is enabled/disabled during + collection and each test phase (setup, call, teardown). After each of + those points, the captured output is obtained and attached to the + collection/runtest report. + + There are two levels of capture: + + * global: enabled by default and can be suppressed by the ``-s`` + option. This is always enabled/disabled during collection and each test + phase. + + * fixture: when a test function or one of its fixture depend on the + ``capsys`` or ``capfd`` fixtures. In this case special handling is + needed to ensure the fixtures take precedence over the global capture. + """ + + def __init__(self, method: _CaptureMethod) -> None: + self._method: Final = method + self._global_capturing: MultiCapture[str] | None = None + self._capture_fixture: CaptureFixture[Any] | None = None + + def __repr__(self) -> str: + return ( + f"" + ) + + def is_capturing(self) -> str | bool: + if self.is_globally_capturing(): + return "global" + if self._capture_fixture: + return f"fixture {self._capture_fixture.request.fixturename}" + return False + + # Global capturing control + + def is_globally_capturing(self) -> bool: + return self._method != "no" + + def start_global_capturing(self) -> None: + assert self._global_capturing is None + self._global_capturing = _get_multicapture(self._method) + self._global_capturing.start_capturing() + + def stop_global_capturing(self) -> None: + if self._global_capturing is not None: + self._global_capturing.pop_outerr_to_orig() + self._global_capturing.stop_capturing() + self._global_capturing = None + + def resume_global_capture(self) -> None: + # During teardown of the python process, and on rare occasions, capture + # attributes can be `None` while trying to resume global capture. + if self._global_capturing is not None: + self._global_capturing.resume_capturing() + + def suspend_global_capture(self, in_: bool = False) -> None: + if self._global_capturing is not None: + self._global_capturing.suspend_capturing(in_=in_) + + def suspend(self, in_: bool = False) -> None: + # Need to undo local capsys-et-al if it exists before disabling global capture. + self.suspend_fixture() + self.suspend_global_capture(in_) + + def resume(self) -> None: + self.resume_global_capture() + self.resume_fixture() + + def read_global_capture(self) -> CaptureResult[str]: + assert self._global_capturing is not None + return self._global_capturing.readouterr() + + # Fixture Control + + def set_fixture(self, capture_fixture: CaptureFixture[Any]) -> None: + if self._capture_fixture: + current_fixture = self._capture_fixture.request.fixturename + requested_fixture = capture_fixture.request.fixturename + capture_fixture.request.raiseerror( + f"cannot use {requested_fixture} and {current_fixture} at the same time" + ) + self._capture_fixture = capture_fixture + + def unset_fixture(self) -> None: + self._capture_fixture = None + + def activate_fixture(self) -> None: + """If the current item is using ``capsys`` or ``capfd``, activate + them so they take precedence over the global capture.""" + if self._capture_fixture: + self._capture_fixture._start() + + def deactivate_fixture(self) -> None: + """Deactivate the ``capsys`` or ``capfd`` fixture of this item, if any.""" + if self._capture_fixture: + self._capture_fixture.close() + + def suspend_fixture(self) -> None: + if self._capture_fixture: + self._capture_fixture._suspend() + + def resume_fixture(self) -> None: + if self._capture_fixture: + self._capture_fixture._resume() + + # Helper context managers + + @contextlib.contextmanager + def global_and_fixture_disabled(self) -> Generator[None]: + """Context manager to temporarily disable global and current fixture capturing.""" + do_fixture = self._capture_fixture and self._capture_fixture._is_started() + if do_fixture: + self.suspend_fixture() + do_global = self._global_capturing and self._global_capturing.is_started() + if do_global: + self.suspend_global_capture() + try: + yield + finally: + if do_global: + self.resume_global_capture() + if do_fixture: + self.resume_fixture() + + @contextlib.contextmanager + def item_capture(self, when: str, item: Item) -> Generator[None]: + self.resume_global_capture() + self.activate_fixture() + try: + yield + finally: + self.deactivate_fixture() + self.suspend_global_capture(in_=False) + + out, err = self.read_global_capture() + item.add_report_section(when, "stdout", out) + item.add_report_section(when, "stderr", err) + + # Hooks + + @hookimpl(wrapper=True) + def pytest_make_collect_report( + self, collector: Collector + ) -> Generator[None, CollectReport, CollectReport]: + if isinstance(collector, File): + self.resume_global_capture() + try: + rep = yield + finally: + self.suspend_global_capture() + out, err = self.read_global_capture() + if out: + rep.sections.append(("Captured stdout", out)) + if err: + rep.sections.append(("Captured stderr", err)) + else: + rep = yield + return rep + + @hookimpl(wrapper=True) + def pytest_runtest_setup(self, item: Item) -> Generator[None]: + with self.item_capture("setup", item): + return (yield) + + @hookimpl(wrapper=True) + def pytest_runtest_call(self, item: Item) -> Generator[None]: + with self.item_capture("call", item): + return (yield) + + @hookimpl(wrapper=True) + def pytest_runtest_teardown(self, item: Item) -> Generator[None]: + with self.item_capture("teardown", item): + return (yield) + + @hookimpl(tryfirst=True) + def pytest_keyboard_interrupt(self) -> None: + self.stop_global_capturing() + + @hookimpl(tryfirst=True) + def pytest_internalerror(self) -> None: + self.stop_global_capturing() + + +class CaptureFixture(Generic[AnyStr]): + """Object returned by the :fixture:`capsys`, :fixture:`capsysbinary`, + :fixture:`capfd` and :fixture:`capfdbinary` fixtures.""" + + def __init__( + self, + captureclass: type[CaptureBase[AnyStr]], + request: SubRequest, + *, + config: dict[str, Any] | None = None, + _ispytest: bool = False, + ) -> None: + check_ispytest(_ispytest) + self.captureclass: type[CaptureBase[AnyStr]] = captureclass + self.request = request + self._config = config if config else {} + self._capture: MultiCapture[AnyStr] | None = None + self._captured_out: AnyStr = self.captureclass.EMPTY_BUFFER + self._captured_err: AnyStr = self.captureclass.EMPTY_BUFFER + + def _start(self) -> None: + if self._capture is None: + self._capture = MultiCapture( + in_=None, + out=self.captureclass(1, **self._config), + err=self.captureclass(2, **self._config), + ) + self._capture.start_capturing() + + def close(self) -> None: + if self._capture is not None: + out, err = self._capture.pop_outerr_to_orig() + self._captured_out += out + self._captured_err += err + self._capture.stop_capturing() + self._capture = None + + def readouterr(self) -> CaptureResult[AnyStr]: + """Read and return the captured output so far, resetting the internal + buffer. + + :returns: + The captured content as a namedtuple with ``out`` and ``err`` + string attributes. + """ + captured_out, captured_err = self._captured_out, self._captured_err + if self._capture is not None: + out, err = self._capture.readouterr() + captured_out += out + captured_err += err + self._captured_out = self.captureclass.EMPTY_BUFFER + self._captured_err = self.captureclass.EMPTY_BUFFER + return CaptureResult(captured_out, captured_err) + + def _suspend(self) -> None: + """Suspend this fixture's own capturing temporarily.""" + if self._capture is not None: + self._capture.suspend_capturing() + + def _resume(self) -> None: + """Resume this fixture's own capturing temporarily.""" + if self._capture is not None: + self._capture.resume_capturing() + + def _is_started(self) -> bool: + """Whether actively capturing -- not disabled or closed.""" + if self._capture is not None: + return self._capture.is_started() + return False + + @contextlib.contextmanager + def disabled(self) -> Generator[None]: + """Temporarily disable capturing while inside the ``with`` block.""" + capmanager: CaptureManager = self.request.config.pluginmanager.getplugin( + "capturemanager" + ) + with capmanager.global_and_fixture_disabled(): + yield + + +# The fixtures. + + +@fixture +def capsys(request: SubRequest) -> Generator[CaptureFixture[str]]: + r"""Enable text capturing of writes to ``sys.stdout`` and ``sys.stderr``. + + The captured output is made available via ``capsys.readouterr()`` method + calls, which return a ``(out, err)`` namedtuple. + ``out`` and ``err`` will be ``text`` objects. + + Returns an instance of :class:`CaptureFixture[str] `. + + Example: + + .. code-block:: python + + def test_output(capsys): + print("hello") + captured = capsys.readouterr() + assert captured.out == "hello\n" + """ + capman: CaptureManager = request.config.pluginmanager.getplugin("capturemanager") + capture_fixture = CaptureFixture(SysCapture, request, _ispytest=True) + capman.set_fixture(capture_fixture) + capture_fixture._start() + yield capture_fixture + capture_fixture.close() + capman.unset_fixture() + + +@fixture +def capteesys(request: SubRequest) -> Generator[CaptureFixture[str]]: + r"""Enable simultaneous text capturing and pass-through of writes + to ``sys.stdout`` and ``sys.stderr`` as defined by ``--capture=``. + + + The captured output is made available via ``capteesys.readouterr()`` method + calls, which return a ``(out, err)`` namedtuple. + ``out`` and ``err`` will be ``text`` objects. + + The output is also passed-through, allowing it to be "live-printed", + reported, or both as defined by ``--capture=``. + + Returns an instance of :class:`CaptureFixture[str] `. + + Example: + + .. code-block:: python + + def test_output(capteesys): + print("hello") + captured = capteesys.readouterr() + assert captured.out == "hello\n" + """ + capman: CaptureManager = request.config.pluginmanager.getplugin("capturemanager") + capture_fixture = CaptureFixture( + SysCapture, request, config=dict(tee=True), _ispytest=True + ) + capman.set_fixture(capture_fixture) + capture_fixture._start() + yield capture_fixture + capture_fixture.close() + capman.unset_fixture() + + +@fixture +def capsysbinary(request: SubRequest) -> Generator[CaptureFixture[bytes]]: + r"""Enable bytes capturing of writes to ``sys.stdout`` and ``sys.stderr``. + + The captured output is made available via ``capsysbinary.readouterr()`` + method calls, which return a ``(out, err)`` namedtuple. + ``out`` and ``err`` will be ``bytes`` objects. + + Returns an instance of :class:`CaptureFixture[bytes] `. + + Example: + + .. code-block:: python + + def test_output(capsysbinary): + print("hello") + captured = capsysbinary.readouterr() + assert captured.out == b"hello\n" + """ + capman: CaptureManager = request.config.pluginmanager.getplugin("capturemanager") + capture_fixture = CaptureFixture(SysCaptureBinary, request, _ispytest=True) + capman.set_fixture(capture_fixture) + capture_fixture._start() + yield capture_fixture + capture_fixture.close() + capman.unset_fixture() + + +@fixture +def capfd(request: SubRequest) -> Generator[CaptureFixture[str]]: + r"""Enable text capturing of writes to file descriptors ``1`` and ``2``. + + The captured output is made available via ``capfd.readouterr()`` method + calls, which return a ``(out, err)`` namedtuple. + ``out`` and ``err`` will be ``text`` objects. + + Returns an instance of :class:`CaptureFixture[str] `. + + Example: + + .. code-block:: python + + def test_system_echo(capfd): + os.system('echo "hello"') + captured = capfd.readouterr() + assert captured.out == "hello\n" + """ + capman: CaptureManager = request.config.pluginmanager.getplugin("capturemanager") + capture_fixture = CaptureFixture(FDCapture, request, _ispytest=True) + capman.set_fixture(capture_fixture) + capture_fixture._start() + yield capture_fixture + capture_fixture.close() + capman.unset_fixture() + + +@fixture +def capfdbinary(request: SubRequest) -> Generator[CaptureFixture[bytes]]: + r"""Enable bytes capturing of writes to file descriptors ``1`` and ``2``. + + The captured output is made available via ``capfd.readouterr()`` method + calls, which return a ``(out, err)`` namedtuple. + ``out`` and ``err`` will be ``byte`` objects. + + Returns an instance of :class:`CaptureFixture[bytes] `. + + Example: + + .. code-block:: python + + def test_system_echo(capfdbinary): + os.system('echo "hello"') + captured = capfdbinary.readouterr() + assert captured.out == b"hello\n" + + """ + capman: CaptureManager = request.config.pluginmanager.getplugin("capturemanager") + capture_fixture = CaptureFixture(FDCaptureBinary, request, _ispytest=True) + capman.set_fixture(capture_fixture) + capture_fixture._start() + yield capture_fixture + capture_fixture.close() + capman.unset_fixture() diff --git a/venv/Lib/site-packages/_pytest/compat.py b/venv/Lib/site-packages/_pytest/compat.py new file mode 100644 index 0000000..72c3d09 --- /dev/null +++ b/venv/Lib/site-packages/_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/venv/Lib/site-packages/_pytest/config/__init__.py b/venv/Lib/site-packages/_pytest/config/__init__.py new file mode 100644 index 0000000..9b2afe3 --- /dev/null +++ b/venv/Lib/site-packages/_pytest/config/__init__.py @@ -0,0 +1,2166 @@ +# 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 Sequence +import contextlib +import copy +import dataclasses +import enum +from functools import lru_cache +import glob +import importlib.metadata +import inspect +import os +import pathlib +import re +import shlex +import sys +from textwrap import dedent +import types +from types import FunctionType +from typing import Any +from typing import cast +from typing import Final +from typing import final +from typing import IO +from typing import TextIO +from typing import TYPE_CHECKING +import warnings + +import pluggy +from pluggy import HookimplMarker +from pluggy import HookimplOpts +from pluggy import HookspecMarker +from pluggy import HookspecOpts +from pluggy import PluginManager + +from .compat import PathAwareHookProxy +from .exceptions import PrintHelp as PrintHelp +from .exceptions import UsageError as UsageError +from .findpaths import determine_setup +from _pytest import __version__ +import _pytest._code +from _pytest._code import ExceptionInfo +from _pytest._code import filter_traceback +from _pytest._code.code import TracebackStyle +from _pytest._io import TerminalWriter +from _pytest.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", +} + + +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) + + +@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 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/venv/Lib/site-packages/_pytest/config/__pycache__/__init__.cpython-312.pyc b/venv/Lib/site-packages/_pytest/config/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f6b0ad63037b271193996b39edbef1576c1e077f GIT binary patch literal 89848 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≀4PAe(>6EF5GO26@D&CP=7qfbVk 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/_pytest/config/__pycache__/argparsing.cpython-312.pyc b/venv/Lib/site-packages/_pytest/config/__pycache__/argparsing.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d6487a3051b2ee616cf23a61027da51737dd5581 GIT binary patch literal 26411 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/_pytest/config/__pycache__/compat.cpython-312.pyc b/venv/Lib/site-packages/_pytest/config/__pycache__/compat.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..13fb90ecb8a3dcaf5f81a356cc5eb0b0b7b42491 GIT binary patch literal 3581 zcmai1U2GKB6~6PgUhj|p)_{Y-jJ-By!8Q~F1&biN5I_(N3Q4PMjYi|0>)o+uXSR1{ zO?I_NPJ)CNRmD6+5Vcj2+J_(_l|GfnK33|BT_<8YF%?qCL;B=&(;xBDo-?z%Hk7v4 z_MLO@IrrXk&(C+R{}7Fa2(*v>bx{8>OvslwsJDPmaMe$MxI^9{8qqj|WVs~AAw-BC=+j;N$_rK$}$X^!b3b!SM(O}Zu5+8P;ZJw@BrQoXDu!N z(p<=PROa|AgC4EvEFrmDas`Llj-sbci&3R^h#RX-*TxE-;da+1bA|eJR-L2%VQy!Q z$!N}kgECarD06#WoZYy?jBe_7rns|as~_498h68E4+m&n|A31J-$$oWm! zNr;n030=0QC?(|H*X82TkUk?%TTVtsbIYo!$uqW3GAgI_d1}f|h8AR#QcZR&d78@f zM|ssq@JxaO-O%?{BTwI8%wjNw9&!Wz4mMszmY@IN)Y#O40|zd>JC#)(I;^I^a@oRE zt}yJV_S|qfuWQuMO*#c<%}>3lPfyu8$mP`3oSLTgl;YcXf66jv^fY|3px;QY;D(h{ zhNkBH*yx7Cw!@!&47vrf))Zbm{b6az`D0V}T1!`@CAM^WY3%Mh_YU3Lvrf2ur#a}O zr@5*iw1(D+&>UJ9NN`6L7Lycq^;Yu-SKR|McL*g(P9sTPoCDo-3j10N_Xan6kvO!WY*sDE8Fil4{GZCGW*Wo{a3_;B znxm!$8i5+}bUy8?{z4q14ZS$-@N8J(N<^vKmlVi-4xuFMC4j=Hp%x&?MVlHkZU|NJ z=W5}^(8+F4Q6Rw_MJaawr=3UY%zYqgBP=hFO83B`_<694?SNsbkw`GW#PO6H0^)@v zBeR>65xCeM)Ye`mOokz7Dge^3UZgqJ2Ok?HWM9uFMFB3Tx&4)Ddb|-_6N3(-wzXsF z(BoKqB^F<8?OzV|`|5611C!2E_aGc|L%1Q{kp92YfETqrZi*$&5n&In&2y~1ESAV@ ztwPo3@mu1sMwG?NWJA8hy#{kru-8rLrrAm?#48PLK%(U(HVi=mkWs(pkQ*~+l;!yw{ zT(uv}`H^skzy|2Qms(lUNZo|jAbejbBW#l4OZ@vo8=ySfF!{#DPU8}@jSFmW%R*WD zwBa_5|5T`-&w*>+?}q)O4m>pchGy8{o83-ygHw0IHQsD3@eBMp@&V_xHu!vl4~3&d z$`ZQ01codNNbwK?ny7+#IRivDTdJlRl{OXAQ%EU!^raLOCsR%b64FE?yt;asc|EI zd8v&;jS+K2L<=`qlVvtK7XWu*VZqPFkOyxb&J+*6G9l9x-Z>5=?8tG$0*JLwjBKpI z%3}W#x_>T^r+bHfz5D*|hh2{jt?nIP3ROlA|Mtjljy&4?J8gCJ{8FS6+qE3qx7NAi z&V}0-*1Cosca5%ejaK#@{(bOUfq!lLdVur~{H>*?&+??62ElxcBIlf48+*& zmOpLlzFoXKeY?2a*6RUBLSzR)U50jW13vzH1E^0;wX1r_#kQgZfU2ga094`4l{Zt4 zWf`_Bc_qpfO*KmalyfwNayU|yM8E?)SD2#(b`(!KjNK9J;8jBE4j3OF_Z<*cl!k?i zJ^$MS-@y%TLH8VRu`lsS|Kq)5D|^Qtom}l3Ulc3hmd~~hu7n3Gy#tG_ zp(ew72nWSxL$@7#_FaKh6UY`|;Pcuytv~6N{zMZSN12eK)ZV#@8&MQ|bNN|7x+97eFf3%HqJ%1(8dB*!U6C08B literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/_pytest/config/__pycache__/exceptions.cpython-312.pyc b/venv/Lib/site-packages/_pytest/config/__pycache__/exceptions.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..eeb20a89e03fffd77bf950deca393e2c937addad GIT binary patch literal 811 zcmZuvPm2>V6i+78=^wZ1f`aQQh==W=-Lgj&PcDi`Sr$Av4um*K+t8WJn9NjXZ$dx7 zUSvPRvLD57Fd!)6!CSHGWiOh{w910-@ZK-)_mcPcy?p9+JHXh--}~Yr1mK%JY|N_x zTpby33K(E80x|R3&QYIPAL?X30HpW8IC|o~K1v8p8xVmNhDd50|4EPR%K4Oj!3)FMV&Pz=v{6MKh z9pA44780jL#&w2s%i%l1?D6*@Pb&=uX`!t6m9s$GB`v^LckpF<=fiV#eMA0Vq??AmDpKL} zP}x3ILT1l+lpgaVDl}*K-IV{8NKcbIVmP%$SY#ScO(o6}tY^Z+DaXnxnT##&U}IEG z@|c_IlqxOc1XIcIoJNAx0*_S74%)S@8u^Ar+Z$)|x@p`3{B&Srx301DsMoZ680T58 z%pR2eQ>B*`scE8IduShQ)T))L9=+P=vh}E5&k-gj_k=>eyC e;UIYTWM>6TSRs>G+Qh|{19wl_zkmtVV1EF`5zZ(8 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/_pytest/config/__pycache__/findpaths.cpython-312.pyc b/venv/Lib/site-packages/_pytest/config/__pycache__/findpaths.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..23961e0cff57acc19691db68344a95fd5c2ab967 GIT binary patch literal 13259 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 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/venv/Lib/site-packages/_pytest/config/compat.py b/venv/Lib/site-packages/_pytest/config/compat.py new file mode 100644 index 0000000..21eab4c --- /dev/null +++ b/venv/Lib/site-packages/_pytest/config/compat.py @@ -0,0 +1,85 @@ +from __future__ import annotations + +from collections.abc import Mapping +import functools +from pathlib import Path +from typing import Any +import warnings + +import pluggy + +from ..compat import LEGACY_PATH +from ..compat import legacy_path +from ..deprecated import HOOK_LEGACY_PATH_ARG + + +# hookname: (Path, LEGACY_PATH) +imply_paths_hooks: Mapping[str, tuple[str, str]] = { + "pytest_ignore_collect": ("collection_path", "path"), + "pytest_collect_file": ("file_path", "path"), + "pytest_pycollect_makemodule": ("module_path", "path"), + "pytest_report_header": ("start_path", "startdir"), + "pytest_report_collectionfinish": ("start_path", "startdir"), +} + + +def _check_path(path: Path, fspath: LEGACY_PATH) -> None: + if Path(fspath) != path: + raise ValueError( + f"Path({fspath!r}) != {path!r}\n" + "if both path and fspath are given they need to be equal" + ) + + +class PathAwareHookProxy: + """ + this helper wraps around hook callers + until pluggy supports fixingcalls, this one will do + + it currently doesn't return full hook caller proxies for fixed hooks, + this may have to be changed later depending on bugs + """ + + def __init__(self, hook_relay: pluggy.HookRelay) -> None: + self._hook_relay = hook_relay + + def __dir__(self) -> list[str]: + return dir(self._hook_relay) + + def __getattr__(self, key: str) -> pluggy.HookCaller: + hook: pluggy.HookCaller = getattr(self._hook_relay, key) + if key not in imply_paths_hooks: + self.__dict__[key] = hook + return hook + else: + path_var, fspath_var = imply_paths_hooks[key] + + @functools.wraps(hook) + def fixed_hook(**kw: Any) -> Any: + path_value: Path | None = kw.pop(path_var, None) + fspath_value: LEGACY_PATH | None = kw.pop(fspath_var, None) + if fspath_value is not None: + warnings.warn( + HOOK_LEGACY_PATH_ARG.format( + pylib_path_arg=fspath_var, pathlib_path_arg=path_var + ), + stacklevel=2, + ) + if path_value is not None: + if fspath_value is not None: + _check_path(path_value, fspath_value) + else: + fspath_value = legacy_path(path_value) + else: + assert fspath_value is not None + path_value = Path(fspath_value) + + kw[path_var] = path_value + kw[fspath_var] = fspath_value + return hook(**kw) + + fixed_hook.name = hook.name # type: ignore[attr-defined] + fixed_hook.spec = hook.spec # type: ignore[attr-defined] + fixed_hook.__name__ = key + self.__dict__[key] = fixed_hook + return fixed_hook # type: ignore[return-value] diff --git a/venv/Lib/site-packages/_pytest/config/exceptions.py b/venv/Lib/site-packages/_pytest/config/exceptions.py new file mode 100644 index 0000000..d84a9ea --- /dev/null +++ b/venv/Lib/site-packages/_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/venv/Lib/site-packages/_pytest/config/findpaths.py b/venv/Lib/site-packages/_pytest/config/findpaths.py new file mode 100644 index 0000000..3c628a0 --- /dev/null +++ b/venv/Lib/site-packages/_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/venv/Lib/site-packages/_pytest/debugging.py b/venv/Lib/site-packages/_pytest/debugging.py new file mode 100644 index 0000000..de1b268 --- /dev/null +++ b/venv/Lib/site-packages/_pytest/debugging.py @@ -0,0 +1,407 @@ +# mypy: allow-untyped-defs +# ruff: noqa: T100 +"""Interactive debugging with PDB, the Python Debugger.""" + +from __future__ import annotations + +import argparse +from collections.abc import Callable +from collections.abc import Generator +import functools +import sys +import types +from typing import Any +import unittest + +from _pytest import outcomes +from _pytest._code import ExceptionInfo +from _pytest.capture import CaptureManager +from _pytest.config import Config +from _pytest.config import ConftestImportFailure +from _pytest.config import hookimpl +from _pytest.config import PytestPluginManager +from _pytest.config.argparsing import Parser +from _pytest.config.exceptions import UsageError +from _pytest.nodes import Node +from _pytest.reports import BaseReport +from _pytest.runner import CallInfo + + +def _validate_usepdb_cls(value: str) -> tuple[str, str]: + """Validate syntax of --pdbcls option.""" + try: + modname, classname = value.split(":") + except ValueError as e: + raise argparse.ArgumentTypeError( + f"{value!r} is not in the format 'modname:classname'" + ) from e + return (modname, classname) + + +def pytest_addoption(parser: Parser) -> None: + group = parser.getgroup("general") + group.addoption( + "--pdb", + dest="usepdb", + action="store_true", + help="Start the interactive Python debugger on errors or KeyboardInterrupt", + ) + group.addoption( + "--pdbcls", + dest="usepdb_cls", + metavar="modulename:classname", + type=_validate_usepdb_cls, + help="Specify a custom interactive Python debugger for use with --pdb." + "For example: --pdbcls=IPython.terminal.debugger:TerminalPdb", + ) + group.addoption( + "--trace", + dest="trace", + action="store_true", + help="Immediately break when running each test", + ) + + +def pytest_configure(config: Config) -> None: + import pdb + + if config.getvalue("trace"): + config.pluginmanager.register(PdbTrace(), "pdbtrace") + if config.getvalue("usepdb"): + config.pluginmanager.register(PdbInvoke(), "pdbinvoke") + + pytestPDB._saved.append( + (pdb.set_trace, pytestPDB._pluginmanager, pytestPDB._config) + ) + pdb.set_trace = pytestPDB.set_trace + pytestPDB._pluginmanager = config.pluginmanager + pytestPDB._config = config + + # NOTE: not using pytest_unconfigure, since it might get called although + # pytest_configure was not (if another plugin raises UsageError). + def fin() -> None: + ( + pdb.set_trace, + pytestPDB._pluginmanager, + pytestPDB._config, + ) = pytestPDB._saved.pop() + + config.add_cleanup(fin) + + +class pytestPDB: + """Pseudo PDB that defers to the real pdb.""" + + _pluginmanager: PytestPluginManager | None = None + _config: Config | None = None + _saved: list[ + tuple[Callable[..., None], PytestPluginManager | None, Config | None] + ] = [] + _recursive_debug = 0 + _wrapped_pdb_cls: tuple[type[Any], type[Any]] | None = None + + @classmethod + def _is_capturing(cls, capman: CaptureManager | None) -> str | bool: + if capman: + return capman.is_capturing() + return False + + @classmethod + def _import_pdb_cls(cls, capman: CaptureManager | None): + if not cls._config: + import pdb + + # Happens when using pytest.set_trace outside of a test. + return pdb.Pdb + + usepdb_cls = cls._config.getvalue("usepdb_cls") + + if cls._wrapped_pdb_cls and cls._wrapped_pdb_cls[0] == usepdb_cls: + return cls._wrapped_pdb_cls[1] + + if usepdb_cls: + modname, classname = usepdb_cls + + try: + __import__(modname) + mod = sys.modules[modname] + + # Handle --pdbcls=pdb:pdb.Pdb (useful e.g. with pdbpp). + parts = classname.split(".") + pdb_cls = getattr(mod, parts[0]) + for part in parts[1:]: + pdb_cls = getattr(pdb_cls, part) + except Exception as exc: + value = ":".join((modname, classname)) + raise UsageError( + f"--pdbcls: could not import {value!r}: {exc}" + ) from exc + else: + import pdb + + pdb_cls = pdb.Pdb + + wrapped_cls = cls._get_pdb_wrapper_class(pdb_cls, capman) + cls._wrapped_pdb_cls = (usepdb_cls, wrapped_cls) + return wrapped_cls + + @classmethod + def _get_pdb_wrapper_class(cls, pdb_cls, capman: CaptureManager | None): + import _pytest.config + + class PytestPdbWrapper(pdb_cls): + _pytest_capman = capman + _continued = False + + def do_debug(self, arg): + cls._recursive_debug += 1 + ret = super().do_debug(arg) + cls._recursive_debug -= 1 + return ret + + if hasattr(pdb_cls, "do_debug"): + do_debug.__doc__ = pdb_cls.do_debug.__doc__ + + def do_continue(self, arg): + ret = super().do_continue(arg) + if cls._recursive_debug == 0: + assert cls._config is not None + tw = _pytest.config.create_terminal_writer(cls._config) + tw.line() + + capman = self._pytest_capman + capturing = pytestPDB._is_capturing(capman) + if capturing: + if capturing == "global": + tw.sep(">", "PDB continue (IO-capturing resumed)") + else: + tw.sep( + ">", + f"PDB continue (IO-capturing resumed for {capturing})", + ) + assert capman is not None + capman.resume() + else: + tw.sep(">", "PDB continue") + assert cls._pluginmanager is not None + cls._pluginmanager.hook.pytest_leave_pdb(config=cls._config, pdb=self) + self._continued = True + return ret + + if hasattr(pdb_cls, "do_continue"): + do_continue.__doc__ = pdb_cls.do_continue.__doc__ + + do_c = do_cont = do_continue + + def do_quit(self, arg): + # Raise Exit outcome when quit command is used in pdb. + # + # This is a bit of a hack - it would be better if BdbQuit + # could be handled, but this would require to wrap the + # whole pytest run, and adjust the report etc. + ret = super().do_quit(arg) + + if cls._recursive_debug == 0: + outcomes.exit("Quitting debugger") + + return ret + + if hasattr(pdb_cls, "do_quit"): + do_quit.__doc__ = pdb_cls.do_quit.__doc__ + + do_q = do_quit + do_exit = do_quit + + def setup(self, f, tb): + """Suspend on setup(). + + Needed after do_continue resumed, and entering another + breakpoint again. + """ + ret = super().setup(f, tb) + if not ret and self._continued: + # pdb.setup() returns True if the command wants to exit + # from the interaction: do not suspend capturing then. + if self._pytest_capman: + self._pytest_capman.suspend_global_capture(in_=True) + return ret + + def get_stack(self, f, t): + stack, i = super().get_stack(f, t) + if f is None: + # Find last non-hidden frame. + i = max(0, len(stack) - 1) + while i and stack[i][0].f_locals.get("__tracebackhide__", False): + i -= 1 + return stack, i + + return PytestPdbWrapper + + @classmethod + def _init_pdb(cls, method, *args, **kwargs): + """Initialize PDB debugging, dropping any IO capturing.""" + import _pytest.config + + if cls._pluginmanager is None: + capman: CaptureManager | None = None + else: + capman = cls._pluginmanager.getplugin("capturemanager") + if capman: + capman.suspend(in_=True) + + if cls._config: + tw = _pytest.config.create_terminal_writer(cls._config) + tw.line() + + if cls._recursive_debug == 0: + # Handle header similar to pdb.set_trace in py37+. + header = kwargs.pop("header", None) + if header is not None: + tw.sep(">", header) + else: + capturing = cls._is_capturing(capman) + if capturing == "global": + tw.sep(">", f"PDB {method} (IO-capturing turned off)") + elif capturing: + tw.sep( + ">", + f"PDB {method} (IO-capturing turned off for {capturing})", + ) + else: + tw.sep(">", f"PDB {method}") + + _pdb = cls._import_pdb_cls(capman)(**kwargs) + + if cls._pluginmanager: + cls._pluginmanager.hook.pytest_enter_pdb(config=cls._config, pdb=_pdb) + return _pdb + + @classmethod + def set_trace(cls, *args, **kwargs) -> None: + """Invoke debugging via ``Pdb.set_trace``, dropping any IO capturing.""" + frame = sys._getframe().f_back + _pdb = cls._init_pdb("set_trace", *args, **kwargs) + _pdb.set_trace(frame) + + +class PdbInvoke: + def pytest_exception_interact( + self, node: Node, call: CallInfo[Any], report: BaseReport + ) -> None: + capman = node.config.pluginmanager.getplugin("capturemanager") + if capman: + capman.suspend_global_capture(in_=True) + out, err = capman.read_global_capture() + sys.stdout.write(out) + sys.stdout.write(err) + assert call.excinfo is not None + + if not isinstance(call.excinfo.value, unittest.SkipTest): + _enter_pdb(node, call.excinfo, report) + + def pytest_internalerror(self, excinfo: ExceptionInfo[BaseException]) -> None: + exc_or_tb = _postmortem_exc_or_tb(excinfo) + post_mortem(exc_or_tb) + + +class PdbTrace: + @hookimpl(wrapper=True) + def pytest_pyfunc_call(self, pyfuncitem) -> Generator[None, object, object]: + wrap_pytest_function_for_tracing(pyfuncitem) + return (yield) + + +def wrap_pytest_function_for_tracing(pyfuncitem) -> None: + """Change the Python function object of the given Function item by a + wrapper which actually enters pdb before calling the python function + itself, effectively leaving the user in the pdb prompt in the first + statement of the function.""" + _pdb = pytestPDB._init_pdb("runcall") + testfunction = pyfuncitem.obj + + # we can't just return `partial(pdb.runcall, testfunction)` because (on + # python < 3.7.4) runcall's first param is `func`, which means we'd get + # an exception if one of the kwargs to testfunction was called `func`. + @functools.wraps(testfunction) + def wrapper(*args, **kwargs) -> None: + func = functools.partial(testfunction, *args, **kwargs) + _pdb.runcall(func) + + pyfuncitem.obj = wrapper + + +def maybe_wrap_pytest_function_for_tracing(pyfuncitem) -> None: + """Wrap the given pytestfunct item for tracing support if --trace was given in + the command line.""" + if pyfuncitem.config.getvalue("trace"): + wrap_pytest_function_for_tracing(pyfuncitem) + + +def _enter_pdb( + node: Node, excinfo: ExceptionInfo[BaseException], rep: BaseReport +) -> BaseReport: + # XXX we reuse the TerminalReporter's terminalwriter + # because this seems to avoid some encoding related troubles + # for not completely clear reasons. + tw = node.config.pluginmanager.getplugin("terminalreporter")._tw + tw.line() + + showcapture = node.config.option.showcapture + + for sectionname, content in ( + ("stdout", rep.capstdout), + ("stderr", rep.capstderr), + ("log", rep.caplog), + ): + if showcapture in (sectionname, "all") and content: + tw.sep(">", "captured " + sectionname) + if content[-1:] == "\n": + content = content[:-1] + tw.line(content) + + tw.sep(">", "traceback") + rep.toterminal(tw) + tw.sep(">", "entering PDB") + tb_or_exc = _postmortem_exc_or_tb(excinfo) + rep._pdbshown = True # type: ignore[attr-defined] + post_mortem(tb_or_exc) + return rep + + +def _postmortem_exc_or_tb( + excinfo: ExceptionInfo[BaseException], +) -> types.TracebackType | BaseException: + from doctest import UnexpectedException + + get_exc = sys.version_info >= (3, 13) + if isinstance(excinfo.value, UnexpectedException): + # A doctest.UnexpectedException is not useful for post_mortem. + # Use the underlying exception instead: + underlying_exc = excinfo.value + if get_exc: + return underlying_exc.exc_info[1] + + return underlying_exc.exc_info[2] + elif isinstance(excinfo.value, ConftestImportFailure): + # A config.ConftestImportFailure is not useful for post_mortem. + # Use the underlying exception instead: + cause = excinfo.value.cause + if get_exc: + return cause + + assert cause.__traceback__ is not None + return cause.__traceback__ + else: + assert excinfo._excinfo is not None + if get_exc: + return excinfo._excinfo[1] + + return excinfo._excinfo[2] + + +def post_mortem(tb_or_exc: types.TracebackType | BaseException) -> None: + p = pytestPDB._init_pdb("post_mortem") + p.reset() + p.interaction(None, tb_or_exc) + if p.quitting: + outcomes.exit("Quitting debugger") diff --git a/venv/Lib/site-packages/_pytest/deprecated.py b/venv/Lib/site-packages/_pytest/deprecated.py new file mode 100644 index 0000000..cb5d2e9 --- /dev/null +++ b/venv/Lib/site-packages/_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/venv/Lib/site-packages/_pytest/doctest.py b/venv/Lib/site-packages/_pytest/doctest.py new file mode 100644 index 0000000..cd255f5 --- /dev/null +++ b/venv/Lib/site-packages/_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/venv/Lib/site-packages/_pytest/faulthandler.py b/venv/Lib/site-packages/_pytest/faulthandler.py new file mode 100644 index 0000000..080cf58 --- /dev/null +++ b/venv/Lib/site-packages/_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/venv/Lib/site-packages/_pytest/fixtures.py b/venv/Lib/site-packages/_pytest/fixtures.py new file mode 100644 index 0000000..27846db --- /dev/null +++ b/venv/Lib/site-packages/_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/venv/Lib/site-packages/_pytest/freeze_support.py b/venv/Lib/site-packages/_pytest/freeze_support.py new file mode 100644 index 0000000..959ff07 --- /dev/null +++ b/venv/Lib/site-packages/_pytest/freeze_support.py @@ -0,0 +1,45 @@ +"""Provides a function to report all internal modules for using freezing +tools.""" + +from __future__ import annotations + +from collections.abc import Iterator +import types + + +def freeze_includes() -> list[str]: + """Return a list of module names used by pytest that should be + included by cx_freeze.""" + import _pytest + + result = list(_iter_all_modules(_pytest)) + return result + + +def _iter_all_modules( + package: str | types.ModuleType, + prefix: str = "", +) -> Iterator[str]: + """Iterate over the names of all modules that can be found in the given + package, recursively. + + >>> import _pytest + >>> list(_iter_all_modules(_pytest)) + ['_pytest._argcomplete', '_pytest._code.code', ...] + """ + import os + import pkgutil + + if isinstance(package, str): + path = package + else: + # Type ignored because typeshed doesn't define ModuleType.__path__ + # (only defined on packages). + package_path = package.__path__ + path, prefix = package_path[0], package.__name__ + "." + for _, name, is_package in pkgutil.iter_modules([path]): + if is_package: + for m in _iter_all_modules(os.path.join(path, name), prefix=name + "."): + yield prefix + m + else: + yield prefix + name diff --git a/venv/Lib/site-packages/_pytest/helpconfig.py b/venv/Lib/site-packages/_pytest/helpconfig.py new file mode 100644 index 0000000..6a22c9f --- /dev/null +++ b/venv/Lib/site-packages/_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/venv/Lib/site-packages/_pytest/hookspec.py b/venv/Lib/site-packages/_pytest/hookspec.py new file mode 100644 index 0000000..c5bcc36 --- /dev/null +++ b/venv/Lib/site-packages/_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/venv/Lib/site-packages/_pytest/junitxml.py b/venv/Lib/site-packages/_pytest/junitxml.py new file mode 100644 index 0000000..ae8d2b9 --- /dev/null +++ b/venv/Lib/site-packages/_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/venv/Lib/site-packages/_pytest/legacypath.py b/venv/Lib/site-packages/_pytest/legacypath.py new file mode 100644 index 0000000..59e8ef6 --- /dev/null +++ b/venv/Lib/site-packages/_pytest/legacypath.py @@ -0,0 +1,468 @@ +# mypy: allow-untyped-defs +"""Add backward compatibility support for the legacy py path type.""" + +from __future__ import annotations + +import dataclasses +from pathlib import Path +import shlex +import subprocess +from typing import Final +from typing import final +from typing import TYPE_CHECKING + +from iniconfig import SectionWrapper + +from _pytest.cacheprovider import Cache +from _pytest.compat import LEGACY_PATH +from _pytest.compat import legacy_path +from _pytest.config import Config +from _pytest.config import hookimpl +from _pytest.config import PytestPluginManager +from _pytest.deprecated import check_ispytest +from _pytest.fixtures import fixture +from _pytest.fixtures import FixtureRequest +from _pytest.main import Session +from _pytest.monkeypatch import MonkeyPatch +from _pytest.nodes import Collector +from _pytest.nodes import Item +from _pytest.nodes import Node +from _pytest.pytester import HookRecorder +from _pytest.pytester import Pytester +from _pytest.pytester import RunResult +from _pytest.terminal import TerminalReporter +from _pytest.tmpdir import TempPathFactory + + +if TYPE_CHECKING: + import pexpect + + +@final +class Testdir: + """ + Similar to :class:`Pytester`, but this class works with legacy legacy_path objects instead. + + All methods just forward to an internal :class:`Pytester` instance, converting results + to `legacy_path` objects as necessary. + """ + + __test__ = False + + CLOSE_STDIN: Final = Pytester.CLOSE_STDIN + TimeoutExpired: Final = Pytester.TimeoutExpired + + def __init__(self, pytester: Pytester, *, _ispytest: bool = False) -> None: + check_ispytest(_ispytest) + self._pytester = pytester + + @property + def tmpdir(self) -> LEGACY_PATH: + """Temporary directory where tests are executed.""" + return legacy_path(self._pytester.path) + + @property + def test_tmproot(self) -> LEGACY_PATH: + return legacy_path(self._pytester._test_tmproot) + + @property + def request(self): + return self._pytester._request + + @property + def plugins(self): + return self._pytester.plugins + + @plugins.setter + def plugins(self, plugins): + self._pytester.plugins = plugins + + @property + def monkeypatch(self) -> MonkeyPatch: + return self._pytester._monkeypatch + + def make_hook_recorder(self, pluginmanager) -> HookRecorder: + """See :meth:`Pytester.make_hook_recorder`.""" + return self._pytester.make_hook_recorder(pluginmanager) + + def chdir(self) -> None: + """See :meth:`Pytester.chdir`.""" + return self._pytester.chdir() + + def finalize(self) -> None: + return self._pytester._finalize() + + def makefile(self, ext, *args, **kwargs) -> LEGACY_PATH: + """See :meth:`Pytester.makefile`.""" + if ext and not ext.startswith("."): + # pytester.makefile is going to throw a ValueError in a way that + # testdir.makefile did not, because + # pathlib.Path is stricter suffixes than py.path + # This ext arguments is likely user error, but since testdir has + # allowed this, we will prepend "." as a workaround to avoid breaking + # testdir usage that worked before + ext = "." + ext + return legacy_path(self._pytester.makefile(ext, *args, **kwargs)) + + def makeconftest(self, source) -> LEGACY_PATH: + """See :meth:`Pytester.makeconftest`.""" + return legacy_path(self._pytester.makeconftest(source)) + + def makeini(self, source) -> LEGACY_PATH: + """See :meth:`Pytester.makeini`.""" + return legacy_path(self._pytester.makeini(source)) + + def getinicfg(self, source: str) -> SectionWrapper: + """See :meth:`Pytester.getinicfg`.""" + return self._pytester.getinicfg(source) + + def makepyprojecttoml(self, source) -> LEGACY_PATH: + """See :meth:`Pytester.makepyprojecttoml`.""" + return legacy_path(self._pytester.makepyprojecttoml(source)) + + def makepyfile(self, *args, **kwargs) -> LEGACY_PATH: + """See :meth:`Pytester.makepyfile`.""" + return legacy_path(self._pytester.makepyfile(*args, **kwargs)) + + def maketxtfile(self, *args, **kwargs) -> LEGACY_PATH: + """See :meth:`Pytester.maketxtfile`.""" + return legacy_path(self._pytester.maketxtfile(*args, **kwargs)) + + def syspathinsert(self, path=None) -> None: + """See :meth:`Pytester.syspathinsert`.""" + return self._pytester.syspathinsert(path) + + def mkdir(self, name) -> LEGACY_PATH: + """See :meth:`Pytester.mkdir`.""" + return legacy_path(self._pytester.mkdir(name)) + + def mkpydir(self, name) -> LEGACY_PATH: + """See :meth:`Pytester.mkpydir`.""" + return legacy_path(self._pytester.mkpydir(name)) + + def copy_example(self, name=None) -> LEGACY_PATH: + """See :meth:`Pytester.copy_example`.""" + return legacy_path(self._pytester.copy_example(name)) + + def getnode(self, config: Config, arg) -> Item | Collector | None: + """See :meth:`Pytester.getnode`.""" + return self._pytester.getnode(config, arg) + + def getpathnode(self, path): + """See :meth:`Pytester.getpathnode`.""" + return self._pytester.getpathnode(path) + + def genitems(self, colitems: list[Item | Collector]) -> list[Item]: + """See :meth:`Pytester.genitems`.""" + return self._pytester.genitems(colitems) + + def runitem(self, source): + """See :meth:`Pytester.runitem`.""" + return self._pytester.runitem(source) + + def inline_runsource(self, source, *cmdlineargs): + """See :meth:`Pytester.inline_runsource`.""" + return self._pytester.inline_runsource(source, *cmdlineargs) + + def inline_genitems(self, *args): + """See :meth:`Pytester.inline_genitems`.""" + return self._pytester.inline_genitems(*args) + + def inline_run(self, *args, plugins=(), no_reraise_ctrlc: bool = False): + """See :meth:`Pytester.inline_run`.""" + return self._pytester.inline_run( + *args, plugins=plugins, no_reraise_ctrlc=no_reraise_ctrlc + ) + + def runpytest_inprocess(self, *args, **kwargs) -> RunResult: + """See :meth:`Pytester.runpytest_inprocess`.""" + return self._pytester.runpytest_inprocess(*args, **kwargs) + + def runpytest(self, *args, **kwargs) -> RunResult: + """See :meth:`Pytester.runpytest`.""" + return self._pytester.runpytest(*args, **kwargs) + + def parseconfig(self, *args) -> Config: + """See :meth:`Pytester.parseconfig`.""" + return self._pytester.parseconfig(*args) + + def parseconfigure(self, *args) -> Config: + """See :meth:`Pytester.parseconfigure`.""" + return self._pytester.parseconfigure(*args) + + def getitem(self, source, funcname="test_func"): + """See :meth:`Pytester.getitem`.""" + return self._pytester.getitem(source, funcname) + + def getitems(self, source): + """See :meth:`Pytester.getitems`.""" + return self._pytester.getitems(source) + + def getmodulecol(self, source, configargs=(), withinit=False): + """See :meth:`Pytester.getmodulecol`.""" + return self._pytester.getmodulecol( + source, configargs=configargs, withinit=withinit + ) + + def collect_by_name(self, modcol: Collector, name: str) -> Item | Collector | None: + """See :meth:`Pytester.collect_by_name`.""" + return self._pytester.collect_by_name(modcol, name) + + def popen( + self, + cmdargs, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + stdin=CLOSE_STDIN, + **kw, + ): + """See :meth:`Pytester.popen`.""" + return self._pytester.popen(cmdargs, stdout, stderr, stdin, **kw) + + def run(self, *cmdargs, timeout=None, stdin=CLOSE_STDIN) -> RunResult: + """See :meth:`Pytester.run`.""" + return self._pytester.run(*cmdargs, timeout=timeout, stdin=stdin) + + def runpython(self, script) -> RunResult: + """See :meth:`Pytester.runpython`.""" + return self._pytester.runpython(script) + + def runpython_c(self, command): + """See :meth:`Pytester.runpython_c`.""" + return self._pytester.runpython_c(command) + + def runpytest_subprocess(self, *args, timeout=None) -> RunResult: + """See :meth:`Pytester.runpytest_subprocess`.""" + return self._pytester.runpytest_subprocess(*args, timeout=timeout) + + def spawn_pytest(self, string: str, expect_timeout: float = 10.0) -> pexpect.spawn: + """See :meth:`Pytester.spawn_pytest`.""" + return self._pytester.spawn_pytest(string, expect_timeout=expect_timeout) + + def spawn(self, cmd: str, expect_timeout: float = 10.0) -> pexpect.spawn: + """See :meth:`Pytester.spawn`.""" + return self._pytester.spawn(cmd, expect_timeout=expect_timeout) + + def __repr__(self) -> str: + return f"" + + def __str__(self) -> str: + return str(self.tmpdir) + + +class LegacyTestdirPlugin: + @staticmethod + @fixture + def testdir(pytester: Pytester) -> Testdir: + """ + Identical to :fixture:`pytester`, and provides an instance whose methods return + legacy ``LEGACY_PATH`` objects instead when applicable. + + New code should avoid using :fixture:`testdir` in favor of :fixture:`pytester`. + """ + return Testdir(pytester, _ispytest=True) + + +@final +@dataclasses.dataclass +class TempdirFactory: + """Backward compatibility wrapper that implements ``py.path.local`` + for :class:`TempPathFactory`. + + .. note:: + These days, it is preferred to use ``tmp_path_factory``. + + :ref:`About the tmpdir and tmpdir_factory fixtures`. + + """ + + _tmppath_factory: TempPathFactory + + def __init__( + self, tmppath_factory: TempPathFactory, *, _ispytest: bool = False + ) -> None: + check_ispytest(_ispytest) + self._tmppath_factory = tmppath_factory + + def mktemp(self, basename: str, numbered: bool = True) -> LEGACY_PATH: + """Same as :meth:`TempPathFactory.mktemp`, but returns a ``py.path.local`` object.""" + return legacy_path(self._tmppath_factory.mktemp(basename, numbered).resolve()) + + def getbasetemp(self) -> LEGACY_PATH: + """Same as :meth:`TempPathFactory.getbasetemp`, but returns a ``py.path.local`` object.""" + return legacy_path(self._tmppath_factory.getbasetemp().resolve()) + + +class LegacyTmpdirPlugin: + @staticmethod + @fixture(scope="session") + def tmpdir_factory(request: FixtureRequest) -> TempdirFactory: + """Return a :class:`pytest.TempdirFactory` instance for the test session.""" + # Set dynamically by pytest_configure(). + return request.config._tmpdirhandler # type: ignore + + @staticmethod + @fixture + def tmpdir(tmp_path: Path) -> LEGACY_PATH: + """Return a temporary directory (as `legacy_path`_ object) + which is unique to each test function invocation. + The temporary directory is created as a subdirectory + of the base temporary directory, with configurable retention, + as discussed in :ref:`temporary directory location and retention`. + + .. note:: + These days, it is preferred to use ``tmp_path``. + + :ref:`About the tmpdir and tmpdir_factory fixtures`. + + .. _legacy_path: https://py.readthedocs.io/en/latest/path.html + """ + return legacy_path(tmp_path) + + +def Cache_makedir(self: Cache, name: str) -> LEGACY_PATH: + """Return a directory path object with the given name. + + Same as :func:`mkdir`, but returns a legacy py path instance. + """ + return legacy_path(self.mkdir(name)) + + +def FixtureRequest_fspath(self: FixtureRequest) -> LEGACY_PATH: + """(deprecated) The file system path of the test module which collected this test.""" + return legacy_path(self.path) + + +def TerminalReporter_startdir(self: TerminalReporter) -> LEGACY_PATH: + """The directory from which pytest was invoked. + + Prefer to use ``startpath`` which is a :class:`pathlib.Path`. + + :type: LEGACY_PATH + """ + return legacy_path(self.startpath) + + +def Config_invocation_dir(self: Config) -> LEGACY_PATH: + """The directory from which pytest was invoked. + + Prefer to use :attr:`invocation_params.dir `, + which is a :class:`pathlib.Path`. + + :type: LEGACY_PATH + """ + return legacy_path(str(self.invocation_params.dir)) + + +def Config_rootdir(self: Config) -> LEGACY_PATH: + """The path to the :ref:`rootdir `. + + Prefer to use :attr:`rootpath`, which is a :class:`pathlib.Path`. + + :type: LEGACY_PATH + """ + return legacy_path(str(self.rootpath)) + + +def Config_inifile(self: Config) -> LEGACY_PATH | None: + """The path to the :ref:`configfile `. + + Prefer to use :attr:`inipath`, which is a :class:`pathlib.Path`. + + :type: Optional[LEGACY_PATH] + """ + return legacy_path(str(self.inipath)) if self.inipath else None + + +def Session_startdir(self: Session) -> LEGACY_PATH: + """The path from which pytest was invoked. + + Prefer to use ``startpath`` which is a :class:`pathlib.Path`. + + :type: LEGACY_PATH + """ + return legacy_path(self.startpath) + + +def Config__getini_unknown_type(self, name: str, type: str, value: str | list[str]): + if type == "pathlist": + # TODO: This assert is probably not valid in all cases. + assert self.inipath is not None + dp = self.inipath.parent + input_values = shlex.split(value) if isinstance(value, str) else value + return [legacy_path(str(dp / x)) for x in input_values] + else: + raise ValueError(f"unknown configuration type: {type}", value) + + +def Node_fspath(self: Node) -> LEGACY_PATH: + """(deprecated) returns a legacy_path copy of self.path""" + return legacy_path(self.path) + + +def Node_fspath_set(self: Node, value: LEGACY_PATH) -> None: + self.path = Path(value) + + +@hookimpl(tryfirst=True) +def pytest_load_initial_conftests(early_config: Config) -> None: + """Monkeypatch legacy path attributes in several classes, as early as possible.""" + mp = MonkeyPatch() + early_config.add_cleanup(mp.undo) + + # Add Cache.makedir(). + mp.setattr(Cache, "makedir", Cache_makedir, raising=False) + + # Add FixtureRequest.fspath property. + mp.setattr(FixtureRequest, "fspath", property(FixtureRequest_fspath), raising=False) + + # Add TerminalReporter.startdir property. + mp.setattr( + TerminalReporter, "startdir", property(TerminalReporter_startdir), raising=False + ) + + # Add Config.{invocation_dir,rootdir,inifile} properties. + mp.setattr(Config, "invocation_dir", property(Config_invocation_dir), raising=False) + mp.setattr(Config, "rootdir", property(Config_rootdir), raising=False) + mp.setattr(Config, "inifile", property(Config_inifile), raising=False) + + # Add Session.startdir property. + mp.setattr(Session, "startdir", property(Session_startdir), raising=False) + + # Add pathlist configuration type. + mp.setattr(Config, "_getini_unknown_type", Config__getini_unknown_type) + + # Add Node.fspath property. + mp.setattr(Node, "fspath", property(Node_fspath, Node_fspath_set), raising=False) + + +@hookimpl +def pytest_configure(config: Config) -> None: + """Installs the LegacyTmpdirPlugin if the ``tmpdir`` plugin is also installed.""" + if config.pluginmanager.has_plugin("tmpdir"): + mp = MonkeyPatch() + config.add_cleanup(mp.undo) + # Create TmpdirFactory and attach it to the config object. + # + # This is to comply with existing plugins which expect the handler to be + # available at pytest_configure time, but ideally should be moved entirely + # to the tmpdir_factory session fixture. + try: + tmp_path_factory = config._tmp_path_factory # type: ignore[attr-defined] + except AttributeError: + # tmpdir plugin is blocked. + pass + else: + _tmpdirhandler = TempdirFactory(tmp_path_factory, _ispytest=True) + mp.setattr(config, "_tmpdirhandler", _tmpdirhandler, raising=False) + + config.pluginmanager.register(LegacyTmpdirPlugin, "legacypath-tmpdir") + + +@hookimpl +def pytest_plugin_registered(plugin: object, manager: PytestPluginManager) -> None: + # pytester is not loaded by default and is commonly loaded from a conftest, + # so checking for it in `pytest_configure` is not enough. + is_pytester = plugin is manager.get_plugin("pytester") + if is_pytester and not manager.is_registered(LegacyTestdirPlugin): + manager.register(LegacyTestdirPlugin, "legacypath-pytester") diff --git a/venv/Lib/site-packages/_pytest/logging.py b/venv/Lib/site-packages/_pytest/logging.py new file mode 100644 index 0000000..e4fed57 --- /dev/null +++ b/venv/Lib/site-packages/_pytest/logging.py @@ -0,0 +1,960 @@ +# mypy: allow-untyped-defs +"""Access and control log capturing.""" + +from __future__ import annotations + +from collections.abc import Generator +from collections.abc import Mapping +from collections.abc import Set as AbstractSet +from contextlib import contextmanager +from contextlib import nullcontext +from datetime import datetime +from datetime import timedelta +from datetime import timezone +import io +from io import StringIO +import logging +from logging import LogRecord +import os +from pathlib import Path +import re +from types import TracebackType +from typing import final +from typing import Generic +from typing import Literal +from typing import TYPE_CHECKING +from typing import TypeVar + +from _pytest import nodes +from _pytest._io import TerminalWriter +from _pytest.capture import CaptureManager +from _pytest.config import _strtobool +from _pytest.config import Config +from _pytest.config import create_terminal_writer +from _pytest.config import hookimpl +from _pytest.config import UsageError +from _pytest.config.argparsing import Parser +from _pytest.deprecated import check_ispytest +from _pytest.fixtures import fixture +from _pytest.fixtures import FixtureRequest +from _pytest.main import Session +from _pytest.stash import StashKey +from _pytest.terminal import TerminalReporter + + +if TYPE_CHECKING: + logging_StreamHandler = logging.StreamHandler[StringIO] +else: + logging_StreamHandler = logging.StreamHandler + +DEFAULT_LOG_FORMAT = "%(levelname)-8s %(name)s:%(filename)s:%(lineno)d %(message)s" +DEFAULT_LOG_DATE_FORMAT = "%H:%M:%S" +_ANSI_ESCAPE_SEQ = re.compile(r"\x1b\[[\d;]+m") +caplog_handler_key = StashKey["LogCaptureHandler"]() +caplog_records_key = StashKey[dict[str, list[logging.LogRecord]]]() + + +def _remove_ansi_escape_sequences(text: str) -> str: + return _ANSI_ESCAPE_SEQ.sub("", text) + + +class DatetimeFormatter(logging.Formatter): + """A logging formatter which formats record with + :func:`datetime.datetime.strftime` formatter instead of + :func:`time.strftime` in case of microseconds in format string. + """ + + def formatTime(self, record: LogRecord, datefmt: str | None = None) -> str: + if datefmt and "%f" in datefmt: + ct = self.converter(record.created) + tz = timezone(timedelta(seconds=ct.tm_gmtoff), ct.tm_zone) + # Construct `datetime.datetime` object from `struct_time` + # and msecs information from `record` + # Using int() instead of round() to avoid it exceeding 1_000_000 and causing a ValueError (#11861). + dt = datetime(*ct[0:6], microsecond=int(record.msecs * 1000), tzinfo=tz) + return dt.strftime(datefmt) + # Use `logging.Formatter` for non-microsecond formats + return super().formatTime(record, datefmt) + + +class ColoredLevelFormatter(DatetimeFormatter): + """A logging formatter which colorizes the %(levelname)..s part of the + log format passed to __init__.""" + + LOGLEVEL_COLOROPTS: Mapping[int, AbstractSet[str]] = { + logging.CRITICAL: {"red"}, + logging.ERROR: {"red", "bold"}, + logging.WARNING: {"yellow"}, + logging.WARN: {"yellow"}, + logging.INFO: {"green"}, + logging.DEBUG: {"purple"}, + logging.NOTSET: set(), + } + LEVELNAME_FMT_REGEX = re.compile(r"%\(levelname\)([+-.]?\d*(?:\.\d+)?s)") + + def __init__(self, terminalwriter: TerminalWriter, *args, **kwargs) -> None: + super().__init__(*args, **kwargs) + self._terminalwriter = terminalwriter + self._original_fmt = self._style._fmt + self._level_to_fmt_mapping: dict[int, str] = {} + + for level, color_opts in self.LOGLEVEL_COLOROPTS.items(): + self.add_color_level(level, *color_opts) + + def add_color_level(self, level: int, *color_opts: str) -> None: + """Add or update color opts for a log level. + + :param level: + Log level to apply a style to, e.g. ``logging.INFO``. + :param color_opts: + ANSI escape sequence color options. Capitalized colors indicates + background color, i.e. ``'green', 'Yellow', 'bold'`` will give bold + green text on yellow background. + + .. warning:: + This is an experimental API. + """ + assert self._fmt is not None + levelname_fmt_match = self.LEVELNAME_FMT_REGEX.search(self._fmt) + if not levelname_fmt_match: + return + levelname_fmt = levelname_fmt_match.group() + + formatted_levelname = levelname_fmt % {"levelname": logging.getLevelName(level)} + + # add ANSI escape sequences around the formatted levelname + color_kwargs = {name: True for name in color_opts} + colorized_formatted_levelname = self._terminalwriter.markup( + formatted_levelname, **color_kwargs + ) + self._level_to_fmt_mapping[level] = self.LEVELNAME_FMT_REGEX.sub( + colorized_formatted_levelname, self._fmt + ) + + def format(self, record: logging.LogRecord) -> str: + fmt = self._level_to_fmt_mapping.get(record.levelno, self._original_fmt) + self._style._fmt = fmt + return super().format(record) + + +class PercentStyleMultiline(logging.PercentStyle): + """A logging style with special support for multiline messages. + + If the message of a record consists of multiple lines, this style + formats the message as if each line were logged separately. + """ + + def __init__(self, fmt: str, auto_indent: int | str | bool | None) -> None: + super().__init__(fmt) + self._auto_indent = self._get_auto_indent(auto_indent) + + @staticmethod + def _get_auto_indent(auto_indent_option: int | str | bool | None) -> int: + """Determine the current auto indentation setting. + + Specify auto indent behavior (on/off/fixed) by passing in + extra={"auto_indent": [value]} to the call to logging.log() or + using a --log-auto-indent [value] command line or the + log_auto_indent [value] config option. + + Default behavior is auto-indent off. + + Using the string "True" or "on" or the boolean True as the value + turns auto indent on, using the string "False" or "off" or the + boolean False or the int 0 turns it off, and specifying a + positive integer fixes the indentation position to the value + specified. + + Any other values for the option are invalid, and will silently be + converted to the default. + + :param None|bool|int|str auto_indent_option: + User specified option for indentation from command line, config + or extra kwarg. Accepts int, bool or str. str option accepts the + same range of values as boolean config options, as well as + positive integers represented in str form. + + :returns: + Indentation value, which can be + -1 (automatically determine indentation) or + 0 (auto-indent turned off) or + >0 (explicitly set indentation position). + """ + if auto_indent_option is None: + return 0 + elif isinstance(auto_indent_option, bool): + if auto_indent_option: + return -1 + else: + return 0 + elif isinstance(auto_indent_option, int): + return int(auto_indent_option) + elif isinstance(auto_indent_option, str): + try: + return int(auto_indent_option) + except ValueError: + pass + try: + if _strtobool(auto_indent_option): + return -1 + except ValueError: + return 0 + + return 0 + + def format(self, record: logging.LogRecord) -> str: + if "\n" in record.message: + if hasattr(record, "auto_indent"): + # Passed in from the "extra={}" kwarg on the call to logging.log(). + auto_indent = self._get_auto_indent(record.auto_indent) + else: + auto_indent = self._auto_indent + + if auto_indent: + lines = record.message.splitlines() + formatted = self._fmt % {**record.__dict__, "message": lines[0]} + + if auto_indent < 0: + indentation = _remove_ansi_escape_sequences(formatted).find( + lines[0] + ) + else: + # Optimizes logging by allowing a fixed indentation. + indentation = auto_indent + lines[0] = formatted + return ("\n" + " " * indentation).join(lines) + return self._fmt % record.__dict__ + + +def get_option_ini(config: Config, *names: str): + for name in names: + ret = config.getoption(name) # 'default' arg won't work as expected + if ret is None: + ret = config.getini(name) + if ret: + return ret + + +def pytest_addoption(parser: Parser) -> None: + """Add options to control log capturing.""" + group = parser.getgroup("logging") + + def add_option_ini(option, dest, default=None, type=None, **kwargs): + parser.addini( + dest, default=default, type=type, help="Default value for " + option + ) + group.addoption(option, dest=dest, **kwargs) + + add_option_ini( + "--log-level", + dest="log_level", + default=None, + metavar="LEVEL", + help=( + "Level of messages to catch/display." + " Not set by default, so it depends on the root/parent log handler's" + ' effective level, where it is "WARNING" by default.' + ), + ) + add_option_ini( + "--log-format", + dest="log_format", + default=DEFAULT_LOG_FORMAT, + help="Log format used by the logging module", + ) + add_option_ini( + "--log-date-format", + dest="log_date_format", + default=DEFAULT_LOG_DATE_FORMAT, + help="Log date format used by the logging module", + ) + parser.addini( + "log_cli", + default=False, + type="bool", + help='Enable log display during test run (also known as "live logging")', + ) + add_option_ini( + "--log-cli-level", dest="log_cli_level", default=None, help="CLI logging level" + ) + add_option_ini( + "--log-cli-format", + dest="log_cli_format", + default=None, + help="Log format used by the logging module", + ) + add_option_ini( + "--log-cli-date-format", + dest="log_cli_date_format", + default=None, + help="Log date format used by the logging module", + ) + add_option_ini( + "--log-file", + dest="log_file", + default=None, + help="Path to a file when logging will be written to", + ) + add_option_ini( + "--log-file-mode", + dest="log_file_mode", + default="w", + choices=["w", "a"], + help="Log file open mode", + ) + add_option_ini( + "--log-file-level", + dest="log_file_level", + default=None, + help="Log file logging level", + ) + add_option_ini( + "--log-file-format", + dest="log_file_format", + default=None, + help="Log format used by the logging module", + ) + add_option_ini( + "--log-file-date-format", + dest="log_file_date_format", + default=None, + help="Log date format used by the logging module", + ) + add_option_ini( + "--log-auto-indent", + dest="log_auto_indent", + default=None, + help="Auto-indent multiline messages passed to the logging module. Accepts true|on, false|off or an integer.", + ) + group.addoption( + "--log-disable", + action="append", + default=[], + dest="logger_disable", + help="Disable a logger by name. Can be passed multiple times.", + ) + + +_HandlerType = TypeVar("_HandlerType", bound=logging.Handler) + + +# Not using @contextmanager for performance reasons. +class catching_logs(Generic[_HandlerType]): + """Context manager that prepares the whole logging machinery properly.""" + + __slots__ = ("handler", "level", "orig_level") + + def __init__(self, handler: _HandlerType, level: int | None = None) -> None: + self.handler = handler + self.level = level + + def __enter__(self) -> _HandlerType: + root_logger = logging.getLogger() + if self.level is not None: + self.handler.setLevel(self.level) + root_logger.addHandler(self.handler) + if self.level is not None: + self.orig_level = root_logger.level + root_logger.setLevel(min(self.orig_level, self.level)) + return self.handler + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + root_logger = logging.getLogger() + if self.level is not None: + root_logger.setLevel(self.orig_level) + root_logger.removeHandler(self.handler) + + +class LogCaptureHandler(logging_StreamHandler): + """A logging handler that stores log records and the log text.""" + + def __init__(self) -> None: + """Create a new log handler.""" + super().__init__(StringIO()) + self.records: list[logging.LogRecord] = [] + + def emit(self, record: logging.LogRecord) -> None: + """Keep the log records in a list in addition to the log text.""" + self.records.append(record) + super().emit(record) + + def reset(self) -> None: + self.records = [] + self.stream = StringIO() + + def clear(self) -> None: + self.records.clear() + self.stream = StringIO() + + def handleError(self, record: logging.LogRecord) -> None: + if logging.raiseExceptions: + # Fail the test if the log message is bad (emit failed). + # The default behavior of logging is to print "Logging error" + # to stderr with the call stack and some extra details. + # pytest wants to make such mistakes visible during testing. + raise # noqa: PLE0704 + + +@final +class LogCaptureFixture: + """Provides access and control of log capturing.""" + + def __init__(self, item: nodes.Node, *, _ispytest: bool = False) -> None: + check_ispytest(_ispytest) + self._item = item + self._initial_handler_level: int | None = None + # Dict of log name -> log level. + self._initial_logger_levels: dict[str | None, int] = {} + self._initial_disabled_logging_level: int | None = None + + def _finalize(self) -> None: + """Finalize the fixture. + + This restores the log levels and the disabled logging levels changed by :meth:`set_level`. + """ + # Restore log levels. + if self._initial_handler_level is not None: + self.handler.setLevel(self._initial_handler_level) + for logger_name, level in self._initial_logger_levels.items(): + logger = logging.getLogger(logger_name) + logger.setLevel(level) + # Disable logging at the original disabled logging level. + if self._initial_disabled_logging_level is not None: + logging.disable(self._initial_disabled_logging_level) + self._initial_disabled_logging_level = None + + @property + def handler(self) -> LogCaptureHandler: + """Get the logging handler used by the fixture.""" + return self._item.stash[caplog_handler_key] + + def get_records( + self, when: Literal["setup", "call", "teardown"] + ) -> list[logging.LogRecord]: + """Get the logging records for one of the possible test phases. + + :param when: + Which test phase to obtain the records from. + Valid values are: "setup", "call" and "teardown". + + :returns: The list of captured records at the given stage. + + .. versionadded:: 3.4 + """ + return self._item.stash[caplog_records_key].get(when, []) + + @property + def text(self) -> str: + """The formatted log text.""" + return _remove_ansi_escape_sequences(self.handler.stream.getvalue()) + + @property + def records(self) -> list[logging.LogRecord]: + """The list of log records.""" + return self.handler.records + + @property + def record_tuples(self) -> list[tuple[str, int, str]]: + """A list of a stripped down version of log records intended + for use in assertion comparison. + + The format of the tuple is: + + (logger_name, log_level, message) + """ + return [(r.name, r.levelno, r.getMessage()) for r in self.records] + + @property + def messages(self) -> list[str]: + """A list of format-interpolated log messages. + + Unlike 'records', which contains the format string and parameters for + interpolation, log messages in this list are all interpolated. + + Unlike 'text', which contains the output from the handler, log + messages in this list are unadorned with levels, timestamps, etc, + making exact comparisons more reliable. + + Note that traceback or stack info (from :func:`logging.exception` or + the `exc_info` or `stack_info` arguments to the logging functions) is + not included, as this is added by the formatter in the handler. + + .. versionadded:: 3.7 + """ + return [r.getMessage() for r in self.records] + + def clear(self) -> None: + """Reset the list of log records and the captured log text.""" + self.handler.clear() + + def _force_enable_logging( + self, level: int | str, logger_obj: logging.Logger + ) -> int: + """Enable the desired logging level if the global level was disabled via ``logging.disabled``. + + Only enables logging levels greater than or equal to the requested ``level``. + + Does nothing if the desired ``level`` wasn't disabled. + + :param level: + The logger level caplog should capture. + All logging is enabled if a non-standard logging level string is supplied. + Valid level strings are in :data:`logging._nameToLevel`. + :param logger_obj: The logger object to check. + + :return: The original disabled logging level. + """ + original_disable_level: int = logger_obj.manager.disable + + if isinstance(level, str): + # Try to translate the level string to an int for `logging.disable()` + level = logging.getLevelName(level) + + if not isinstance(level, int): + # The level provided was not valid, so just un-disable all logging. + logging.disable(logging.NOTSET) + elif not logger_obj.isEnabledFor(level): + # Each level is `10` away from other levels. + # https://docs.python.org/3/library/logging.html#logging-levels + disable_level = max(level - 10, logging.NOTSET) + logging.disable(disable_level) + + return original_disable_level + + def set_level(self, level: int | str, logger: str | None = None) -> None: + """Set the threshold level of a logger for the duration of a test. + + Logging messages which are less severe than this level will not be captured. + + .. versionchanged:: 3.4 + The levels of the loggers changed by this function will be + restored to their initial values at the end of the test. + + Will enable the requested logging level if it was disabled via :func:`logging.disable`. + + :param level: The level. + :param logger: The logger to update. If not given, the root logger. + """ + logger_obj = logging.getLogger(logger) + # Save the original log-level to restore it during teardown. + self._initial_logger_levels.setdefault(logger, logger_obj.level) + logger_obj.setLevel(level) + if self._initial_handler_level is None: + self._initial_handler_level = self.handler.level + self.handler.setLevel(level) + initial_disabled_logging_level = self._force_enable_logging(level, logger_obj) + if self._initial_disabled_logging_level is None: + self._initial_disabled_logging_level = initial_disabled_logging_level + + @contextmanager + def at_level(self, level: int | str, logger: str | None = None) -> Generator[None]: + """Context manager that sets the level for capturing of logs. After + the end of the 'with' statement the level is restored to its original + value. + + Will enable the requested logging level if it was disabled via :func:`logging.disable`. + + :param level: The level. + :param logger: The logger to update. If not given, the root logger. + """ + logger_obj = logging.getLogger(logger) + orig_level = logger_obj.level + logger_obj.setLevel(level) + handler_orig_level = self.handler.level + self.handler.setLevel(level) + original_disable_level = self._force_enable_logging(level, logger_obj) + try: + yield + finally: + logger_obj.setLevel(orig_level) + self.handler.setLevel(handler_orig_level) + logging.disable(original_disable_level) + + @contextmanager + def filtering(self, filter_: logging.Filter) -> Generator[None]: + """Context manager that temporarily adds the given filter to the caplog's + :meth:`handler` for the 'with' statement block, and removes that filter at the + end of the block. + + :param filter_: A custom :class:`logging.Filter` object. + + .. versionadded:: 7.5 + """ + self.handler.addFilter(filter_) + try: + yield + finally: + self.handler.removeFilter(filter_) + + +@fixture +def caplog(request: FixtureRequest) -> Generator[LogCaptureFixture]: + """Access and control log capturing. + + Captured logs are available through the following properties/methods:: + + * caplog.messages -> list of format-interpolated log messages + * caplog.text -> string containing formatted log output + * caplog.records -> list of logging.LogRecord instances + * caplog.record_tuples -> list of (logger_name, level, message) tuples + * caplog.clear() -> clear captured records and formatted log output string + """ + result = LogCaptureFixture(request.node, _ispytest=True) + yield result + result._finalize() + + +def get_log_level_for_setting(config: Config, *setting_names: str) -> int | None: + for setting_name in setting_names: + log_level = config.getoption(setting_name) + if log_level is None: + log_level = config.getini(setting_name) + if log_level: + break + else: + return None + + if isinstance(log_level, str): + log_level = log_level.upper() + try: + return int(getattr(logging, log_level, log_level)) + except ValueError as e: + # Python logging does not recognise this as a logging level + raise UsageError( + f"'{log_level}' is not recognized as a logging level name for " + f"'{setting_name}'. Please consider passing the " + "logging level num instead." + ) from e + + +# run after terminalreporter/capturemanager are configured +@hookimpl(trylast=True) +def pytest_configure(config: Config) -> None: + config.pluginmanager.register(LoggingPlugin(config), "logging-plugin") + + +class LoggingPlugin: + """Attaches to the logging module and captures log messages for each test.""" + + def __init__(self, config: Config) -> None: + """Create a new plugin to capture log messages. + + The formatter can be safely shared across all handlers so + create a single one for the entire test session here. + """ + self._config = config + + # Report logging. + self.formatter = self._create_formatter( + get_option_ini(config, "log_format"), + get_option_ini(config, "log_date_format"), + get_option_ini(config, "log_auto_indent"), + ) + self.log_level = get_log_level_for_setting(config, "log_level") + self.caplog_handler = LogCaptureHandler() + self.caplog_handler.setFormatter(self.formatter) + self.report_handler = LogCaptureHandler() + self.report_handler.setFormatter(self.formatter) + + # File logging. + self.log_file_level = get_log_level_for_setting( + config, "log_file_level", "log_level" + ) + log_file = get_option_ini(config, "log_file") or os.devnull + if log_file != os.devnull: + directory = os.path.dirname(os.path.abspath(log_file)) + if not os.path.isdir(directory): + os.makedirs(directory) + + self.log_file_mode = get_option_ini(config, "log_file_mode") or "w" + self.log_file_handler = _FileHandler( + log_file, mode=self.log_file_mode, encoding="UTF-8" + ) + log_file_format = get_option_ini(config, "log_file_format", "log_format") + log_file_date_format = get_option_ini( + config, "log_file_date_format", "log_date_format" + ) + + log_file_formatter = DatetimeFormatter( + log_file_format, datefmt=log_file_date_format + ) + self.log_file_handler.setFormatter(log_file_formatter) + + # CLI/live logging. + self.log_cli_level = get_log_level_for_setting( + config, "log_cli_level", "log_level" + ) + if self._log_cli_enabled(): + terminal_reporter = config.pluginmanager.get_plugin("terminalreporter") + # Guaranteed by `_log_cli_enabled()`. + assert terminal_reporter is not None + capture_manager = config.pluginmanager.get_plugin("capturemanager") + # if capturemanager plugin is disabled, live logging still works. + self.log_cli_handler: ( + _LiveLoggingStreamHandler | _LiveLoggingNullHandler + ) = _LiveLoggingStreamHandler(terminal_reporter, capture_manager) + else: + self.log_cli_handler = _LiveLoggingNullHandler() + log_cli_formatter = self._create_formatter( + get_option_ini(config, "log_cli_format", "log_format"), + get_option_ini(config, "log_cli_date_format", "log_date_format"), + get_option_ini(config, "log_auto_indent"), + ) + self.log_cli_handler.setFormatter(log_cli_formatter) + self._disable_loggers(loggers_to_disable=config.option.logger_disable) + + def _disable_loggers(self, loggers_to_disable: list[str]) -> None: + if not loggers_to_disable: + return + + for name in loggers_to_disable: + logger = logging.getLogger(name) + logger.disabled = True + + def _create_formatter(self, log_format, log_date_format, auto_indent): + # Color option doesn't exist if terminal plugin is disabled. + color = getattr(self._config.option, "color", "no") + if color != "no" and ColoredLevelFormatter.LEVELNAME_FMT_REGEX.search( + log_format + ): + formatter: logging.Formatter = ColoredLevelFormatter( + create_terminal_writer(self._config), log_format, log_date_format + ) + else: + formatter = DatetimeFormatter(log_format, log_date_format) + + formatter._style = PercentStyleMultiline( + formatter._style._fmt, auto_indent=auto_indent + ) + + return formatter + + def set_log_path(self, fname: str) -> None: + """Set the filename parameter for Logging.FileHandler(). + + Creates parent directory if it does not exist. + + .. warning:: + This is an experimental API. + """ + fpath = Path(fname) + + if not fpath.is_absolute(): + fpath = self._config.rootpath / fpath + + if not fpath.parent.exists(): + fpath.parent.mkdir(exist_ok=True, parents=True) + + # https://github.com/python/mypy/issues/11193 + stream: io.TextIOWrapper = fpath.open(mode=self.log_file_mode, encoding="UTF-8") # type: ignore[assignment] + old_stream = self.log_file_handler.setStream(stream) + if old_stream: + old_stream.close() + + def _log_cli_enabled(self) -> bool: + """Return whether live logging is enabled.""" + enabled = self._config.getoption( + "--log-cli-level" + ) is not None or self._config.getini("log_cli") + if not enabled: + return False + + terminal_reporter = self._config.pluginmanager.get_plugin("terminalreporter") + if terminal_reporter is None: + # terminal reporter is disabled e.g. by pytest-xdist. + return False + + return True + + @hookimpl(wrapper=True, tryfirst=True) + def pytest_sessionstart(self) -> Generator[None]: + self.log_cli_handler.set_when("sessionstart") + + with catching_logs(self.log_cli_handler, level=self.log_cli_level): + with catching_logs(self.log_file_handler, level=self.log_file_level): + return (yield) + + @hookimpl(wrapper=True, tryfirst=True) + def pytest_collection(self) -> Generator[None]: + self.log_cli_handler.set_when("collection") + + with catching_logs(self.log_cli_handler, level=self.log_cli_level): + with catching_logs(self.log_file_handler, level=self.log_file_level): + return (yield) + + @hookimpl(wrapper=True) + def pytest_runtestloop(self, session: Session) -> Generator[None, object, object]: + if session.config.option.collectonly: + return (yield) + + if self._log_cli_enabled() and self._config.get_verbosity() < 1: + # The verbose flag is needed to avoid messy test progress output. + self._config.option.verbose = 1 + + with catching_logs(self.log_cli_handler, level=self.log_cli_level): + with catching_logs(self.log_file_handler, level=self.log_file_level): + return (yield) # Run all the tests. + + @hookimpl + def pytest_runtest_logstart(self) -> None: + self.log_cli_handler.reset() + self.log_cli_handler.set_when("start") + + @hookimpl + def pytest_runtest_logreport(self) -> None: + self.log_cli_handler.set_when("logreport") + + @contextmanager + def _runtest_for(self, item: nodes.Item, when: str) -> Generator[None]: + """Implement the internals of the pytest_runtest_xxx() hooks.""" + with ( + catching_logs( + self.caplog_handler, + level=self.log_level, + ) as caplog_handler, + catching_logs( + self.report_handler, + level=self.log_level, + ) as report_handler, + ): + caplog_handler.reset() + report_handler.reset() + item.stash[caplog_records_key][when] = caplog_handler.records + item.stash[caplog_handler_key] = caplog_handler + + try: + yield + finally: + log = report_handler.stream.getvalue().strip() + item.add_report_section(when, "log", log) + + @hookimpl(wrapper=True) + def pytest_runtest_setup(self, item: nodes.Item) -> Generator[None]: + self.log_cli_handler.set_when("setup") + + empty: dict[str, list[logging.LogRecord]] = {} + item.stash[caplog_records_key] = empty + with self._runtest_for(item, "setup"): + yield + + @hookimpl(wrapper=True) + def pytest_runtest_call(self, item: nodes.Item) -> Generator[None]: + self.log_cli_handler.set_when("call") + + with self._runtest_for(item, "call"): + yield + + @hookimpl(wrapper=True) + def pytest_runtest_teardown(self, item: nodes.Item) -> Generator[None]: + self.log_cli_handler.set_when("teardown") + + try: + with self._runtest_for(item, "teardown"): + yield + finally: + del item.stash[caplog_records_key] + del item.stash[caplog_handler_key] + + @hookimpl + def pytest_runtest_logfinish(self) -> None: + self.log_cli_handler.set_when("finish") + + @hookimpl(wrapper=True, tryfirst=True) + def pytest_sessionfinish(self) -> Generator[None]: + self.log_cli_handler.set_when("sessionfinish") + + with catching_logs(self.log_cli_handler, level=self.log_cli_level): + with catching_logs(self.log_file_handler, level=self.log_file_level): + return (yield) + + @hookimpl + def pytest_unconfigure(self) -> None: + # Close the FileHandler explicitly. + # (logging.shutdown might have lost the weakref?!) + self.log_file_handler.close() + + +class _FileHandler(logging.FileHandler): + """A logging FileHandler with pytest tweaks.""" + + def handleError(self, record: logging.LogRecord) -> None: + # Handled by LogCaptureHandler. + pass + + +class _LiveLoggingStreamHandler(logging_StreamHandler): + """A logging StreamHandler used by the live logging feature: it will + write a newline before the first log message in each test. + + During live logging we must also explicitly disable stdout/stderr + capturing otherwise it will get captured and won't appear in the + terminal. + """ + + # Officially stream needs to be a IO[str], but TerminalReporter + # isn't. So force it. + stream: TerminalReporter = None # type: ignore + + def __init__( + self, + terminal_reporter: TerminalReporter, + capture_manager: CaptureManager | None, + ) -> None: + super().__init__(stream=terminal_reporter) # type: ignore[arg-type] + self.capture_manager = capture_manager + self.reset() + self.set_when(None) + self._test_outcome_written = False + + def reset(self) -> None: + """Reset the handler; should be called before the start of each test.""" + self._first_record_emitted = False + + def set_when(self, when: str | None) -> None: + """Prepare for the given test phase (setup/call/teardown).""" + self._when = when + self._section_name_shown = False + if when == "start": + self._test_outcome_written = False + + def emit(self, record: logging.LogRecord) -> None: + ctx_manager = ( + self.capture_manager.global_and_fixture_disabled() + if self.capture_manager + else nullcontext() + ) + with ctx_manager: + if not self._first_record_emitted: + self.stream.write("\n") + self._first_record_emitted = True + elif self._when in ("teardown", "finish"): + if not self._test_outcome_written: + self._test_outcome_written = True + self.stream.write("\n") + if not self._section_name_shown and self._when: + self.stream.section("live log " + self._when, sep="-", bold=True) + self._section_name_shown = True + super().emit(record) + + def handleError(self, record: logging.LogRecord) -> None: + # Handled by LogCaptureHandler. + pass + + +class _LiveLoggingNullHandler(logging.NullHandler): + """A logging handler used when live logging is disabled.""" + + def reset(self) -> None: + pass + + def set_when(self, when: str) -> None: + pass + + def handleError(self, record: logging.LogRecord) -> None: + # Handled by LogCaptureHandler. + pass diff --git a/venv/Lib/site-packages/_pytest/main.py b/venv/Lib/site-packages/_pytest/main.py new file mode 100644 index 0000000..9bc930d --- /dev/null +++ b/venv/Lib/site-packages/_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/venv/Lib/site-packages/_pytest/mark/__init__.py b/venv/Lib/site-packages/_pytest/mark/__init__.py new file mode 100644 index 0000000..841d781 --- /dev/null +++ b/venv/Lib/site-packages/_pytest/mark/__init__.py @@ -0,0 +1,301 @@ +"""Generic mechanism for marking and selecting python functions.""" + +from __future__ import annotations + +import collections +from collections.abc import Collection +from collections.abc import Iterable +from collections.abc import Set as AbstractSet +import dataclasses +from typing import 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/venv/Lib/site-packages/_pytest/mark/__pycache__/__init__.cpython-312.pyc b/venv/Lib/site-packages/_pytest/mark/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6bc9f75585e8e093b5682fb0f6bff37c49b1147e GIT binary patch literal 13072 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/_pytest/mark/__pycache__/expression.cpython-312.pyc b/venv/Lib/site-packages/_pytest/mark/__pycache__/expression.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..71046fb6aea68f772159d928d857ecd00ce06826 GIT binary patch literal 17359 zcmeHuTTmQVmSAR8)`NQU5FijJc^QhAux!~9Mh`5Z7M6r;f!bq?T`G|Y6e_Cl%|a5T z(wKH@q9NM5g0{9nE%gfRddFz&oyIXUVa3cuOn=Nr&*P)YMGw_!I>r&>kLig0pc_r} zjDC90x%sFEC0Xm9&rOM2H}B)zbMLwLyza?=a5`-aJpb)?djtQcg<<{$U$n=fWY{f- zfngSzbBw?YG6E|Yg6xE0(7MH|+n|m9+6V3Q*D>gTUrW$A;Tm+&K32%P2i-JZ0C~@#hvsdNFB~kSc{}8b28(Fk z0r}#=V#qrM*QjBzL~sx85Ikb3P}l|2F)-g^gre(=P%IXVw`&tk|9)&#@`L3DMzoIa zP1k5Sq2!YtAG69Eg;omC%07k_!slb&dEQ>z=S6eLnZlnA>8qrl%W4%P@QLJgGF3avsd{MJ#JbpX>P7VDVS=f{(_t%tS^ z+W0zd4YckVsB09>;xl5yh+!wa^L? z9XlzI!+g(~x845?VR#4oize6*v3Y#^;OS+Pe z@r_N5bX>mC+X$}#K@3Isi^rFNGpF~DuPpi zVpv|0T!K}-C{AAv6TwD7Yq(8?L}}PRDGFX*ijY8Pln(|XBJl@dPEBpz!*6!99eAq+ z=X=~Alti3r;{cgbGJWBY*r?D(5-0qjNMKmv{X_)vwDCO?lac8R`tqVmd5MpNc?zu^ znkfsHRfq8TFj3m@{^4Q38Zm=zetwdKFF_wc**_xTN2Wr<5!gRzV3z}tF@6&82t~$V z@_-WTBX#zG-ssc>)JWO*eDg8GWDmgKgNv=rA{%Ew0=D%OEa`=CI4JrP7P7e`e z@p8fO-cvokzV6dK!#D-)QJEaO^*od;G9oj`f&efGEHHe-ASW0HO#(M)7EFT{!3>Pu z0!-Zs47~uDxoyxfZTC9lf`RZwF*GneDMmd(aU_zaWk8A+lECO#R*`H76WItfkd3f} zvU2tUBLR^_9U@RZJi-S;lT(pL?7^rjdy_w6Tl>6D+3NG*QS$j@o6k297N&xjcldno z0M*nQ^Ql+6&-V1m=CjJL;mldt*xh$bHujwvkWIbEdin-rBk&;E)brZ8?o+b4f8cCy z-?wDb(KDw{ci(5WaETthajbKwqod>1H->=!iEaL2zaUOb4^2+DMf}pmw$Ulr+rdCc z9J(ZiE)AUuTo{sorfrk{;fwxJQ5y0oRM`W3@Zz2{4{V>DCPe_BCeJ}e!u7w-q@25F zj;EYFBn8fyms2+P%uywAl+W~}Y~}Fka6`^n1x+08@AQyH4a@*+7xM@SM`6D~nf@4+ z9J|#4nKX$((o-UfKxCAf0tY!0&6y!*!Q8afTOgY#dA&y2I2o2?E&_CxO_V7qQ)q*R zvJnVHP}a)EKqx|5A=l?M5fnM30+ULtEAaWGU^pTHwcI{m#u4)Q$TQfAPNoTSprX=D^@3ZlUN^(Z@CXF}$vSQG7Rr|X zVVIamV8JZIQv|u1Y@!=lHb?NFNW-cgK`}(?z#qFsD55bo#`tIjCNXAQrJi1drL3l# zKwBMFMAhVIFLnBvy)ydfwbRUf7RmGiG?a}&@d|knb6iNg5+VB_>ov%nBnC%l3kk>K zd4lx8GZ+X3B0gW#qfWS8D>(`E5;mG)QadVUx%X|U(u&2gg|Vfn55n+j-zckGI(GNu zt&_>JrdjU4I4BLXRt4u$hpWPv80I9y0tp57njvP0s0=G}6)pl^^9FJX){g2pg5eTF zEHQ&%j5-UZ0MhKxaKForCNP9Y{C3QxPPL@}lc zh4ueoj2(AxhZ8f>^@9pc=e)+nIH7=IQyD4LdI+|d@irsae{75(pQMkV_l>b5oYwz6 z7RLq{#wfI^V6K_+k1RN}6971xVMN&Kq%31ZXVyq&1zz?@d3Q~TieAP~EDv=vcCJao zMA@IAe~y%CoyW_yT>4jV;S2?sn>OcbmY9Y7am+G~Ts7wrbnXBvVG`VQWvlaIzAL9I zyl%%lGz?HYPk>VWR8Wopl)@)K;hzdhRo;2jK26^&-jPdz>WqC*r0r$cf5lI={~pVg z2=JcwPvynAeml-WN$we&X8yPFGV9&ZN09Y;4Ubs<5nJ#pfrB8g~4{KjXG+<4C5yPV7i4yAF)Hx=cyfmO{n@^J5)>U4{2(0 zGE5?%uLnY)N|KA9v8UCR=&q*D!;dwONHHRV$ARpCBw7l_%9TlR7@-Z1`H3HLgs&w% zP$@IsVw5-`MbJ}S2)84t!UR}9LmIFd2fDH#+xw?O5&xAQLc)Z2uo9O~HW1K6s9h?X zMoD;TlHgt>yD{N0slfzyhu2C_pP^!mDkrjyu8a@ylAWM-g6;?_&=|iO;>4cJ~T@!#HKwrgn*MM68%KSTsKbL#fm~s`~Ji281;FYAS zI^`~z@1Kh7m_cv16w^GY;Pcp$|$d}O<4Tj^Yt z9=!JHQKD=<&B-S%iYV;%4_#7ubSfJFKn5N zm8F}EaYyN<5$@sfS>rytaQUfhKc%g-o) zuC2b4Xx#sxAzpqY4e6nkDchy5oVePbEIshK=K##}rnI#0gJ{B2vtazHyyC9)mNi~; zbVC;lhsE)FF%qhDZBIB z($wwFL`lQK(XVi|I#zeAN}pD&Iv#Y#%MU$a#ok~UcagH6)?C#O&%M_Leqo3D8HFkoeQw%d?eB>oe zP^+i3@N#MT(gN9}*d!dZK)$B1wS4RTKD3a&4GB=#?!M7A*R^inm9WE6N!aUCCFP5! z7EaCf0E4>`or`|B^P{SJRS9?d#_qc1V;`NocQU!VW1hR|NEMgQp8AEoFy-*vI5~H6 zsbtBr=HOHA;%qX)Fbr~AO2&tfS!B}slfdS)iUh+r?!TM|Ogylcj_E$z^kV1J_Q&&R z>Ul|~Zk~v)DTAHBT1|aTI$g#=HgauYq=)Z~H5@%=<+gY#-DyC=2kiRQ7?sQ`KYk@q+4Ub1 zZnyVJ%URM58E*kWlM$R3VFJ=z=ElQ;P{w9Pw}K+|TQP^ms@FjGv24|rClgV+vD0ir zLGg#sOB#k`hS_lKOgU=S9Zd-b2;tI2`+^;iTHU`^*_HHkr935zjs?fMrv_xkx~DPW zX#_Fjbb*9%l`XML&)@C3)wN#PlBjH1@h2+xtd@LM@o7ce(Y0kayKS3{xxhyEavz*E zM3wA;LbLi?+fZ`efg6DD2V$FT2poK0zb!g;cK(}oF4tW+z$w5p| zF(P(Mke5)3tX z|DVSf44)V^>yTwKaL_rFHWJK4_Y{ri;ZAAql7>+~?1#SN0|c2rDAJUmRAEN}1D;$d zB3qAwA3Op#3NnWbLf$bFxZ+c-4IlCb>L@2D%kCRwR+5cy9x~6YoN+-!LeT7w;ZJ%U z5)d~Q>-VmF=gQ53MdyNZ%~HKlUU}DX%dvbeS>7^d{K{1{|LpfiLC>}p+_27BZ|+>I zTBur%%v#s1ts8dNy1gC*)Q9~ay?yWP)uYLV{Ym?QICnsiH<|0K?*fkQg0#HJp$+Vm z+~>(kfVPh0Twa)lw1SXN)@0P?q2Hw{_85dBGJ)krGA3nfa{;Wwb=i99*;2Ty`jqB0gx84f&5RkwE4J689-ILok3Ul5Z`x{t8TR|E=CO>?^7$=| zqfDem!!y0!AX`;#`k%J)Mu4PHpiUbv{rhBHBcnFY&dI3FL%k8BMn5zhL9VM}-3XpG zxR{qY42Y1e>g~JaEz02eJRnFY&$pZl`N=deePdrZLM~xBb>2}9Pd#|Jh@kAkEv@XJ zcdfox&-NVe{R73h#+`!OZJ9gj55m1RBt9juLNig*Lc~PF4d6lw08yhMZKKjrG5H(# zlL|m)=g}10MK`X_UA^<%-4}1Yc>BPIV=M3c&x=X--nezIu2igh_=JaFvjC6ERV3C6 zYZ8SuEA^;K>`A$cW^E~l^Tvs}6F1*j^ey<3j)s|@UsxO)Fv^v=D|Z~rQy*QucQsMI z_s@@hcH+|$fA&h!eR!5jfsooixBq7EdP#kvq<+obuu-&Qv1y@cnOoWW*S4QN`&Z7j zqT{o0v8%#f-_elR(Xb-So>+4{2TagYJZsxDGR0-k29fxZGkx#GcTT(?xG^y|ap#To zs!M{mk2;Wl z|3=b%>a|&>e`C7%&;>GFGJ=6v(+4J(b-ctj;eZJG(14mEvob9J#k&Hnd z_+wCf$qp4BjZ~8DG+xF>eXR;|=Frr{1(8r&Ny5{Gl1+26JMfkcf|{sUBp*QSKf#~$ zcZ!fJE?L~au>VdkUe&A~o8O--J}`4CWiMN|S0(IK>-Oe^y?M>vif%4f>&no2+u=mp z;dtwjxTAZ+Sqd(!EcaF6XJwz3#T^I0$%XDKV}T7kT~_zHr95FNU)r_4vn8>!WzC|1 z+q>Ge{#;Muxt{pm<3IJs9Vea&AUI-7W-7AYxZ#`gEf36`SmT<%EG@&JkUP`syZ0n^ z?@5++#4Q~&S0PDxpI`TOCcK>wo_%mO?maSR(%asB_tv|sk@>M?Y1gbhRa&!N+L$P9 zj5``ZsH%jW4-&}8aAp!qOUYzUss;DYe)V%W2dP`TLr7PZi@WE?w z&*6>2ily$Qs}zsI#O}ST-JgBy({IH+U9f(|E?7VC6xtZO>qg65%Y1OXv?)>A^hw=M zTJN{6P9>WUCT)kn*0sByXr#u0*qILRv?4mnQU%lq4RW5RbU}2&sm~*i^EoCoK1e0V zHy@FGAI8+22`0LkGfry*OhYz9yd+$~l8f+?=@cUGK>ml9A2~4rL6WFwmI9)u4VRv!)w+$(3*4)0krtJt?tY8(gQU8QGI*t7Rth$a4}8^ z7lVjs6G463e-sv@n+Jb)(0?H)@|VW~!(*-dXaGV0A#^p9+kxYR4^N`+3!+IC9k-R2 z#==uU0pfhoA#B4#e|b!Vpi>BxR--o|1Qu@J0aWKOTtG{m=X>8_r|zPEI}tde|{t!hA?jxfHJcu_lD?$TmVhaLfrE~R8d>`TB+8HapNk)oS~sJ zij{P5LX+4&+Rh8%P-BF@3~omnYfLXDDUJ$46qrOK2O!v*1fZQaz5M=wBpgi}48neP9g!;6c6?YHMk5G%kiZQWhNSpzJFi zmo=+SA43R_HeG(EFxE2w0-qA(T_Pb0)4R-{h~T~U!hRH9N>2EtRlp^m>c zpkC3K2|}e;G-L^KkII0Q`3R@njR??WdsM5%=M%!i5W<^b=iY zer?!cvOa8QOy^j|RSBi~qjFkyYcCX~SrLZoiV%~HVJw0XavaTB1rv>BEfCBY%Q|iG z+TS%p;3;s!nx}g}z7Pwu*U~y%>IP)xf5J!eTc56Q!Ilop9rI|E?miTj znYi_t{61c~g>bkNO+X8d-t~Dg7AamEBg0 z$nSzyh?YSRg1tt}U zMHyrucG_8;CP=Xc?LjWsnsG_$5>DFU?RFf`I1mB6_X<1~2Mq6SY}mEdWi07IwY z0|6V|h?Z%y*DkwMs;ZkWdW>d3^k{5T{|H3+Mnt(hhD!R`1WG6ZpG^c%lEJ4%_?1SJ zw_Oh-K{OFVx>eDx0oO%V)U+DV0=L7$&dUg%8Tg+#mm zHiDIrIVsuh^NorT_yl1Bx#70S>2<9*KOP`~LgfY*7b>U^`v120Z-1&Ukhs7z1odl2DLamF&PNL$(_jgUNG}(!M2N3jcJ=uHNJst zO%pz#;6a_$KF<<(|ENCz_shHx7#R`qiwcdANQ&|T5i2I&a99v&#Ko{WBRVa8xAJs$ z*_;C6#u*F{%JoGTdRd-%rK6m@=WKrv^Wy4*Xaula_ zbfgN)9$Gk$ZI%P;-czyEuw0U7egX|4iaE9X{QMX?e;H=m+nYh~iCSPfDq|GH`;=j^0h zRBVU?KAq}LOj~r>vsO2yx*}joiV_-=s)?DiVcYqm^kX%!s?k${Mj7CtkA>rDfK&i5 zp!pY=Rj{R-`mqyQGJrc(^V32Qr?NdPj0!V7HcYltuf_!UJi-FOLrL%oFITeR z0CbcWaBgkz28I(vhFLdlOktf8B~}A@bK!3cPE+F+M2Hn`V(wwJQ!}gpL3)>nhg*@s zF5nOCX%?`(ffXAd2iaa_$12sWyS=tB7?2`wW;A)r>!o5GKT;i;0%s3=q)JdUDR-!D z_^3As;nnyBal8M*FyXKYg#kh3P__&Hh@Y;M2wz=_;4UWL$I4S!X`+OJ4+^L8Lu2}d zFTv;qg3O0tq=FKB=fK9G+ED9Q@qVM}Aagwk50z)RKp#i>6oal6>X-_@4Pb5(lS`P~ zzyuxAif@im9(5dL6H#r15-wX!d1UQUKGQu!{vH|wK^dt7(oG}FvcEBuu-w13GHl(y zFt%SZ^}l55ApL?V`hu~4!MMI)3cg?*zhuhk|B7ESmCAo~Di@}>vQ3lGZkVxbI+&6@ zGX=Bzl1%$UvyJu4bBhHF1rHg>ZdNdcvYE)d1@fqi4B-^AD7 zy3Vnz?bi&Zo0kl5?K)}7 z9P57QDre8JOV)=B{%*d&7@W$WzcpCdXEzy0aJ;fje0^BzWb5Z&U+Rz7J|C}oKCxrp bLk3DVdB$M*t)UqKFqmQw%dcqx)!_awRM=-b literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/_pytest/mark/__pycache__/structures.cpython-312.pyc b/venv/Lib/site-packages/_pytest/mark/__pycache__/structures.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d8d7cd379c2759c13c7463febd0019cf6e7c204f GIT binary patch literal 27494 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≦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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/_pytest/mark/expression.py b/venv/Lib/site-packages/_pytest/mark/expression.py new file mode 100644 index 0000000..3bdbd03 --- /dev/null +++ b/venv/Lib/site-packages/_pytest/mark/expression.py @@ -0,0 +1,353 @@ +r"""Evaluate match expressions, as used by `-k` and `-m`. + +The grammar is: + +expression: expr? EOF +expr: and_expr ('or' and_expr)* +and_expr: not_expr ('and' not_expr)* +not_expr: 'not' not_expr | '(' expr ')' | ident kwargs? + +ident: (\w|:|\+|-|\.|\[|\]|\\|/)+ +kwargs: ('(' name '=' value ( ', ' name '=' value )* ')') +name: a valid ident, but not a reserved keyword +value: (unescaped) string literal | (-)?[0-9]+ | 'False' | 'True' | 'None' + +The semantics are: + +- Empty expression evaluates to False. +- ident evaluates to True or False according to a provided matcher function. +- ident with parentheses and keyword arguments evaluates to True or False according to a provided matcher function. +- or/and/not evaluate according to the usual boolean semantics. +""" + +from __future__ import annotations + +import ast +from collections.abc import Iterator +from collections.abc import Mapping +from collections.abc import Sequence +import dataclasses +import enum +import keyword +import re +import types +from typing import Final +from typing import final +from typing import Literal +from typing import NoReturn +from typing import overload +from typing import Protocol + + +__all__ = [ + "Expression", + "ExpressionMatcher", +] + + +FILE_NAME: Final = "" + + +class TokenType(enum.Enum): + LPAREN = "left parenthesis" + RPAREN = "right parenthesis" + OR = "or" + AND = "and" + NOT = "not" + IDENT = "identifier" + EOF = "end of input" + EQUAL = "=" + STRING = "string literal" + COMMA = "," + + +@dataclasses.dataclass(frozen=True) +class Token: + __slots__ = ("pos", "type", "value") + type: TokenType + value: str + pos: int + + +class Scanner: + __slots__ = ("current", "input", "tokens") + + def __init__(self, input: str) -> 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/venv/Lib/site-packages/_pytest/mark/structures.py b/venv/Lib/site-packages/_pytest/mark/structures.py new file mode 100644 index 0000000..16bb6d8 --- /dev/null +++ b/venv/Lib/site-packages/_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/venv/Lib/site-packages/_pytest/monkeypatch.py b/venv/Lib/site-packages/_pytest/monkeypatch.py new file mode 100644 index 0000000..07cc3fc --- /dev/null +++ b/venv/Lib/site-packages/_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/venv/Lib/site-packages/_pytest/nodes.py b/venv/Lib/site-packages/_pytest/nodes.py new file mode 100644 index 0000000..6690f6a --- /dev/null +++ b/venv/Lib/site-packages/_pytest/nodes.py @@ -0,0 +1,772 @@ +# mypy: allow-untyped-defs +from __future__ import annotations + +import abc +from collections.abc import Callable +from collections.abc import Iterable +from collections.abc import Iterator +from collections.abc import MutableMapping +from functools import cached_property +from functools import lru_cache +import os +import pathlib +from pathlib import Path +from typing import Any +from typing import cast +from typing import NoReturn +from typing import overload +from typing import TYPE_CHECKING +from typing import TypeVar +import warnings + +import pluggy + +import _pytest._code +from _pytest._code import getfslineno +from _pytest._code.code import ExceptionInfo +from _pytest._code.code import TerminalRepr +from _pytest._code.code import Traceback +from _pytest._code.code import TracebackStyle +from _pytest.compat import LEGACY_PATH +from _pytest.compat import signature +from _pytest.config import Config +from _pytest.config import ConftestImportFailure +from _pytest.config.compat import _check_path +from _pytest.deprecated import NODE_CTOR_FSPATH_ARG +from _pytest.mark.structures import Mark +from _pytest.mark.structures import MarkDecorator +from _pytest.mark.structures import NodeKeywords +from _pytest.outcomes import fail +from _pytest.pathlib import absolutepath +from _pytest.stash import Stash +from _pytest.warning_types import PytestWarning + + +if TYPE_CHECKING: + from typing_extensions import Self + + # Imported here due to circular import. + from _pytest.main import Session + + +SEP = "/" + +tracebackcutdir = Path(_pytest.__file__).parent + + +_T = TypeVar("_T") + + +def _imply_path( + node_type: type[Node], + path: Path | None, + fspath: LEGACY_PATH | None, +) -> Path: + if fspath is not None: + warnings.warn( + NODE_CTOR_FSPATH_ARG.format( + node_type_name=node_type.__name__, + ), + stacklevel=6, + ) + if path is not None: + if fspath is not None: + _check_path(path, fspath) + return path + else: + assert fspath is not None + return Path(fspath) + + +_NodeType = TypeVar("_NodeType", bound="Node") + + +class NodeMeta(abc.ABCMeta): + """Metaclass used by :class:`Node` to enforce that direct construction raises + :class:`Failed`. + + This behaviour supports the indirection introduced with :meth:`Node.from_parent`, + the named constructor to be used instead of direct construction. The design + decision to enforce indirection with :class:`NodeMeta` was made as a + temporary aid for refactoring the collection tree, which was diagnosed to + have :class:`Node` objects whose creational patterns were overly entangled. + Once the refactoring is complete, this metaclass can be removed. + + See https://github.com/pytest-dev/pytest/projects/3 for an overview of the + progress on detangling the :class:`Node` classes. + """ + + def __call__(cls, *k, **kw) -> NoReturn: + msg = ( + "Direct construction of {name} has been deprecated, please use {name}.from_parent.\n" + "See " + "https://docs.pytest.org/en/stable/deprecations.html#node-construction-changed-to-node-from-parent" + " for more details." + ).format(name=f"{cls.__module__}.{cls.__name__}") + fail(msg, pytrace=False) + + def _create(cls: type[_T], *k, **kw) -> _T: + try: + return super().__call__(*k, **kw) # type: ignore[no-any-return,misc] + except TypeError: + sig = signature(getattr(cls, "__init__")) + known_kw = {k: v for k, v in kw.items() if k in sig.parameters} + from .warning_types import PytestDeprecationWarning + + warnings.warn( + PytestDeprecationWarning( + f"{cls} is not using a cooperative constructor and only takes {set(known_kw)}.\n" + "See https://docs.pytest.org/en/stable/deprecations.html" + "#constructors-of-custom-pytest-node-subclasses-should-take-kwargs " + "for more details." + ) + ) + + return super().__call__(*k, **known_kw) # type: ignore[no-any-return,misc] + + +class Node(abc.ABC, metaclass=NodeMeta): + r"""Base class of :class:`Collector` and :class:`Item`, the components of + the test collection tree. + + ``Collector``\'s are the internal nodes of the tree, and ``Item``\'s are the + leaf nodes. + """ + + # Implemented in the legacypath plugin. + #: A ``LEGACY_PATH`` copy of the :attr:`path` attribute. Intended for usage + #: for methods not migrated to ``pathlib.Path`` yet, such as + #: :meth:`Item.reportinfo `. Will be deprecated in + #: a future release, prefer using :attr:`path` instead. + fspath: LEGACY_PATH + + # Use __slots__ to make attribute access faster. + # Note that __dict__ is still available. + __slots__ = ( + "__dict__", + "_nodeid", + "_store", + "config", + "name", + "parent", + "path", + "session", + ) + + def __init__( + self, + name: str, + parent: Node | None = None, + config: Config | None = None, + session: Session | None = None, + fspath: LEGACY_PATH | None = None, + path: Path | None = None, + nodeid: str | None = None, + ) -> None: + #: A unique name within the scope of the parent node. + self.name: str = name + + #: The parent collector node. + self.parent = parent + + if config: + #: The pytest config object. + self.config: Config = config + else: + if not parent: + raise TypeError("config or parent must be provided") + self.config = parent.config + + if session: + #: The pytest session this node is part of. + self.session: Session = session + else: + if not parent: + raise TypeError("session or parent must be provided") + self.session = parent.session + + if path is None and fspath is None: + path = getattr(parent, "path", None) + #: Filesystem path where this node was collected from (can be None). + self.path: pathlib.Path = _imply_path(type(self), path, fspath=fspath) + + # The explicit annotation is to avoid publicly exposing NodeKeywords. + #: Keywords/markers collected from all scopes. + self.keywords: MutableMapping[str, Any] = NodeKeywords(self) + + #: The marker objects belonging to this node. + self.own_markers: list[Mark] = [] + + #: Allow adding of extra keywords to use for matching. + self.extra_keyword_matches: set[str] = set() + + if nodeid is not None: + assert "::()" not in nodeid + self._nodeid = nodeid + else: + if not self.parent: + raise TypeError("nodeid or parent must be provided") + self._nodeid = self.parent.nodeid + "::" + self.name + + #: A place where plugins can store information on the node for their + #: own use. + self.stash: Stash = Stash() + # Deprecated alias. Was never public. Can be removed in a few releases. + self._store = self.stash + + @classmethod + def from_parent(cls, parent: Node, **kw) -> Self: + """Public constructor for Nodes. + + This indirection got introduced in order to enable removing + the fragile logic from the node constructors. + + Subclasses can use ``super().from_parent(...)`` when overriding the + construction. + + :param parent: The parent node of this Node. + """ + if "config" in kw: + raise TypeError("config is not a valid argument for from_parent") + if "session" in kw: + raise TypeError("session is not a valid argument for from_parent") + return cls._create(parent=parent, **kw) + + @property + def ihook(self) -> pluggy.HookRelay: + """fspath-sensitive hook proxy used to call pytest hooks.""" + return self.session.gethookproxy(self.path) + + def __repr__(self) -> str: + return "<{} {}>".format(self.__class__.__name__, getattr(self, "name", None)) + + def warn(self, warning: Warning) -> None: + """Issue a warning for this Node. + + Warnings will be displayed after the test session, unless explicitly suppressed. + + :param Warning warning: + The warning instance to issue. + + :raises ValueError: If ``warning`` instance is not a subclass of Warning. + + Example usage: + + .. code-block:: python + + node.warn(PytestWarning("some message")) + node.warn(UserWarning("some message")) + + .. versionchanged:: 6.2 + Any subclass of :class:`Warning` is now accepted, rather than only + :class:`PytestWarning ` subclasses. + """ + # enforce type checks here to avoid getting a generic type error later otherwise. + if not isinstance(warning, Warning): + raise ValueError( + f"warning must be an instance of Warning or subclass, got {warning!r}" + ) + path, lineno = get_fslocation_from_item(self) + assert lineno is not None + warnings.warn_explicit( + warning, + category=None, + filename=str(path), + lineno=lineno + 1, + ) + + # Methods for ordering nodes. + + @property + def nodeid(self) -> str: + """A ::-separated string denoting its collection tree address.""" + return self._nodeid + + def __hash__(self) -> int: + return hash(self._nodeid) + + def setup(self) -> None: + pass + + def teardown(self) -> None: + pass + + def iter_parents(self) -> Iterator[Node]: + """Iterate over all parent collectors starting from and including self + up to the root of the collection tree. + + .. versionadded:: 8.1 + """ + parent: Node | None = self + while parent is not None: + yield parent + parent = parent.parent + + def listchain(self) -> list[Node]: + """Return a list of all parent collectors starting from the root of the + collection tree down to and including self.""" + chain = [] + item: Node | None = self + while item is not None: + chain.append(item) + item = item.parent + chain.reverse() + return chain + + def add_marker(self, marker: str | MarkDecorator, append: bool = True) -> None: + """Dynamically add a marker object to the node. + + :param marker: + The marker. + :param append: + Whether to append the marker, or prepend it. + """ + from _pytest.mark import MARK_GEN + + if isinstance(marker, MarkDecorator): + marker_ = marker + elif isinstance(marker, str): + marker_ = getattr(MARK_GEN, marker) + else: + raise ValueError("is not a string or pytest.mark.* Marker") + self.keywords[marker_.name] = marker_ + if append: + self.own_markers.append(marker_.mark) + else: + self.own_markers.insert(0, marker_.mark) + + def iter_markers(self, name: str | None = None) -> Iterator[Mark]: + """Iterate over all markers of the node. + + :param name: If given, filter the results by the name attribute. + :returns: An iterator of the markers of the node. + """ + return (x[1] for x in self.iter_markers_with_node(name=name)) + + def iter_markers_with_node( + self, name: str | None = None + ) -> Iterator[tuple[Node, Mark]]: + """Iterate over all markers of the node. + + :param name: If given, filter the results by the name attribute. + :returns: An iterator of (node, mark) tuples. + """ + for node in self.iter_parents(): + for mark in node.own_markers: + if name is None or getattr(mark, "name", None) == name: + yield node, mark + + @overload + def get_closest_marker(self, name: str) -> Mark | None: ... + + @overload + def get_closest_marker(self, name: str, default: Mark) -> Mark: ... + + def get_closest_marker(self, name: str, default: Mark | None = None) -> Mark | None: + """Return the first marker matching the name, from closest (for + example function) to farther level (for example module level). + + :param default: Fallback return value if no marker was found. + :param name: Name to filter by. + """ + return next(self.iter_markers(name=name), default) + + def listextrakeywords(self) -> set[str]: + """Return a set of all extra keywords in self and any parents.""" + extra_keywords: set[str] = set() + for item in self.listchain(): + extra_keywords.update(item.extra_keyword_matches) + return extra_keywords + + def listnames(self) -> list[str]: + return [x.name for x in self.listchain()] + + def addfinalizer(self, fin: Callable[[], object]) -> None: + """Register a function to be called without arguments when this node is + finalized. + + This method can only be called when this node is active + in a setup chain, for example during self.setup(). + """ + self.session._setupstate.addfinalizer(fin, self) + + def getparent(self, cls: type[_NodeType]) -> _NodeType | None: + """Get the closest parent node (including self) which is an instance of + the given class. + + :param cls: The node class to search for. + :returns: The node, if found. + """ + for node in self.iter_parents(): + if isinstance(node, cls): + return node + return None + + def _traceback_filter(self, excinfo: ExceptionInfo[BaseException]) -> Traceback: + return excinfo.traceback + + def _repr_failure_py( + self, + excinfo: ExceptionInfo[BaseException], + style: TracebackStyle | None = None, + ) -> TerminalRepr: + from _pytest.fixtures import FixtureLookupError + + if isinstance(excinfo.value, ConftestImportFailure): + excinfo = ExceptionInfo.from_exception(excinfo.value.cause) + if isinstance(excinfo.value, fail.Exception): + if not excinfo.value.pytrace: + style = "value" + if isinstance(excinfo.value, FixtureLookupError): + return excinfo.value.formatrepr() + + tbfilter: bool | Callable[[ExceptionInfo[BaseException]], Traceback] + if self.config.getoption("fulltrace", False): + style = "long" + tbfilter = False + else: + tbfilter = self._traceback_filter + if style == "auto": + style = "long" + # XXX should excinfo.getrepr record all data and toterminal() process it? + if style is None: + if self.config.getoption("tbstyle", "auto") == "short": + style = "short" + else: + style = "long" + + if self.config.get_verbosity() > 1: + truncate_locals = False + else: + truncate_locals = True + + truncate_args = False if self.config.get_verbosity() > 2 else True + + # excinfo.getrepr() formats paths relative to the CWD if `abspath` is False. + # It is possible for a fixture/test to change the CWD while this code runs, which + # would then result in the user seeing confusing paths in the failure message. + # To fix this, if the CWD changed, always display the full absolute path. + # It will be better to just always display paths relative to invocation_dir, but + # this requires a lot of plumbing (#6428). + try: + abspath = Path(os.getcwd()) != self.config.invocation_params.dir + except OSError: + abspath = True + + return excinfo.getrepr( + funcargs=True, + abspath=abspath, + showlocals=self.config.getoption("showlocals", False), + style=style, + tbfilter=tbfilter, + truncate_locals=truncate_locals, + truncate_args=truncate_args, + ) + + def repr_failure( + self, + excinfo: ExceptionInfo[BaseException], + style: TracebackStyle | None = None, + ) -> str | TerminalRepr: + """Return a representation of a collection or test failure. + + .. seealso:: :ref:`non-python tests` + + :param excinfo: Exception information for the failure. + """ + return self._repr_failure_py(excinfo, style) + + +def get_fslocation_from_item(node: Node) -> tuple[str | Path, int | None]: + """Try to extract the actual location from a node, depending on available attributes: + + * "location": a pair (path, lineno) + * "obj": a Python object that the node wraps. + * "path": just a path + + :rtype: A tuple of (str|Path, int) with filename and 0-based line number. + """ + # See Item.location. + location: tuple[str, int | None, str] | None = getattr(node, "location", None) + if location is not None: + return location[:2] + obj = getattr(node, "obj", None) + if obj is not None: + return getfslineno(obj) + return getattr(node, "path", "unknown location"), -1 + + +class Collector(Node, abc.ABC): + """Base class of all collectors. + + Collector create children through `collect()` and thus iteratively build + the collection tree. + """ + + class CollectError(Exception): + """An error during collection, contains a custom message.""" + + @abc.abstractmethod + def collect(self) -> Iterable[Item | Collector]: + """Collect children (items and collectors) for this collector.""" + raise NotImplementedError("abstract") + + # TODO: This omits the style= parameter which breaks Liskov Substitution. + def repr_failure( # type: ignore[override] + self, excinfo: ExceptionInfo[BaseException] + ) -> str | TerminalRepr: + """Return a representation of a collection failure. + + :param excinfo: Exception information for the failure. + """ + if isinstance(excinfo.value, self.CollectError) and not self.config.getoption( + "fulltrace", False + ): + exc = excinfo.value + return str(exc.args[0]) + + # Respect explicit tbstyle option, but default to "short" + # (_repr_failure_py uses "long" with "fulltrace" option always). + tbstyle = self.config.getoption("tbstyle", "auto") + if tbstyle == "auto": + tbstyle = "short" + + return self._repr_failure_py(excinfo, style=tbstyle) + + def _traceback_filter(self, excinfo: ExceptionInfo[BaseException]) -> Traceback: + if hasattr(self, "path"): + traceback = excinfo.traceback + ntraceback = traceback.cut(path=self.path) + if ntraceback == traceback: + ntraceback = ntraceback.cut(excludepath=tracebackcutdir) + return ntraceback.filter(excinfo) + return excinfo.traceback + + +@lru_cache(maxsize=1000) +def _check_initialpaths_for_relpath( + initial_paths: frozenset[Path], path: Path +) -> str | None: + if path in initial_paths: + return "" + + for parent in path.parents: + if parent in initial_paths: + return str(path.relative_to(parent)) + + return None + + +class FSCollector(Collector, abc.ABC): + """Base class for filesystem collectors.""" + + def __init__( + self, + fspath: LEGACY_PATH | None = None, + path_or_parent: Path | Node | None = None, + path: Path | None = None, + name: str | None = None, + parent: Node | None = None, + config: Config | None = None, + session: Session | None = None, + nodeid: str | None = None, + ) -> None: + if path_or_parent: + if isinstance(path_or_parent, Node): + assert parent is None + parent = cast(FSCollector, path_or_parent) + elif isinstance(path_or_parent, Path): + assert path is None + path = path_or_parent + + path = _imply_path(type(self), path, fspath=fspath) + if name is None: + name = path.name + if parent is not None and parent.path != path: + try: + rel = path.relative_to(parent.path) + except ValueError: + pass + else: + name = str(rel) + name = name.replace(os.sep, SEP) + self.path = path + + if session is None: + assert parent is not None + session = parent.session + + if nodeid is None: + try: + nodeid = str(self.path.relative_to(session.config.rootpath)) + except ValueError: + nodeid = _check_initialpaths_for_relpath(session._initialpaths, path) + + if nodeid and os.sep != SEP: + nodeid = nodeid.replace(os.sep, SEP) + + super().__init__( + name=name, + parent=parent, + config=config, + session=session, + nodeid=nodeid, + path=path, + ) + + @classmethod + def from_parent( + cls, + parent, + *, + fspath: LEGACY_PATH | None = None, + path: Path | None = None, + **kw, + ) -> Self: + """The public constructor.""" + return super().from_parent(parent=parent, fspath=fspath, path=path, **kw) + + +class File(FSCollector, abc.ABC): + """Base class for collecting tests from a file. + + :ref:`non-python tests`. + """ + + +class Directory(FSCollector, abc.ABC): + """Base class for collecting files from a directory. + + A basic directory collector does the following: goes over the files and + sub-directories in the directory and creates collectors for them by calling + the hooks :hook:`pytest_collect_directory` and :hook:`pytest_collect_file`, + after checking that they are not ignored using + :hook:`pytest_ignore_collect`. + + The default directory collectors are :class:`~pytest.Dir` and + :class:`~pytest.Package`. + + .. versionadded:: 8.0 + + :ref:`custom directory collectors`. + """ + + +class Item(Node, abc.ABC): + """Base class of all test invocation items. + + Note that for a single function there might be multiple test invocation items. + """ + + nextitem = None + + def __init__( + self, + name, + parent=None, + config: Config | None = None, + session: Session | None = None, + nodeid: str | None = None, + **kw, + ) -> None: + # The first two arguments are intentionally passed positionally, + # to keep plugins who define a node type which inherits from + # (pytest.Item, pytest.File) working (see issue #8435). + # They can be made kwargs when the deprecation above is done. + super().__init__( + name, + parent, + config=config, + session=session, + nodeid=nodeid, + **kw, + ) + self._report_sections: list[tuple[str, str, str]] = [] + + #: A list of tuples (name, value) that holds user defined properties + #: for this test. + self.user_properties: list[tuple[str, object]] = [] + + self._check_item_and_collector_diamond_inheritance() + + def _check_item_and_collector_diamond_inheritance(self) -> None: + """ + Check if the current type inherits from both File and Collector + at the same time, emitting a warning accordingly (#8447). + """ + cls = type(self) + + # We inject an attribute in the type to avoid issuing this warning + # for the same class more than once, which is not helpful. + # It is a hack, but was deemed acceptable in order to avoid + # flooding the user in the common case. + attr_name = "_pytest_diamond_inheritance_warning_shown" + if getattr(cls, attr_name, False): + return + setattr(cls, attr_name, True) + + problems = ", ".join( + base.__name__ for base in cls.__bases__ if issubclass(base, Collector) + ) + if problems: + warnings.warn( + f"{cls.__name__} is an Item subclass and should not be a collector, " + f"however its bases {problems} are collectors.\n" + "Please split the Collectors and the Item into separate node types.\n" + "Pytest Doc example: https://docs.pytest.org/en/latest/example/nonpython.html\n" + "example pull request on a plugin: https://github.com/asmeurer/pytest-flakes/pull/40/", + PytestWarning, + ) + + @abc.abstractmethod + def runtest(self) -> None: + """Run the test case for this item. + + Must be implemented by subclasses. + + .. seealso:: :ref:`non-python tests` + """ + raise NotImplementedError("runtest must be implemented by Item subclass") + + def add_report_section(self, when: str, key: str, content: str) -> None: + """Add a new report section, similar to what's done internally to add + stdout and stderr captured output:: + + item.add_report_section("call", "stdout", "report section contents") + + :param str when: + One of the possible capture states, ``"setup"``, ``"call"``, ``"teardown"``. + :param str key: + Name of the section, can be customized at will. Pytest uses ``"stdout"`` and + ``"stderr"`` internally. + :param str content: + The full contents as a string. + """ + if content: + self._report_sections.append((when, key, content)) + + def reportinfo(self) -> tuple[os.PathLike[str] | str, int | None, str]: + """Get location information for this item for test reports. + + Returns a tuple with three elements: + + - The path of the test (default ``self.path``) + - The 0-based line number of the test (default ``None``) + - A name of the test to be shown (default ``""``) + + .. seealso:: :ref:`non-python tests` + """ + return self.path, None, "" + + @cached_property + def location(self) -> tuple[str, int | None, str]: + """ + Returns a tuple of ``(relfspath, lineno, testname)`` for this item + where ``relfspath`` is file path relative to ``config.rootpath`` + and lineno is a 0-based line number. + """ + location = self.reportinfo() + path = absolutepath(location[0]) + relfspath = self.session._node_location_to_relpath(path) + assert type(location[2]) is str + return (relfspath, location[1], location[2]) diff --git a/venv/Lib/site-packages/_pytest/outcomes.py b/venv/Lib/site-packages/_pytest/outcomes.py new file mode 100644 index 0000000..766be95 --- /dev/null +++ b/venv/Lib/site-packages/_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/venv/Lib/site-packages/_pytest/pastebin.py b/venv/Lib/site-packages/_pytest/pastebin.py new file mode 100644 index 0000000..c7b39d9 --- /dev/null +++ b/venv/Lib/site-packages/_pytest/pastebin.py @@ -0,0 +1,117 @@ +# mypy: allow-untyped-defs +"""Submit failure or test session information to a pastebin service.""" + +from __future__ import annotations + +from io import StringIO +import tempfile +from typing import IO + +from _pytest.config import Config +from _pytest.config import create_terminal_writer +from _pytest.config.argparsing import Parser +from _pytest.stash import StashKey +from _pytest.terminal import TerminalReporter +import pytest + + +pastebinfile_key = StashKey[IO[bytes]]() + + +def pytest_addoption(parser: Parser) -> None: + group = parser.getgroup("terminal reporting") + group.addoption( + "--pastebin", + metavar="mode", + action="store", + dest="pastebin", + default=None, + choices=["failed", "all"], + help="Send failed|all info to bpaste.net pastebin service", + ) + + +@pytest.hookimpl(trylast=True) +def pytest_configure(config: Config) -> None: + if config.option.pastebin == "all": + tr = config.pluginmanager.getplugin("terminalreporter") + # If no terminal reporter plugin is present, nothing we can do here; + # this can happen when this function executes in a worker node + # when using pytest-xdist, for example. + if tr is not None: + # pastebin file will be UTF-8 encoded binary file. + config.stash[pastebinfile_key] = tempfile.TemporaryFile("w+b") + oldwrite = tr._tw.write + + def tee_write(s, **kwargs): + oldwrite(s, **kwargs) + if isinstance(s, str): + s = s.encode("utf-8") + config.stash[pastebinfile_key].write(s) + + tr._tw.write = tee_write + + +def pytest_unconfigure(config: Config) -> None: + if pastebinfile_key in config.stash: + pastebinfile = config.stash[pastebinfile_key] + # Get terminal contents and delete file. + pastebinfile.seek(0) + sessionlog = pastebinfile.read() + pastebinfile.close() + del config.stash[pastebinfile_key] + # Undo our patching in the terminal reporter. + tr = config.pluginmanager.getplugin("terminalreporter") + del tr._tw.__dict__["write"] + # Write summary. + tr.write_sep("=", "Sending information to Paste Service") + pastebinurl = create_new_paste(sessionlog) + tr.write_line(f"pastebin session-log: {pastebinurl}\n") + + +def create_new_paste(contents: str | bytes) -> str: + """Create a new paste using the bpaste.net service. + + :contents: Paste contents string. + :returns: URL to the pasted contents, or an error message. + """ + import re + from urllib.error import HTTPError + from urllib.parse import urlencode + from urllib.request import urlopen + + params = {"code": contents, "lexer": "text", "expiry": "1week"} + url = "https://bpa.st" + try: + response: str = ( + urlopen(url, data=urlencode(params).encode("ascii")).read().decode("utf-8") + ) + except HTTPError as e: + with e: # HTTPErrors are also http responses that must be closed! + return f"bad response: {e}" + except OSError as e: # eg urllib.error.URLError + return f"bad response: {e}" + m = re.search(r'href="/raw/(\w+)"', response) + if m: + return f"{url}/show/{m.group(1)}" + else: + return "bad response: invalid format ('" + response + "')" + + +def pytest_terminal_summary(terminalreporter: TerminalReporter) -> None: + if terminalreporter.config.option.pastebin != "failed": + return + if "failed" in terminalreporter.stats: + terminalreporter.write_sep("=", "Sending information to Paste Service") + for rep in terminalreporter.stats["failed"]: + try: + msg = rep.longrepr.reprtraceback.reprentries[-1].reprfileloc + except AttributeError: + msg = terminalreporter._getfailureheadline(rep) + file = StringIO() + tw = create_terminal_writer(terminalreporter.config, file) + rep.toterminal(tw) + s = file.getvalue() + assert len(s) + pastebinurl = create_new_paste(s) + terminalreporter.write_line(f"{msg} --> {pastebinurl}") diff --git a/venv/Lib/site-packages/_pytest/pathlib.py b/venv/Lib/site-packages/_pytest/pathlib.py new file mode 100644 index 0000000..cd15434 --- /dev/null +++ b/venv/Lib/site-packages/_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/venv/Lib/site-packages/_pytest/py.typed b/venv/Lib/site-packages/_pytest/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/venv/Lib/site-packages/_pytest/pytester.py b/venv/Lib/site-packages/_pytest/pytester.py new file mode 100644 index 0000000..1cd5f05 --- /dev/null +++ b/venv/Lib/site-packages/_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/venv/Lib/site-packages/_pytest/pytester_assertions.py b/venv/Lib/site-packages/_pytest/pytester_assertions.py new file mode 100644 index 0000000..915cc8a --- /dev/null +++ b/venv/Lib/site-packages/_pytest/pytester_assertions.py @@ -0,0 +1,74 @@ +"""Helper plugin for pytester; should not be loaded on its own.""" + +# This plugin contains assertions used by pytester. pytester cannot +# contain them itself, since it is imported by the `pytest` module, +# hence cannot be subject to assertion rewriting, which requires a +# module to not be already imported. +from __future__ import annotations + +from collections.abc import Sequence + +from _pytest.reports import CollectReport +from _pytest.reports import TestReport + + +def assertoutcome( + outcomes: tuple[ + Sequence[TestReport], + Sequence[CollectReport | TestReport], + Sequence[CollectReport | TestReport], + ], + passed: int = 0, + skipped: int = 0, + failed: int = 0, +) -> None: + __tracebackhide__ = True + + realpassed, realskipped, realfailed = outcomes + obtained = { + "passed": len(realpassed), + "skipped": len(realskipped), + "failed": len(realfailed), + } + expected = {"passed": passed, "skipped": skipped, "failed": failed} + assert obtained == expected, outcomes + + +def assert_outcomes( + outcomes: dict[str, int], + passed: int = 0, + skipped: int = 0, + failed: int = 0, + errors: int = 0, + xpassed: int = 0, + xfailed: int = 0, + warnings: int | None = None, + deselected: int | None = None, +) -> None: + """Assert that the specified outcomes appear with the respective + numbers (0 means it didn't occur) in the text output from a test run.""" + __tracebackhide__ = True + + obtained = { + "passed": outcomes.get("passed", 0), + "skipped": outcomes.get("skipped", 0), + "failed": outcomes.get("failed", 0), + "errors": outcomes.get("errors", 0), + "xpassed": outcomes.get("xpassed", 0), + "xfailed": outcomes.get("xfailed", 0), + } + expected = { + "passed": passed, + "skipped": skipped, + "failed": failed, + "errors": errors, + "xpassed": xpassed, + "xfailed": xfailed, + } + if warnings is not None: + obtained["warnings"] = outcomes.get("warnings", 0) + expected["warnings"] = warnings + if deselected is not None: + obtained["deselected"] = outcomes.get("deselected", 0) + expected["deselected"] = deselected + assert obtained == expected diff --git a/venv/Lib/site-packages/_pytest/python.py b/venv/Lib/site-packages/_pytest/python.py new file mode 100644 index 0000000..e637518 --- /dev/null +++ b/venv/Lib/site-packages/_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/venv/Lib/site-packages/_pytest/python_api.py b/venv/Lib/site-packages/_pytest/python_api.py new file mode 100644 index 0000000..1e389eb --- /dev/null +++ b/venv/Lib/site-packages/_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/venv/Lib/site-packages/_pytest/raises.py b/venv/Lib/site-packages/_pytest/raises.py new file mode 100644 index 0000000..7c246fd --- /dev/null +++ b/venv/Lib/site-packages/_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/venv/Lib/site-packages/_pytest/recwarn.py b/venv/Lib/site-packages/_pytest/recwarn.py new file mode 100644 index 0000000..e3db717 --- /dev/null +++ b/venv/Lib/site-packages/_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/venv/Lib/site-packages/_pytest/reports.py b/venv/Lib/site-packages/_pytest/reports.py new file mode 100644 index 0000000..011a69d --- /dev/null +++ b/venv/Lib/site-packages/_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/venv/Lib/site-packages/_pytest/runner.py b/venv/Lib/site-packages/_pytest/runner.py new file mode 100644 index 0000000..9c20ff9 --- /dev/null +++ b/venv/Lib/site-packages/_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/venv/Lib/site-packages/_pytest/scope.py b/venv/Lib/site-packages/_pytest/scope.py new file mode 100644 index 0000000..2b007e8 --- /dev/null +++ b/venv/Lib/site-packages/_pytest/scope.py @@ -0,0 +1,91 @@ +""" +Scope definition and related utilities. + +Those are defined here, instead of in the 'fixtures' module because +their use is spread across many other pytest modules, and centralizing it in 'fixtures' +would cause circular references. + +Also this makes the module light to import, as it should. +""" + +from __future__ import annotations + +from enum import Enum +from functools import total_ordering +from typing import Literal + + +_ScopeName = Literal["session", "package", "module", "class", "function"] + + +@total_ordering +class Scope(Enum): + """ + Represents one of the possible fixture scopes in pytest. + + Scopes are ordered from lower to higher, that is: + + ->>> higher ->>> + + Function < Class < Module < Package < Session + + <<<- lower <<<- + """ + + # Scopes need to be listed from lower to higher. + Function = "function" + Class = "class" + Module = "module" + Package = "package" + Session = "session" + + def next_lower(self) -> Scope: + """Return the next lower scope.""" + index = _SCOPE_INDICES[self] + if index == 0: + raise ValueError(f"{self} is the lower-most scope") + return _ALL_SCOPES[index - 1] + + def next_higher(self) -> Scope: + """Return the next higher scope.""" + index = _SCOPE_INDICES[self] + if index == len(_SCOPE_INDICES) - 1: + raise ValueError(f"{self} is the upper-most scope") + return _ALL_SCOPES[index + 1] + + def __lt__(self, other: Scope) -> bool: + self_index = _SCOPE_INDICES[self] + other_index = _SCOPE_INDICES[other] + return self_index < other_index + + @classmethod + def from_user( + cls, scope_name: _ScopeName, descr: str, where: str | None = None + ) -> Scope: + """ + Given a scope name from the user, return the equivalent Scope enum. Should be used + whenever we want to convert a user provided scope name to its enum object. + + If the scope name is invalid, construct a user friendly message and call pytest.fail. + """ + from _pytest.outcomes import fail + + try: + # Holding this reference is necessary for mypy at the moment. + scope = Scope(scope_name) + except ValueError: + fail( + "{} {}got an unexpected scope value '{}'".format( + descr, f"from {where} " if where else "", scope_name + ), + pytrace=False, + ) + return scope + + +_ALL_SCOPES = list(Scope) +_SCOPE_INDICES = {scope: index for index, scope in enumerate(_ALL_SCOPES)} + + +# Ordered list of scopes which can contain many tests (in practice all except Function). +HIGH_SCOPES = [x for x in Scope if x is not Scope.Function] diff --git a/venv/Lib/site-packages/_pytest/setuponly.py b/venv/Lib/site-packages/_pytest/setuponly.py new file mode 100644 index 0000000..7e6b46b --- /dev/null +++ b/venv/Lib/site-packages/_pytest/setuponly.py @@ -0,0 +1,98 @@ +from __future__ import annotations + +from collections.abc import Generator + +from _pytest._io.saferepr import saferepr +from _pytest.config import Config +from _pytest.config import ExitCode +from _pytest.config.argparsing import Parser +from _pytest.fixtures import FixtureDef +from _pytest.fixtures import SubRequest +from _pytest.scope import Scope +import pytest + + +def pytest_addoption(parser: Parser) -> None: + group = parser.getgroup("debugconfig") + group.addoption( + "--setuponly", + "--setup-only", + action="store_true", + help="Only setup fixtures, do not execute tests", + ) + group.addoption( + "--setupshow", + "--setup-show", + action="store_true", + help="Show setup of fixtures while executing tests", + ) + + +@pytest.hookimpl(wrapper=True) +def pytest_fixture_setup( + fixturedef: FixtureDef[object], request: SubRequest +) -> Generator[None, object, object]: + try: + return (yield) + finally: + if request.config.option.setupshow: + if hasattr(request, "param"): + # Save the fixture parameter so ._show_fixture_action() can + # display it now and during the teardown (in .finish()). + if fixturedef.ids: + if callable(fixturedef.ids): + param = fixturedef.ids(request.param) + else: + param = fixturedef.ids[request.param_index] + else: + param = request.param + fixturedef.cached_param = param # type: ignore[attr-defined] + _show_fixture_action(fixturedef, request.config, "SETUP") + + +def pytest_fixture_post_finalizer( + fixturedef: FixtureDef[object], request: SubRequest +) -> None: + if fixturedef.cached_result is not None: + config = request.config + if config.option.setupshow: + _show_fixture_action(fixturedef, request.config, "TEARDOWN") + if hasattr(fixturedef, "cached_param"): + del fixturedef.cached_param + + +def _show_fixture_action( + fixturedef: FixtureDef[object], config: Config, msg: str +) -> None: + capman = config.pluginmanager.getplugin("capturemanager") + if capman: + capman.suspend_global_capture() + + tw = config.get_terminal_writer() + tw.line() + # Use smaller indentation the higher the scope: Session = 0, Package = 1, etc. + scope_indent = list(reversed(Scope)).index(fixturedef._scope) + tw.write(" " * 2 * scope_indent) + + scopename = fixturedef.scope[0].upper() + tw.write(f"{msg:<8} {scopename} {fixturedef.argname}") + + if msg == "SETUP": + deps = sorted(arg for arg in fixturedef.argnames if arg != "request") + if deps: + tw.write(" (fixtures used: {})".format(", ".join(deps))) + + if hasattr(fixturedef, "cached_param"): + tw.write(f"[{saferepr(fixturedef.cached_param, maxsize=42)}]") + + tw.flush() + + if capman: + capman.resume_global_capture() + + +@pytest.hookimpl(tryfirst=True) +def pytest_cmdline_main(config: Config) -> int | ExitCode | None: + if config.option.setuponly: + config.option.setupshow = True + return None diff --git a/venv/Lib/site-packages/_pytest/setupplan.py b/venv/Lib/site-packages/_pytest/setupplan.py new file mode 100644 index 0000000..4e124cc --- /dev/null +++ b/venv/Lib/site-packages/_pytest/setupplan.py @@ -0,0 +1,39 @@ +from __future__ import annotations + +from _pytest.config import Config +from _pytest.config import ExitCode +from _pytest.config.argparsing import Parser +from _pytest.fixtures import FixtureDef +from _pytest.fixtures import SubRequest +import pytest + + +def pytest_addoption(parser: Parser) -> None: + group = parser.getgroup("debugconfig") + group.addoption( + "--setupplan", + "--setup-plan", + action="store_true", + help="Show what fixtures and tests would be executed but " + "don't execute anything", + ) + + +@pytest.hookimpl(tryfirst=True) +def pytest_fixture_setup( + fixturedef: FixtureDef[object], request: SubRequest +) -> object | None: + # Will return a dummy fixture if the setuponly option is provided. + if request.config.option.setupplan: + my_cache_key = fixturedef.cache_key(request) + fixturedef.cached_result = (None, my_cache_key, None) + return fixturedef.cached_result + return None + + +@pytest.hookimpl(tryfirst=True) +def pytest_cmdline_main(config: Config) -> int | ExitCode | None: + if config.option.setupplan: + config.option.setuponly = True + config.option.setupshow = True + return None diff --git a/venv/Lib/site-packages/_pytest/skipping.py b/venv/Lib/site-packages/_pytest/skipping.py new file mode 100644 index 0000000..3b06762 --- /dev/null +++ b/venv/Lib/site-packages/_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/venv/Lib/site-packages/_pytest/stash.py b/venv/Lib/site-packages/_pytest/stash.py new file mode 100644 index 0000000..6a9ff88 --- /dev/null +++ b/venv/Lib/site-packages/_pytest/stash.py @@ -0,0 +1,116 @@ +from __future__ import annotations + +from typing import Any +from typing import cast +from typing import Generic +from typing import TypeVar + + +__all__ = ["Stash", "StashKey"] + + +T = TypeVar("T") +D = TypeVar("D") + + +class StashKey(Generic[T]): + """``StashKey`` is an object used as a key to a :class:`Stash`. + + A ``StashKey`` is associated with the type ``T`` of the value of the key. + + A ``StashKey`` is unique and cannot conflict with another key. + + .. versionadded:: 7.0 + """ + + __slots__ = () + + +class Stash: + r"""``Stash`` is a type-safe heterogeneous mutable mapping that + allows keys and value types to be defined separately from + where it (the ``Stash``) is created. + + Usually you will be given an object which has a ``Stash``, for example + :class:`~pytest.Config` or a :class:`~_pytest.nodes.Node`: + + .. code-block:: python + + stash: Stash = some_object.stash + + If a module or plugin wants to store data in this ``Stash``, it creates + :class:`StashKey`\s for its keys (at the module level): + + .. code-block:: python + + # At the top-level of the module + some_str_key = StashKey[str]() + some_bool_key = StashKey[bool]() + + To store information: + + .. code-block:: python + + # Value type must match the key. + stash[some_str_key] = "value" + stash[some_bool_key] = True + + To retrieve the information: + + .. code-block:: python + + # The static type of some_str is str. + some_str = stash[some_str_key] + # The static type of some_bool is bool. + some_bool = stash[some_bool_key] + + .. versionadded:: 7.0 + """ + + __slots__ = ("_storage",) + + def __init__(self) -> None: + self._storage: dict[StashKey[Any], object] = {} + + def __setitem__(self, key: StashKey[T], value: T) -> None: + """Set a value for key.""" + self._storage[key] = value + + def __getitem__(self, key: StashKey[T]) -> T: + """Get the value for key. + + Raises ``KeyError`` if the key wasn't set before. + """ + return cast(T, self._storage[key]) + + def get(self, key: StashKey[T], default: D) -> T | D: + """Get the value for key, or return default if the key wasn't set + before.""" + try: + return self[key] + except KeyError: + return default + + def setdefault(self, key: StashKey[T], default: T) -> T: + """Return the value of key if already set, otherwise set the value + of key to default and return default.""" + try: + return self[key] + except KeyError: + self[key] = default + return default + + def __delitem__(self, key: StashKey[T]) -> None: + """Delete the value for key. + + Raises ``KeyError`` if the key wasn't set before. + """ + del self._storage[key] + + def __contains__(self, key: StashKey[T]) -> bool: + """Return whether key was set.""" + return key in self._storage + + def __len__(self) -> int: + """Return how many items exist in the stash.""" + return len(self._storage) diff --git a/venv/Lib/site-packages/_pytest/stepwise.py b/venv/Lib/site-packages/_pytest/stepwise.py new file mode 100644 index 0000000..8901540 --- /dev/null +++ b/venv/Lib/site-packages/_pytest/stepwise.py @@ -0,0 +1,209 @@ +from __future__ import annotations + +import dataclasses +from datetime import datetime +from datetime import timedelta +from typing import Any +from typing import TYPE_CHECKING + +from _pytest import nodes +from _pytest.cacheprovider import Cache +from _pytest.config import Config +from _pytest.config.argparsing import Parser +from _pytest.main import Session +from _pytest.reports import TestReport + + +if TYPE_CHECKING: + from typing_extensions import Self + +STEPWISE_CACHE_DIR = "cache/stepwise" + + +def pytest_addoption(parser: Parser) -> None: + group = parser.getgroup("general") + group.addoption( + "--sw", + "--stepwise", + action="store_true", + default=False, + dest="stepwise", + help="Exit on test failure and continue from last failing test next time", + ) + group.addoption( + "--sw-skip", + "--stepwise-skip", + action="store_true", + default=False, + dest="stepwise_skip", + help="Ignore the first failing test but stop on the next failing test. " + "Implicitly enables --stepwise.", + ) + group.addoption( + "--sw-reset", + "--stepwise-reset", + action="store_true", + default=False, + dest="stepwise_reset", + help="Resets stepwise state, restarting the stepwise workflow. " + "Implicitly enables --stepwise.", + ) + + +def pytest_configure(config: Config) -> None: + # --stepwise-skip/--stepwise-reset implies stepwise. + if config.option.stepwise_skip or config.option.stepwise_reset: + config.option.stepwise = True + if config.getoption("stepwise"): + config.pluginmanager.register(StepwisePlugin(config), "stepwiseplugin") + + +def pytest_sessionfinish(session: Session) -> None: + if not session.config.getoption("stepwise"): + assert session.config.cache is not None + if hasattr(session.config, "workerinput"): + # Do not update cache if this process is a xdist worker to prevent + # race conditions (#10641). + return + + +@dataclasses.dataclass +class StepwiseCacheInfo: + # The nodeid of the last failed test. + last_failed: str | None + + # The number of tests in the last time --stepwise was run. + # We use this information as a simple way to invalidate the cache information, avoiding + # confusing behavior in case the cache is stale. + last_test_count: int | None + + # The date when the cache was last updated, for information purposes only. + last_cache_date_str: str + + @property + def last_cache_date(self) -> datetime: + return datetime.fromisoformat(self.last_cache_date_str) + + @classmethod + def empty(cls) -> Self: + return cls( + last_failed=None, + last_test_count=None, + last_cache_date_str=datetime.now().isoformat(), + ) + + def update_date_to_now(self) -> None: + self.last_cache_date_str = datetime.now().isoformat() + + +class StepwisePlugin: + def __init__(self, config: Config) -> None: + self.config = config + self.session: Session | None = None + self.report_status: list[str] = [] + assert config.cache is not None + self.cache: Cache = config.cache + self.skip: bool = config.getoption("stepwise_skip") + self.reset: bool = config.getoption("stepwise_reset") + self.cached_info = self._load_cached_info() + + def _load_cached_info(self) -> StepwiseCacheInfo: + cached_dict: dict[str, Any] | None = self.cache.get(STEPWISE_CACHE_DIR, None) + if cached_dict: + try: + return StepwiseCacheInfo( + cached_dict["last_failed"], + cached_dict["last_test_count"], + cached_dict["last_cache_date_str"], + ) + except (KeyError, TypeError) as e: + error = f"{type(e).__name__}: {e}" + self.report_status.append(f"error reading cache, discarding ({error})") + + # Cache not found or error during load, return a new cache. + return StepwiseCacheInfo.empty() + + def pytest_sessionstart(self, session: Session) -> None: + self.session = session + + def pytest_collection_modifyitems( + self, config: Config, items: list[nodes.Item] + ) -> None: + last_test_count = self.cached_info.last_test_count + self.cached_info.last_test_count = len(items) + + if self.reset: + self.report_status.append("resetting state, not skipping.") + self.cached_info.last_failed = None + return + + if not self.cached_info.last_failed: + self.report_status.append("no previously failed tests, not skipping.") + return + + if last_test_count is not None and last_test_count != len(items): + self.report_status.append( + f"test count changed, not skipping (now {len(items)} tests, previously {last_test_count})." + ) + self.cached_info.last_failed = None + return + + # Check all item nodes until we find a match on last failed. + failed_index = None + for index, item in enumerate(items): + if item.nodeid == self.cached_info.last_failed: + failed_index = index + break + + # If the previously failed test was not found among the test items, + # do not skip any tests. + if failed_index is None: + self.report_status.append("previously failed test not found, not skipping.") + else: + cache_age = datetime.now() - self.cached_info.last_cache_date + # Round up to avoid showing microseconds. + cache_age = timedelta(seconds=int(cache_age.total_seconds())) + self.report_status.append( + f"skipping {failed_index} already passed items (cache from {cache_age} ago," + f" use --sw-reset to discard)." + ) + deselected = items[:failed_index] + del items[:failed_index] + config.hook.pytest_deselected(items=deselected) + + def pytest_runtest_logreport(self, report: TestReport) -> None: + if report.failed: + if self.skip: + # Remove test from the failed ones (if it exists) and unset the skip option + # to make sure the following tests will not be skipped. + if report.nodeid == self.cached_info.last_failed: + self.cached_info.last_failed = None + + self.skip = False + else: + # Mark test as the last failing and interrupt the test session. + self.cached_info.last_failed = report.nodeid + assert self.session is not None + self.session.shouldstop = ( + "Test failed, continuing from this test next run." + ) + + else: + # If the test was actually run and did pass. + if report.when == "call": + # Remove test from the failed ones, if exists. + if report.nodeid == self.cached_info.last_failed: + self.cached_info.last_failed = None + + def pytest_report_collectionfinish(self) -> list[str] | None: + if self.config.get_verbosity() >= 0 and self.report_status: + return [f"stepwise: {x}" for x in self.report_status] + return None + + def pytest_sessionfinish(self) -> None: + if hasattr(self.config, "workerinput"): + # Do not update cache if this process is a xdist worker to prevent + # race conditions (#10641). + return + self.cached_info.update_date_to_now() + self.cache.set(STEPWISE_CACHE_DIR, dataclasses.asdict(self.cached_info)) diff --git a/venv/Lib/site-packages/_pytest/subtests.py b/venv/Lib/site-packages/_pytest/subtests.py new file mode 100644 index 0000000..e0ceb27 --- /dev/null +++ b/venv/Lib/site-packages/_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/venv/Lib/site-packages/_pytest/terminal.py b/venv/Lib/site-packages/_pytest/terminal.py new file mode 100644 index 0000000..4517b05 --- /dev/null +++ b/venv/Lib/site-packages/_pytest/terminal.py @@ -0,0 +1,1770 @@ +# 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 +import os +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) + + if reporter.isatty(): + # Some terminals interpret OSC 9;4 as desktop notification, + # skip on those we know (#13896). + should_skip_terminal_progress = ( + # iTerm2 (reported on version 3.6.5). + "ITERM_SESSION_ID" in os.environ + ) + if not should_skip_terminal_progress: + plugin = TerminalProgressPlugin(reporter) + config.pluginmanager.register(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/venv/Lib/site-packages/_pytest/threadexception.py b/venv/Lib/site-packages/_pytest/threadexception.py new file mode 100644 index 0000000..eb57783 --- /dev/null +++ b/venv/Lib/site-packages/_pytest/threadexception.py @@ -0,0 +1,152 @@ +from __future__ import annotations + +import collections +from collections.abc import Callable +import functools +import sys +import threading +import traceback +from typing import NamedTuple +from typing import TYPE_CHECKING +import warnings + +from _pytest.config import Config +from _pytest.nodes import Item +from _pytest.stash import StashKey +from _pytest.tracemalloc import tracemalloc_message +import pytest + + +if TYPE_CHECKING: + pass + +if sys.version_info < (3, 11): + from exceptiongroup import ExceptionGroup + + +class ThreadExceptionMeta(NamedTuple): + msg: str + cause_msg: str + exc_value: BaseException | None + + +thread_exceptions: StashKey[collections.deque[ThreadExceptionMeta | BaseException]] = ( + StashKey() +) + + +def collect_thread_exception(config: Config) -> None: + pop_thread_exception = config.stash[thread_exceptions].pop + errors: list[pytest.PytestUnhandledThreadExceptionWarning | RuntimeError] = [] + meta = None + hook_error = None + try: + while True: + try: + meta = pop_thread_exception() + except IndexError: + break + + if isinstance(meta, BaseException): + hook_error = RuntimeError("Failed to process thread exception") + hook_error.__cause__ = meta + errors.append(hook_error) + continue + + msg = meta.msg + try: + warnings.warn(pytest.PytestUnhandledThreadExceptionWarning(msg)) + except pytest.PytestUnhandledThreadExceptionWarning as e: + # This except happens when the warning is treated as an error (e.g. `-Werror`). + if meta.exc_value is not None: + # Exceptions have a better way to show the traceback, but + # warnings do not, so hide the traceback from the msg and + # set the cause so the traceback shows up in the right place. + e.args = (meta.cause_msg,) + e.__cause__ = meta.exc_value + errors.append(e) + + if len(errors) == 1: + raise errors[0] + if errors: + raise ExceptionGroup("multiple thread exception warnings", errors) + finally: + del errors, meta, hook_error + + +def cleanup( + *, config: Config, prev_hook: Callable[[threading.ExceptHookArgs], object] +) -> None: + try: + try: + # We don't join threads here, so exceptions raised from any + # threads still running by the time _threading_atexits joins them + # do not get captured (see #13027). + collect_thread_exception(config) + finally: + threading.excepthook = prev_hook + finally: + del config.stash[thread_exceptions] + + +def thread_exception_hook( + args: threading.ExceptHookArgs, + /, + *, + append: Callable[[ThreadExceptionMeta | BaseException], object], +) -> None: + try: + # we need to compute these strings here as they might change after + # the excepthook finishes and before the metadata object is + # collected by a pytest hook + thread_name = "" if args.thread is None else args.thread.name + summary = f"Exception in thread {thread_name}" + traceback_message = "\n\n" + "".join( + traceback.format_exception( + args.exc_type, + args.exc_value, + args.exc_traceback, + ) + ) + tracemalloc_tb = "\n" + tracemalloc_message(args.thread) + msg = summary + traceback_message + tracemalloc_tb + cause_msg = summary + tracemalloc_tb + + append( + ThreadExceptionMeta( + # Compute these strings here as they might change later + msg=msg, + cause_msg=cause_msg, + exc_value=args.exc_value, + ) + ) + except BaseException as e: + append(e) + # Raising this will cause the exception to be logged twice, once in our + # collect_thread_exception and once by sys.excepthook + # which is fine - this should never happen anyway and if it does + # it should probably be reported as a pytest bug. + raise + + +def pytest_configure(config: Config) -> None: + prev_hook = threading.excepthook + deque: collections.deque[ThreadExceptionMeta | BaseException] = collections.deque() + config.stash[thread_exceptions] = deque + config.add_cleanup(functools.partial(cleanup, config=config, prev_hook=prev_hook)) + threading.excepthook = functools.partial(thread_exception_hook, append=deque.append) + + +@pytest.hookimpl(trylast=True) +def pytest_runtest_setup(item: Item) -> None: + collect_thread_exception(item.config) + + +@pytest.hookimpl(trylast=True) +def pytest_runtest_call(item: Item) -> None: + collect_thread_exception(item.config) + + +@pytest.hookimpl(trylast=True) +def pytest_runtest_teardown(item: Item) -> None: + collect_thread_exception(item.config) diff --git a/venv/Lib/site-packages/_pytest/timing.py b/venv/Lib/site-packages/_pytest/timing.py new file mode 100644 index 0000000..51c3db2 --- /dev/null +++ b/venv/Lib/site-packages/_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/venv/Lib/site-packages/_pytest/tmpdir.py b/venv/Lib/site-packages/_pytest/tmpdir.py new file mode 100644 index 0000000..dcd5784 --- /dev/null +++ b/venv/Lib/site-packages/_pytest/tmpdir.py @@ -0,0 +1,312 @@ +# mypy: allow-untyped-defs +"""Support for providing temporary directories to test functions.""" + +from __future__ import annotations + +from collections.abc import Generator +import dataclasses +import os +from pathlib import Path +import re +from shutil import rmtree +import tempfile +from typing import Any +from typing import final +from typing import Literal + +from .pathlib import cleanup_dead_symlinks +from .pathlib import LOCK_TIMEOUT +from .pathlib import make_numbered_dir +from .pathlib import make_numbered_dir_with_cleanup +from .pathlib import rm_rf +from _pytest.compat import get_user_id +from _pytest.config import Config +from _pytest.config import ExitCode +from _pytest.config import hookimpl +from _pytest.config.argparsing import Parser +from _pytest.deprecated import check_ispytest +from _pytest.fixtures import fixture +from _pytest.fixtures import FixtureRequest +from _pytest.monkeypatch import MonkeyPatch +from _pytest.nodes import Item +from _pytest.reports import TestReport +from _pytest.stash import StashKey + + +tmppath_result_key = StashKey[dict[str, bool]]() +RetentionType = Literal["all", "failed", "none"] + + +@final +@dataclasses.dataclass +class TempPathFactory: + """Factory for temporary directories under the common base temp directory, + as discussed at :ref:`temporary directory location and retention`. + """ + + _given_basetemp: Path | None + # pluggy TagTracerSub, not currently exposed, so Any. + _trace: Any + _basetemp: Path | None + _retention_count: int + _retention_policy: RetentionType + + def __init__( + self, + given_basetemp: Path | None, + retention_count: int, + retention_policy: RetentionType, + trace, + basetemp: Path | None = None, + *, + _ispytest: bool = False, + ) -> None: + check_ispytest(_ispytest) + if given_basetemp is None: + self._given_basetemp = None + else: + # Use os.path.abspath() to get absolute path instead of resolve() as it + # does not work the same in all platforms (see #4427). + # Path.absolute() exists, but it is not public (see https://bugs.python.org/issue25012). + self._given_basetemp = Path(os.path.abspath(str(given_basetemp))) + self._trace = trace + self._retention_count = retention_count + self._retention_policy = retention_policy + self._basetemp = basetemp + + @classmethod + def from_config( + cls, + config: Config, + *, + _ispytest: bool = False, + ) -> TempPathFactory: + """Create a factory according to pytest configuration. + + :meta private: + """ + check_ispytest(_ispytest) + count = int(config.getini("tmp_path_retention_count")) + if count < 0: + raise ValueError( + f"tmp_path_retention_count must be >= 0. Current input: {count}." + ) + + policy = config.getini("tmp_path_retention_policy") + if policy not in ("all", "failed", "none"): + raise ValueError( + f"tmp_path_retention_policy must be either all, failed, none. Current input: {policy}." + ) + + return cls( + given_basetemp=config.option.basetemp, + trace=config.trace.get("tmpdir"), + retention_count=count, + retention_policy=policy, + _ispytest=True, + ) + + def _ensure_relative_to_basetemp(self, basename: str) -> str: + basename = os.path.normpath(basename) + if (self.getbasetemp() / basename).resolve().parent != self.getbasetemp(): + raise ValueError(f"{basename} is not a normalized and relative path") + return basename + + def mktemp(self, basename: str, numbered: bool = True) -> Path: + """Create a new temporary directory managed by the factory. + + :param basename: + Directory base name, must be a relative path. + + :param numbered: + If ``True``, ensure the directory is unique by adding a numbered + suffix greater than any existing one: ``basename="foo-"`` and ``numbered=True`` + means that this function will create directories named ``"foo-0"``, + ``"foo-1"``, ``"foo-2"`` and so on. + + :returns: + The path to the new directory. + """ + basename = self._ensure_relative_to_basetemp(basename) + if not numbered: + p = self.getbasetemp().joinpath(basename) + p.mkdir(mode=0o700) + else: + p = make_numbered_dir(root=self.getbasetemp(), prefix=basename, mode=0o700) + self._trace("mktemp", p) + return p + + def getbasetemp(self) -> Path: + """Return the base temporary directory, creating it if needed. + + :returns: + The base temporary directory. + """ + if self._basetemp is not None: + return self._basetemp + + if self._given_basetemp is not None: + basetemp = self._given_basetemp + if basetemp.exists(): + rm_rf(basetemp) + basetemp.mkdir(mode=0o700) + basetemp = basetemp.resolve() + else: + from_env = os.environ.get("PYTEST_DEBUG_TEMPROOT") + temproot = Path(from_env or tempfile.gettempdir()).resolve() + user = get_user() or "unknown" + # use a sub-directory in the temproot to speed-up + # make_numbered_dir() call + rootdir = temproot.joinpath(f"pytest-of-{user}") + try: + rootdir.mkdir(mode=0o700, exist_ok=True) + except OSError: + # getuser() likely returned illegal characters for the platform, use unknown back off mechanism + rootdir = temproot.joinpath("pytest-of-unknown") + rootdir.mkdir(mode=0o700, exist_ok=True) + # Because we use exist_ok=True with a predictable name, make sure + # we are the owners, to prevent any funny business (on unix, where + # temproot is usually shared). + # Also, to keep things private, fixup any world-readable temp + # rootdir's permissions. Historically 0o755 was used, so we can't + # just error out on this, at least for a while. + uid = get_user_id() + if uid is not None: + rootdir_stat = rootdir.stat() + if rootdir_stat.st_uid != uid: + raise OSError( + f"The temporary directory {rootdir} is not owned by the current user. " + "Fix this and try again." + ) + if (rootdir_stat.st_mode & 0o077) != 0: + os.chmod(rootdir, rootdir_stat.st_mode & ~0o077) + keep = self._retention_count + if self._retention_policy == "none": + keep = 0 + basetemp = make_numbered_dir_with_cleanup( + prefix="pytest-", + root=rootdir, + keep=keep, + lock_timeout=LOCK_TIMEOUT, + mode=0o700, + ) + assert basetemp is not None, basetemp + self._basetemp = basetemp + self._trace("new basetemp", basetemp) + return basetemp + + +def get_user() -> str | None: + """Return the current user name, or None if getuser() does not work + in the current environment (see #1010).""" + try: + # In some exotic environments, getpass may not be importable. + import getpass + + return getpass.getuser() + except (ImportError, OSError, KeyError): + return None + + +def pytest_configure(config: Config) -> None: + """Create a TempPathFactory and attach it to the config object. + + This is to comply with existing plugins which expect the handler to be + available at pytest_configure time, but ideally should be moved entirely + to the tmp_path_factory session fixture. + """ + mp = MonkeyPatch() + config.add_cleanup(mp.undo) + _tmp_path_factory = TempPathFactory.from_config(config, _ispytest=True) + mp.setattr(config, "_tmp_path_factory", _tmp_path_factory, raising=False) + + +def pytest_addoption(parser: Parser) -> None: + parser.addini( + "tmp_path_retention_count", + help="How many sessions should we keep the `tmp_path` directories, according to `tmp_path_retention_policy`.", + default=3, + ) + + parser.addini( + "tmp_path_retention_policy", + help="Controls which directories created by the `tmp_path` fixture are kept around, based on test outcome. " + "(all/failed/none)", + default="all", + ) + + +@fixture(scope="session") +def tmp_path_factory(request: FixtureRequest) -> TempPathFactory: + """Return a :class:`pytest.TempPathFactory` instance for the test session.""" + # Set dynamically by pytest_configure() above. + return request.config._tmp_path_factory # type: ignore + + +def _mk_tmp(request: FixtureRequest, factory: TempPathFactory) -> Path: + name = request.node.name + name = re.sub(r"[\W]", "_", name) + MAXVAL = 30 + name = name[:MAXVAL] + return factory.mktemp(name, numbered=True) + + +@fixture +def tmp_path( + request: FixtureRequest, tmp_path_factory: TempPathFactory +) -> Generator[Path]: + """Return a temporary directory (as :class:`pathlib.Path` object) + which is unique to each test function invocation. + The temporary directory is created as a subdirectory + of the base temporary directory, with configurable retention, + as discussed in :ref:`temporary directory location and retention`. + """ + path = _mk_tmp(request, tmp_path_factory) + yield path + + # Remove the tmpdir if the policy is "failed" and the test passed. + policy = tmp_path_factory._retention_policy + result_dict = request.node.stash[tmppath_result_key] + + if policy == "failed" and result_dict.get("call", True): + # We do a "best effort" to remove files, but it might not be possible due to some leaked resource, + # permissions, etc, in which case we ignore it. + rmtree(path, ignore_errors=True) + + del request.node.stash[tmppath_result_key] + + +def pytest_sessionfinish(session, exitstatus: int | ExitCode): + """After each session, remove base directory if all the tests passed, + the policy is "failed", and the basetemp is not specified by a user. + """ + tmp_path_factory: TempPathFactory = session.config._tmp_path_factory + basetemp = tmp_path_factory._basetemp + if basetemp is None: + return + + policy = tmp_path_factory._retention_policy + if ( + exitstatus == 0 + and policy == "failed" + and tmp_path_factory._given_basetemp is None + ): + if basetemp.is_dir(): + # We do a "best effort" to remove files, but it might not be possible due to some leaked resource, + # permissions, etc, in which case we ignore it. + rmtree(basetemp, ignore_errors=True) + + # Remove dead symlinks. + if basetemp.is_dir(): + cleanup_dead_symlinks(basetemp) + + +@hookimpl(wrapper=True, tryfirst=True) +def pytest_runtest_makereport( + item: Item, call +) -> Generator[None, TestReport, TestReport]: + rep = yield + assert rep.when is not None + empty: dict[str, bool] = {} + item.stash.setdefault(tmppath_result_key, empty)[rep.when] = rep.passed + return rep diff --git a/venv/Lib/site-packages/_pytest/tracemalloc.py b/venv/Lib/site-packages/_pytest/tracemalloc.py new file mode 100644 index 0000000..5d0b198 --- /dev/null +++ b/venv/Lib/site-packages/_pytest/tracemalloc.py @@ -0,0 +1,24 @@ +from __future__ import annotations + + +def tracemalloc_message(source: object) -> str: + if source is None: + return "" + + try: + import tracemalloc + except ImportError: + return "" + + tb = tracemalloc.get_object_traceback(source) + if tb is not None: + formatted_tb = "\n".join(tb.format()) + # Use a leading new line to better separate the (large) output + # from the traceback to the previous warning text. + return f"\nObject allocated at:\n{formatted_tb}" + # No need for a leading new line. + url = "https://docs.pytest.org/en/stable/how-to/capture-warnings.html#resource-warnings" + return ( + "Enable tracemalloc to get traceback where the object was allocated.\n" + f"See {url} for more info." + ) diff --git a/venv/Lib/site-packages/_pytest/unittest.py b/venv/Lib/site-packages/_pytest/unittest.py new file mode 100644 index 0000000..7498f1b --- /dev/null +++ b/venv/Lib/site-packages/_pytest/unittest.py @@ -0,0 +1,614 @@ +# 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() + + 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 = [ + x + for x, y in self.instance._outcome.errors + if isinstance(x, _SubTest) and y is not None + ] + 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): + from unittest.case import _SubTest # type: ignore[attr-defined] + + non_subtest_skip = [ + (x, y) + for x, y in self.instance._outcome.skipped + if not isinstance(x, _SubTest) + ] + subtest_errors = [ + (x, y) + for x, y in self.instance._outcome.errors + if isinstance(x, _SubTest) and y is not None + ] + # 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) + + +@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/venv/Lib/site-packages/_pytest/unraisableexception.py b/venv/Lib/site-packages/_pytest/unraisableexception.py new file mode 100644 index 0000000..0faca36 --- /dev/null +++ b/venv/Lib/site-packages/_pytest/unraisableexception.py @@ -0,0 +1,163 @@ +from __future__ import annotations + +import collections +from collections.abc import Callable +import functools +import gc +import sys +import traceback +from typing import NamedTuple +from typing import TYPE_CHECKING +import warnings + +from _pytest.config import Config +from _pytest.nodes import Item +from _pytest.stash import StashKey +from _pytest.tracemalloc import tracemalloc_message +import pytest + + +if TYPE_CHECKING: + pass + +if sys.version_info < (3, 11): + from exceptiongroup import ExceptionGroup + + +# This is a stash item and not a simple constant to allow pytester to override it. +gc_collect_iterations_key = StashKey[int]() + + +def gc_collect_harder(iterations: int) -> None: + for _ in range(iterations): + gc.collect() + + +class UnraisableMeta(NamedTuple): + msg: str + cause_msg: str + exc_value: BaseException | None + + +unraisable_exceptions: StashKey[collections.deque[UnraisableMeta | BaseException]] = ( + StashKey() +) + + +def collect_unraisable(config: Config) -> None: + pop_unraisable = config.stash[unraisable_exceptions].pop + errors: list[pytest.PytestUnraisableExceptionWarning | RuntimeError] = [] + meta = None + hook_error = None + try: + while True: + try: + meta = pop_unraisable() + except IndexError: + break + + if isinstance(meta, BaseException): + hook_error = RuntimeError("Failed to process unraisable exception") + hook_error.__cause__ = meta + errors.append(hook_error) + continue + + msg = meta.msg + try: + warnings.warn(pytest.PytestUnraisableExceptionWarning(msg)) + except pytest.PytestUnraisableExceptionWarning as e: + # This except happens when the warning is treated as an error (e.g. `-Werror`). + if meta.exc_value is not None: + # Exceptions have a better way to show the traceback, but + # warnings do not, so hide the traceback from the msg and + # set the cause so the traceback shows up in the right place. + e.args = (meta.cause_msg,) + e.__cause__ = meta.exc_value + errors.append(e) + + if len(errors) == 1: + raise errors[0] + if errors: + raise ExceptionGroup("multiple unraisable exception warnings", errors) + finally: + del errors, meta, hook_error + + +def cleanup( + *, config: Config, prev_hook: Callable[[sys.UnraisableHookArgs], object] +) -> None: + # A single collection doesn't necessarily collect everything. + # Constant determined experimentally by the Trio project. + gc_collect_iterations = config.stash.get(gc_collect_iterations_key, 5) + try: + try: + gc_collect_harder(gc_collect_iterations) + collect_unraisable(config) + finally: + sys.unraisablehook = prev_hook + finally: + del config.stash[unraisable_exceptions] + + +def unraisable_hook( + unraisable: sys.UnraisableHookArgs, + /, + *, + append: Callable[[UnraisableMeta | BaseException], object], +) -> None: + try: + # we need to compute these strings here as they might change after + # the unraisablehook finishes and before the metadata object is + # collected by a pytest hook + err_msg = ( + "Exception ignored in" if unraisable.err_msg is None else unraisable.err_msg + ) + summary = f"{err_msg}: {unraisable.object!r}" + traceback_message = "\n\n" + "".join( + traceback.format_exception( + unraisable.exc_type, + unraisable.exc_value, + unraisable.exc_traceback, + ) + ) + tracemalloc_tb = "\n" + tracemalloc_message(unraisable.object) + msg = summary + traceback_message + tracemalloc_tb + cause_msg = summary + tracemalloc_tb + + append( + UnraisableMeta( + msg=msg, + cause_msg=cause_msg, + exc_value=unraisable.exc_value, + ) + ) + except BaseException as e: + append(e) + # Raising this will cause the exception to be logged twice, once in our + # collect_unraisable and once by the unraisablehook calling machinery + # which is fine - this should never happen anyway and if it does + # it should probably be reported as a pytest bug. + raise + + +def pytest_configure(config: Config) -> None: + prev_hook = sys.unraisablehook + deque: collections.deque[UnraisableMeta | BaseException] = collections.deque() + config.stash[unraisable_exceptions] = deque + config.add_cleanup(functools.partial(cleanup, config=config, prev_hook=prev_hook)) + sys.unraisablehook = functools.partial(unraisable_hook, append=deque.append) + + +@pytest.hookimpl(trylast=True) +def pytest_runtest_setup(item: Item) -> None: + collect_unraisable(item.config) + + +@pytest.hookimpl(trylast=True) +def pytest_runtest_call(item: Item) -> None: + collect_unraisable(item.config) + + +@pytest.hookimpl(trylast=True) +def pytest_runtest_teardown(item: Item) -> None: + collect_unraisable(item.config) diff --git a/venv/Lib/site-packages/_pytest/warning_types.py b/venv/Lib/site-packages/_pytest/warning_types.py new file mode 100644 index 0000000..93071b4 --- /dev/null +++ b/venv/Lib/site-packages/_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/venv/Lib/site-packages/_pytest/warnings.py b/venv/Lib/site-packages/_pytest/warnings.py new file mode 100644 index 0000000..1dbf002 --- /dev/null +++ b/venv/Lib/site-packages/_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/venv/Lib/site-packages/colorama-0.4.6.dist-info/INSTALLER b/venv/Lib/site-packages/colorama-0.4.6.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/venv/Lib/site-packages/colorama-0.4.6.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/Lib/site-packages/colorama-0.4.6.dist-info/METADATA b/venv/Lib/site-packages/colorama-0.4.6.dist-info/METADATA new file mode 100644 index 0000000..a1b5c57 --- /dev/null +++ b/venv/Lib/site-packages/colorama-0.4.6.dist-info/METADATA @@ -0,0 +1,441 @@ +Metadata-Version: 2.1 +Name: colorama +Version: 0.4.6 +Summary: Cross-platform colored terminal text. +Project-URL: Homepage, https://github.com/tartley/colorama +Author-email: Jonathan Hartley +License-File: LICENSE.txt +Keywords: ansi,color,colour,crossplatform,terminal,text,windows,xplatform +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Console +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Topic :: Terminals +Requires-Python: !=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7 +Description-Content-Type: text/x-rst + +.. image:: https://img.shields.io/pypi/v/colorama.svg + :target: https://pypi.org/project/colorama/ + :alt: Latest Version + +.. image:: https://img.shields.io/pypi/pyversions/colorama.svg + :target: https://pypi.org/project/colorama/ + :alt: Supported Python versions + +.. image:: https://github.com/tartley/colorama/actions/workflows/test.yml/badge.svg + :target: https://github.com/tartley/colorama/actions/workflows/test.yml + :alt: Build Status + +Colorama +======== + +Makes ANSI escape character sequences (for producing colored terminal text and +cursor positioning) work under MS Windows. + +.. |donate| image:: https://www.paypalobjects.com/en_US/i/btn/btn_donate_SM.gif + :target: https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=2MZ9D2GMLYCUJ&item_name=Colorama¤cy_code=USD + :alt: Donate with Paypal + +`PyPI for releases `_ | +`Github for source `_ | +`Colorama for enterprise on Tidelift `_ + +If you find Colorama useful, please |donate| to the authors. Thank you! + +Installation +------------ + +Tested on CPython 2.7, 3.7, 3.8, 3.9 and 3.10 and Pypy 2.7 and 3.8. + +No requirements other than the standard library. + +.. code-block:: bash + + pip install colorama + # or + conda install -c anaconda colorama + +Description +----------- + +ANSI escape character sequences have long been used to produce colored terminal +text and cursor positioning on Unix and Macs. Colorama makes this work on +Windows, too, by wrapping ``stdout``, stripping ANSI sequences it finds (which +would appear as gobbledygook in the output), and converting them into the +appropriate win32 calls to modify the state of the terminal. On other platforms, +Colorama does nothing. + +This has the upshot of providing a simple cross-platform API for printing +colored terminal text from Python, and has the happy side-effect that existing +applications or libraries which use ANSI sequences to produce colored output on +Linux or Macs can now also work on Windows, simply by calling +``colorama.just_fix_windows_console()`` (since v0.4.6) or ``colorama.init()`` +(all versions, but may have other side-effects – see below). + +An alternative approach is to install ``ansi.sys`` on Windows machines, which +provides the same behaviour for all applications running in terminals. Colorama +is intended for situations where that isn't easy (e.g., maybe your app doesn't +have an installer.) + +Demo scripts in the source code repository print some colored text using +ANSI sequences. Compare their output under Gnome-terminal's built in ANSI +handling, versus on Windows Command-Prompt using Colorama: + +.. image:: https://github.com/tartley/colorama/raw/master/screenshots/ubuntu-demo.png + :width: 661 + :height: 357 + :alt: ANSI sequences on Ubuntu under gnome-terminal. + +.. image:: https://github.com/tartley/colorama/raw/master/screenshots/windows-demo.png + :width: 668 + :height: 325 + :alt: Same ANSI sequences on Windows, using Colorama. + +These screenshots show that, on Windows, Colorama does not support ANSI 'dim +text'; it looks the same as 'normal text'. + +Usage +----- + +Initialisation +.............. + +If the only thing you want from Colorama is to get ANSI escapes to work on +Windows, then run: + +.. code-block:: python + + from colorama import just_fix_windows_console + just_fix_windows_console() + +If you're on a recent version of Windows 10 or better, and your stdout/stderr +are pointing to a Windows console, then this will flip the magic configuration +switch to enable Windows' built-in ANSI support. + +If you're on an older version of Windows, and your stdout/stderr are pointing to +a Windows console, then this will wrap ``sys.stdout`` and/or ``sys.stderr`` in a +magic file object that intercepts ANSI escape sequences and issues the +appropriate Win32 calls to emulate them. + +In all other circumstances, it does nothing whatsoever. Basically the idea is +that this makes Windows act like Unix with respect to ANSI escape handling. + +It's safe to call this function multiple times. It's safe to call this function +on non-Windows platforms, but it won't do anything. It's safe to call this +function when one or both of your stdout/stderr are redirected to a file – it +won't do anything to those streams. + +Alternatively, you can use the older interface with more features (but also more +potential footguns): + +.. code-block:: python + + from colorama import init + init() + +This does the same thing as ``just_fix_windows_console``, except for the +following differences: + +- It's not safe to call ``init`` multiple times; you can end up with multiple + layers of wrapping and broken ANSI support. + +- Colorama will apply a heuristic to guess whether stdout/stderr support ANSI, + and if it thinks they don't, then it will wrap ``sys.stdout`` and + ``sys.stderr`` in a magic file object that strips out ANSI escape sequences + before printing them. This happens on all platforms, and can be convenient if + you want to write your code to emit ANSI escape sequences unconditionally, and + let Colorama decide whether they should actually be output. But note that + Colorama's heuristic is not particularly clever. + +- ``init`` also accepts explicit keyword args to enable/disable various + functionality – see below. + +To stop using Colorama before your program exits, simply call ``deinit()``. +This will restore ``stdout`` and ``stderr`` to their original values, so that +Colorama is disabled. To resume using Colorama again, call ``reinit()``; it is +cheaper than calling ``init()`` again (but does the same thing). + +Most users should depend on ``colorama >= 0.4.6``, and use +``just_fix_windows_console``. The old ``init`` interface will be supported +indefinitely for backwards compatibility, but we don't plan to fix any issues +with it, also for backwards compatibility. + +Colored Output +.............. + +Cross-platform printing of colored text can then be done using Colorama's +constant shorthand for ANSI escape sequences. These are deliberately +rudimentary, see below. + +.. code-block:: python + + from colorama import Fore, Back, Style + print(Fore.RED + 'some red text') + print(Back.GREEN + 'and with a green background') + print(Style.DIM + 'and in dim text') + print(Style.RESET_ALL) + print('back to normal now') + +...or simply by manually printing ANSI sequences from your own code: + +.. code-block:: python + + print('\033[31m' + 'some red text') + print('\033[39m') # and reset to default color + +...or, Colorama can be used in conjunction with existing ANSI libraries +such as the venerable `Termcolor `_ +the fabulous `Blessings `_, +or the incredible `_Rich `_. + +If you wish Colorama's Fore, Back and Style constants were more capable, +then consider using one of the above highly capable libraries to generate +colors, etc, and use Colorama just for its primary purpose: to convert +those ANSI sequences to also work on Windows: + +SIMILARLY, do not send PRs adding the generation of new ANSI types to Colorama. +We are only interested in converting ANSI codes to win32 API calls, not +shortcuts like the above to generate ANSI characters. + +.. code-block:: python + + from colorama import just_fix_windows_console + from termcolor import colored + + # use Colorama to make Termcolor work on Windows too + just_fix_windows_console() + + # then use Termcolor for all colored text output + print(colored('Hello, World!', 'green', 'on_red')) + +Available formatting constants are:: + + Fore: BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE, RESET. + Back: BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE, RESET. + Style: DIM, NORMAL, BRIGHT, RESET_ALL + +``Style.RESET_ALL`` resets foreground, background, and brightness. Colorama will +perform this reset automatically on program exit. + +These are fairly well supported, but not part of the standard:: + + Fore: LIGHTBLACK_EX, LIGHTRED_EX, LIGHTGREEN_EX, LIGHTYELLOW_EX, LIGHTBLUE_EX, LIGHTMAGENTA_EX, LIGHTCYAN_EX, LIGHTWHITE_EX + Back: LIGHTBLACK_EX, LIGHTRED_EX, LIGHTGREEN_EX, LIGHTYELLOW_EX, LIGHTBLUE_EX, LIGHTMAGENTA_EX, LIGHTCYAN_EX, LIGHTWHITE_EX + +Cursor Positioning +.................. + +ANSI codes to reposition the cursor are supported. See ``demos/demo06.py`` for +an example of how to generate them. + +Init Keyword Args +................. + +``init()`` accepts some ``**kwargs`` to override default behaviour. + +init(autoreset=False): + If you find yourself repeatedly sending reset sequences to turn off color + changes at the end of every print, then ``init(autoreset=True)`` will + automate that: + + .. code-block:: python + + from colorama import init + init(autoreset=True) + print(Fore.RED + 'some red text') + print('automatically back to default color again') + +init(strip=None): + Pass ``True`` or ``False`` to override whether ANSI codes should be + stripped from the output. The default behaviour is to strip if on Windows + or if output is redirected (not a tty). + +init(convert=None): + Pass ``True`` or ``False`` to override whether to convert ANSI codes in the + output into win32 calls. The default behaviour is to convert if on Windows + and output is to a tty (terminal). + +init(wrap=True): + On Windows, Colorama works by replacing ``sys.stdout`` and ``sys.stderr`` + with proxy objects, which override the ``.write()`` method to do their work. + If this wrapping causes you problems, then this can be disabled by passing + ``init(wrap=False)``. The default behaviour is to wrap if ``autoreset`` or + ``strip`` or ``convert`` are True. + + When wrapping is disabled, colored printing on non-Windows platforms will + continue to work as normal. To do cross-platform colored output, you can + use Colorama's ``AnsiToWin32`` proxy directly: + + .. code-block:: python + + import sys + from colorama import init, AnsiToWin32 + init(wrap=False) + stream = AnsiToWin32(sys.stderr).stream + + # Python 2 + print >>stream, Fore.BLUE + 'blue text on stderr' + + # Python 3 + print(Fore.BLUE + 'blue text on stderr', file=stream) + +Recognised ANSI Sequences +......................... + +ANSI sequences generally take the form:: + + ESC [ ; ... + +Where ```` is an integer, and ```` is a single letter. Zero or +more params are passed to a ````. If no params are passed, it is +generally synonymous with passing a single zero. No spaces exist in the +sequence; they have been inserted here simply to read more easily. + +The only ANSI sequences that Colorama converts into win32 calls are:: + + ESC [ 0 m # reset all (colors and brightness) + ESC [ 1 m # bright + ESC [ 2 m # dim (looks same as normal brightness) + ESC [ 22 m # normal brightness + + # FOREGROUND: + ESC [ 30 m # black + ESC [ 31 m # red + ESC [ 32 m # green + ESC [ 33 m # yellow + ESC [ 34 m # blue + ESC [ 35 m # magenta + ESC [ 36 m # cyan + ESC [ 37 m # white + ESC [ 39 m # reset + + # BACKGROUND + ESC [ 40 m # black + ESC [ 41 m # red + ESC [ 42 m # green + ESC [ 43 m # yellow + ESC [ 44 m # blue + ESC [ 45 m # magenta + ESC [ 46 m # cyan + ESC [ 47 m # white + ESC [ 49 m # reset + + # cursor positioning + ESC [ y;x H # position cursor at x across, y down + ESC [ y;x f # position cursor at x across, y down + ESC [ n A # move cursor n lines up + ESC [ n B # move cursor n lines down + ESC [ n C # move cursor n characters forward + ESC [ n D # move cursor n characters backward + + # clear the screen + ESC [ mode J # clear the screen + + # clear the line + ESC [ mode K # clear the line + +Multiple numeric params to the ``'m'`` command can be combined into a single +sequence:: + + ESC [ 36 ; 45 ; 1 m # bright cyan text on magenta background + +All other ANSI sequences of the form ``ESC [ ; ... `` +are silently stripped from the output on Windows. + +Any other form of ANSI sequence, such as single-character codes or alternative +initial characters, are not recognised or stripped. It would be cool to add +them though. Let me know if it would be useful for you, via the Issues on +GitHub. + +Status & Known Problems +----------------------- + +I've personally only tested it on Windows XP (CMD, Console2), Ubuntu +(gnome-terminal, xterm), and OS X. + +Some valid ANSI sequences aren't recognised. + +If you're hacking on the code, see `README-hacking.md`_. ESPECIALLY, see the +explanation there of why we do not want PRs that allow Colorama to generate new +types of ANSI codes. + +See outstanding issues and wish-list: +https://github.com/tartley/colorama/issues + +If anything doesn't work for you, or doesn't do what you expected or hoped for, +I'd love to hear about it on that issues list, would be delighted by patches, +and would be happy to grant commit access to anyone who submits a working patch +or two. + +.. _README-hacking.md: README-hacking.md + +License +------- + +Copyright Jonathan Hartley & Arnon Yaari, 2013-2020. BSD 3-Clause license; see +LICENSE file. + +Professional support +-------------------- + +.. |tideliftlogo| image:: https://cdn2.hubspot.net/hubfs/4008838/website/logos/logos_for_download/Tidelift_primary-shorthand-logo.png + :alt: Tidelift + :target: https://tidelift.com/subscription/pkg/pypi-colorama?utm_source=pypi-colorama&utm_medium=referral&utm_campaign=readme + +.. list-table:: + :widths: 10 100 + + * - |tideliftlogo| + - Professional support for colorama is available as part of the + `Tidelift Subscription`_. + Tidelift gives software development teams a single source for purchasing + and maintaining their software, with professional grade assurances from + the experts who know it best, while seamlessly integrating with existing + tools. + +.. _Tidelift Subscription: https://tidelift.com/subscription/pkg/pypi-colorama?utm_source=pypi-colorama&utm_medium=referral&utm_campaign=readme + +Thanks +------ + +See the CHANGELOG for more thanks! + +* Marc Schlaich (schlamar) for a ``setup.py`` fix for Python2.5. +* Marc Abramowitz, reported & fixed a crash on exit with closed ``stdout``, + providing a solution to issue #7's setuptools/distutils debate, + and other fixes. +* User 'eryksun', for guidance on correctly instantiating ``ctypes.windll``. +* Matthew McCormick for politely pointing out a longstanding crash on non-Win. +* Ben Hoyt, for a magnificent fix under 64-bit Windows. +* Jesse at Empty Square for submitting a fix for examples in the README. +* User 'jamessp', an observant documentation fix for cursor positioning. +* User 'vaal1239', Dave Mckee & Lackner Kristof for a tiny but much-needed Win7 + fix. +* Julien Stuyck, for wisely suggesting Python3 compatible updates to README. +* Daniel Griffith for multiple fabulous patches. +* Oscar Lesta for a valuable fix to stop ANSI chars being sent to non-tty + output. +* Roger Binns, for many suggestions, valuable feedback, & bug reports. +* Tim Golden for thought and much appreciated feedback on the initial idea. +* User 'Zearin' for updates to the README file. +* John Szakmeister for adding support for light colors +* Charles Merriam for adding documentation to demos +* Jurko for a fix on 64-bit Windows CPython2.5 w/o ctypes +* Florian Bruhin for a fix when stdout or stderr are None +* Thomas Weininger for fixing ValueError on Windows +* Remi Rampin for better Github integration and fixes to the README file +* Simeon Visser for closing a file handle using 'with' and updating classifiers + to include Python 3.3 and 3.4 +* Andy Neff for fixing RESET of LIGHT_EX colors. +* Jonathan Hartley for the initial idea and implementation. diff --git a/venv/Lib/site-packages/colorama-0.4.6.dist-info/RECORD b/venv/Lib/site-packages/colorama-0.4.6.dist-info/RECORD new file mode 100644 index 0000000..cd6b130 --- /dev/null +++ b/venv/Lib/site-packages/colorama-0.4.6.dist-info/RECORD @@ -0,0 +1,31 @@ +colorama-0.4.6.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +colorama-0.4.6.dist-info/METADATA,sha256=e67SnrUMOym9sz_4TjF3vxvAV4T3aF7NyqRHHH3YEMw,17158 +colorama-0.4.6.dist-info/RECORD,, +colorama-0.4.6.dist-info/WHEEL,sha256=cdcF4Fbd0FPtw2EMIOwH-3rSOTUdTCeOSXRMD1iLUb8,105 +colorama-0.4.6.dist-info/licenses/LICENSE.txt,sha256=ysNcAmhuXQSlpxQL-zs25zrtSWZW6JEQLkKIhteTAxg,1491 +colorama/__init__.py,sha256=wePQA4U20tKgYARySLEC047ucNX-g8pRLpYBuiHlLb8,266 +colorama/__pycache__/__init__.cpython-312.pyc,, +colorama/__pycache__/ansi.cpython-312.pyc,, +colorama/__pycache__/ansitowin32.cpython-312.pyc,, +colorama/__pycache__/initialise.cpython-312.pyc,, +colorama/__pycache__/win32.cpython-312.pyc,, +colorama/__pycache__/winterm.cpython-312.pyc,, +colorama/ansi.py,sha256=Top4EeEuaQdBWdteKMEcGOTeKeF19Q-Wo_6_Cj5kOzQ,2522 +colorama/ansitowin32.py,sha256=vPNYa3OZbxjbuFyaVo0Tmhmy1FZ1lKMWCnT7odXpItk,11128 +colorama/initialise.py,sha256=-hIny86ClXo39ixh5iSCfUIa2f_h_bgKRDW7gqs-KLU,3325 +colorama/tests/__init__.py,sha256=MkgPAEzGQd-Rq0w0PZXSX2LadRWhUECcisJY8lSrm4Q,75 +colorama/tests/__pycache__/__init__.cpython-312.pyc,, +colorama/tests/__pycache__/ansi_test.cpython-312.pyc,, +colorama/tests/__pycache__/ansitowin32_test.cpython-312.pyc,, +colorama/tests/__pycache__/initialise_test.cpython-312.pyc,, +colorama/tests/__pycache__/isatty_test.cpython-312.pyc,, +colorama/tests/__pycache__/utils.cpython-312.pyc,, +colorama/tests/__pycache__/winterm_test.cpython-312.pyc,, +colorama/tests/ansi_test.py,sha256=FeViDrUINIZcr505PAxvU4AjXz1asEiALs9GXMhwRaE,2839 +colorama/tests/ansitowin32_test.py,sha256=RN7AIhMJ5EqDsYaCjVo-o4u8JzDD4ukJbmevWKS70rY,10678 +colorama/tests/initialise_test.py,sha256=BbPy-XfyHwJ6zKozuQOvNvQZzsx9vdb_0bYXn7hsBTc,6741 +colorama/tests/isatty_test.py,sha256=Pg26LRpv0yQDB5Ac-sxgVXG7hsA1NYvapFgApZfYzZg,1866 +colorama/tests/utils.py,sha256=1IIRylG39z5-dzq09R_ngufxyPZxgldNbrxKxUGwGKE,1079 +colorama/tests/winterm_test.py,sha256=qoWFPEjym5gm2RuMwpf3pOis3a5r_PJZFCzK254JL8A,3709 +colorama/win32.py,sha256=YQOKwMTwtGBbsY4dL5HYTvwTeP9wIQra5MvPNddpxZs,6181 +colorama/winterm.py,sha256=XCQFDHjPi6AHYNdZwy0tA02H-Jh48Jp-HvCjeLeLp3U,7134 diff --git a/venv/Lib/site-packages/colorama-0.4.6.dist-info/WHEEL b/venv/Lib/site-packages/colorama-0.4.6.dist-info/WHEEL new file mode 100644 index 0000000..d79189f --- /dev/null +++ b/venv/Lib/site-packages/colorama-0.4.6.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: hatchling 1.11.1 +Root-Is-Purelib: true +Tag: py2-none-any +Tag: py3-none-any diff --git a/venv/Lib/site-packages/colorama-0.4.6.dist-info/licenses/LICENSE.txt b/venv/Lib/site-packages/colorama-0.4.6.dist-info/licenses/LICENSE.txt new file mode 100644 index 0000000..3105888 --- /dev/null +++ b/venv/Lib/site-packages/colorama-0.4.6.dist-info/licenses/LICENSE.txt @@ -0,0 +1,27 @@ +Copyright (c) 2010 Jonathan Hartley +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the copyright holders, nor those of its contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/venv/Lib/site-packages/colorama/__init__.py b/venv/Lib/site-packages/colorama/__init__.py new file mode 100644 index 0000000..383101c --- /dev/null +++ b/venv/Lib/site-packages/colorama/__init__.py @@ -0,0 +1,7 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. +from .initialise import init, deinit, reinit, colorama_text, just_fix_windows_console +from .ansi import Fore, Back, Style, Cursor +from .ansitowin32 import AnsiToWin32 + +__version__ = '0.4.6' + diff --git a/venv/Lib/site-packages/colorama/__pycache__/__init__.cpython-312.pyc b/venv/Lib/site-packages/colorama/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..dab934a58a9cdcc4c9aa7f1b966329a8cb7c1390 GIT binary patch literal 480 zcmXv~!D<^Z5S6^z_1bIuP*P~`xh%%6u|p0a6q1J0Ln#5*go*{BdR3!ZS{Z5Wcyr4~ zgnUE4rN`2XptR6K>8;?KFRc{Ub9giJ9-4WY&+T>-xVZZ}YH! zK@J6w5JOqRH5uSQB8+5+L+G`dh-4ktWsGClzzx~NZlL1>k;oQqxfe`U0DvS~=MHanOnpt#Cr*j*Q z%StdCy;y3abP8>9Rv7+PUGZWxya@;WqyF)mv}2oo8K;69rUU0gBM+g6$V1(MMby33 zvF{rm{E2kpmBcHha$ZJ5-FjdMSut(6DhT-mHjV<@@x@6t7z{4oWs;VxN2fGrvdR`! zucT()n=N_H1TR>&V#O+Z&EIEkDC;fUN9c^1Y=44hgm}Az^cU4OK9wq83iezdxE=n6 g=Ba}}0fg`un0y73@1XkybiaYaADzdyiGJkNe??)3`Tzg` literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/colorama/__pycache__/ansi.cpython-312.pyc b/venv/Lib/site-packages/colorama/__pycache__/ansi.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9b1e8af69dcf87bc49351728639a05af9067db13 GIT binary patch literal 3938 zcmcgu&2JmW6`%d$@|zOrFUe^`$4*lwwj`%Tn<_;eilk)46eWmK9B<@qH{6Z1Hbttl zE2mTyfChAkVILGYg>^JgA7c2@|DzWRK>}ysTTXkkL!NwT-*)<@*Ic`{3Yu!2auxKi+cl%=jD^dVHJM&>-HpldajQ1= zseRSnuuR(+tJBqSl*Vyh-Eu0%hNa`A;Vtp_M^G+56V!^(@d7Iu3WlzA8sH9b0sYVI z1eB0%@}Tp_7Ic{cVwq&b5);<%i%iOuXPM-z(HjB7Yo&#Z3Rrd73T*2*=jc5S6-f4t(@FmA)BTs2lLXN7xitQ+G-&9TQe z8Z3sB>uz1gcsUd)6@}6|4A3NxBk?a&yGyr6Zk*dI??=)P<#Y==$bh#ZD7d%3ghs!& zzE|4>eRFXX|9YNoiEqD~0axeCIVMdNir}i^+HTcyxUUpQO<2gWT%Bv_Fvtlg&Jh4^ z+wP$olY7d3K45~X7YFRak7A2w3 z2*OJ?;sQ5<9)hX+BNO&5z}==J(1$zLw?0U{uglFA+IWXp1daj%M}YtbfYcB(5({O4 z4>|O4AbLR<&$sp#|IzJE)JNYux%?YKJ`=WuD?M`e76xK>X-ifL2~%m zo_u@u2O@^-b!H$H!j1E8jK0KK9nG@_6Xzp1fz?oBPM>U!QvC zo1x!5Rbf0QM=2vSSPo6mbr#lj{|JM2RM-E!X;l3l`Z_Fl-{%vzksb#H2i$N6qqaj@ zqN@1Q@}3GPdtW!{TE7NW-^Q!3^#a@jswqM(LoGqAKrKTZFoSS6z+KP?WI`;=qp?Yy zI<0i)!v+4E%)wCfHsI{`T7(`52SkcXl^Y)PhYBInv0O)5~Q~bsjX@HB*v5LOT_Bm5CTNB9$h0Z_Vs=s@t8GF1d3F=?@oXUfcCp-^Ih zONC-_{vwm7ipvG2&Shr`rKK#BbCHHb-KgSP|2e9!K+lVlt)XNB{?n@LHmVjsB|N+zJbW;EP)I4G z9wdHXY7x21r2On03zX&;=dwi>;P(WUx^C7hx=sTi(>MY?vH6>cKe+kRmcK7qp#Jd{tK#~P5ZqqQY`zhE zeJ11a0Lck#U<&t`1#S>gOlQ&13@^_(Oze?Ub-c-H|rEb1*J-}o2Z?y|FtpET3 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/colorama/__pycache__/ansitowin32.cpython-312.pyc b/venv/Lib/site-packages/colorama/__pycache__/ansitowin32.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..abe503b3f5c7ac9f22cd0db4202e55be44d696cb GIT binary patch literal 16409 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/colorama/__pycache__/initialise.cpython-312.pyc b/venv/Lib/site-packages/colorama/__pycache__/initialise.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c48ca4d7a4fe9f02a4ac609e29a4a1adf7e9c48e GIT binary patch literal 3538 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 F{y#qO6fgh) literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/colorama/__pycache__/winterm.cpython-312.pyc b/venv/Lib/site-packages/colorama/__pycache__/winterm.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ee0856621f37e49f5c23d84f437c73fe25658ce3 GIT binary patch literal 9076 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 LRF+H_7 zCOJPRzbG*`QNJX$xTIJ=K0Y%qvm`!Vub}c5hfQvNN@-52T@fqLCPpAG1~EP|Gcqz3 HF#}lu>6bD& literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/colorama/tests/__pycache__/ansi_test.cpython-312.pyc b/venv/Lib/site-packages/colorama/tests/__pycache__/ansi_test.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..393260e2c128421815171ea99268e4aaae89fa9a GIT binary patch literal 5455 zcmeHL-ES0C6u+}Gvpem!g;FZrZq*{7F7%s~vW1|9E~QcyQ%aC+lg-c>aogQ)y)y;3 z8q)_%Fv0j_nkFRF#Ke;N2lxm0=F@5kc%#vT2g6&_^2CdtbMJn4z|5nj_u-s-&%M8M zKWBdD-k<90YYC*U|L#lf^AqwI3x%ZSGh25dvqTJHNGTFW?MX>#Pu!E1nciD)S<-Po*jq~FLM~qKA;ZM*-Xez2^sLFk9&a#r z8QRR7giy^xOnEKeTq0R1D}`#FU^y1@Fm2ql>@&J$G9|4iXP%GnGx znR?02rp%DU{DZSray)Y-IeWBkD({dhDPPz+44EaEKwL6N++#?$NL)5NAQeLfsTvAM zuc3nUWxXLE)3{3*!c@Try26FTiERA^HO5Hjs8Ez=tCMNnHaqnx-7wSH$+>K&ty?pl*XNUlnM%%@ zlQ+!S8DNtbaw?O`P(7`8+pxq|w+{Q4z--rCmZ_F$U!GfNDC|HN zFE>EH)-e!^WW!5p>K|&soEE%0^lj63t>3i%($?|)%(^!4NUL+Gc%Thz0g_bgC9XB! z%Xc8NL~M{E+4)@{BvH)S#r-R*E$(iX(0agzROS~;m&nlMb-EjDv=Ib1!y*dv9yB+B z5Nk=Bv4v)vIvvW~oLy)v&bwg3kIq78i{!WF_B-;OTCTpVrTf5GF)17ppVScKo_j!x z)0!?$0dVJ_R_sG~xKg?dYb&>R9wrbez?Xjtrk0=PnY__nDs;!4(!n z9e0Vv23{vzW4SI5y&T2TE?&xaRjS4=u_S!)uA#W?U%v;L4kMUB+os8D^H5v}mi_gs zD+4fd>q8*kMZ)>5C8xFA-FvTfxpgJJuJx4LT9#W@2G+H%a+_1T;Bs(fdR^-+Zx>h& zth~Ffb(h=PmfKbq*0sKJn^QZdJ_@!xU5ima3>?V?j;zACkC*v;7zpJ8p;Z|9u?nkm z+m2kIV--dpsc3sR7dX5M#ot0i7| z&c&w`7w+)t_VeS!t6@QmVL^;xL5yKRjA22HVL^;xL5yL+t6|_(Z*Zcw2k=~j1FpdX z*WiL{@WC}W;TpVfjr>YqhCxyc;VH&3^i>q?DE6bk*n%EFfl&fIgyMA+Z=ir1JfR^J zc(Cir z)qKi%?~`@yL0^d;X z6YhpTJy3WpBwXNS*uvJg$flyGCpPOm>i*5zCN;9z(WDM+b;xR;z##+&fG=~2L})jE z>R{UZY|_Scq4)$r6>OyF@U$cTF&My8zQTSq+L@d9UdLaV_$vv;IG6I*5xmxkarI+v z00q8v@Bu&5PKxQCqHlu@AEc~d5F4^2NsmeGALPJaOkTNkpl;z3{Uf_*Kz9Kf56lrImN|55B;GB>(^b literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/colorama/tests/__pycache__/ansitowin32_test.cpython-312.pyc b/venv/Lib/site-packages/colorama/tests/__pycache__/ansitowin32_test.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..83ba4f6e8ac11d2ec1efb58c5dfb6ecc149ac630 GIT binary patch literal 18091 zcmc&cTW}LsmfdQ#)RJ1VWsI@>Pz*SLFgAV?h z60b^B4^}7KgKqL{jn^cW3@(8@C)nb(iKT-}Y05 zrOTrAg6q{a6eV&dD$2fBm^4kDtvXw^l8SPzlxQQfW#TqvZZA?751ppYFfUT4=|T&f z8zq?IuG0BJ%R#lJulhcY?K8>T;glE~KC$