From 19697ea1928b794dea9d38fc2cd0347767c0c816 Mon Sep 17 00:00:00 2001 From: Chris Date: Wed, 21 Jul 2021 21:33:05 +0200 Subject: [PATCH] Initial commit --- .idea/.gitignore | 8 + .idea/OpenHome.iml | 10 + .../inspectionProfiles/profiles_settings.xml | 6 + .idea/misc.xml | 4 + .idea/modules.xml | 8 + __pycache__/cun.cpython-39.pyc | Bin 0 -> 8134 bytes cun.py | 225 + main.py | 35 + .../site/python3.9/greenlet/greenlet.h | 146 + .../Flask-2.0.1.dist-info/INSTALLER | 1 + .../Flask-2.0.1.dist-info/LICENSE.rst | 28 + .../Flask-2.0.1.dist-info/METADATA | 124 + .../Flask-2.0.1.dist-info/RECORD | 51 + .../site-packages/Flask-2.0.1.dist-info/WHEEL | 5 + .../Flask-2.0.1.dist-info/entry_points.txt | 3 + .../Flask-2.0.1.dist-info/top_level.txt | 1 + .../INSTALLER | 1 + .../LICENSE.rst | 28 + .../Flask_SQLAlchemy-2.5.1.dist-info/METADATA | 94 + .../Flask_SQLAlchemy-2.5.1.dist-info/RECORD | 14 + .../Flask_SQLAlchemy-2.5.1.dist-info/WHEEL | 6 + .../top_level.txt | 1 + .../Jinja2-3.0.1.dist-info/INSTALLER | 1 + .../Jinja2-3.0.1.dist-info/LICENSE.rst | 28 + .../Jinja2-3.0.1.dist-info/METADATA | 112 + .../Jinja2-3.0.1.dist-info/RECORD | 58 + .../Jinja2-3.0.1.dist-info/WHEEL | 5 + .../Jinja2-3.0.1.dist-info/entry_points.txt | 3 + .../Jinja2-3.0.1.dist-info/top_level.txt | 1 + .../MarkupSafe-2.0.1.dist-info/INSTALLER | 1 + .../MarkupSafe-2.0.1.dist-info/LICENSE.rst | 28 + .../MarkupSafe-2.0.1.dist-info/METADATA | 100 + .../MarkupSafe-2.0.1.dist-info/RECORD | 13 + .../MarkupSafe-2.0.1.dist-info/WHEEL | 5 + .../MarkupSafe-2.0.1.dist-info/top_level.txt | 1 + .../SQLAlchemy-1.4.20.dist-info/INSTALLER | 1 + .../SQLAlchemy-1.4.20.dist-info/LICENSE | 19 + .../SQLAlchemy-1.4.20.dist-info/METADATA | 234 + .../SQLAlchemy-1.4.20.dist-info/RECORD | 479 + .../SQLAlchemy-1.4.20.dist-info/WHEEL | 5 + .../SQLAlchemy-1.4.20.dist-info/top_level.txt | 1 + .../Werkzeug-2.0.1.dist-info/INSTALLER | 1 + .../Werkzeug-2.0.1.dist-info/LICENSE.rst | 28 + .../Werkzeug-2.0.1.dist-info/METADATA | 128 + .../Werkzeug-2.0.1.dist-info/RECORD | 111 + .../Werkzeug-2.0.1.dist-info/WHEEL | 5 + .../Werkzeug-2.0.1.dist-info/top_level.txt | 1 + .../__pycache__/pyparsing.cpython-39.pyc | Bin 0 -> 240369 bytes .../site-packages/_distutils_hack/__init__.py | 128 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 5100 bytes .../__pycache__/override.cpython-39.pyc | Bin 0 -> 231 bytes .../site-packages/_distutils_hack/override.py | 1 + venv/Lib/site-packages/_pytest/__init__.py | 8 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 285 bytes .../__pycache__/_argcomplete.cpython-39.pyc | Bin 0 -> 4033 bytes .../__pycache__/_version.cpython-39.pyc | Bin 0 -> 209 bytes .../__pycache__/cacheprovider.cpython-39.pyc | Bin 0 -> 18987 bytes .../__pycache__/capture.cpython-39.pyc | Bin 0 -> 32017 bytes .../_pytest/__pycache__/compat.cpython-39.pyc | Bin 0 -> 10792 bytes .../__pycache__/debugging.cpython-39.pyc | Bin 0 -> 11401 bytes .../__pycache__/deprecated.cpython-39.pyc | Bin 0 -> 2198 bytes .../__pycache__/doctest.cpython-39.pyc | Bin 0 -> 21362 bytes .../__pycache__/faulthandler.cpython-39.pyc | Bin 0 -> 3768 bytes .../__pycache__/fixtures.cpython-39.pyc | Bin 0 -> 45603 bytes .../__pycache__/freeze_support.cpython-39.pyc | Bin 0 -> 1418 bytes .../__pycache__/helpconfig.cpython-39.pyc | Bin 0 -> 7341 bytes .../__pycache__/hookspec.cpython-39.pyc | Bin 0 -> 32432 bytes .../__pycache__/junitxml.cpython-39.pyc | Bin 0 -> 21197 bytes .../__pycache__/logging.cpython-39.pyc | Bin 0 -> 26814 bytes .../_pytest/__pycache__/main.cpython-39.pyc | Bin 0 -> 22914 bytes .../__pycache__/monkeypatch.cpython-39.pyc | Bin 0 -> 11185 bytes .../_pytest/__pycache__/nodes.cpython-39.pyc | Bin 0 -> 18097 bytes .../_pytest/__pycache__/nose.cpython-39.pyc | Bin 0 -> 1391 bytes .../__pycache__/outcomes.cpython-39.pyc | Bin 0 -> 7397 bytes .../__pycache__/pastebin.cpython-39.pyc | Bin 0 -> 3548 bytes .../__pycache__/pathlib.cpython-39.pyc | Bin 0 -> 18178 bytes .../__pycache__/pytester.cpython-39.pyc | Bin 0 -> 67587 bytes .../pytester_assertions.cpython-39.pyc | Bin 0 -> 1544 bytes .../_pytest/__pycache__/python.cpython-39.pyc | Bin 0 -> 49503 bytes .../__pycache__/python_api.cpython-39.pyc | Bin 0 -> 26327 bytes .../__pycache__/recwarn.cpython-39.pyc | Bin 0 -> 9696 bytes .../__pycache__/reports.cpython-39.pyc | Bin 0 -> 16736 bytes .../_pytest/__pycache__/runner.cpython-39.pyc | Bin 0 -> 13613 bytes .../__pycache__/setuponly.cpython-39.pyc | Bin 0 -> 2846 bytes .../__pycache__/setupplan.cpython-39.pyc | Bin 0 -> 1358 bytes .../__pycache__/skipping.cpython-39.pyc | Bin 0 -> 8550 bytes .../__pycache__/stepwise.cpython-39.pyc | Bin 0 -> 3663 bytes .../_pytest/__pycache__/store.cpython-39.pyc | Bin 0 -> 4351 bytes .../__pycache__/terminal.cpython-39.pyc | Bin 0 -> 40798 bytes .../threadexception.cpython-39.pyc | Bin 0 -> 3256 bytes .../_pytest/__pycache__/timing.cpython-39.pyc | Bin 0 -> 543 bytes .../_pytest/__pycache__/tmpdir.cpython-39.pyc | Bin 0 -> 8406 bytes .../__pycache__/unittest.cpython-39.pyc | Bin 0 -> 11187 bytes .../unraisableexception.cpython-39.pyc | Bin 0 -> 3250 bytes .../__pycache__/warning_types.cpython-39.pyc | Bin 0 -> 4405 bytes .../__pycache__/warnings.cpython-39.pyc | Bin 0 -> 3896 bytes .../Lib/site-packages/_pytest/_argcomplete.py | 117 + .../site-packages/_pytest/_code/__init__.py | 22 + .../_code/__pycache__/__init__.cpython-39.pyc | Bin 0 -> 590 bytes .../_code/__pycache__/code.cpython-39.pyc | Bin 0 -> 37708 bytes .../_code/__pycache__/source.cpython-39.pyc | Bin 0 -> 6971 bytes venv/Lib/site-packages/_pytest/_code/code.py | 1259 +++ .../Lib/site-packages/_pytest/_code/source.py | 212 + .../Lib/site-packages/_pytest/_io/__init__.py | 8 + .../_io/__pycache__/__init__.cpython-39.pyc | Bin 0 -> 285 bytes .../_io/__pycache__/saferepr.cpython-39.pyc | Bin 0 -> 4377 bytes .../__pycache__/terminalwriter.cpython-39.pyc | Bin 0 -> 5926 bytes .../_io/__pycache__/wcwidth.cpython-39.pyc | Bin 0 -> 1271 bytes .../Lib/site-packages/_pytest/_io/saferepr.py | 129 + .../_pytest/_io/terminalwriter.py | 210 + venv/Lib/site-packages/_pytest/_io/wcwidth.py | 55 + venv/Lib/site-packages/_pytest/_version.py | 5 + .../_pytest/assertion/__init__.py | 179 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 6601 bytes .../__pycache__/rewrite.cpython-39.pyc | Bin 0 -> 32928 bytes .../__pycache__/truncate.cpython-39.pyc | Bin 0 -> 2862 bytes .../assertion/__pycache__/util.cpython-39.pyc | Bin 0 -> 12659 bytes .../_pytest/assertion/rewrite.py | 1125 +++ .../_pytest/assertion/truncate.py | 100 + .../site-packages/_pytest/assertion/util.py | 477 + .../site-packages/_pytest/cacheprovider.py | 575 ++ venv/Lib/site-packages/_pytest/capture.py | 967 ++ venv/Lib/site-packages/_pytest/compat.py | 400 + .../site-packages/_pytest/config/__init__.py | 1606 ++++ .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 44707 bytes .../__pycache__/argparsing.cpython-39.pyc | Bin 0 -> 17310 bytes .../__pycache__/exceptions.cpython-39.pyc | Bin 0 -> 666 bytes .../__pycache__/findpaths.cpython-39.pyc | Bin 0 -> 5818 bytes .../_pytest/config/argparsing.py | 522 + .../_pytest/config/exceptions.py | 11 + .../site-packages/_pytest/config/findpaths.py | 211 + venv/Lib/site-packages/_pytest/debugging.py | 388 + venv/Lib/site-packages/_pytest/deprecated.py | 87 + venv/Lib/site-packages/_pytest/doctest.py | 724 ++ .../Lib/site-packages/_pytest/faulthandler.py | 116 + venv/Lib/site-packages/_pytest/fixtures.py | 1680 ++++ .../site-packages/_pytest/freeze_support.py | 45 + venv/Lib/site-packages/_pytest/helpconfig.py | 261 + venv/Lib/site-packages/_pytest/hookspec.py | 891 ++ venv/Lib/site-packages/_pytest/junitxml.py | 700 ++ venv/Lib/site-packages/_pytest/logging.py | 821 ++ venv/Lib/site-packages/_pytest/main.py | 876 ++ .../site-packages/_pytest/mark/__init__.py | 282 + .../mark/__pycache__/__init__.cpython-39.pyc | Bin 0 -> 8670 bytes .../__pycache__/expression.cpython-39.pyc | Bin 0 -> 7316 bytes .../__pycache__/structures.cpython-39.pyc | Bin 0 -> 17316 bytes .../site-packages/_pytest/mark/expression.py | 221 + .../site-packages/_pytest/mark/structures.py | 559 ++ venv/Lib/site-packages/_pytest/monkeypatch.py | 379 + venv/Lib/site-packages/_pytest/nodes.py | 591 ++ venv/Lib/site-packages/_pytest/nose.py | 39 + venv/Lib/site-packages/_pytest/outcomes.py | 227 + venv/Lib/site-packages/_pytest/pastebin.py | 110 + venv/Lib/site-packages/_pytest/pathlib.py | 654 ++ venv/Lib/site-packages/_pytest/py.typed | 0 venv/Lib/site-packages/_pytest/pytester.py | 1922 ++++ .../_pytest/pytester_assertions.py | 66 + venv/Lib/site-packages/_pytest/python.py | 1689 ++++ venv/Lib/site-packages/_pytest/python_api.py | 786 ++ venv/Lib/site-packages/_pytest/recwarn.py | 296 + venv/Lib/site-packages/_pytest/reports.py | 572 ++ venv/Lib/site-packages/_pytest/runner.py | 462 + venv/Lib/site-packages/_pytest/setuponly.py | 94 + venv/Lib/site-packages/_pytest/setupplan.py | 40 + venv/Lib/site-packages/_pytest/skipping.py | 324 + venv/Lib/site-packages/_pytest/stepwise.py | 119 + venv/Lib/site-packages/_pytest/store.py | 125 + venv/Lib/site-packages/_pytest/terminal.py | 1405 +++ .../site-packages/_pytest/threadexception.py | 90 + venv/Lib/site-packages/_pytest/timing.py | 12 + venv/Lib/site-packages/_pytest/tmpdir.py | 253 + venv/Lib/site-packages/_pytest/unittest.py | 405 + .../_pytest/unraisableexception.py | 93 + .../site-packages/_pytest/warning_types.py | 132 + venv/Lib/site-packages/_pytest/warnings.py | 139 + .../atomicwrites-1.4.0.dist-info/INSTALLER | 1 + .../atomicwrites-1.4.0.dist-info/LICENSE | 19 + .../atomicwrites-1.4.0.dist-info/METADATA | 145 + .../atomicwrites-1.4.0.dist-info/RECORD | 8 + .../atomicwrites-1.4.0.dist-info/WHEEL | 6 + .../top_level.txt | 1 + .../site-packages/atomicwrites/__init__.py | 226 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 6582 bytes venv/Lib/site-packages/attr/__init__.py | 78 + venv/Lib/site-packages/attr/__init__.pyi | 475 + .../attr/__pycache__/__init__.cpython-39.pyc | Bin 0 -> 1697 bytes .../attr/__pycache__/_cmp.cpython-39.pyc | Bin 0 -> 3845 bytes .../attr/__pycache__/_compat.cpython-39.pyc | Bin 0 -> 6064 bytes .../attr/__pycache__/_config.cpython-39.pyc | Bin 0 -> 724 bytes .../attr/__pycache__/_funcs.cpython-39.pyc | Bin 0 -> 10015 bytes .../attr/__pycache__/_make.cpython-39.pyc | Bin 0 -> 72712 bytes .../attr/__pycache__/_next_gen.cpython-39.pyc | Bin 0 -> 3228 bytes .../__pycache__/_version_info.cpython-39.pyc | Bin 0 -> 2393 bytes .../__pycache__/converters.cpython-39.pyc | Bin 0 -> 2883 bytes .../__pycache__/exceptions.cpython-39.pyc | Bin 0 -> 3222 bytes .../attr/__pycache__/filters.cpython-39.pyc | Bin 0 -> 1724 bytes .../attr/__pycache__/setters.cpython-39.pyc | Bin 0 -> 1596 bytes .../__pycache__/validators.cpython-39.pyc | Bin 0 -> 11368 bytes venv/Lib/site-packages/attr/_cmp.py | 152 + venv/Lib/site-packages/attr/_cmp.pyi | 14 + venv/Lib/site-packages/attr/_compat.py | 242 + venv/Lib/site-packages/attr/_config.py | 23 + venv/Lib/site-packages/attr/_funcs.py | 395 + venv/Lib/site-packages/attr/_make.py | 3052 ++++++ venv/Lib/site-packages/attr/_next_gen.py | 158 + venv/Lib/site-packages/attr/_version_info.py | 85 + venv/Lib/site-packages/attr/_version_info.pyi | 9 + venv/Lib/site-packages/attr/converters.py | 111 + venv/Lib/site-packages/attr/converters.pyi | 13 + venv/Lib/site-packages/attr/exceptions.py | 92 + venv/Lib/site-packages/attr/exceptions.pyi | 18 + venv/Lib/site-packages/attr/filters.py | 52 + venv/Lib/site-packages/attr/filters.pyi | 7 + venv/Lib/site-packages/attr/py.typed | 0 venv/Lib/site-packages/attr/setters.py | 77 + venv/Lib/site-packages/attr/setters.pyi | 20 + venv/Lib/site-packages/attr/validators.py | 379 + venv/Lib/site-packages/attr/validators.pyi | 68 + .../attrs-21.2.0.dist-info/AUTHORS.rst | 11 + .../attrs-21.2.0.dist-info/INSTALLER | 1 + .../attrs-21.2.0.dist-info/LICENSE | 21 + .../attrs-21.2.0.dist-info/METADATA | 211 + .../attrs-21.2.0.dist-info/RECORD | 42 + .../attrs-21.2.0.dist-info/WHEEL | 6 + .../attrs-21.2.0.dist-info/top_level.txt | 1 + .../blinker-1.4-py3.9.egg-info/PKG-INFO | 105 + .../blinker-1.4-py3.9.egg-info/SOURCES.txt | 49 + .../dependency_links.txt | 1 + .../installed-files.txt | 12 + .../blinker-1.4-py3.9.egg-info/top_level.txt | 1 + venv/Lib/site-packages/blinker/__init__.py | 22 + venv/Lib/site-packages/blinker/_saferef.py | 234 + venv/Lib/site-packages/blinker/_utilities.py | 163 + venv/Lib/site-packages/blinker/base.py | 455 + .../click-8.0.1.dist-info/INSTALLER | 1 + .../click-8.0.1.dist-info/LICENSE.rst | 28 + .../click-8.0.1.dist-info/METADATA | 110 + .../click-8.0.1.dist-info/RECORD | 41 + .../site-packages/click-8.0.1.dist-info/WHEEL | 5 + .../click-8.0.1.dist-info/top_level.txt | 1 + venv/Lib/site-packages/click/__init__.py | 75 + .../click/__pycache__/__init__.cpython-39.pyc | Bin 0 -> 2670 bytes .../click/__pycache__/_compat.cpython-39.pyc | Bin 0 -> 16019 bytes .../__pycache__/_termui_impl.cpython-39.pyc | Bin 0 -> 15945 bytes .../__pycache__/_textwrap.cpython-39.pyc | Bin 0 -> 1512 bytes .../__pycache__/_unicodefun.cpython-39.pyc | Bin 0 -> 2307 bytes .../__pycache__/_winconsole.cpython-39.pyc | Bin 0 -> 7763 bytes .../click/__pycache__/core.cpython-39.pyc | Bin 0 -> 88312 bytes .../__pycache__/decorators.cpython-39.pyc | Bin 0 -> 14274 bytes .../__pycache__/exceptions.cpython-39.pyc | Bin 0 -> 10111 bytes .../__pycache__/formatting.cpython-39.pyc | Bin 0 -> 9391 bytes .../click/__pycache__/globals.cpython-39.pyc | Bin 0 -> 2388 bytes .../click/__pycache__/parser.cpython-39.pyc | Bin 0 -> 13545 bytes .../shell_completion.cpython-39.pyc | Bin 0 -> 16624 bytes .../click/__pycache__/termui.cpython-39.pyc | Bin 0 -> 26504 bytes .../click/__pycache__/testing.cpython-39.pyc | Bin 0 -> 15095 bytes .../click/__pycache__/types.cpython-39.pyc | Bin 0 -> 32885 bytes .../click/__pycache__/utils.cpython-39.pyc | Bin 0 -> 17749 bytes venv/Lib/site-packages/click/_compat.py | 627 ++ venv/Lib/site-packages/click/_termui_impl.py | 717 ++ venv/Lib/site-packages/click/_textwrap.py | 49 + venv/Lib/site-packages/click/_unicodefun.py | 100 + venv/Lib/site-packages/click/_winconsole.py | 279 + venv/Lib/site-packages/click/core.py | 2957 ++++++ venv/Lib/site-packages/click/decorators.py | 437 + venv/Lib/site-packages/click/exceptions.py | 287 + venv/Lib/site-packages/click/formatting.py | 301 + venv/Lib/site-packages/click/globals.py | 69 + venv/Lib/site-packages/click/parser.py | 529 ++ venv/Lib/site-packages/click/py.typed | 0 .../site-packages/click/shell_completion.py | 574 ++ venv/Lib/site-packages/click/termui.py | 807 ++ venv/Lib/site-packages/click/testing.py | 479 + venv/Lib/site-packages/click/types.py | 1052 +++ venv/Lib/site-packages/click/utils.py | 579 ++ .../colorama-0.4.4.dist-info/INSTALLER | 1 + .../colorama-0.4.4.dist-info/LICENSE.txt | 27 + .../colorama-0.4.4.dist-info/METADATA | 415 + .../colorama-0.4.4.dist-info/RECORD | 18 + .../colorama-0.4.4.dist-info/WHEEL | 6 + .../colorama-0.4.4.dist-info/top_level.txt | 1 + venv/Lib/site-packages/colorama/__init__.py | 6 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 406 bytes .../colorama/__pycache__/ansi.cpython-39.pyc | Bin 0 -> 3191 bytes .../__pycache__/ansitowin32.cpython-39.pyc | Bin 0 -> 7657 bytes .../__pycache__/initialise.cpython-39.pyc | Bin 0 -> 1673 bytes .../colorama/__pycache__/win32.cpython-39.pyc | Bin 0 -> 3905 bytes .../__pycache__/winterm.cpython-39.pyc | Bin 0 -> 4627 bytes venv/Lib/site-packages/colorama/ansi.py | 102 + .../Lib/site-packages/colorama/ansitowin32.py | 258 + venv/Lib/site-packages/colorama/initialise.py | 80 + venv/Lib/site-packages/colorama/win32.py | 152 + venv/Lib/site-packages/colorama/winterm.py | 169 + .../detach-1.0-py3.9.egg-info/PKG-INFO | 18 + .../detach-1.0-py3.9.egg-info/SOURCES.txt | 7 + .../dependency_links.txt | 1 + .../installed-files.txt | 6 + .../detach-1.0-py3.9.egg-info/top_level.txt | 1 + venv/Lib/site-packages/detach.py | 135 + .../site-packages/distutils-precedence.pth | 1 + venv/Lib/site-packages/flask/__init__.py | 46 + venv/Lib/site-packages/flask/__main__.py | 3 + .../flask/__pycache__/__init__.cpython-39.pyc | Bin 0 -> 1861 bytes .../flask/__pycache__/__main__.cpython-39.pyc | Bin 0 -> 192 bytes .../flask/__pycache__/app.cpython-39.pyc | Bin 0 -> 63552 bytes .../__pycache__/blueprints.cpython-39.pyc | Bin 0 -> 21716 bytes .../flask/__pycache__/cli.cpython-39.pyc | Bin 0 -> 27036 bytes .../flask/__pycache__/config.cpython-39.pyc | Bin 0 -> 11473 bytes .../flask/__pycache__/ctx.cpython-39.pyc | Bin 0 -> 15519 bytes .../__pycache__/debughelpers.cpython-39.pyc | Bin 0 -> 6437 bytes .../flask/__pycache__/globals.cpython-39.pyc | Bin 0 -> 1811 bytes .../flask/__pycache__/helpers.cpython-39.pyc | Bin 0 -> 27293 bytes .../flask/__pycache__/logging.cpython-39.pyc | Bin 0 -> 2428 bytes .../flask/__pycache__/scaffold.cpython-39.pyc | Bin 0 -> 24656 bytes .../flask/__pycache__/sessions.cpython-39.pyc | Bin 0 -> 13075 bytes .../flask/__pycache__/signals.cpython-39.pyc | Bin 0 -> 2342 bytes .../__pycache__/templating.cpython-39.pyc | Bin 0 -> 5529 bytes .../flask/__pycache__/testing.cpython-39.pyc | Bin 0 -> 8996 bytes .../flask/__pycache__/typing.cpython-39.pyc | Bin 0 -> 1276 bytes .../flask/__pycache__/views.cpython-39.pyc | Bin 0 -> 4886 bytes .../flask/__pycache__/wrappers.cpython-39.pyc | Bin 0 -> 5001 bytes venv/Lib/site-packages/flask/app.py | 2088 ++++ venv/Lib/site-packages/flask/blueprints.py | 603 ++ venv/Lib/site-packages/flask/cli.py | 994 ++ venv/Lib/site-packages/flask/config.py | 291 + venv/Lib/site-packages/flask/ctx.py | 480 + venv/Lib/site-packages/flask/debughelpers.py | 171 + venv/Lib/site-packages/flask/globals.py | 59 + venv/Lib/site-packages/flask/helpers.py | 836 ++ venv/Lib/site-packages/flask/json/__init__.py | 350 + .../json/__pycache__/__init__.cpython-39.pyc | Bin 0 -> 11314 bytes .../flask/json/__pycache__/tag.cpython-39.pyc | Bin 0 -> 11443 bytes venv/Lib/site-packages/flask/json/tag.py | 312 + venv/Lib/site-packages/flask/logging.py | 74 + venv/Lib/site-packages/flask/py.typed | 0 venv/Lib/site-packages/flask/scaffold.py | 864 ++ venv/Lib/site-packages/flask/sessions.py | 404 + venv/Lib/site-packages/flask/signals.py | 56 + venv/Lib/site-packages/flask/templating.py | 165 + venv/Lib/site-packages/flask/testing.py | 280 + venv/Lib/site-packages/flask/typing.py | 46 + venv/Lib/site-packages/flask/views.py | 157 + venv/Lib/site-packages/flask/wrappers.py | 167 + .../flask_sqlalchemy/__init__.py | 1132 +++ .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 36902 bytes .../__pycache__/_compat.cpython-39.pyc | Bin 0 -> 1155 bytes .../__pycache__/model.cpython-39.pyc | Bin 0 -> 4678 bytes .../__pycache__/utils.cpython-39.pyc | Bin 0 -> 1438 bytes .../site-packages/flask_sqlalchemy/_compat.py | 44 + .../site-packages/flask_sqlalchemy/model.py | 154 + .../site-packages/flask_sqlalchemy/utils.py | 45 + .../greenlet-1.1.0.dist-info/AUTHORS | 51 + .../greenlet-1.1.0.dist-info/INSTALLER | 1 + .../greenlet-1.1.0.dist-info/LICENSE | 30 + .../greenlet-1.1.0.dist-info/LICENSE.PSF | 47 + .../greenlet-1.1.0.dist-info/METADATA | 95 + .../greenlet-1.1.0.dist-info/RECORD | 71 + .../greenlet-1.1.0.dist-info/WHEEL | 5 + .../greenlet-1.1.0.dist-info/top_level.txt | 1 + venv/Lib/site-packages/greenlet/__init__.py | 62 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 818 bytes .../greenlet/_greenlet.cp39-win_amd64.pyd | Bin 0 -> 30208 bytes venv/Lib/site-packages/greenlet/greenlet.c | 2011 ++++ venv/Lib/site-packages/greenlet/greenlet.h | 146 + .../platform/setup_switch_x64_masm.cmd | 2 + .../greenlet/platform/switch_aarch64_gcc.h | 69 + .../greenlet/platform/switch_alpha_unix.h | 30 + .../greenlet/platform/switch_amd64_unix.h | 84 + .../greenlet/platform/switch_arm32_gcc.h | 79 + .../greenlet/platform/switch_arm32_ios.h | 67 + .../greenlet/platform/switch_csky_gcc.h | 48 + .../greenlet/platform/switch_m68k_gcc.h | 38 + .../greenlet/platform/switch_mips_unix.h | 64 + .../greenlet/platform/switch_ppc64_aix.h | 103 + .../greenlet/platform/switch_ppc64_linux.h | 105 + .../greenlet/platform/switch_ppc_aix.h | 87 + .../greenlet/platform/switch_ppc_linux.h | 84 + .../greenlet/platform/switch_ppc_macosx.h | 82 + .../greenlet/platform/switch_ppc_unix.h | 82 + .../greenlet/platform/switch_riscv_unix.h | 32 + .../greenlet/platform/switch_s390_unix.h | 87 + .../greenlet/platform/switch_sparc_sun_gcc.h | 92 + .../greenlet/platform/switch_x32_unix.h | 63 + .../greenlet/platform/switch_x64_masm.asm | 111 + .../greenlet/platform/switch_x64_masm.obj | Bin 0 -> 1078 bytes .../greenlet/platform/switch_x64_msvc.h | 60 + .../greenlet/platform/switch_x86_msvc.h | 88 + .../greenlet/platform/switch_x86_unix.h | 105 + .../greenlet/slp_platformselect.h | 58 + .../site-packages/greenlet/tests/__init__.py | 0 .../tests/__pycache__/__init__.cpython-39.pyc | Bin 0 -> 161 bytes .../test_contextvars.cpython-39.pyc | Bin 0 -> 7342 bytes .../tests/__pycache__/test_cpp.cpython-39.pyc | Bin 0 -> 827 bytes .../test_extension_interface.cpython-39.pyc | Bin 0 -> 3530 bytes .../tests/__pycache__/test_gc.cpython-39.pyc | Bin 0 -> 2883 bytes .../__pycache__/test_generator.cpython-39.pyc | Bin 0 -> 2203 bytes .../test_generator_nested.cpython-39.pyc | Bin 0 -> 5310 bytes .../__pycache__/test_greenlet.cpython-39.pyc | Bin 0 -> 24905 bytes .../__pycache__/test_leaks.cpython-39.pyc | Bin 0 -> 3157 bytes .../test_stack_saved.cpython-39.pyc | Bin 0 -> 839 bytes .../__pycache__/test_throw.cpython-39.pyc | Bin 0 -> 2931 bytes .../__pycache__/test_tracing.cpython-39.pyc | Bin 0 -> 2216 bytes .../__pycache__/test_version.cpython-39.pyc | Bin 0 -> 1511 bytes .../__pycache__/test_weakref.cpython-39.pyc | Bin 0 -> 1830 bytes .../greenlet/tests/_test_extension.c | 216 + .../tests/_test_extension.cp39-win_amd64.pyd | Bin 0 -> 13312 bytes .../_test_extension_cpp.cp39-win_amd64.pyd | Bin 0 -> 12288 bytes .../greenlet/tests/_test_extension_cpp.cpp | 121 + .../greenlet/tests/test_contextvars.py | 266 + .../site-packages/greenlet/tests/test_cpp.py | 18 + .../tests/test_extension_interface.py | 77 + .../site-packages/greenlet/tests/test_gc.py | 77 + .../greenlet/tests/test_generator.py | 59 + .../greenlet/tests/test_generator_nested.py | 165 + .../greenlet/tests/test_greenlet.py | 627 ++ .../greenlet/tests/test_leaks.py | 85 + .../greenlet/tests/test_stack_saved.py | 19 + .../greenlet/tests/test_throw.py | 100 + .../greenlet/tests/test_tracing.py | 52 + .../greenlet/tests/test_version.py | 39 + .../greenlet/tests/test_weakref.py | 34 + .../iniconfig-1.1.1.dist-info/INSTALLER | 1 + .../iniconfig-1.1.1.dist-info/LICENSE | 19 + .../iniconfig-1.1.1.dist-info/METADATA | 78 + .../iniconfig-1.1.1.dist-info/RECORD | 10 + .../iniconfig-1.1.1.dist-info/WHEEL | 6 + .../iniconfig-1.1.1.dist-info/top_level.txt | 1 + venv/Lib/site-packages/iniconfig/__init__.py | 165 + venv/Lib/site-packages/iniconfig/__init__.pyi | 31 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 5210 bytes venv/Lib/site-packages/iniconfig/py.typed | 0 .../itsdangerous-2.0.1.dist-info/INSTALLER | 1 + .../itsdangerous-2.0.1.dist-info/LICENSE.rst | 28 + .../itsdangerous-2.0.1.dist-info/METADATA | 96 + .../itsdangerous-2.0.1.dist-info/RECORD | 25 + .../itsdangerous-2.0.1.dist-info/WHEEL | 5 + .../top_level.txt | 1 + .../site-packages/itsdangerous/__init__.py | 22 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 987 bytes .../__pycache__/_json.cpython-39.pyc | Bin 0 -> 1521 bytes .../__pycache__/encoding.cpython-39.pyc | Bin 0 -> 1833 bytes .../__pycache__/exc.cpython-39.pyc | Bin 0 -> 3414 bytes .../__pycache__/jws.cpython-39.pyc | Bin 0 -> 7561 bytes .../__pycache__/serializer.cpython-39.pyc | Bin 0 -> 9716 bytes .../__pycache__/signer.cpython-39.pyc | Bin 0 -> 8413 bytes .../__pycache__/timed.cpython-39.pyc | Bin 0 -> 6334 bytes .../__pycache__/url_safe.cpython-39.pyc | Bin 0 -> 2682 bytes venv/Lib/site-packages/itsdangerous/_json.py | 34 + .../site-packages/itsdangerous/encoding.py | 54 + venv/Lib/site-packages/itsdangerous/exc.py | 107 + venv/Lib/site-packages/itsdangerous/jws.py | 259 + venv/Lib/site-packages/itsdangerous/py.typed | 0 .../site-packages/itsdangerous/serializer.py | 295 + venv/Lib/site-packages/itsdangerous/signer.py | 257 + venv/Lib/site-packages/itsdangerous/timed.py | 227 + .../site-packages/itsdangerous/url_safe.py | 80 + venv/Lib/site-packages/jinja2/__init__.py | 45 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 1886 bytes .../__pycache__/_identifier.cpython-39.pyc | Bin 0 -> 1873 bytes .../__pycache__/async_utils.cpython-39.pyc | Bin 0 -> 2286 bytes .../jinja2/__pycache__/bccache.cpython-39.pyc | Bin 0 -> 13267 bytes .../__pycache__/compiler.cpython-39.pyc | Bin 0 -> 54163 bytes .../__pycache__/constants.cpython-39.pyc | Bin 0 -> 1517 bytes .../jinja2/__pycache__/debug.cpython-39.pyc | Bin 0 -> 5510 bytes .../__pycache__/defaults.cpython-39.pyc | Bin 0 -> 1317 bytes .../__pycache__/environment.cpython-39.pyc | Bin 0 -> 53423 bytes .../__pycache__/exceptions.cpython-39.pyc | Bin 0 -> 5558 bytes .../jinja2/__pycache__/ext.cpython-39.pyc | Bin 0 -> 26324 bytes .../jinja2/__pycache__/filters.cpython-39.pyc | Bin 0 -> 49961 bytes .../__pycache__/idtracking.cpython-39.pyc | Bin 0 -> 11079 bytes .../jinja2/__pycache__/lexer.cpython-39.pyc | Bin 0 -> 20286 bytes .../jinja2/__pycache__/loaders.cpython-39.pyc | Bin 0 -> 20128 bytes .../jinja2/__pycache__/meta.cpython-39.pyc | Bin 0 -> 3775 bytes .../__pycache__/nativetypes.cpython-39.pyc | Bin 0 -> 4764 bytes .../jinja2/__pycache__/nodes.cpython-39.pyc | Bin 0 -> 40793 bytes .../__pycache__/optimizer.cpython-39.pyc | Bin 0 -> 1906 bytes .../jinja2/__pycache__/parser.cpython-39.pyc | Bin 0 -> 27566 bytes .../jinja2/__pycache__/runtime.cpython-39.pyc | Bin 0 -> 33087 bytes .../jinja2/__pycache__/sandbox.cpython-39.pyc | Bin 0 -> 11897 bytes .../jinja2/__pycache__/tests.cpython-39.pyc | Bin 0 -> 6549 bytes .../jinja2/__pycache__/utils.cpython-39.pyc | Bin 0 -> 27421 bytes .../jinja2/__pycache__/visitor.cpython-39.pyc | Bin 0 -> 3871 bytes venv/Lib/site-packages/jinja2/_identifier.py | 6 + venv/Lib/site-packages/jinja2/async_utils.py | 68 + venv/Lib/site-packages/jinja2/bccache.py | 364 + venv/Lib/site-packages/jinja2/compiler.py | 1954 ++++ venv/Lib/site-packages/jinja2/constants.py | 20 + venv/Lib/site-packages/jinja2/debug.py | 279 + venv/Lib/site-packages/jinja2/defaults.py | 48 + venv/Lib/site-packages/jinja2/environment.py | 1674 ++++ venv/Lib/site-packages/jinja2/exceptions.py | 166 + venv/Lib/site-packages/jinja2/ext.py | 879 ++ venv/Lib/site-packages/jinja2/filters.py | 1824 ++++ venv/Lib/site-packages/jinja2/idtracking.py | 318 + venv/Lib/site-packages/jinja2/lexer.py | 869 ++ venv/Lib/site-packages/jinja2/loaders.py | 642 ++ venv/Lib/site-packages/jinja2/meta.py | 111 + venv/Lib/site-packages/jinja2/nativetypes.py | 118 + venv/Lib/site-packages/jinja2/nodes.py | 1205 +++ venv/Lib/site-packages/jinja2/optimizer.py | 47 + venv/Lib/site-packages/jinja2/parser.py | 1040 ++ venv/Lib/site-packages/jinja2/py.typed | 0 venv/Lib/site-packages/jinja2/runtime.py | 1103 +++ venv/Lib/site-packages/jinja2/sandbox.py | 428 + venv/Lib/site-packages/jinja2/tests.py | 255 + venv/Lib/site-packages/jinja2/utils.py | 854 ++ venv/Lib/site-packages/jinja2/visitor.py | 92 + venv/Lib/site-packages/markupsafe/__init__.py | 288 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 10662 bytes .../__pycache__/_native.cpython-39.pyc | Bin 0 -> 2309 bytes venv/Lib/site-packages/markupsafe/_native.py | 75 + .../markupsafe/_speedups.cp39-win_amd64.pyd | Bin 0 -> 16384 bytes .../site-packages/markupsafe/_speedups.pyi | 9 + venv/Lib/site-packages/markupsafe/py.typed | 0 .../maxcul-0.1.1-py3.9.egg-info/PKG-INFO | 43 + .../maxcul-0.1.1-py3.9.egg-info/SOURCES.txt | 13 + .../dependency_links.txt | 1 + .../installed-files.txt | 15 + .../maxcul-0.1.1-py3.9.egg-info/requires.txt | 9 + .../maxcul-0.1.1-py3.9.egg-info/top_level.txt | 1 + venv/Lib/site-packages/maxcul/__init__.py | 21 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 371 bytes .../Lib/site-packages/maxcul/communication.py | 372 + venv/Lib/site-packages/maxcul/exceptions.py | 45 + venv/Lib/site-packages/maxcul/messages.py | 647 ++ venv/Lib/site-packages/maxcul/signals.py | 25 + .../packaging-21.0.dist-info/INSTALLER | 1 + .../packaging-21.0.dist-info/LICENSE | 3 + .../packaging-21.0.dist-info/LICENSE.APACHE | 177 + .../packaging-21.0.dist-info/LICENSE.BSD | 23 + .../packaging-21.0.dist-info/METADATA | 425 + .../packaging-21.0.dist-info/RECORD | 31 + .../packaging-21.0.dist-info/WHEEL | 5 + .../packaging-21.0.dist-info/top_level.txt | 1 + venv/Lib/site-packages/packaging/__about__.py | 26 + venv/Lib/site-packages/packaging/__init__.py | 25 + .../__pycache__/__about__.cpython-39.pyc | Bin 0 -> 553 bytes .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 409 bytes .../__pycache__/_manylinux.cpython-39.pyc | Bin 0 -> 7257 bytes .../__pycache__/_musllinux.cpython-39.pyc | Bin 0 -> 4572 bytes .../__pycache__/_structures.cpython-39.pyc | Bin 0 -> 3045 bytes .../__pycache__/markers.cpython-39.pyc | Bin 0 -> 9404 bytes .../__pycache__/requirements.cpython-39.pyc | Bin 0 -> 3925 bytes .../__pycache__/specifiers.cpython-39.pyc | Bin 0 -> 22173 bytes .../packaging/__pycache__/tags.cpython-39.pyc | Bin 0 -> 12252 bytes .../__pycache__/utils.cpython-39.pyc | Bin 0 -> 3574 bytes .../__pycache__/version.cpython-39.pyc | Bin 0 -> 13115 bytes .../Lib/site-packages/packaging/_manylinux.py | 301 + .../Lib/site-packages/packaging/_musllinux.py | 136 + .../site-packages/packaging/_structures.py | 67 + venv/Lib/site-packages/packaging/markers.py | 304 + venv/Lib/site-packages/packaging/py.typed | 0 .../site-packages/packaging/requirements.py | 146 + .../Lib/site-packages/packaging/specifiers.py | 828 ++ venv/Lib/site-packages/packaging/tags.py | 484 + venv/Lib/site-packages/packaging/utils.py | 136 + venv/Lib/site-packages/packaging/version.py | 504 + .../pip-21.1.3.dist-info/INSTALLER | 1 + .../pip-21.1.3.dist-info/LICENSE.txt | 20 + .../pip-21.1.3.dist-info/METADATA | 91 + .../site-packages/pip-21.1.3.dist-info/RECORD | 796 ++ .../site-packages/pip-21.1.3.dist-info/WHEEL | 5 + .../pip-21.1.3.dist-info/entry_points.txt | 5 + .../pip-21.1.3.dist-info/top_level.txt | 1 + venv/Lib/site-packages/pip/__init__.py | 14 + venv/Lib/site-packages/pip/__main__.py | 31 + .../pip/__pycache__/__init__.cpython-39.pyc | Bin 0 -> 583 bytes .../pip/__pycache__/__main__.cpython-39.pyc | Bin 0 -> 580 bytes .../site-packages/pip/_internal/__init__.py | 15 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 632 bytes .../__pycache__/build_env.cpython-39.pyc | Bin 0 -> 8945 bytes .../__pycache__/cache.cpython-39.pyc | Bin 0 -> 7835 bytes .../__pycache__/configuration.cpython-39.pyc | Bin 0 -> 10687 bytes .../__pycache__/exceptions.cpython-39.pyc | Bin 0 -> 15636 bytes .../_internal/__pycache__/main.cpython-39.pyc | Bin 0 -> 569 bytes .../__pycache__/pyproject.cpython-39.pyc | Bin 0 -> 3447 bytes .../self_outdated_check.cpython-39.pyc | Bin 0 -> 4332 bytes .../__pycache__/wheel_builder.cpython-39.pyc | Bin 0 -> 8270 bytes .../site-packages/pip/_internal/build_env.py | 286 + venv/Lib/site-packages/pip/_internal/cache.py | 287 + .../pip/_internal/cli/__init__.py | 4 + .../cli/__pycache__/__init__.cpython-39.pyc | Bin 0 -> 265 bytes .../__pycache__/autocompletion.cpython-39.pyc | Bin 0 -> 4963 bytes .../__pycache__/base_command.cpython-39.pyc | Bin 0 -> 5786 bytes .../cli/__pycache__/cmdoptions.cpython-39.pyc | Bin 0 -> 21020 bytes .../command_context.cpython-39.pyc | Bin 0 -> 1214 bytes .../cli/__pycache__/main.cpython-39.pyc | Bin 0 -> 1314 bytes .../__pycache__/main_parser.cpython-39.pyc | Bin 0 -> 2095 bytes .../cli/__pycache__/parser.cpython-39.pyc | Bin 0 -> 9359 bytes .../__pycache__/progress_bars.cpython-39.pyc | Bin 0 -> 7431 bytes .../__pycache__/req_command.cpython-39.pyc | Bin 0 -> 11274 bytes .../cli/__pycache__/spinners.cpython-39.pyc | Bin 0 -> 4602 bytes .../__pycache__/status_codes.cpython-39.pyc | Bin 0 -> 344 bytes .../pip/_internal/cli/autocompletion.py | 162 + .../pip/_internal/cli/base_command.py | 221 + .../pip/_internal/cli/cmdoptions.py | 1024 ++ .../pip/_internal/cli/command_context.py | 30 + .../site-packages/pip/_internal/cli/main.py | 71 + .../pip/_internal/cli/main_parser.py | 89 + .../site-packages/pip/_internal/cli/parser.py | 305 + .../pip/_internal/cli/progress_bars.py | 261 + .../pip/_internal/cli/req_command.py | 462 + .../pip/_internal/cli/spinners.py | 172 + .../pip/_internal/cli/status_codes.py | 6 + .../pip/_internal/commands/__init__.py | 110 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 2858 bytes .../commands/__pycache__/cache.cpython-39.pyc | Bin 0 -> 5740 bytes .../commands/__pycache__/check.cpython-39.pyc | Bin 0 -> 1506 bytes .../__pycache__/completion.cpython-39.pyc | Bin 0 -> 3062 bytes .../__pycache__/configuration.cpython-39.pyc | Bin 0 -> 8043 bytes .../commands/__pycache__/debug.cpython-39.pyc | Bin 0 -> 6427 bytes .../__pycache__/download.cpython-39.pyc | Bin 0 -> 3913 bytes .../__pycache__/freeze.cpython-39.pyc | Bin 0 -> 2911 bytes .../commands/__pycache__/hash.cpython-39.pyc | Bin 0 -> 2023 bytes .../commands/__pycache__/help.cpython-39.pyc | Bin 0 -> 1242 bytes .../__pycache__/install.cpython-39.pyc | Bin 0 -> 16689 bytes .../commands/__pycache__/list.cpython-39.pyc | Bin 0 -> 8800 bytes .../__pycache__/search.cpython-39.pyc | Bin 0 -> 4899 bytes .../commands/__pycache__/show.cpython-39.pyc | Bin 0 -> 6298 bytes .../__pycache__/uninstall.cpython-39.pyc | Bin 0 -> 2847 bytes .../commands/__pycache__/wheel.cpython-39.pyc | Bin 0 -> 4786 bytes .../pip/_internal/commands/cache.py | 228 + .../pip/_internal/commands/check.py | 48 + .../pip/_internal/commands/completion.py | 93 + .../pip/_internal/commands/configuration.py | 280 + .../pip/_internal/commands/debug.py | 215 + .../pip/_internal/commands/download.py | 141 + .../pip/_internal/commands/freeze.py | 104 + .../pip/_internal/commands/hash.py | 58 + .../pip/_internal/commands/help.py | 42 + .../pip/_internal/commands/install.py | 740 ++ .../pip/_internal/commands/list.py | 319 + .../pip/_internal/commands/search.py | 162 + .../pip/_internal/commands/show.py | 181 + .../pip/_internal/commands/uninstall.py | 92 + .../pip/_internal/commands/wheel.py | 178 + .../pip/_internal/configuration.py | 403 + .../pip/_internal/distributions/__init__.py | 20 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 761 bytes .../__pycache__/base.cpython-39.pyc | Bin 0 -> 1817 bytes .../__pycache__/installed.cpython-39.pyc | Bin 0 -> 1162 bytes .../__pycache__/sdist.cpython-39.pyc | Bin 0 -> 3425 bytes .../__pycache__/wheel.cpython-39.pyc | Bin 0 -> 1506 bytes .../pip/_internal/distributions/base.py | 39 + .../pip/_internal/distributions/installed.py | 22 + .../pip/_internal/distributions/sdist.py | 95 + .../pip/_internal/distributions/wheel.py | 34 + .../site-packages/pip/_internal/exceptions.py | 397 + .../pip/_internal/index/__init__.py | 2 + .../index/__pycache__/__init__.cpython-39.pyc | Bin 0 -> 219 bytes .../__pycache__/collector.cpython-39.pyc | Bin 0 -> 15075 bytes .../__pycache__/package_finder.cpython-39.pyc | Bin 0 -> 26602 bytes .../index/__pycache__/sources.cpython-39.pyc | Bin 0 -> 7184 bytes .../pip/_internal/index/collector.py | 556 ++ .../pip/_internal/index/package_finder.py | 1012 ++ .../pip/_internal/index/sources.py | 224 + .../pip/_internal/locations/__init__.py | 184 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 3675 bytes .../__pycache__/_distutils.cpython-39.pyc | Bin 0 -> 3872 bytes .../__pycache__/_sysconfig.cpython-39.pyc | Bin 0 -> 4490 bytes .../locations/__pycache__/base.cpython-39.pyc | Bin 0 -> 1203 bytes .../pip/_internal/locations/_distutils.py | 150 + .../pip/_internal/locations/_sysconfig.py | 180 + .../pip/_internal/locations/base.py | 48 + venv/Lib/site-packages/pip/_internal/main.py | 13 + .../pip/_internal/metadata/__init__.py | 43 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 1718 bytes .../metadata/__pycache__/base.cpython-39.pyc | Bin 0 -> 5292 bytes .../__pycache__/pkg_resources.cpython-39.pyc | Bin 0 -> 4246 bytes .../pip/_internal/metadata/base.py | 142 + .../pip/_internal/metadata/pkg_resources.py | 126 + .../pip/_internal/models/__init__.py | 2 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 253 bytes .../__pycache__/candidate.cpython-39.pyc | Bin 0 -> 1366 bytes .../__pycache__/direct_url.cpython-39.pyc | Bin 0 -> 6279 bytes .../__pycache__/format_control.cpython-39.pyc | Bin 0 -> 2523 bytes .../models/__pycache__/index.cpython-39.pyc | Bin 0 -> 1176 bytes .../models/__pycache__/link.cpython-39.pyc | Bin 0 -> 7180 bytes .../models/__pycache__/scheme.cpython-39.pyc | Bin 0 -> 931 bytes .../__pycache__/search_scope.cpython-39.pyc | Bin 0 -> 3332 bytes .../selection_prefs.cpython-39.pyc | Bin 0 -> 1566 bytes .../__pycache__/target_python.cpython-39.pyc | Bin 0 -> 3263 bytes .../models/__pycache__/wheel.cpython-39.pyc | Bin 0 -> 4178 bytes .../pip/_internal/models/candidate.py | 34 + .../pip/_internal/models/direct_url.py | 233 + .../pip/_internal/models/format_control.py | 86 + .../pip/_internal/models/index.py | 34 + .../pip/_internal/models/link.py | 248 + .../pip/_internal/models/scheme.py | 31 + .../pip/_internal/models/search_scope.py | 131 + .../pip/_internal/models/selection_prefs.py | 47 + .../pip/_internal/models/target_python.py | 114 + .../pip/_internal/models/wheel.py | 95 + .../pip/_internal/network/__init__.py | 2 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 241 bytes .../network/__pycache__/auth.cpython-39.pyc | Bin 0 -> 7038 bytes .../network/__pycache__/cache.cpython-39.pyc | Bin 0 -> 2720 bytes .../__pycache__/download.cpython-39.pyc | Bin 0 -> 5080 bytes .../__pycache__/lazy_wheel.cpython-39.pyc | Bin 0 -> 7905 bytes .../__pycache__/session.cpython-39.pyc | Bin 0 -> 9488 bytes .../network/__pycache__/utils.cpython-39.pyc | Bin 0 -> 1307 bytes .../network/__pycache__/xmlrpc.cpython-39.pyc | Bin 0 -> 1886 bytes .../pip/_internal/network/auth.py | 312 + .../pip/_internal/network/cache.py | 76 + .../pip/_internal/network/download.py | 196 + .../pip/_internal/network/lazy_wheel.py | 224 + .../pip/_internal/network/session.py | 449 + .../pip/_internal/network/utils.py | 95 + .../pip/_internal/network/xmlrpc.py | 49 + .../pip/_internal/operations/__init__.py | 0 .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 189 bytes .../__pycache__/check.cpython-39.pyc | Bin 0 -> 3685 bytes .../__pycache__/freeze.cpython-39.pyc | Bin 0 -> 5638 bytes .../__pycache__/prepare.cpython-39.pyc | Bin 0 -> 14225 bytes .../_internal/operations/build/__init__.py | 0 .../build/__pycache__/__init__.cpython-39.pyc | Bin 0 -> 195 bytes .../build/__pycache__/metadata.cpython-39.pyc | Bin 0 -> 1141 bytes .../metadata_legacy.cpython-39.pyc | Bin 0 -> 1915 bytes .../build/__pycache__/wheel.cpython-39.pyc | Bin 0 -> 1120 bytes .../__pycache__/wheel_legacy.cpython-39.pyc | Bin 0 -> 2514 bytes .../_internal/operations/build/metadata.py | 35 + .../operations/build/metadata_legacy.py | 74 + .../pip/_internal/operations/build/wheel.py | 38 + .../operations/build/wheel_legacy.py | 110 + .../pip/_internal/operations/check.py | 153 + .../pip/_internal/operations/freeze.py | 264 + .../_internal/operations/install/__init__.py | 2 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 253 bytes .../editable_legacy.cpython-39.pyc | Bin 0 -> 1296 bytes .../install/__pycache__/legacy.cpython-39.pyc | Bin 0 -> 3184 bytes .../install/__pycache__/wheel.cpython-39.pyc | Bin 0 -> 20806 bytes .../operations/install/editable_legacy.py | 47 + .../_internal/operations/install/legacy.py | 125 + .../pip/_internal/operations/install/wheel.py | 819 ++ .../pip/_internal/operations/prepare.py | 655 ++ .../site-packages/pip/_internal/pyproject.py | 183 + .../pip/_internal/req/__init__.py | 98 + .../req/__pycache__/__init__.cpython-39.pyc | Bin 0 -> 2336 bytes .../__pycache__/constructors.cpython-39.pyc | Bin 0 -> 10996 bytes .../req/__pycache__/req_file.cpython-39.pyc | Bin 0 -> 12429 bytes .../__pycache__/req_install.cpython-39.pyc | Bin 0 -> 20507 bytes .../req/__pycache__/req_set.cpython-39.pyc | Bin 0 -> 5673 bytes .../__pycache__/req_tracker.cpython-39.pyc | Bin 0 -> 4018 bytes .../__pycache__/req_uninstall.cpython-39.pyc | Bin 0 -> 17454 bytes .../pip/_internal/req/constructors.py | 486 + .../pip/_internal/req/req_file.py | 551 ++ .../pip/_internal/req/req_install.py | 878 ++ .../pip/_internal/req/req_set.py | 199 + .../pip/_internal/req/req_tracker.py | 140 + .../pip/_internal/req/req_uninstall.py | 640 ++ .../pip/_internal/resolution/__init__.py | 0 .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 189 bytes .../__pycache__/base.cpython-39.pyc | Bin 0 -> 932 bytes .../pip/_internal/resolution/base.py | 16 + .../_internal/resolution/legacy/__init__.py | 0 .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 196 bytes .../__pycache__/resolver.cpython-39.pyc | Bin 0 -> 11469 bytes .../_internal/resolution/legacy/resolver.py | 462 + .../resolution/resolvelib/__init__.py | 0 .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 200 bytes .../__pycache__/base.cpython-39.pyc | Bin 0 -> 6302 bytes .../__pycache__/candidates.cpython-39.pyc | Bin 0 -> 17882 bytes .../__pycache__/factory.cpython-39.pyc | Bin 0 -> 16349 bytes .../found_candidates.cpython-39.pyc | Bin 0 -> 4648 bytes .../__pycache__/provider.cpython-39.pyc | Bin 0 -> 6568 bytes .../__pycache__/reporter.cpython-39.pyc | Bin 0 -> 3132 bytes .../__pycache__/requirements.cpython-39.pyc | Bin 0 -> 7037 bytes .../__pycache__/resolver.cpython-39.pyc | Bin 0 -> 8044 bytes .../_internal/resolution/resolvelib/base.py | 165 + .../resolution/resolvelib/candidates.py | 604 ++ .../resolution/resolvelib/factory.py | 661 ++ .../resolution/resolvelib/found_candidates.py | 145 + .../resolution/resolvelib/provider.py | 175 + .../resolution/resolvelib/reporter.py | 78 + .../resolution/resolvelib/requirements.py | 198 + .../resolution/resolvelib/resolver.py | 305 + .../pip/_internal/self_outdated_check.py | 187 + .../pip/_internal/utils/__init__.py | 0 .../utils/__pycache__/__init__.cpython-39.pyc | Bin 0 -> 184 bytes .../utils/__pycache__/appdirs.cpython-39.pyc | Bin 0 -> 1249 bytes .../utils/__pycache__/compat.cpython-39.pyc | Bin 0 -> 1451 bytes .../compatibility_tags.cpython-39.pyc | Bin 0 -> 3827 bytes .../utils/__pycache__/datetime.cpython-39.pyc | Bin 0 -> 455 bytes .../__pycache__/deprecation.cpython-39.pyc | Bin 0 -> 2750 bytes .../direct_url_helpers.cpython-39.pyc | Bin 0 -> 2504 bytes .../__pycache__/distutils_args.cpython-39.pyc | Bin 0 -> 1049 bytes .../utils/__pycache__/encoding.cpython-39.pyc | Bin 0 -> 1214 bytes .../__pycache__/entrypoints.cpython-39.pyc | Bin 0 -> 1253 bytes .../__pycache__/filesystem.cpython-39.pyc | Bin 0 -> 4923 bytes .../__pycache__/filetypes.cpython-39.pyc | Bin 0 -> 796 bytes .../utils/__pycache__/glibc.cpython-39.pyc | Bin 0 -> 1606 bytes .../utils/__pycache__/hashes.cpython-39.pyc | Bin 0 -> 4988 bytes .../inject_securetransport.cpython-39.pyc | Bin 0 -> 957 bytes .../utils/__pycache__/logging.cpython-39.pyc | Bin 0 -> 8908 bytes .../utils/__pycache__/misc.cpython-39.pyc | Bin 0 -> 21512 bytes .../utils/__pycache__/models.cpython-39.pyc | Bin 0 -> 1888 bytes .../__pycache__/packaging.cpython-39.pyc | Bin 0 -> 2512 bytes .../utils/__pycache__/parallel.cpython-39.pyc | Bin 0 -> 3029 bytes .../__pycache__/pkg_resources.cpython-39.pyc | Bin 0 -> 1710 bytes .../setuptools_build.cpython-39.pyc | Bin 0 -> 2954 bytes .../__pycache__/subprocess.cpython-39.pyc | Bin 0 -> 5677 bytes .../utils/__pycache__/temp_dir.cpython-39.pyc | Bin 0 -> 6828 bytes .../__pycache__/unpacking.cpython-39.pyc | Bin 0 -> 6449 bytes .../utils/__pycache__/urls.cpython-39.pyc | Bin 0 -> 1340 bytes .../__pycache__/virtualenv.cpython-39.pyc | Bin 0 -> 3207 bytes .../utils/__pycache__/wheel.cpython-39.pyc | Bin 0 -> 5958 bytes .../pip/_internal/utils/appdirs.py | 38 + .../pip/_internal/utils/compat.py | 65 + .../pip/_internal/utils/compatibility_tags.py | 174 + .../pip/_internal/utils/datetime.py | 12 + .../pip/_internal/utils/deprecation.py | 102 + .../pip/_internal/utils/direct_url_helpers.py | 117 + .../pip/_internal/utils/distutils_args.py | 43 + .../pip/_internal/utils/encoding.py | 37 + .../pip/_internal/utils/entrypoints.py | 28 + .../pip/_internal/utils/filesystem.py | 193 + .../pip/_internal/utils/filetypes.py | 28 + .../pip/_internal/utils/glibc.py | 92 + .../pip/_internal/utils/hashes.py | 165 + .../_internal/utils/inject_securetransport.py | 36 + .../pip/_internal/utils/logging.py | 387 + .../site-packages/pip/_internal/utils/misc.py | 821 ++ .../pip/_internal/utils/models.py | 47 + .../pip/_internal/utils/packaging.py | 89 + .../pip/_internal/utils/parallel.py | 101 + .../pip/_internal/utils/pkg_resources.py | 40 + .../pip/_internal/utils/setuptools_build.py | 173 + .../pip/_internal/utils/subprocess.py | 281 + .../pip/_internal/utils/temp_dir.py | 260 + .../pip/_internal/utils/unpacking.py | 267 + .../site-packages/pip/_internal/utils/urls.py | 49 + .../pip/_internal/utils/virtualenv.py | 111 + .../pip/_internal/utils/wheel.py | 189 + .../pip/_internal/vcs/__init__.py | 14 + .../vcs/__pycache__/__init__.cpython-39.pyc | Bin 0 -> 477 bytes .../vcs/__pycache__/bazaar.cpython-39.pyc | Bin 0 -> 2993 bytes .../vcs/__pycache__/git.cpython-39.pyc | Bin 0 -> 9969 bytes .../vcs/__pycache__/mercurial.cpython-39.pyc | Bin 0 -> 4614 bytes .../vcs/__pycache__/subversion.cpython-39.pyc | Bin 0 -> 7948 bytes .../__pycache__/versioncontrol.cpython-39.pyc | Bin 0 -> 19093 bytes .../site-packages/pip/_internal/vcs/bazaar.py | 96 + .../site-packages/pip/_internal/vcs/git.py | 450 + .../pip/_internal/vcs/mercurial.py | 158 + .../pip/_internal/vcs/subversion.py | 329 + .../pip/_internal/vcs/versioncontrol.py | 715 ++ .../pip/_internal/wheel_builder.py | 360 + .../Lib/site-packages/pip/_vendor/__init__.py | 113 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 2939 bytes .../__pycache__/appdirs.cpython-39.pyc | Bin 0 -> 21392 bytes .../_vendor/__pycache__/distro.cpython-39.pyc | Bin 0 -> 36871 bytes .../__pycache__/pyparsing.cpython-39.pyc | Bin 0 -> 240428 bytes .../_vendor/__pycache__/six.cpython-39.pyc | Bin 0 -> 26938 bytes venv/Lib/site-packages/pip/_vendor/appdirs.py | 633 ++ .../pip/_vendor/cachecontrol/__init__.py | 11 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 542 bytes .../__pycache__/_cmd.cpython-39.pyc | Bin 0 -> 1563 bytes .../__pycache__/adapter.cpython-39.pyc | Bin 0 -> 3068 bytes .../__pycache__/cache.cpython-39.pyc | Bin 0 -> 1815 bytes .../__pycache__/compat.cpython-39.pyc | Bin 0 -> 739 bytes .../__pycache__/controller.cpython-39.pyc | Bin 0 -> 7756 bytes .../__pycache__/filewrapper.cpython-39.pyc | Bin 0 -> 2164 bytes .../__pycache__/heuristics.cpython-39.pyc | Bin 0 -> 4696 bytes .../__pycache__/serialize.cpython-39.pyc | Bin 0 -> 4215 bytes .../__pycache__/wrapper.cpython-39.pyc | Bin 0 -> 666 bytes .../pip/_vendor/cachecontrol/_cmd.py | 57 + .../pip/_vendor/cachecontrol/adapter.py | 133 + .../pip/_vendor/cachecontrol/cache.py | 39 + .../_vendor/cachecontrol/caches/__init__.py | 2 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 286 bytes .../__pycache__/file_cache.cpython-39.pyc | Bin 0 -> 3302 bytes .../__pycache__/redis_cache.cpython-39.pyc | Bin 0 -> 1558 bytes .../_vendor/cachecontrol/caches/file_cache.py | 146 + .../cachecontrol/caches/redis_cache.py | 33 + .../pip/_vendor/cachecontrol/compat.py | 29 + .../pip/_vendor/cachecontrol/controller.py | 376 + .../pip/_vendor/cachecontrol/filewrapper.py | 80 + .../pip/_vendor/cachecontrol/heuristics.py | 135 + .../pip/_vendor/cachecontrol/serialize.py | 188 + .../pip/_vendor/cachecontrol/wrapper.py | 29 + .../pip/_vendor/certifi/__init__.py | 3 + .../pip/_vendor/certifi/__main__.py | 12 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 268 bytes .../__pycache__/__main__.cpython-39.pyc | Bin 0 -> 445 bytes .../certifi/__pycache__/core.cpython-39.pyc | Bin 0 -> 1536 bytes .../pip/_vendor/certifi/cacert.pem | 4325 +++++++++ .../site-packages/pip/_vendor/certifi/core.py | 76 + .../pip/_vendor/chardet/__init__.py | 83 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 1892 bytes .../__pycache__/big5freq.cpython-39.pyc | Bin 0 -> 27171 bytes .../__pycache__/big5prober.cpython-39.pyc | Bin 0 -> 1126 bytes .../chardistribution.cpython-39.pyc | Bin 0 -> 6212 bytes .../charsetgroupprober.cpython-39.pyc | Bin 0 -> 2253 bytes .../__pycache__/charsetprober.cpython-39.pyc | Bin 0 -> 3475 bytes .../codingstatemachine.cpython-39.pyc | Bin 0 -> 2902 bytes .../chardet/__pycache__/compat.cpython-39.pyc | Bin 0 -> 391 bytes .../__pycache__/cp949prober.cpython-39.pyc | Bin 0 -> 1133 bytes .../chardet/__pycache__/enums.cpython-39.pyc | Bin 0 -> 2640 bytes .../__pycache__/escprober.cpython-39.pyc | Bin 0 -> 2625 bytes .../chardet/__pycache__/escsm.cpython-39.pyc | Bin 0 -> 7074 bytes .../__pycache__/eucjpprober.cpython-39.pyc | Bin 0 -> 2439 bytes .../__pycache__/euckrfreq.cpython-39.pyc | Bin 0 -> 12055 bytes .../__pycache__/euckrprober.cpython-39.pyc | Bin 0 -> 1134 bytes .../__pycache__/euctwfreq.cpython-39.pyc | Bin 0 -> 27175 bytes .../__pycache__/euctwprober.cpython-39.pyc | Bin 0 -> 1134 bytes .../__pycache__/gb2312freq.cpython-39.pyc | Bin 0 -> 19099 bytes .../__pycache__/gb2312prober.cpython-39.pyc | Bin 0 -> 1142 bytes .../__pycache__/hebrewprober.cpython-39.pyc | Bin 0 -> 3011 bytes .../__pycache__/jisfreq.cpython-39.pyc | Bin 0 -> 22127 bytes .../chardet/__pycache__/jpcntx.cpython-39.pyc | Bin 0 -> 37600 bytes .../langbulgarianmodel.cpython-39.pyc | Bin 0 -> 21802 bytes .../__pycache__/langgreekmodel.cpython-39.pyc | Bin 0 -> 20478 bytes .../langhebrewmodel.cpython-39.pyc | Bin 0 -> 20546 bytes .../langhungarianmodel.cpython-39.pyc | Bin 0 -> 21747 bytes .../langrussianmodel.cpython-39.pyc | Bin 0 -> 26350 bytes .../__pycache__/langthaimodel.cpython-39.pyc | Bin 0 -> 20722 bytes .../langturkishmodel.cpython-39.pyc | Bin 0 -> 20562 bytes .../__pycache__/latin1prober.cpython-39.pyc | Bin 0 -> 2947 bytes .../mbcharsetprober.cpython-39.pyc | Bin 0 -> 2254 bytes .../mbcsgroupprober.cpython-39.pyc | Bin 0 -> 1123 bytes .../chardet/__pycache__/mbcssm.cpython-39.pyc | Bin 0 -> 15710 bytes .../sbcharsetprober.cpython-39.pyc | Bin 0 -> 3107 bytes .../sbcsgroupprober.cpython-39.pyc | Bin 0 -> 1692 bytes .../__pycache__/sjisprober.cpython-39.pyc | Bin 0 -> 2475 bytes .../universaldetector.cpython-39.pyc | Bin 0 -> 5823 bytes .../__pycache__/utf8prober.cpython-39.pyc | Bin 0 -> 1984 bytes .../__pycache__/version.cpython-39.pyc | Bin 0 -> 431 bytes .../pip/_vendor/chardet/big5freq.py | 386 + .../pip/_vendor/chardet/big5prober.py | 47 + .../pip/_vendor/chardet/chardistribution.py | 233 + .../pip/_vendor/chardet/charsetgroupprober.py | 107 + .../pip/_vendor/chardet/charsetprober.py | 145 + .../pip/_vendor/chardet/cli/__init__.py | 1 + .../cli/__pycache__/__init__.cpython-39.pyc | Bin 0 -> 188 bytes .../cli/__pycache__/chardetect.cpython-39.pyc | Bin 0 -> 2682 bytes .../pip/_vendor/chardet/cli/chardetect.py | 84 + .../pip/_vendor/chardet/codingstatemachine.py | 88 + .../pip/_vendor/chardet/compat.py | 36 + .../pip/_vendor/chardet/cp949prober.py | 49 + .../pip/_vendor/chardet/enums.py | 76 + .../pip/_vendor/chardet/escprober.py | 101 + .../pip/_vendor/chardet/escsm.py | 246 + .../pip/_vendor/chardet/eucjpprober.py | 92 + .../pip/_vendor/chardet/euckrfreq.py | 195 + .../pip/_vendor/chardet/euckrprober.py | 47 + .../pip/_vendor/chardet/euctwfreq.py | 387 + .../pip/_vendor/chardet/euctwprober.py | 46 + .../pip/_vendor/chardet/gb2312freq.py | 283 + .../pip/_vendor/chardet/gb2312prober.py | 46 + .../pip/_vendor/chardet/hebrewprober.py | 292 + .../pip/_vendor/chardet/jisfreq.py | 325 + .../pip/_vendor/chardet/jpcntx.py | 233 + .../pip/_vendor/chardet/langbulgarianmodel.py | 4650 +++++++++ .../pip/_vendor/chardet/langgreekmodel.py | 4398 +++++++++ .../pip/_vendor/chardet/langhebrewmodel.py | 4383 +++++++++ .../pip/_vendor/chardet/langhungarianmodel.py | 4650 +++++++++ .../pip/_vendor/chardet/langrussianmodel.py | 5718 +++++++++++ .../pip/_vendor/chardet/langthaimodel.py | 4383 +++++++++ .../pip/_vendor/chardet/langturkishmodel.py | 4383 +++++++++ .../pip/_vendor/chardet/latin1prober.py | 145 + .../pip/_vendor/chardet/mbcharsetprober.py | 91 + .../pip/_vendor/chardet/mbcsgroupprober.py | 54 + .../pip/_vendor/chardet/mbcssm.py | 572 ++ .../pip/_vendor/chardet/metadata/__init__.py | 0 .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 193 bytes .../__pycache__/languages.cpython-39.pyc | Bin 0 -> 7924 bytes .../pip/_vendor/chardet/metadata/languages.py | 310 + .../pip/_vendor/chardet/sbcharsetprober.py | 145 + .../pip/_vendor/chardet/sbcsgroupprober.py | 83 + .../pip/_vendor/chardet/sjisprober.py | 92 + .../pip/_vendor/chardet/universaldetector.py | 286 + .../pip/_vendor/chardet/utf8prober.py | 82 + .../pip/_vendor/chardet/version.py | 9 + .../pip/_vendor/colorama/__init__.py | 6 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 436 bytes .../colorama/__pycache__/ansi.cpython-39.pyc | Bin 0 -> 3221 bytes .../__pycache__/ansitowin32.cpython-39.pyc | Bin 0 -> 7687 bytes .../__pycache__/initialise.cpython-39.pyc | Bin 0 -> 1703 bytes .../colorama/__pycache__/win32.cpython-39.pyc | Bin 0 -> 3935 bytes .../__pycache__/winterm.cpython-39.pyc | Bin 0 -> 4657 bytes .../pip/_vendor/colorama/ansi.py | 102 + .../pip/_vendor/colorama/ansitowin32.py | 258 + .../pip/_vendor/colorama/initialise.py | 80 + .../pip/_vendor/colorama/win32.py | 152 + .../pip/_vendor/colorama/winterm.py | 169 + .../pip/_vendor/distlib/__init__.py | 23 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 1049 bytes .../distlib/__pycache__/compat.cpython-39.pyc | Bin 0 -> 32151 bytes .../__pycache__/database.cpython-39.pyc | Bin 0 -> 42473 bytes .../distlib/__pycache__/index.cpython-39.pyc | Bin 0 -> 17494 bytes .../__pycache__/locators.cpython-39.pyc | Bin 0 -> 38482 bytes .../__pycache__/manifest.cpython-39.pyc | Bin 0 -> 10185 bytes .../__pycache__/markers.cpython-39.pyc | Bin 0 -> 4476 bytes .../__pycache__/metadata.cpython-39.pyc | Bin 0 -> 26402 bytes .../__pycache__/resources.cpython-39.pyc | Bin 0 -> 11015 bytes .../__pycache__/scripts.cpython-39.pyc | Bin 0 -> 10923 bytes .../distlib/__pycache__/util.cpython-39.pyc | Bin 0 -> 48195 bytes .../__pycache__/version.cpython-39.pyc | Bin 0 -> 20300 bytes .../distlib/__pycache__/wheel.cpython-39.pyc | Bin 0 -> 26384 bytes .../pip/_vendor/distlib/_backport/__init__.py | 6 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 476 bytes .../_backport/__pycache__/misc.cpython-39.pyc | Bin 0 -> 1096 bytes .../__pycache__/shutil.cpython-39.pyc | Bin 0 -> 21670 bytes .../__pycache__/sysconfig.cpython-39.pyc | Bin 0 -> 15960 bytes .../__pycache__/tarfile.cpython-39.pyc | Bin 0 -> 62724 bytes .../pip/_vendor/distlib/_backport/misc.py | 41 + .../pip/_vendor/distlib/_backport/shutil.py | 764 ++ .../_vendor/distlib/_backport/sysconfig.cfg | 84 + .../_vendor/distlib/_backport/sysconfig.py | 786 ++ .../pip/_vendor/distlib/_backport/tarfile.py | 2607 +++++ .../pip/_vendor/distlib/compat.py | 1120 +++ .../pip/_vendor/distlib/database.py | 1339 +++ .../pip/_vendor/distlib/index.py | 516 + .../pip/_vendor/distlib/locators.py | 1302 +++ .../pip/_vendor/distlib/manifest.py | 393 + .../pip/_vendor/distlib/markers.py | 131 + .../pip/_vendor/distlib/metadata.py | 1056 +++ .../pip/_vendor/distlib/resources.py | 355 + .../pip/_vendor/distlib/scripts.py | 419 + .../site-packages/pip/_vendor/distlib/t32.exe | Bin 0 -> 96768 bytes .../site-packages/pip/_vendor/distlib/t64.exe | Bin 0 -> 105984 bytes .../site-packages/pip/_vendor/distlib/util.py | 1761 ++++ .../pip/_vendor/distlib/version.py | 736 ++ .../site-packages/pip/_vendor/distlib/w32.exe | Bin 0 -> 90112 bytes .../site-packages/pip/_vendor/distlib/w64.exe | Bin 0 -> 99840 bytes .../pip/_vendor/distlib/wheel.py | 1018 ++ venv/Lib/site-packages/pip/_vendor/distro.py | 1230 +++ .../pip/_vendor/html5lib/__init__.py | 35 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 1295 bytes .../__pycache__/_ihatexml.cpython-39.pyc | Bin 0 -> 13764 bytes .../__pycache__/_inputstream.cpython-39.pyc | Bin 0 -> 21623 bytes .../__pycache__/_tokenizer.cpython-39.pyc | Bin 0 -> 39718 bytes .../__pycache__/_utils.cpython-39.pyc | Bin 0 -> 4795 bytes .../__pycache__/constants.cpython-39.pyc | Bin 0 -> 66333 bytes .../__pycache__/html5parser.cpython-39.pyc | Bin 0 -> 91004 bytes .../__pycache__/serializer.cpython-39.pyc | Bin 0 -> 10806 bytes .../pip/_vendor/html5lib/_ihatexml.py | 289 + .../pip/_vendor/html5lib/_inputstream.py | 918 ++ .../pip/_vendor/html5lib/_tokenizer.py | 1735 ++++ .../pip/_vendor/html5lib/_trie/__init__.py | 5 + .../_trie/__pycache__/__init__.cpython-39.pyc | Bin 0 -> 345 bytes .../_trie/__pycache__/_base.cpython-39.pyc | Bin 0 -> 1589 bytes .../_trie/__pycache__/py.cpython-39.pyc | Bin 0 -> 2250 bytes .../pip/_vendor/html5lib/_trie/_base.py | 40 + .../pip/_vendor/html5lib/_trie/py.py | 67 + .../pip/_vendor/html5lib/_utils.py | 159 + .../pip/_vendor/html5lib/constants.py | 2946 ++++++ .../pip/_vendor/html5lib/filters/__init__.py | 0 .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 193 bytes .../alphabeticalattributes.cpython-39.pyc | Bin 0 -> 1315 bytes .../filters/__pycache__/base.cpython-39.pyc | Bin 0 -> 863 bytes .../inject_meta_charset.cpython-39.pyc | Bin 0 -> 1869 bytes .../filters/__pycache__/lint.cpython-39.pyc | Bin 0 -> 2611 bytes .../__pycache__/optionaltags.cpython-39.pyc | Bin 0 -> 2756 bytes .../__pycache__/sanitizer.cpython-39.pyc | Bin 0 -> 16879 bytes .../__pycache__/whitespace.cpython-39.pyc | Bin 0 -> 1361 bytes .../filters/alphabeticalattributes.py | 29 + .../pip/_vendor/html5lib/filters/base.py | 12 + .../html5lib/filters/inject_meta_charset.py | 73 + .../pip/_vendor/html5lib/filters/lint.py | 93 + .../_vendor/html5lib/filters/optionaltags.py | 207 + .../pip/_vendor/html5lib/filters/sanitizer.py | 916 ++ .../_vendor/html5lib/filters/whitespace.py | 38 + .../pip/_vendor/html5lib/html5parser.py | 2795 ++++++ .../pip/_vendor/html5lib/serializer.py | 409 + .../_vendor/html5lib/treeadapters/__init__.py | 30 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 930 bytes .../__pycache__/genshi.cpython-39.pyc | Bin 0 -> 1538 bytes .../__pycache__/sax.cpython-39.pyc | Bin 0 -> 1457 bytes .../_vendor/html5lib/treeadapters/genshi.py | 54 + .../pip/_vendor/html5lib/treeadapters/sax.py | 50 + .../_vendor/html5lib/treebuilders/__init__.py | 88 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 3325 bytes .../__pycache__/base.cpython-39.pyc | Bin 0 -> 11309 bytes .../__pycache__/dom.cpython-39.pyc | Bin 0 -> 9446 bytes .../__pycache__/etree.cpython-39.pyc | Bin 0 -> 11814 bytes .../__pycache__/etree_lxml.cpython-39.pyc | Bin 0 -> 12997 bytes .../pip/_vendor/html5lib/treebuilders/base.py | 417 + .../pip/_vendor/html5lib/treebuilders/dom.py | 239 + .../_vendor/html5lib/treebuilders/etree.py | 343 + .../html5lib/treebuilders/etree_lxml.py | 392 + .../_vendor/html5lib/treewalkers/__init__.py | 154 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 3991 bytes .../__pycache__/base.cpython-39.pyc | Bin 0 -> 6990 bytes .../__pycache__/dom.cpython-39.pyc | Bin 0 -> 1725 bytes .../__pycache__/etree.cpython-39.pyc | Bin 0 -> 3487 bytes .../__pycache__/etree_lxml.cpython-39.pyc | Bin 0 -> 6624 bytes .../__pycache__/genshi.cpython-39.pyc | Bin 0 -> 1881 bytes .../pip/_vendor/html5lib/treewalkers/base.py | 252 + .../pip/_vendor/html5lib/treewalkers/dom.py | 43 + .../pip/_vendor/html5lib/treewalkers/etree.py | 131 + .../html5lib/treewalkers/etree_lxml.py | 215 + .../_vendor/html5lib/treewalkers/genshi.py | 69 + .../pip/_vendor/idna/__init__.py | 2 + .../idna/__pycache__/__init__.cpython-39.pyc | Bin 0 -> 250 bytes .../idna/__pycache__/codec.cpython-39.pyc | Bin 0 -> 2784 bytes .../idna/__pycache__/compat.cpython-39.pyc | Bin 0 -> 622 bytes .../idna/__pycache__/core.cpython-39.pyc | Bin 0 -> 9078 bytes .../idna/__pycache__/idnadata.cpython-39.pyc | Bin 0 -> 22131 bytes .../idna/__pycache__/intranges.cpython-39.pyc | Bin 0 -> 1802 bytes .../__pycache__/package_data.cpython-39.pyc | Bin 0 -> 204 bytes .../idna/__pycache__/uts46data.cpython-39.pyc | Bin 0 -> 146139 bytes .../site-packages/pip/_vendor/idna/codec.py | 110 + .../site-packages/pip/_vendor/idna/compat.py | 12 + .../site-packages/pip/_vendor/idna/core.py | 396 + .../pip/_vendor/idna/idnadata.py | 2050 ++++ .../pip/_vendor/idna/intranges.py | 53 + .../pip/_vendor/idna/package_data.py | 2 + .../pip/_vendor/idna/uts46data.py | 8356 +++++++++++++++++ .../pip/_vendor/msgpack/__init__.py | 54 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 1407 bytes .../__pycache__/_version.cpython-39.pyc | Bin 0 -> 211 bytes .../__pycache__/exceptions.cpython-39.pyc | Bin 0 -> 1845 bytes .../msgpack/__pycache__/ext.cpython-39.pyc | Bin 0 -> 6273 bytes .../__pycache__/fallback.cpython-39.pyc | Bin 0 -> 26719 bytes .../pip/_vendor/msgpack/_version.py | 1 + .../pip/_vendor/msgpack/exceptions.py | 48 + .../site-packages/pip/_vendor/msgpack/ext.py | 193 + .../pip/_vendor/msgpack/fallback.py | 1087 +++ .../pip/_vendor/packaging/__about__.py | 27 + .../pip/_vendor/packaging/__init__.py | 26 + .../__pycache__/__about__.cpython-39.pyc | Bin 0 -> 682 bytes .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 538 bytes .../__pycache__/_compat.cpython-39.pyc | Bin 0 -> 1136 bytes .../__pycache__/_structures.cpython-39.pyc | Bin 0 -> 2890 bytes .../__pycache__/_typing.cpython-39.pyc | Bin 0 -> 1493 bytes .../__pycache__/markers.cpython-39.pyc | Bin 0 -> 9264 bytes .../__pycache__/requirements.cpython-39.pyc | Bin 0 -> 4137 bytes .../__pycache__/specifiers.cpython-39.pyc | Bin 0 -> 20919 bytes .../packaging/__pycache__/tags.cpython-39.pyc | Bin 0 -> 18536 bytes .../__pycache__/utils.cpython-39.pyc | Bin 0 -> 3604 bytes .../__pycache__/version.cpython-39.pyc | Bin 0 -> 12668 bytes .../pip/_vendor/packaging/_compat.py | 38 + .../pip/_vendor/packaging/_structures.py | 86 + .../pip/_vendor/packaging/_typing.py | 48 + .../pip/_vendor/packaging/markers.py | 336 + .../pip/_vendor/packaging/requirements.py | 160 + .../pip/_vendor/packaging/specifiers.py | 864 ++ .../pip/_vendor/packaging/tags.py | 866 ++ .../pip/_vendor/packaging/utils.py | 138 + .../pip/_vendor/packaging/version.py | 556 ++ .../pip/_vendor/pep517/__init__.py | 6 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 306 bytes .../pep517/__pycache__/build.cpython-39.pyc | Bin 0 -> 3555 bytes .../pep517/__pycache__/check.cpython-39.pyc | Bin 0 -> 5090 bytes .../__pycache__/colorlog.cpython-39.pyc | Bin 0 -> 2931 bytes .../pep517/__pycache__/compat.cpython-39.pyc | Bin 0 -> 1108 bytes .../__pycache__/dirtools.cpython-39.pyc | Bin 0 -> 1340 bytes .../__pycache__/envbuild.cpython-39.pyc | Bin 0 -> 4473 bytes .../pep517/__pycache__/meta.cpython-39.pyc | Bin 0 -> 2913 bytes .../__pycache__/wrappers.cpython-39.pyc | Bin 0 -> 10473 bytes .../site-packages/pip/_vendor/pep517/build.py | 127 + .../site-packages/pip/_vendor/pep517/check.py | 206 + .../pip/_vendor/pep517/colorlog.py | 115 + .../pip/_vendor/pep517/compat.py | 34 + .../pip/_vendor/pep517/dirtools.py | 44 + .../pip/_vendor/pep517/envbuild.py | 167 + .../pip/_vendor/pep517/in_process/__init__.py | 17 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 908 bytes .../__pycache__/_in_process.cpython-39.pyc | Bin 0 -> 8270 bytes .../_vendor/pep517/in_process/_in_process.py | 280 + .../site-packages/pip/_vendor/pep517/meta.py | 92 + .../pip/_vendor/pep517/wrappers.py | 318 + .../pip/_vendor/pkg_resources/__init__.py | 3296 +++++++ .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 100324 bytes .../__pycache__/py31compat.cpython-39.pyc | Bin 0 -> 643 bytes .../pip/_vendor/pkg_resources/py31compat.py | 23 + .../pip/_vendor/progress/__init__.py | 177 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 5650 bytes .../progress/__pycache__/bar.cpython-39.pyc | Bin 0 -> 2632 bytes .../__pycache__/counter.cpython-39.pyc | Bin 0 -> 1466 bytes .../__pycache__/spinner.cpython-39.pyc | Bin 0 -> 1383 bytes .../site-packages/pip/_vendor/progress/bar.py | 91 + .../pip/_vendor/progress/counter.py | 41 + .../pip/_vendor/progress/spinner.py | 43 + .../site-packages/pip/_vendor/pyparsing.py | 7107 ++++++++++++++ .../pip/_vendor/requests/__init__.py | 142 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 3662 bytes .../__pycache__/__version__.cpython-39.pyc | Bin 0 -> 548 bytes .../_internal_utils.cpython-39.pyc | Bin 0 -> 1295 bytes .../__pycache__/adapters.cpython-39.pyc | Bin 0 -> 16966 bytes .../requests/__pycache__/api.cpython-39.pyc | Bin 0 -> 6752 bytes .../requests/__pycache__/auth.cpython-39.pyc | Bin 0 -> 8324 bytes .../requests/__pycache__/certs.cpython-39.pyc | Bin 0 -> 626 bytes .../__pycache__/compat.cpython-39.pyc | Bin 0 -> 1605 bytes .../__pycache__/cookies.cpython-39.pyc | Bin 0 -> 18815 bytes .../__pycache__/exceptions.cpython-39.pyc | Bin 0 -> 5238 bytes .../requests/__pycache__/help.cpython-39.pyc | Bin 0 -> 2709 bytes .../requests/__pycache__/hooks.cpython-39.pyc | Bin 0 -> 983 bytes .../__pycache__/models.cpython-39.pyc | Bin 0 -> 23932 bytes .../__pycache__/packages.cpython-39.pyc | Bin 0 -> 495 bytes .../__pycache__/sessions.cpython-39.pyc | Bin 0 -> 19837 bytes .../__pycache__/status_codes.cpython-39.pyc | Bin 0 -> 4232 bytes .../__pycache__/structures.cpython-39.pyc | Bin 0 -> 4453 bytes .../requests/__pycache__/utils.cpython-39.pyc | Bin 0 -> 22610 bytes .../pip/_vendor/requests/__version__.py | 14 + .../pip/_vendor/requests/_internal_utils.py | 42 + .../pip/_vendor/requests/adapters.py | 533 ++ .../site-packages/pip/_vendor/requests/api.py | 161 + .../pip/_vendor/requests/auth.py | 305 + .../pip/_vendor/requests/certs.py | 18 + .../pip/_vendor/requests/compat.py | 76 + .../pip/_vendor/requests/cookies.py | 549 ++ .../pip/_vendor/requests/exceptions.py | 123 + .../pip/_vendor/requests/help.py | 119 + .../pip/_vendor/requests/hooks.py | 34 + .../pip/_vendor/requests/models.py | 956 ++ .../pip/_vendor/requests/packages.py | 16 + .../pip/_vendor/requests/sessions.py | 781 ++ .../pip/_vendor/requests/status_codes.py | 123 + .../pip/_vendor/requests/structures.py | 105 + .../pip/_vendor/requests/utils.py | 992 ++ .../pip/_vendor/resolvelib/__init__.py | 26 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 599 bytes .../__pycache__/providers.cpython-39.pyc | Bin 0 -> 6519 bytes .../__pycache__/reporters.cpython-39.pyc | Bin 0 -> 2295 bytes .../__pycache__/resolvers.cpython-39.pyc | Bin 0 -> 15190 bytes .../__pycache__/structs.cpython-39.pyc | Bin 0 -> 6913 bytes .../pip/_vendor/resolvelib/compat/__init__.py | 0 .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 194 bytes .../collections_abc.cpython-39.pyc | Bin 0 -> 368 bytes .../resolvelib/compat/collections_abc.py | 6 + .../pip/_vendor/resolvelib/providers.py | 124 + .../pip/_vendor/resolvelib/reporters.py | 37 + .../pip/_vendor/resolvelib/resolvers.py | 474 + .../pip/_vendor/resolvelib/structs.py | 153 + venv/Lib/site-packages/pip/_vendor/six.py | 982 ++ .../pip/_vendor/tenacity/__init__.py | 525 ++ .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 14671 bytes .../__pycache__/_asyncio.cpython-39.pyc | Bin 0 -> 2196 bytes .../__pycache__/_utils.cpython-39.pyc | Bin 0 -> 3944 bytes .../tenacity/__pycache__/after.cpython-39.pyc | Bin 0 -> 971 bytes .../__pycache__/before.cpython-39.pyc | Bin 0 -> 867 bytes .../__pycache__/before_sleep.cpython-39.pyc | Bin 0 -> 1215 bytes .../__pycache__/compat.cpython-39.pyc | Bin 0 -> 951 bytes .../tenacity/__pycache__/nap.cpython-39.pyc | Bin 0 -> 1020 bytes .../tenacity/__pycache__/retry.cpython-39.pyc | Bin 0 -> 7405 bytes .../tenacity/__pycache__/stop.cpython-39.pyc | Bin 0 -> 3781 bytes .../__pycache__/tornadoweb.cpython-39.pyc | Bin 0 -> 1335 bytes .../tenacity/__pycache__/wait.cpython-39.pyc | Bin 0 -> 7338 bytes .../pip/_vendor/tenacity/_asyncio.py | 81 + .../pip/_vendor/tenacity/_utils.py | 159 + .../pip/_vendor/tenacity/after.py | 40 + .../pip/_vendor/tenacity/before.py | 35 + .../pip/_vendor/tenacity/before_sleep.py | 51 + .../pip/_vendor/tenacity/compat.py | 23 + .../site-packages/pip/_vendor/tenacity/nap.py | 40 + .../pip/_vendor/tenacity/retry.py | 192 + .../pip/_vendor/tenacity/stop.py | 95 + .../pip/_vendor/tenacity/tornadoweb.py | 49 + .../pip/_vendor/tenacity/wait.py | 183 + .../pip/_vendor/toml/__init__.py | 25 + .../toml/__pycache__/__init__.cpython-39.pyc | Bin 0 -> 725 bytes .../toml/__pycache__/decoder.cpython-39.pyc | Bin 0 -> 23217 bytes .../toml/__pycache__/encoder.cpython-39.pyc | Bin 0 -> 9400 bytes .../toml/__pycache__/ordered.cpython-39.pyc | Bin 0 -> 964 bytes .../toml/__pycache__/tz.cpython-39.pyc | Bin 0 -> 1266 bytes .../site-packages/pip/_vendor/toml/decoder.py | 1057 +++ .../site-packages/pip/_vendor/toml/encoder.py | 304 + .../site-packages/pip/_vendor/toml/ordered.py | 15 + venv/Lib/site-packages/pip/_vendor/toml/tz.py | 24 + .../pip/_vendor/urllib3/__init__.py | 85 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 2182 bytes .../__pycache__/_collections.cpython-39.pyc | Bin 0 -> 10777 bytes .../__pycache__/_version.cpython-39.pyc | Bin 0 -> 206 bytes .../__pycache__/connection.cpython-39.pyc | Bin 0 -> 13362 bytes .../__pycache__/connectionpool.cpython-39.pyc | Bin 0 -> 24461 bytes .../__pycache__/exceptions.cpython-39.pyc | Bin 0 -> 11639 bytes .../urllib3/__pycache__/fields.cpython-39.pyc | Bin 0 -> 8154 bytes .../__pycache__/filepost.cpython-39.pyc | Bin 0 -> 2755 bytes .../__pycache__/poolmanager.cpython-39.pyc | Bin 0 -> 15157 bytes .../__pycache__/request.cpython-39.pyc | Bin 0 -> 5618 bytes .../__pycache__/response.cpython-39.pyc | Bin 0 -> 20829 bytes .../pip/_vendor/urllib3/_collections.py | 337 + .../pip/_vendor/urllib3/_version.py | 2 + .../pip/_vendor/urllib3/connection.py | 539 ++ .../pip/_vendor/urllib3/connectionpool.py | 1067 +++ .../pip/_vendor/urllib3/contrib/__init__.py | 0 .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 192 bytes .../_appengine_environ.cpython-39.pyc | Bin 0 -> 1412 bytes .../__pycache__/appengine.cpython-39.pyc | Bin 0 -> 8265 bytes .../__pycache__/ntlmpool.cpython-39.pyc | Bin 0 -> 3257 bytes .../__pycache__/pyopenssl.cpython-39.pyc | Bin 0 -> 15524 bytes .../securetransport.cpython-39.pyc | Bin 0 -> 21836 bytes .../contrib/__pycache__/socks.cpython-39.pyc | Bin 0 -> 5628 bytes .../urllib3/contrib/_appengine_environ.py | 36 + .../contrib/_securetransport/__init__.py | 0 .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 209 bytes .../__pycache__/bindings.cpython-39.pyc | Bin 0 -> 10716 bytes .../__pycache__/low_level.cpython-39.pyc | Bin 0 -> 9165 bytes .../contrib/_securetransport/bindings.py | 519 + .../contrib/_securetransport/low_level.py | 396 + .../pip/_vendor/urllib3/contrib/appengine.py | 314 + .../pip/_vendor/urllib3/contrib/ntlmpool.py | 121 + .../pip/_vendor/urllib3/contrib/pyopenssl.py | 509 + .../urllib3/contrib/securetransport.py | 920 ++ .../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 | 5 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 306 bytes .../packages/__pycache__/six.cpython-39.pyc | Bin 0 -> 26510 bytes .../urllib3/packages/backports/__init__.py | 0 .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 203 bytes .../__pycache__/makefile.cpython-39.pyc | Bin 0 -> 1301 bytes .../urllib3/packages/backports/makefile.py | 51 + .../pip/_vendor/urllib3/packages/six.py | 1021 ++ .../packages/ssl_match_hostname/__init__.py | 22 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 543 bytes .../_implementation.cpython-39.pyc | Bin 0 -> 3296 bytes .../ssl_match_hostname/_implementation.py | 160 + .../pip/_vendor/urllib3/poolmanager.py | 536 ++ .../pip/_vendor/urllib3/request.py | 170 + .../pip/_vendor/urllib3/response.py | 821 ++ .../pip/_vendor/urllib3/util/__init__.py | 49 + .../util/__pycache__/__init__.cpython-39.pyc | Bin 0 -> 1102 bytes .../__pycache__/connection.cpython-39.pyc | Bin 0 -> 3456 bytes .../util/__pycache__/proxy.cpython-39.pyc | Bin 0 -> 1338 bytes .../util/__pycache__/queue.cpython-39.pyc | Bin 0 -> 1057 bytes .../util/__pycache__/request.cpython-39.pyc | Bin 0 -> 3445 bytes .../util/__pycache__/response.cpython-39.pyc | Bin 0 -> 2342 bytes .../util/__pycache__/retry.cpython-39.pyc | Bin 0 -> 15959 bytes .../util/__pycache__/ssl_.cpython-39.pyc | Bin 0 -> 11020 bytes .../__pycache__/ssltransport.cpython-39.pyc | Bin 0 -> 7508 bytes .../util/__pycache__/timeout.cpython-39.pyc | Bin 0 -> 8940 bytes .../util/__pycache__/url.cpython-39.pyc | Bin 0 -> 10616 bytes .../util/__pycache__/wait.cpython-39.pyc | Bin 0 -> 3125 bytes .../pip/_vendor/urllib3/util/connection.py | 150 + .../pip/_vendor/urllib3/util/proxy.py | 56 + .../pip/_vendor/urllib3/util/queue.py | 22 + .../pip/_vendor/urllib3/util/request.py | 143 + .../pip/_vendor/urllib3/util/response.py | 107 + .../pip/_vendor/urllib3/util/retry.py | 602 ++ .../pip/_vendor/urllib3/util/ssl_.py | 474 + .../pip/_vendor/urllib3/util/ssltransport.py | 221 + .../pip/_vendor/urllib3/util/timeout.py | 268 + .../pip/_vendor/urllib3/util/url.py | 430 + .../pip/_vendor/urllib3/util/wait.py | 153 + venv/Lib/site-packages/pip/_vendor/vendor.txt | 22 + .../pip/_vendor/webencodings/__init__.py | 342 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 9716 bytes .../__pycache__/labels.cpython-39.pyc | Bin 0 -> 3830 bytes .../__pycache__/mklabels.cpython-39.pyc | Bin 0 -> 1900 bytes .../__pycache__/tests.cpython-39.pyc | Bin 0 -> 5064 bytes .../__pycache__/x_user_defined.cpython-39.pyc | Bin 0 -> 2660 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 + .../site-packages/pkg_resources/__init__.py | 3288 +++++++ .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 100432 bytes .../pkg_resources/_vendor/__init__.py | 0 .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 186 bytes .../__pycache__/appdirs.cpython-39.pyc | Bin 0 -> 20503 bytes .../__pycache__/pyparsing.cpython-39.pyc | Bin 0 -> 201340 bytes .../pkg_resources/_vendor/appdirs.py | 608 ++ .../_vendor/packaging/__about__.py | 27 + .../_vendor/packaging/__init__.py | 26 + .../__pycache__/__about__.cpython-39.pyc | Bin 0 -> 702 bytes .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 548 bytes .../__pycache__/_compat.cpython-39.pyc | Bin 0 -> 1146 bytes .../__pycache__/_structures.cpython-39.pyc | Bin 0 -> 2900 bytes .../__pycache__/_typing.cpython-39.pyc | Bin 0 -> 1491 bytes .../__pycache__/markers.cpython-39.pyc | Bin 0 -> 9307 bytes .../__pycache__/requirements.cpython-39.pyc | Bin 0 -> 4084 bytes .../__pycache__/specifiers.cpython-39.pyc | Bin 0 -> 20583 bytes .../packaging/__pycache__/tags.cpython-39.pyc | Bin 0 -> 17262 bytes .../__pycache__/utils.cpython-39.pyc | Bin 0 -> 1653 bytes .../__pycache__/version.cpython-39.pyc | Bin 0 -> 13320 bytes .../_vendor/packaging/_compat.py | 38 + .../_vendor/packaging/_structures.py | 86 + .../_vendor/packaging/_typing.py | 48 + .../_vendor/packaging/markers.py | 328 + .../_vendor/packaging/requirements.py | 145 + .../_vendor/packaging/specifiers.py | 863 ++ .../pkg_resources/_vendor/packaging/tags.py | 751 ++ .../pkg_resources/_vendor/packaging/utils.py | 65 + .../_vendor/packaging/version.py | 535 ++ .../pkg_resources/_vendor/pyparsing.py | 5742 +++++++++++ .../pkg_resources/extern/__init__.py | 73 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 2875 bytes .../__pycache__/setup.cpython-39.pyc | Bin 0 -> 314 bytes .../data/my-test-package-source/setup.py | 6 + .../pluggy-0.13.1.dist-info/INSTALLER | 1 + .../pluggy-0.13.1.dist-info/LICENSE | 21 + .../pluggy-0.13.1.dist-info/METADATA | 482 + .../pluggy-0.13.1.dist-info/RECORD | 18 + .../pluggy-0.13.1.dist-info/WHEEL | 6 + .../pluggy-0.13.1.dist-info/top_level.txt | 1 + venv/Lib/site-packages/pluggy/__init__.py | 18 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 499 bytes .../__pycache__/_tracing.cpython-39.pyc | Bin 0 -> 2304 bytes .../__pycache__/_version.cpython-39.pyc | Bin 0 -> 171 bytes .../pluggy/__pycache__/callers.cpython-39.pyc | Bin 0 -> 6011 bytes .../pluggy/__pycache__/hooks.cpython-39.pyc | Bin 0 -> 11714 bytes .../pluggy/__pycache__/manager.cpython-39.pyc | Bin 0 -> 14581 bytes venv/Lib/site-packages/pluggy/_tracing.py | 62 + venv/Lib/site-packages/pluggy/_version.py | 4 + venv/Lib/site-packages/pluggy/callers.py | 208 + venv/Lib/site-packages/pluggy/hooks.py | 359 + venv/Lib/site-packages/pluggy/manager.py | 394 + .../py-1.10.0.dist-info/INSTALLER | 1 + .../site-packages/py-1.10.0.dist-info/LICENSE | 19 + .../py-1.10.0.dist-info/METADATA | 66 + .../site-packages/py-1.10.0.dist-info/RECORD | 100 + .../site-packages/py-1.10.0.dist-info/WHEEL | 6 + .../py-1.10.0.dist-info/top_level.txt | 1 + venv/Lib/site-packages/py/__init__.py | 156 + venv/Lib/site-packages/py/__init__.pyi | 20 + venv/Lib/site-packages/py/__metainfo.py | 2 + .../py/__pycache__/__init__.cpython-39.pyc | Bin 0 -> 4106 bytes .../py/__pycache__/__metainfo.cpython-39.pyc | Bin 0 -> 224 bytes .../py/__pycache__/_builtin.cpython-39.pyc | Bin 0 -> 4062 bytes .../py/__pycache__/_error.cpython-39.pyc | Bin 0 -> 2702 bytes .../py/__pycache__/_std.cpython-39.pyc | Bin 0 -> 1157 bytes .../py/__pycache__/_version.cpython-39.pyc | Bin 0 -> 167 bytes .../py/__pycache__/_xmlgen.cpython-39.pyc | Bin 0 -> 10259 bytes .../py/__pycache__/test.cpython-39.pyc | Bin 0 -> 286 bytes venv/Lib/site-packages/py/_builtin.py | 149 + venv/Lib/site-packages/py/_code/__init__.py | 1 + .../_code/__pycache__/__init__.cpython-39.pyc | Bin 0 -> 206 bytes .../__pycache__/_assertionnew.cpython-39.pyc | Bin 0 -> 9413 bytes .../__pycache__/_assertionold.cpython-39.pyc | Bin 0 -> 16293 bytes .../__pycache__/_py2traceback.cpython-39.pyc | Bin 0 -> 2114 bytes .../__pycache__/assertion.cpython-39.pyc | Bin 0 -> 2529 bytes .../py/_code/__pycache__/code.cpython-39.pyc | Bin 0 -> 26417 bytes .../_code/__pycache__/source.cpython-39.pyc | Bin 0 -> 11846 bytes .../site-packages/py/_code/_assertionnew.py | 322 + .../site-packages/py/_code/_assertionold.py | 556 ++ .../site-packages/py/_code/_py2traceback.py | 79 + venv/Lib/site-packages/py/_code/assertion.py | 90 + venv/Lib/site-packages/py/_code/code.py | 796 ++ venv/Lib/site-packages/py/_code/source.py | 410 + venv/Lib/site-packages/py/_error.py | 91 + venv/Lib/site-packages/py/_io/__init__.py | 1 + .../_io/__pycache__/__init__.cpython-39.pyc | Bin 0 -> 187 bytes .../py/_io/__pycache__/capture.cpython-39.pyc | Bin 0 -> 11547 bytes .../_io/__pycache__/saferepr.cpython-39.pyc | Bin 0 -> 2666 bytes .../__pycache__/terminalwriter.cpython-39.pyc | Bin 0 -> 11207 bytes venv/Lib/site-packages/py/_io/capture.py | 371 + venv/Lib/site-packages/py/_io/saferepr.py | 71 + .../site-packages/py/_io/terminalwriter.py | 421 + venv/Lib/site-packages/py/_log/__init__.py | 2 + .../_log/__pycache__/__init__.cpython-39.pyc | Bin 0 -> 232 bytes .../py/_log/__pycache__/log.cpython-39.pyc | Bin 0 -> 7417 bytes .../_log/__pycache__/warning.cpython-39.pyc | Bin 0 -> 2255 bytes venv/Lib/site-packages/py/_log/log.py | 206 + venv/Lib/site-packages/py/_log/warning.py | 79 + venv/Lib/site-packages/py/_path/__init__.py | 1 + .../_path/__pycache__/__init__.cpython-39.pyc | Bin 0 -> 192 bytes .../__pycache__/cacheutil.cpython-39.pyc | Bin 0 -> 4721 bytes .../_path/__pycache__/common.cpython-39.pyc | Bin 0 -> 15017 bytes .../py/_path/__pycache__/local.cpython-39.pyc | Bin 0 -> 31224 bytes .../_path/__pycache__/svnurl.cpython-39.pyc | Bin 0 -> 13344 bytes .../py/_path/__pycache__/svnwc.cpython-39.pyc | Bin 0 -> 36128 bytes venv/Lib/site-packages/py/_path/cacheutil.py | 114 + venv/Lib/site-packages/py/_path/common.py | 459 + venv/Lib/site-packages/py/_path/local.py | 1030 ++ venv/Lib/site-packages/py/_path/svnurl.py | 380 + venv/Lib/site-packages/py/_path/svnwc.py | 1240 +++ .../Lib/site-packages/py/_process/__init__.py | 1 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 203 bytes .../__pycache__/cmdexec.cpython-39.pyc | Bin 0 -> 1877 bytes .../__pycache__/forkedfunc.cpython-39.pyc | Bin 0 -> 3693 bytes .../__pycache__/killproc.cpython-39.pyc | Bin 0 -> 987 bytes venv/Lib/site-packages/py/_process/cmdexec.py | 49 + .../site-packages/py/_process/forkedfunc.py | 120 + .../Lib/site-packages/py/_process/killproc.py | 23 + venv/Lib/site-packages/py/_std.py | 27 + .../py/_vendored_packages/__init__.py | 0 .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 168 bytes .../apipkg-1.5.dist-info/INSTALLER | 1 + .../apipkg-1.5.dist-info/METADATA | 115 + .../apipkg-1.5.dist-info/RECORD | 10 + .../apipkg-1.5.dist-info/REQUESTED | 0 .../apipkg-1.5.dist-info/WHEEL | 6 + .../apipkg-1.5.dist-info/top_level.txt | 1 + .../py/_vendored_packages/apipkg/__init__.py | 209 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 6204 bytes .../apipkg/__pycache__/version.cpython-39.pyc | Bin 0 -> 189 bytes .../py/_vendored_packages/apipkg/version.py | 4 + .../iniconfig-1.1.1.dist-info/INSTALLER | 1 + .../iniconfig-1.1.1.dist-info/LICENSE | 19 + .../iniconfig-1.1.1.dist-info/METADATA | 78 + .../iniconfig-1.1.1.dist-info/RECORD | 11 + .../iniconfig-1.1.1.dist-info/REQUESTED | 0 .../iniconfig-1.1.1.dist-info/WHEEL | 6 + .../iniconfig-1.1.1.dist-info/top_level.txt | 1 + .../_vendored_packages/iniconfig/__init__.py | 165 + .../_vendored_packages/iniconfig/__init__.pyi | 31 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 5232 bytes .../py/_vendored_packages/iniconfig/py.typed | 0 venv/Lib/site-packages/py/_version.py | 4 + venv/Lib/site-packages/py/_xmlgen.py | 255 + venv/Lib/site-packages/py/error.pyi | 129 + venv/Lib/site-packages/py/iniconfig.pyi | 31 + venv/Lib/site-packages/py/io.pyi | 130 + venv/Lib/site-packages/py/path.pyi | 197 + venv/Lib/site-packages/py/py.typed | 0 venv/Lib/site-packages/py/test.py | 10 + venv/Lib/site-packages/py/xml.pyi | 25 + .../pyparsing-2.4.7.dist-info/INSTALLER | 1 + .../pyparsing-2.4.7.dist-info/LICENSE | 18 + .../pyparsing-2.4.7.dist-info/METADATA | 104 + .../pyparsing-2.4.7.dist-info/RECORD | 8 + .../pyparsing-2.4.7.dist-info/WHEEL | 6 + .../pyparsing-2.4.7.dist-info/top_level.txt | 1 + venv/Lib/site-packages/pyparsing.py | 7107 ++++++++++++++ .../pyserial-3.5.dist-info/DESCRIPTION.rst | 13 + .../pyserial-3.5.dist-info/INSTALLER | 1 + .../pyserial-3.5.dist-info/METADATA | 47 + .../pyserial-3.5.dist-info/RECORD | 66 + .../pyserial-3.5.dist-info/WHEEL | 6 + .../pyserial-3.5.dist-info/entry_points.txt | 4 + .../pyserial-3.5.dist-info/metadata.json | 1 + .../pyserial-3.5.dist-info/top_level.txt | 1 + .../pytest-6.2.4.dist-info/INSTALLER | 1 + .../pytest-6.2.4.dist-info/LICENSE | 21 + .../pytest-6.2.4.dist-info/METADATA | 209 + .../pytest-6.2.4.dist-info/RECORD | 139 + .../pytest-6.2.4.dist-info/WHEEL | 5 + .../pytest-6.2.4.dist-info/entry_points.txt | 4 + .../pytest-6.2.4.dist-info/top_level.txt | 2 + venv/Lib/site-packages/pytest/__init__.py | 121 + venv/Lib/site-packages/pytest/__main__.py | 5 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 2935 bytes .../__pycache__/__main__.cpython-39.pyc | Bin 0 -> 281 bytes .../pytest/__pycache__/collect.cpython-39.pyc | Bin 0 -> 1444 bytes venv/Lib/site-packages/pytest/collect.py | 39 + venv/Lib/site-packages/pytest/py.typed | 0 venv/Lib/site-packages/serial/__init__.py | 91 + venv/Lib/site-packages/serial/__main__.py | 3 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 2147 bytes .../__pycache__/__main__.cpython-39.pyc | Bin 0 -> 207 bytes .../serial/__pycache__/rfc2217.cpython-39.pyc | Bin 0 -> 32539 bytes .../serial/__pycache__/rs485.cpython-39.pyc | Bin 0 -> 2915 bytes .../__pycache__/serialcli.cpython-39.pyc | Bin 0 -> 6711 bytes .../__pycache__/serialjava.cpython-39.pyc | Bin 0 -> 7424 bytes .../__pycache__/serialposix.cpython-39.pyc | Bin 0 -> 21806 bytes .../__pycache__/serialutil.cpython-39.pyc | Bin 0 -> 18621 bytes .../__pycache__/serialwin32.cpython-39.pyc | Bin 0 -> 13092 bytes .../serial/__pycache__/win32.cpython-39.pyc | Bin 0 -> 6389 bytes venv/Lib/site-packages/serial/rfc2217.py | 1351 +++ venv/Lib/site-packages/serial/rs485.py | 94 + venv/Lib/site-packages/serial/serialcli.py | 253 + venv/Lib/site-packages/serial/serialjava.py | 251 + venv/Lib/site-packages/serial/serialposix.py | 900 ++ venv/Lib/site-packages/serial/serialutil.py | 697 ++ venv/Lib/site-packages/serial/serialwin32.py | 477 + .../site-packages/serial/threaded/__init__.py | 297 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 9523 bytes .../site-packages/serial/tools/__init__.py | 0 .../tools/__pycache__/__init__.cpython-39.pyc | Bin 0 -> 159 bytes .../__pycache__/hexlify_codec.cpython-39.pyc | Bin 0 -> 4829 bytes .../__pycache__/list_ports.cpython-39.pyc | Bin 0 -> 2525 bytes .../list_ports_common.cpython-39.pyc | Bin 0 -> 3543 bytes .../list_ports_linux.cpython-39.pyc | Bin 0 -> 3017 bytes .../__pycache__/list_ports_osx.cpython-39.pyc | Bin 0 -> 6337 bytes .../list_ports_posix.cpython-39.pyc | Bin 0 -> 3908 bytes .../list_ports_windows.cpython-39.pyc | Bin 0 -> 7616 bytes .../tools/__pycache__/miniterm.cpython-39.pyc | Bin 0 -> 29301 bytes .../serial/tools/hexlify_codec.py | 126 + .../site-packages/serial/tools/list_ports.py | 110 + .../serial/tools/list_ports_common.py | 121 + .../serial/tools/list_ports_linux.py | 109 + .../serial/tools/list_ports_osx.py | 299 + .../serial/tools/list_ports_posix.py | 119 + .../serial/tools/list_ports_windows.py | 427 + .../site-packages/serial/tools/miniterm.py | 1042 ++ .../serial/urlhandler/__init__.py | 0 .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 164 bytes .../__pycache__/protocol_alt.cpython-39.pyc | Bin 0 -> 1386 bytes .../protocol_cp2110.cpython-39.pyc | Bin 0 -> 5707 bytes .../protocol_hwgrep.cpython-39.pyc | Bin 0 -> 1848 bytes .../__pycache__/protocol_loop.cpython-39.pyc | Bin 0 -> 8287 bytes .../protocol_rfc2217.cpython-39.pyc | Bin 0 -> 267 bytes .../protocol_socket.cpython-39.pyc | Bin 0 -> 9023 bytes .../__pycache__/protocol_spy.cpython-39.pyc | Bin 0 -> 9125 bytes .../serial/urlhandler/protocol_alt.py | 57 + .../serial/urlhandler/protocol_cp2110.py | 258 + .../serial/urlhandler/protocol_hwgrep.py | 91 + .../serial/urlhandler/protocol_loop.py | 308 + .../serial/urlhandler/protocol_rfc2217.py | 12 + .../serial/urlhandler/protocol_socket.py | 359 + .../serial/urlhandler/protocol_spy.py | 290 + venv/Lib/site-packages/serial/win32.py | 366 + .../setuptools-57.0.0.dist-info/INSTALLER | 1 + .../setuptools-57.0.0.dist-info/LICENSE | 19 + .../setuptools-57.0.0.dist-info/METADATA | 119 + .../setuptools-57.0.0.dist-info/RECORD | 300 + .../setuptools-57.0.0.dist-info/WHEEL | 5 + .../dependency_links.txt | 2 + .../entry_points.txt | 60 + .../setuptools-57.0.0.dist-info/top_level.txt | 3 + venv/Lib/site-packages/setuptools/__init__.py | 241 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 8619 bytes .../_deprecation_warning.cpython-39.pyc | Bin 0 -> 545 bytes .../__pycache__/_imp.cpython-39.pyc | Bin 0 -> 2080 bytes .../__pycache__/archive_util.cpython-39.pyc | Bin 0 -> 5809 bytes .../__pycache__/build_meta.cpython-39.pyc | Bin 0 -> 9068 bytes .../__pycache__/config.cpython-39.pyc | Bin 0 -> 19958 bytes .../__pycache__/dep_util.cpython-39.pyc | Bin 0 -> 852 bytes .../__pycache__/depends.cpython-39.pyc | Bin 0 -> 5244 bytes .../__pycache__/dist.cpython-39.pyc | Bin 0 -> 36035 bytes .../__pycache__/errors.cpython-39.pyc | Bin 0 -> 845 bytes .../__pycache__/extension.cpython-39.pyc | Bin 0 -> 1939 bytes .../__pycache__/glob.cpython-39.pyc | Bin 0 -> 3689 bytes .../__pycache__/installer.cpython-39.pyc | Bin 0 -> 2766 bytes .../__pycache__/launch.cpython-39.pyc | Bin 0 -> 896 bytes .../__pycache__/lib2to3_ex.cpython-39.pyc | Bin 0 -> 2698 bytes .../__pycache__/monkey.cpython-39.pyc | Bin 0 -> 4608 bytes .../__pycache__/msvc.cpython-39.pyc | Bin 0 -> 42834 bytes .../__pycache__/namespaces.cpython-39.pyc | Bin 0 -> 3595 bytes .../__pycache__/package_index.cpython-39.pyc | Bin 0 -> 32775 bytes .../__pycache__/py34compat.cpython-39.pyc | Bin 0 -> 475 bytes .../__pycache__/sandbox.cpython-39.pyc | Bin 0 -> 15769 bytes .../__pycache__/ssl_support.cpython-39.pyc | Bin 0 -> 6856 bytes .../__pycache__/unicode_utils.cpython-39.pyc | Bin 0 -> 1109 bytes .../__pycache__/version.cpython-39.pyc | Bin 0 -> 319 bytes .../__pycache__/wheel.cpython-39.pyc | Bin 0 -> 7275 bytes .../windows_support.cpython-39.pyc | Bin 0 -> 1018 bytes .../setuptools/_deprecation_warning.py | 7 + .../setuptools/_distutils/__init__.py | 15 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 453 bytes .../__pycache__/_msvccompiler.cpython-39.pyc | Bin 0 -> 13808 bytes .../__pycache__/archive_util.cpython-39.pyc | Bin 0 -> 6640 bytes .../__pycache__/bcppcompiler.cpython-39.pyc | Bin 0 -> 6551 bytes .../__pycache__/ccompiler.cpython-39.pyc | Bin 0 -> 33419 bytes .../_distutils/__pycache__/cmd.cpython-39.pyc | Bin 0 -> 13979 bytes .../__pycache__/config.cpython-39.pyc | Bin 0 -> 3582 bytes .../__pycache__/core.cpython-39.pyc | Bin 0 -> 6707 bytes .../cygwinccompiler.cpython-39.pyc | Bin 0 -> 8556 bytes .../__pycache__/debug.cpython-39.pyc | Bin 0 -> 249 bytes .../__pycache__/dep_util.cpython-39.pyc | Bin 0 -> 2769 bytes .../__pycache__/dir_util.cpython-39.pyc | Bin 0 -> 5870 bytes .../__pycache__/dist.cpython-39.pyc | Bin 0 -> 34440 bytes .../__pycache__/errors.cpython-39.pyc | Bin 0 -> 5305 bytes .../__pycache__/extension.cpython-39.pyc | Bin 0 -> 6970 bytes .../__pycache__/fancy_getopt.cpython-39.pyc | Bin 0 -> 10678 bytes .../__pycache__/file_util.cpython-39.pyc | Bin 0 -> 6036 bytes .../__pycache__/filelist.cpython-39.pyc | Bin 0 -> 9888 bytes .../_distutils/__pycache__/log.cpython-39.pyc | Bin 0 -> 2368 bytes .../__pycache__/msvc9compiler.cpython-39.pyc | Bin 0 -> 17565 bytes .../__pycache__/msvccompiler.cpython-39.pyc | Bin 0 -> 14760 bytes .../__pycache__/py35compat.cpython-39.pyc | Bin 0 -> 625 bytes .../__pycache__/py38compat.cpython-39.pyc | Bin 0 -> 420 bytes .../__pycache__/spawn.cpython-39.pyc | Bin 0 -> 3529 bytes .../__pycache__/sysconfig.cpython-39.pyc | Bin 0 -> 12402 bytes .../__pycache__/text_file.cpython-39.pyc | Bin 0 -> 8494 bytes .../__pycache__/unixccompiler.cpython-39.pyc | Bin 0 -> 6655 bytes .../__pycache__/util.cpython-39.pyc | Bin 0 -> 15688 bytes .../__pycache__/version.cpython-39.pyc | Bin 0 -> 7394 bytes .../versionpredicate.cpython-39.pyc | Bin 0 -> 5178 bytes .../setuptools/_distutils/_msvccompiler.py | 561 ++ .../setuptools/_distutils/archive_util.py | 256 + .../setuptools/_distutils/bcppcompiler.py | 393 + .../setuptools/_distutils/ccompiler.py | 1123 +++ .../setuptools/_distutils/cmd.py | 403 + .../setuptools/_distutils/command/__init__.py | 31 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 528 bytes .../command/__pycache__/bdist.cpython-39.pyc | Bin 0 -> 3658 bytes .../__pycache__/bdist_dumb.cpython-39.pyc | Bin 0 -> 3641 bytes .../__pycache__/bdist_msi.cpython-39.pyc | Bin 0 -> 19823 bytes .../__pycache__/bdist_rpm.cpython-39.pyc | Bin 0 -> 12278 bytes .../__pycache__/bdist_wininst.cpython-39.pyc | Bin 0 -> 8598 bytes .../command/__pycache__/build.cpython-39.pyc | Bin 0 -> 3934 bytes .../__pycache__/build_clib.cpython-39.pyc | Bin 0 -> 4853 bytes .../__pycache__/build_ext.cpython-39.pyc | Bin 0 -> 16302 bytes .../__pycache__/build_py.cpython-39.pyc | Bin 0 -> 10486 bytes .../__pycache__/build_scripts.cpython-39.pyc | Bin 0 -> 4383 bytes .../command/__pycache__/check.cpython-39.pyc | Bin 0 -> 4962 bytes .../command/__pycache__/clean.cpython-39.pyc | Bin 0 -> 2135 bytes .../command/__pycache__/config.cpython-39.pyc | Bin 0 -> 10265 bytes .../__pycache__/install.cpython-39.pyc | Bin 0 -> 13852 bytes .../__pycache__/install_data.cpython-39.pyc | Bin 0 -> 2338 bytes .../install_egg_info.cpython-39.pyc | Bin 0 -> 3073 bytes .../install_headers.cpython-39.pyc | Bin 0 -> 1763 bytes .../__pycache__/install_lib.cpython-39.pyc | Bin 0 -> 5135 bytes .../install_scripts.cpython-39.pyc | Bin 0 -> 2186 bytes .../__pycache__/py37compat.cpython-39.pyc | Bin 0 -> 1029 bytes .../__pycache__/register.cpython-39.pyc | Bin 0 -> 8506 bytes .../command/__pycache__/sdist.cpython-39.pyc | Bin 0 -> 14533 bytes .../command/__pycache__/upload.cpython-39.pyc | Bin 0 -> 5256 bytes .../setuptools/_distutils/command/bdist.py | 143 + .../_distutils/command/bdist_dumb.py | 123 + .../_distutils/command/bdist_msi.py | 749 ++ .../_distutils/command/bdist_rpm.py | 579 ++ .../_distutils/command/bdist_wininst.py | 377 + .../setuptools/_distutils/command/build.py | 157 + .../_distutils/command/build_clib.py | 209 + .../_distutils/command/build_ext.py | 755 ++ .../setuptools/_distutils/command/build_py.py | 416 + .../_distutils/command/build_scripts.py | 160 + .../setuptools/_distutils/command/check.py | 148 + .../setuptools/_distutils/command/clean.py | 76 + .../setuptools/_distutils/command/config.py | 344 + .../setuptools/_distutils/command/install.py | 677 ++ .../_distutils/command/install_data.py | 79 + .../_distutils/command/install_egg_info.py | 77 + .../_distutils/command/install_headers.py | 47 + .../_distutils/command/install_lib.py | 217 + .../_distutils/command/install_scripts.py | 60 + .../_distutils/command/py37compat.py | 30 + .../setuptools/_distutils/command/register.py | 304 + .../setuptools/_distutils/command/sdist.py | 494 + .../setuptools/_distutils/command/upload.py | 214 + .../setuptools/_distutils/config.py | 130 + .../setuptools/_distutils/core.py | 234 + .../setuptools/_distutils/cygwinccompiler.py | 403 + .../setuptools/_distutils/debug.py | 5 + .../setuptools/_distutils/dep_util.py | 92 + .../setuptools/_distutils/dir_util.py | 210 + .../setuptools/_distutils/dist.py | 1257 +++ .../setuptools/_distutils/errors.py | 97 + .../setuptools/_distutils/extension.py | 240 + .../setuptools/_distutils/fancy_getopt.py | 457 + .../setuptools/_distutils/file_util.py | 238 + .../setuptools/_distutils/filelist.py | 327 + .../setuptools/_distutils/log.py | 77 + .../setuptools/_distutils/msvc9compiler.py | 788 ++ .../setuptools/_distutils/msvccompiler.py | 643 ++ .../setuptools/_distutils/py35compat.py | 19 + .../setuptools/_distutils/py38compat.py | 7 + .../setuptools/_distutils/spawn.py | 129 + .../setuptools/_distutils/sysconfig.py | 573 ++ .../setuptools/_distutils/text_file.py | 286 + .../setuptools/_distutils/unixccompiler.py | 328 + .../setuptools/_distutils/util.py | 561 ++ .../setuptools/_distutils/version.py | 347 + .../setuptools/_distutils/versionpredicate.py | 166 + venv/Lib/site-packages/setuptools/_imp.py | 82 + .../setuptools/_vendor/__init__.py | 0 .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 183 bytes .../__pycache__/ordered_set.cpython-39.pyc | Bin 0 -> 16377 bytes .../__pycache__/pyparsing.cpython-39.pyc | Bin 0 -> 201337 bytes .../_vendor/more_itertools/__init__.py | 4 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 264 bytes .../__pycache__/more.cpython-39.pyc | Bin 0 -> 110009 bytes .../__pycache__/recipes.cpython-39.pyc | Bin 0 -> 17929 bytes .../setuptools/_vendor/more_itertools/more.py | 3825 ++++++++ .../_vendor/more_itertools/recipes.py | 620 ++ .../setuptools/_vendor/ordered_set.py | 488 + .../setuptools/_vendor/packaging/__about__.py | 27 + .../setuptools/_vendor/packaging/__init__.py | 26 + .../__pycache__/__about__.cpython-39.pyc | Bin 0 -> 699 bytes .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 545 bytes .../__pycache__/_compat.cpython-39.pyc | Bin 0 -> 1143 bytes .../__pycache__/_structures.cpython-39.pyc | Bin 0 -> 2897 bytes .../__pycache__/_typing.cpython-39.pyc | Bin 0 -> 1488 bytes .../__pycache__/markers.cpython-39.pyc | Bin 0 -> 9301 bytes .../__pycache__/requirements.cpython-39.pyc | Bin 0 -> 4078 bytes .../__pycache__/specifiers.cpython-39.pyc | Bin 0 -> 20580 bytes .../packaging/__pycache__/tags.cpython-39.pyc | Bin 0 -> 17259 bytes .../__pycache__/utils.cpython-39.pyc | Bin 0 -> 1650 bytes .../__pycache__/version.cpython-39.pyc | Bin 0 -> 13317 bytes .../setuptools/_vendor/packaging/_compat.py | 38 + .../_vendor/packaging/_structures.py | 86 + .../setuptools/_vendor/packaging/_typing.py | 48 + .../setuptools/_vendor/packaging/markers.py | 328 + .../_vendor/packaging/requirements.py | 145 + .../_vendor/packaging/specifiers.py | 863 ++ .../setuptools/_vendor/packaging/tags.py | 751 ++ .../setuptools/_vendor/packaging/utils.py | 65 + .../setuptools/_vendor/packaging/version.py | 535 ++ .../setuptools/_vendor/pyparsing.py | 5742 +++++++++++ .../site-packages/setuptools/archive_util.py | 205 + .../site-packages/setuptools/build_meta.py | 281 + venv/Lib/site-packages/setuptools/cli-32.exe | Bin 0 -> 65536 bytes venv/Lib/site-packages/setuptools/cli-64.exe | Bin 0 -> 74752 bytes venv/Lib/site-packages/setuptools/cli.exe | Bin 0 -> 65536 bytes .../setuptools/command/__init__.py | 8 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 372 bytes .../command/__pycache__/alias.cpython-39.pyc | Bin 0 -> 2366 bytes .../__pycache__/bdist_egg.cpython-39.pyc | Bin 0 -> 13032 bytes .../__pycache__/bdist_rpm.cpython-39.pyc | Bin 0 -> 1352 bytes .../__pycache__/build_clib.cpython-39.pyc | Bin 0 -> 2463 bytes .../__pycache__/build_ext.cpython-39.pyc | Bin 0 -> 9736 bytes .../__pycache__/build_py.cpython-39.pyc | Bin 0 -> 8377 bytes .../__pycache__/develop.cpython-39.pyc | Bin 0 -> 6451 bytes .../__pycache__/dist_info.cpython-39.pyc | Bin 0 -> 1390 bytes .../__pycache__/easy_install.cpython-39.pyc | Bin 0 -> 63547 bytes .../__pycache__/egg_info.cpython-39.pyc | Bin 0 -> 21973 bytes .../__pycache__/install.cpython-39.pyc | Bin 0 -> 4031 bytes .../install_egg_info.cpython-39.pyc | Bin 0 -> 2422 bytes .../__pycache__/install_lib.cpython-39.pyc | Bin 0 -> 4129 bytes .../install_scripts.cpython-39.pyc | Bin 0 -> 2417 bytes .../__pycache__/py36compat.cpython-39.pyc | Bin 0 -> 4584 bytes .../__pycache__/register.cpython-39.pyc | Bin 0 -> 840 bytes .../command/__pycache__/rotate.cpython-39.pyc | Bin 0 -> 2499 bytes .../__pycache__/saveopts.cpython-39.pyc | Bin 0 -> 918 bytes .../command/__pycache__/sdist.cpython-39.pyc | Bin 0 -> 6459 bytes .../command/__pycache__/setopt.cpython-39.pyc | Bin 0 -> 4530 bytes .../command/__pycache__/test.cpython-39.pyc | Bin 0 -> 8516 bytes .../command/__pycache__/upload.cpython-39.pyc | Bin 0 -> 813 bytes .../__pycache__/upload_docs.cpython-39.pyc | Bin 0 -> 6156 bytes .../site-packages/setuptools/command/alias.py | 78 + .../setuptools/command/bdist_egg.py | 456 + .../setuptools/command/bdist_rpm.py | 31 + .../setuptools/command/build_clib.py | 101 + .../setuptools/command/build_ext.py | 322 + .../setuptools/command/build_py.py | 252 + .../setuptools/command/develop.py | 216 + .../setuptools/command/dist_info.py | 36 + .../setuptools/command/easy_install.py | 2290 +++++ .../setuptools/command/egg_info.py | 734 ++ .../setuptools/command/install.py | 125 + .../setuptools/command/install_egg_info.py | 62 + .../setuptools/command/install_lib.py | 122 + .../setuptools/command/install_scripts.py | 69 + .../setuptools/command/launcher manifest.xml | 15 + .../setuptools/command/py36compat.py | 134 + .../setuptools/command/register.py | 18 + .../setuptools/command/rotate.py | 64 + .../setuptools/command/saveopts.py | 22 + .../site-packages/setuptools/command/sdist.py | 189 + .../setuptools/command/setopt.py | 148 + .../site-packages/setuptools/command/test.py | 274 + .../setuptools/command/upload.py | 17 + .../setuptools/command/upload_docs.py | 202 + venv/Lib/site-packages/setuptools/config.py | 715 ++ venv/Lib/site-packages/setuptools/dep_util.py | 25 + venv/Lib/site-packages/setuptools/depends.py | 175 + venv/Lib/site-packages/setuptools/dist.py | 1115 +++ venv/Lib/site-packages/setuptools/errors.py | 16 + .../Lib/site-packages/setuptools/extension.py | 55 + .../setuptools/extern/__init__.py | 73 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 2914 bytes venv/Lib/site-packages/setuptools/glob.py | 167 + venv/Lib/site-packages/setuptools/gui-32.exe | Bin 0 -> 65536 bytes venv/Lib/site-packages/setuptools/gui-64.exe | Bin 0 -> 75264 bytes venv/Lib/site-packages/setuptools/gui.exe | Bin 0 -> 65536 bytes .../Lib/site-packages/setuptools/installer.py | 97 + venv/Lib/site-packages/setuptools/launch.py | 36 + .../site-packages/setuptools/lib2to3_ex.py | 68 + venv/Lib/site-packages/setuptools/monkey.py | 177 + venv/Lib/site-packages/setuptools/msvc.py | 1805 ++++ .../site-packages/setuptools/namespaces.py | 107 + .../site-packages/setuptools/package_index.py | 1128 +++ .../site-packages/setuptools/py34compat.py | 13 + venv/Lib/site-packages/setuptools/sandbox.py | 496 + .../setuptools/script (dev).tmpl | 6 + venv/Lib/site-packages/setuptools/script.tmpl | 3 + .../site-packages/setuptools/ssl_support.py | 266 + .../site-packages/setuptools/unicode_utils.py | 42 + venv/Lib/site-packages/setuptools/version.py | 6 + venv/Lib/site-packages/setuptools/wheel.py | 213 + .../setuptools/windows_support.py | 29 + venv/Lib/site-packages/sqlalchemy/__init__.py | 157 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 4692 bytes .../__pycache__/events.cpython-39.pyc | Bin 0 -> 424 bytes .../sqlalchemy/__pycache__/exc.cpython-39.pyc | Bin 0 -> 20829 bytes .../__pycache__/inspection.cpython-39.pyc | Bin 0 -> 2928 bytes .../sqlalchemy/__pycache__/log.cpython-39.pyc | Bin 0 -> 6738 bytes .../__pycache__/processors.cpython-39.pyc | Bin 0 -> 4271 bytes .../__pycache__/schema.cpython-39.pyc | Bin 0 -> 1980 bytes .../__pycache__/types.cpython-39.pyc | Bin 0 -> 2169 bytes .../cimmutabledict.cp39-win_amd64.pyd | Bin 0 -> 14848 bytes .../sqlalchemy/connectors/__init__.py | 10 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 329 bytes .../__pycache__/mxodbc.cpython-39.pyc | Bin 0 -> 5472 bytes .../__pycache__/pyodbc.cpython-39.pyc | Bin 0 -> 5113 bytes .../sqlalchemy/connectors/mxodbc.py | 166 + .../sqlalchemy/connectors/pyodbc.py | 193 + .../sqlalchemy/cprocessors.cp39-win_amd64.pyd | Bin 0 -> 17408 bytes .../cresultproxy.cp39-win_amd64.pyd | Bin 0 -> 20992 bytes .../sqlalchemy/databases/__init__.py | 38 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 829 bytes .../sqlalchemy/dialects/__init__.py | 72 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 1327 bytes .../sqlalchemy/dialects/firebird/__init__.py | 41 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 729 bytes .../firebird/__pycache__/base.cpython-39.pyc | Bin 0 -> 25883 bytes .../firebird/__pycache__/fdb.cpython-39.pyc | Bin 0 -> 4086 bytes .../__pycache__/kinterbasdb.cpython-39.pyc | Bin 0 -> 6491 bytes .../sqlalchemy/dialects/firebird/base.py | 989 ++ .../sqlalchemy/dialects/firebird/fdb.py | 112 + .../dialects/firebird/kinterbasdb.py | 202 + .../sqlalchemy/dialects/mssql/__init__.py | 85 + .../mssql/__pycache__/__init__.cpython-39.pyc | Bin 0 -> 1493 bytes .../mssql/__pycache__/base.cpython-39.pyc | Bin 0 -> 90059 bytes .../information_schema.cpython-39.pyc | Bin 0 -> 5381 bytes .../mssql/__pycache__/json.cpython-39.pyc | Bin 0 -> 4748 bytes .../mssql/__pycache__/mxodbc.cpython-39.pyc | Bin 0 -> 5114 bytes .../__pycache__/provision.cpython-39.pyc | Bin 0 -> 3637 bytes .../mssql/__pycache__/pymssql.cpython-39.pyc | Bin 0 -> 5172 bytes .../mssql/__pycache__/pyodbc.cpython-39.pyc | Bin 0 -> 19126 bytes .../sqlalchemy/dialects/mssql/base.py | 3303 +++++++ .../dialects/mssql/information_schema.py | 232 + .../sqlalchemy/dialects/mssql/json.py | 125 + .../sqlalchemy/dialects/mssql/mxodbc.py | 150 + .../sqlalchemy/dialects/mssql/provision.py | 116 + .../sqlalchemy/dialects/mssql/pymssql.py | 138 + .../sqlalchemy/dialects/mssql/pyodbc.py | 575 ++ .../sqlalchemy/dialects/mysql/__init__.py | 104 + .../mysql/__pycache__/__init__.cpython-39.pyc | Bin 0 -> 1852 bytes .../mysql/__pycache__/aiomysql.cpython-39.pyc | Bin 0 -> 10151 bytes .../mysql/__pycache__/base.cpython-39.pyc | Bin 0 -> 97855 bytes .../mysql/__pycache__/cymysql.cpython-39.pyc | Bin 0 -> 2567 bytes .../mysql/__pycache__/dml.cpython-39.pyc | Bin 0 -> 6000 bytes .../__pycache__/enumerated.cpython-39.pyc | Bin 0 -> 8432 bytes .../__pycache__/expression.cpython-39.pyc | Bin 0 -> 3851 bytes .../mysql/__pycache__/json.cpython-39.pyc | Bin 0 -> 2990 bytes .../mysql/__pycache__/mariadb.cpython-39.pyc | Bin 0 -> 753 bytes .../mariadbconnector.cpython-39.pyc | Bin 0 -> 7027 bytes .../__pycache__/mysqlconnector.cpython-39.pyc | Bin 0 -> 8140 bytes .../mysql/__pycache__/mysqldb.cpython-39.pyc | Bin 0 -> 9608 bytes .../mysql/__pycache__/oursql.cpython-39.pyc | Bin 0 -> 8132 bytes .../__pycache__/provision.cpython-39.pyc | Bin 0 -> 2073 bytes .../mysql/__pycache__/pymysql.cpython-39.pyc | Bin 0 -> 2771 bytes .../mysql/__pycache__/pyodbc.cpython-39.pyc | Bin 0 -> 4453 bytes .../__pycache__/reflection.cpython-39.pyc | Bin 0 -> 12818 bytes .../mysql/__pycache__/types.cpython-39.pyc | Bin 0 -> 26849 bytes .../sqlalchemy/dialects/mysql/aiomysql.py | 312 + .../sqlalchemy/dialects/mysql/base.py | 3526 +++++++ .../sqlalchemy/dialects/mysql/cymysql.py | 82 + .../sqlalchemy/dialects/mysql/dml.py | 173 + .../sqlalchemy/dialects/mysql/enumerated.py | 254 + .../sqlalchemy/dialects/mysql/expression.py | 130 + .../sqlalchemy/dialects/mysql/json.py | 84 + .../sqlalchemy/dialects/mysql/mariadb.py | 23 + .../dialects/mysql/mariadbconnector.py | 237 + .../dialects/mysql/mysqlconnector.py | 240 + .../sqlalchemy/dialects/mysql/mysqldb.py | 335 + .../sqlalchemy/dialects/mysql/oursql.py | 273 + .../sqlalchemy/dialects/mysql/provision.py | 78 + .../sqlalchemy/dialects/mysql/pymysql.py | 98 + .../sqlalchemy/dialects/mysql/pyodbc.py | 137 + .../sqlalchemy/dialects/mysql/reflection.py | 558 ++ .../sqlalchemy/dialects/mysql/types.py | 773 ++ .../sqlalchemy/dialects/oracle/__init__.py | 58 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 1011 bytes .../oracle/__pycache__/base.cpython-39.pyc | Bin 0 -> 73255 bytes .../__pycache__/cx_oracle.cpython-39.pyc | Bin 0 -> 42403 bytes .../__pycache__/provision.cpython-39.pyc | Bin 0 -> 4918 bytes .../sqlalchemy/dialects/oracle/base.py | 2504 +++++ .../sqlalchemy/dialects/oracle/cx_oracle.py | 1327 +++ .../sqlalchemy/dialects/oracle/provision.py | 160 + .../dialects/postgresql/__init__.py | 117 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 2163 bytes .../__pycache__/array.cpython-39.pyc | Bin 0 -> 12167 bytes .../__pycache__/asyncpg.cpython-39.pyc | Bin 0 -> 35098 bytes .../__pycache__/base.cpython-39.pyc | Bin 0 -> 132305 bytes .../postgresql/__pycache__/dml.cpython-39.pyc | Bin 0 -> 8314 bytes .../postgresql/__pycache__/ext.cpython-39.pyc | Bin 0 -> 8376 bytes .../__pycache__/hstore.cpython-39.pyc | Bin 0 -> 12461 bytes .../__pycache__/json.cpython-39.pyc | Bin 0 -> 10635 bytes .../__pycache__/pg8000.cpython-39.pyc | Bin 0 -> 20463 bytes .../__pycache__/provision.cpython-39.pyc | Bin 0 -> 3928 bytes .../__pycache__/psycopg2.cpython-39.pyc | Bin 0 -> 33065 bytes .../__pycache__/psycopg2cffi.cpython-39.pyc | Bin 0 -> 1781 bytes .../__pycache__/pygresql.cpython-39.pyc | Bin 0 -> 8323 bytes .../__pycache__/pypostgresql.cpython-39.pyc | Bin 0 -> 4031 bytes .../__pycache__/ranges.cpython-39.pyc | Bin 0 -> 5608 bytes .../sqlalchemy/dialects/postgresql/array.py | 393 + .../sqlalchemy/dialects/postgresql/asyncpg.py | 1049 +++ .../sqlalchemy/dialects/postgresql/base.py | 4445 +++++++++ .../sqlalchemy/dialects/postgresql/dml.py | 273 + .../sqlalchemy/dialects/postgresql/ext.py | 275 + .../sqlalchemy/dialects/postgresql/hstore.py | 444 + .../sqlalchemy/dialects/postgresql/json.py | 324 + .../sqlalchemy/dialects/postgresql/pg8000.py | 586 ++ .../dialects/postgresql/provision.py | 127 + .../dialects/postgresql/psycopg2.py | 1060 +++ .../dialects/postgresql/psycopg2cffi.py | 60 + .../dialects/postgresql/pygresql.py | 278 + .../dialects/postgresql/pypostgresql.py | 126 + .../sqlalchemy/dialects/postgresql/ranges.py | 135 + .../sqlalchemy/dialects/sqlite/__init__.py | 58 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 1014 bytes .../__pycache__/aiosqlite.cpython-39.pyc | Bin 0 -> 10335 bytes .../sqlite/__pycache__/base.cpython-39.pyc | Bin 0 -> 71344 bytes .../sqlite/__pycache__/dml.cpython-39.pyc | Bin 0 -> 6746 bytes .../sqlite/__pycache__/json.cpython-39.pyc | Bin 0 -> 3239 bytes .../__pycache__/provision.cpython-39.pyc | Bin 0 -> 4096 bytes .../__pycache__/pysqlcipher.cpython-39.pyc | Bin 0 -> 5558 bytes .../__pycache__/pysqlite.cpython-39.pyc | Bin 0 -> 22315 bytes .../sqlalchemy/dialects/sqlite/aiosqlite.py | 332 + .../sqlalchemy/dialects/sqlite/base.py | 2544 +++++ .../sqlalchemy/dialects/sqlite/dml.py | 199 + .../sqlalchemy/dialects/sqlite/json.py | 84 + .../sqlalchemy/dialects/sqlite/provision.py | 142 + .../sqlalchemy/dialects/sqlite/pysqlcipher.py | 164 + .../sqlalchemy/dialects/sqlite/pysqlite.py | 607 ++ .../sqlalchemy/dialects/sybase/__init__.py | 67 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 1128 bytes .../sybase/__pycache__/base.cpython-39.pyc | Bin 0 -> 27652 bytes .../sybase/__pycache__/mxodbc.cpython-39.pyc | Bin 0 -> 1029 bytes .../sybase/__pycache__/pyodbc.cpython-39.pyc | Bin 0 -> 2601 bytes .../__pycache__/pysybase.cpython-39.pyc | Bin 0 -> 3753 bytes .../sqlalchemy/dialects/sybase/base.py | 1100 +++ .../sqlalchemy/dialects/sybase/mxodbc.py | 34 + .../sqlalchemy/dialects/sybase/pyodbc.py | 89 + .../sqlalchemy/dialects/sybase/pysybase.py | 106 + .../sqlalchemy/engine/__init__.py | 60 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 2153 bytes .../engine/__pycache__/base.cpython-39.pyc | Bin 0 -> 95359 bytes .../characteristics.cpython-39.pyc | Bin 0 -> 2662 bytes .../engine/__pycache__/create.cpython-39.pyc | Bin 0 -> 27483 bytes .../engine/__pycache__/cursor.cpython-39.pyc | Bin 0 -> 56143 bytes .../engine/__pycache__/default.cpython-39.pyc | Bin 0 -> 45174 bytes .../engine/__pycache__/events.cpython-39.pyc | Bin 0 -> 33563 bytes .../__pycache__/interfaces.cpython-39.pyc | Bin 0 -> 58364 bytes .../engine/__pycache__/mock.cpython-39.pyc | Bin 0 -> 4307 bytes .../__pycache__/reflection.cpython-39.pyc | Bin 0 -> 31884 bytes .../engine/__pycache__/result.cpython-39.pyc | Bin 0 -> 52080 bytes .../engine/__pycache__/row.cpython-39.pyc | Bin 0 -> 18935 bytes .../__pycache__/strategies.cpython-39.pyc | Bin 0 -> 461 bytes .../engine/__pycache__/url.cpython-39.pyc | Bin 0 -> 23646 bytes .../engine/__pycache__/util.cpython-39.pyc | Bin 0 -> 4712 bytes .../site-packages/sqlalchemy/engine/base.py | 3300 +++++++ .../sqlalchemy/engine/characteristics.py | 56 + .../site-packages/sqlalchemy/engine/create.py | 743 ++ .../site-packages/sqlalchemy/engine/cursor.py | 1928 ++++ .../sqlalchemy/engine/default.py | 1886 ++++ .../site-packages/sqlalchemy/engine/events.py | 817 ++ .../sqlalchemy/engine/interfaces.py | 1721 ++++ .../site-packages/sqlalchemy/engine/mock.py | 118 + .../sqlalchemy/engine/reflection.py | 1149 +++ .../site-packages/sqlalchemy/engine/result.py | 1723 ++++ .../site-packages/sqlalchemy/engine/row.py | 609 ++ .../sqlalchemy/engine/strategies.py | 17 + .../site-packages/sqlalchemy/engine/url.py | 775 ++ .../site-packages/sqlalchemy/engine/util.py | 234 + .../sqlalchemy/event/__init__.py | 17 + .../event/__pycache__/__init__.cpython-39.pyc | Bin 0 -> 510 bytes .../event/__pycache__/api.cpython-39.pyc | Bin 0 -> 6965 bytes .../event/__pycache__/attr.cpython-39.pyc | Bin 0 -> 16269 bytes .../event/__pycache__/base.cpython-39.pyc | Bin 0 -> 10604 bytes .../event/__pycache__/legacy.cpython-39.pyc | Bin 0 -> 5059 bytes .../event/__pycache__/registry.cpython-39.pyc | Bin 0 -> 5798 bytes .../Lib/site-packages/sqlalchemy/event/api.py | 201 + .../site-packages/sqlalchemy/event/attr.py | 468 + .../site-packages/sqlalchemy/event/base.py | 345 + .../site-packages/sqlalchemy/event/legacy.py | 185 + .../sqlalchemy/event/registry.py | 287 + venv/Lib/site-packages/sqlalchemy/events.py | 14 + venv/Lib/site-packages/sqlalchemy/exc.py | 706 ++ .../site-packages/sqlalchemy/ext/__init__.py | 11 + .../ext/__pycache__/__init__.cpython-39.pyc | Bin 0 -> 256 bytes .../associationproxy.cpython-39.pyc | Bin 0 -> 49998 bytes .../ext/__pycache__/automap.cpython-39.pyc | Bin 0 -> 37954 bytes .../ext/__pycache__/baked.cpython-39.pyc | Bin 0 -> 19030 bytes .../ext/__pycache__/compiler.cpython-39.pyc | Bin 0 -> 16950 bytes .../horizontal_shard.cpython-39.pyc | Bin 0 -> 6944 bytes .../ext/__pycache__/hybrid.cpython-39.pyc | Bin 0 -> 42536 bytes .../ext/__pycache__/indexable.cpython-39.pyc | Bin 0 -> 10934 bytes .../instrumentation.cpython-39.pyc | Bin 0 -> 14254 bytes .../ext/__pycache__/mutable.cpython-39.pyc | Bin 0 -> 34197 bytes .../__pycache__/orderinglist.cpython-39.pyc | Bin 0 -> 14197 bytes .../ext/__pycache__/serializer.cpython-39.pyc | Bin 0 -> 5011 bytes .../sqlalchemy/ext/associationproxy.py | 1598 ++++ .../sqlalchemy/ext/asyncio/__init__.py | 21 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 767 bytes .../asyncio/__pycache__/base.cpython-39.pyc | Bin 0 -> 3113 bytes .../asyncio/__pycache__/engine.cpython-39.pyc | Bin 0 -> 22471 bytes .../asyncio/__pycache__/events.cpython-39.pyc | Bin 0 -> 1425 bytes .../asyncio/__pycache__/exc.cpython-39.pyc | Bin 0 -> 868 bytes .../asyncio/__pycache__/result.cpython-39.pyc | Bin 0 -> 20705 bytes .../__pycache__/scoping.cpython-39.pyc | Bin 0 -> 2701 bytes .../__pycache__/session.cpython-39.pyc | Bin 0 -> 14623 bytes .../sqlalchemy/ext/asyncio/base.py | 84 + .../sqlalchemy/ext/asyncio/engine.py | 711 ++ .../sqlalchemy/ext/asyncio/events.py | 36 + .../sqlalchemy/ext/asyncio/exc.py | 21 + .../sqlalchemy/ext/asyncio/result.py | 639 ++ .../sqlalchemy/ext/asyncio/scoping.py | 101 + .../sqlalchemy/ext/asyncio/session.py | 499 + .../site-packages/sqlalchemy/ext/automap.py | 1224 +++ .../Lib/site-packages/sqlalchemy/ext/baked.py | 648 ++ .../site-packages/sqlalchemy/ext/compiler.py | 508 + .../sqlalchemy/ext/declarative/__init__.py | 64 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 1654 bytes .../__pycache__/extensions.cpython-39.pyc | Bin 0 -> 15309 bytes .../sqlalchemy/ext/declarative/extensions.py | 458 + .../sqlalchemy/ext/horizontal_shard.py | 254 + .../site-packages/sqlalchemy/ext/hybrid.py | 1204 +++ .../site-packages/sqlalchemy/ext/indexable.py | 352 + .../sqlalchemy/ext/instrumentation.py | 416 + .../site-packages/sqlalchemy/ext/mutable.py | 951 ++ .../sqlalchemy/ext/mypy/__init__.py | 0 .../mypy/__pycache__/__init__.cpython-39.pyc | Bin 0 -> 166 bytes .../ext/mypy/__pycache__/apply.cpython-39.pyc | Bin 0 -> 5845 bytes .../__pycache__/decl_class.cpython-39.pyc | Bin 0 -> 8005 bytes .../ext/mypy/__pycache__/infer.cpython-39.pyc | Bin 0 -> 8673 bytes .../ext/mypy/__pycache__/names.cpython-39.pyc | Bin 0 -> 5479 bytes .../mypy/__pycache__/plugin.cpython-39.pyc | Bin 0 -> 7168 bytes .../ext/mypy/__pycache__/util.cpython-39.pyc | Bin 0 -> 5602 bytes .../sqlalchemy/ext/mypy/apply.py | 272 + .../sqlalchemy/ext/mypy/decl_class.py | 486 + .../sqlalchemy/ext/mypy/infer.py | 534 ++ .../sqlalchemy/ext/mypy/names.py | 250 + .../sqlalchemy/ext/mypy/plugin.py | 296 + .../site-packages/sqlalchemy/ext/mypy/util.py | 216 + .../sqlalchemy/ext/orderinglist.py | 388 + .../sqlalchemy/ext/serializer.py | 177 + .../sqlalchemy/future/__init__.py | 18 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 479 bytes .../future/__pycache__/engine.cpython-39.pyc | Bin 0 -> 17282 bytes .../site-packages/sqlalchemy/future/engine.py | 426 + .../sqlalchemy/future/orm/__init__.py | 10 + .../orm/__pycache__/__init__.cpython-39.pyc | Bin 0 -> 214 bytes .../site-packages/sqlalchemy/inspection.py | 93 + venv/Lib/site-packages/sqlalchemy/log.py | 225 + .../site-packages/sqlalchemy/orm/__init__.py | 332 + .../orm/__pycache__/__init__.cpython-39.pyc | Bin 0 -> 10300 bytes .../orm/__pycache__/attributes.cpython-39.pyc | Bin 0 -> 58922 bytes .../orm/__pycache__/base.cpython-39.pyc | Bin 0 -> 12891 bytes .../__pycache__/clsregistry.cpython-39.pyc | Bin 0 -> 12058 bytes .../__pycache__/collections.cpython-39.pyc | Bin 0 -> 54108 bytes .../orm/__pycache__/context.cpython-39.pyc | Bin 0 -> 49178 bytes .../orm/__pycache__/decl_api.cpython-39.pyc | Bin 0 -> 34453 bytes .../orm/__pycache__/decl_base.cpython-39.pyc | Bin 0 -> 25152 bytes .../orm/__pycache__/dependency.cpython-39.pyc | Bin 0 -> 23477 bytes .../descriptor_props.cpython-39.pyc | Bin 0 -> 25835 bytes .../orm/__pycache__/dynamic.cpython-39.pyc | Bin 0 -> 13334 bytes .../orm/__pycache__/evaluator.cpython-39.pyc | Bin 0 -> 7027 bytes .../orm/__pycache__/events.cpython-39.pyc | Bin 0 -> 111598 bytes .../orm/__pycache__/exc.cpython-39.pyc | Bin 0 -> 6814 bytes .../orm/__pycache__/identity.cpython-39.pyc | Bin 0 -> 7113 bytes .../instrumentation.cpython-39.pyc | Bin 0 -> 18542 bytes .../orm/__pycache__/interfaces.cpython-39.pyc | Bin 0 -> 29054 bytes .../orm/__pycache__/loading.cpython-39.pyc | Bin 0 -> 25534 bytes .../orm/__pycache__/mapper.cpython-39.pyc | Bin 0 -> 96514 bytes .../__pycache__/path_registry.cpython-39.pyc | Bin 0 -> 13933 bytes .../__pycache__/persistence.cpython-39.pyc | Bin 0 -> 40422 bytes .../orm/__pycache__/properties.cpython-39.pyc | Bin 0 -> 13768 bytes .../orm/__pycache__/query.cpython-39.pyc | Bin 0 -> 110700 bytes .../__pycache__/relationships.cpython-39.pyc | Bin 0 -> 106956 bytes .../orm/__pycache__/scoping.cpython-39.pyc | Bin 0 -> 6035 bytes .../orm/__pycache__/session.cpython-39.pyc | Bin 0 -> 127691 bytes .../orm/__pycache__/state.cpython-39.pyc | Bin 0 -> 28272 bytes .../orm/__pycache__/strategies.cpython-39.pyc | Bin 0 -> 58803 bytes .../strategy_options.cpython-39.pyc | Bin 0 -> 47399 bytes .../orm/__pycache__/sync.cpython-39.pyc | Bin 0 -> 3880 bytes .../orm/__pycache__/unitofwork.cpython-39.pyc | Bin 0 -> 21054 bytes .../orm/__pycache__/util.cpython-39.pyc | Bin 0 -> 58249 bytes .../sqlalchemy/orm/attributes.py | 2281 +++++ venv/Lib/site-packages/sqlalchemy/orm/base.py | 566 ++ .../sqlalchemy/orm/clsregistry.py | 441 + .../sqlalchemy/orm/collections.py | 1706 ++++ .../site-packages/sqlalchemy/orm/context.py | 2922 ++++++ .../site-packages/sqlalchemy/orm/decl_api.py | 1043 ++ .../site-packages/sqlalchemy/orm/decl_base.py | 1155 +++ .../sqlalchemy/orm/dependency.py | 1290 +++ .../sqlalchemy/orm/descriptor_props.py | 744 ++ .../site-packages/sqlalchemy/orm/dynamic.py | 488 + .../site-packages/sqlalchemy/orm/evaluator.py | 241 + .../site-packages/sqlalchemy/orm/events.py | 2848 ++++++ venv/Lib/site-packages/sqlalchemy/orm/exc.py | 204 + .../site-packages/sqlalchemy/orm/identity.py | 240 + .../sqlalchemy/orm/instrumentation.py | 650 ++ .../sqlalchemy/orm/interfaces.py | 926 ++ .../site-packages/sqlalchemy/orm/loading.py | 1463 +++ .../site-packages/sqlalchemy/orm/mapper.py | 3593 +++++++ .../sqlalchemy/orm/path_registry.py | 482 + .../sqlalchemy/orm/persistence.py | 2364 +++++ .../sqlalchemy/orm/properties.py | 437 + .../Lib/site-packages/sqlalchemy/orm/query.py | 3428 +++++++ .../sqlalchemy/orm/relationships.py | 3676 ++++++++ .../site-packages/sqlalchemy/orm/scoping.py | 211 + .../site-packages/sqlalchemy/orm/session.py | 4262 +++++++++ .../Lib/site-packages/sqlalchemy/orm/state.py | 1023 ++ .../sqlalchemy/orm/strategies.py | 3112 ++++++ .../sqlalchemy/orm/strategy_options.py | 1823 ++++ venv/Lib/site-packages/sqlalchemy/orm/sync.py | 167 + .../sqlalchemy/orm/unitofwork.py | 784 ++ venv/Lib/site-packages/sqlalchemy/orm/util.py | 1990 ++++ .../site-packages/sqlalchemy/pool/__init__.py | 56 + .../pool/__pycache__/__init__.cpython-39.pyc | Bin 0 -> 1288 bytes .../pool/__pycache__/base.cpython-39.pyc | Bin 0 -> 28665 bytes .../__pycache__/dbapi_proxy.cpython-39.pyc | Bin 0 -> 4858 bytes .../pool/__pycache__/events.cpython-39.pyc | Bin 0 -> 9321 bytes .../pool/__pycache__/impl.cpython-39.pyc | Bin 0 -> 15954 bytes .../Lib/site-packages/sqlalchemy/pool/base.py | 1020 ++ .../sqlalchemy/pool/dbapi_proxy.py | 147 + .../site-packages/sqlalchemy/pool/events.py | 248 + .../Lib/site-packages/sqlalchemy/pool/impl.py | 514 + .../site-packages/sqlalchemy/processors.py | 176 + venv/Lib/site-packages/sqlalchemy/schema.py | 59 + .../site-packages/sqlalchemy/sql/__init__.py | 150 + .../sql/__pycache__/__init__.cpython-39.pyc | Bin 0 -> 4171 bytes .../sql/__pycache__/annotation.cpython-39.pyc | Bin 0 -> 9572 bytes .../sql/__pycache__/base.cpython-39.pyc | Bin 0 -> 56904 bytes .../sql/__pycache__/coercions.cpython-39.pyc | Bin 0 -> 26398 bytes .../sql/__pycache__/compiler.cpython-39.pyc | Bin 0 -> 123384 bytes .../sql/__pycache__/crud.cpython-39.pyc | Bin 0 -> 16910 bytes .../sql/__pycache__/ddl.cpython-39.pyc | Bin 0 -> 40592 bytes .../default_comparator.cpython-39.pyc | Bin 0 -> 6687 bytes .../sql/__pycache__/dml.cpython-39.pyc | Bin 0 -> 46490 bytes .../sql/__pycache__/elements.cpython-39.pyc | Bin 0 -> 155650 bytes .../sql/__pycache__/events.cpython-39.pyc | Bin 0 -> 13794 bytes .../sql/__pycache__/expression.cpython-39.pyc | Bin 0 -> 6667 bytes .../sql/__pycache__/functions.cpython-39.pyc | Bin 0 -> 48221 bytes .../sql/__pycache__/lambdas.cpython-39.pyc | Bin 0 -> 33688 bytes .../sql/__pycache__/naming.cpython-39.pyc | Bin 0 -> 5212 bytes .../sql/__pycache__/operators.cpython-39.pyc | Bin 0 -> 51186 bytes .../sql/__pycache__/roles.cpython-39.pyc | Bin 0 -> 8543 bytes .../sql/__pycache__/schema.cpython-39.pyc | Bin 0 -> 163960 bytes .../sql/__pycache__/selectable.cpython-39.pyc | Bin 0 -> 204213 bytes .../sql/__pycache__/sqltypes.cpython-39.pyc | Bin 0 -> 103380 bytes .../sql/__pycache__/traversals.cpython-39.pyc | Bin 0 -> 44204 bytes .../sql/__pycache__/type_api.cpython-39.pyc | Bin 0 -> 52400 bytes .../sql/__pycache__/util.cpython-39.pyc | Bin 0 -> 26638 bytes .../sql/__pycache__/visitors.cpython-39.pyc | Bin 0 -> 21891 bytes .../sqlalchemy/sql/annotation.py | 358 + venv/Lib/site-packages/sqlalchemy/sql/base.py | 1677 ++++ .../site-packages/sqlalchemy/sql/coercions.py | 1045 +++ .../site-packages/sqlalchemy/sql/compiler.py | 5097 ++++++++++ venv/Lib/site-packages/sqlalchemy/sql/crud.py | 1052 +++ venv/Lib/site-packages/sqlalchemy/sql/ddl.py | 1336 +++ .../sqlalchemy/sql/default_comparator.py | 352 + venv/Lib/site-packages/sqlalchemy/sql/dml.py | 1427 +++ .../site-packages/sqlalchemy/sql/elements.py | 5118 ++++++++++ .../site-packages/sqlalchemy/sql/events.py | 326 + .../sqlalchemy/sql/expression.py | 278 + .../site-packages/sqlalchemy/sql/functions.py | 1528 +++ .../site-packages/sqlalchemy/sql/lambdas.py | 1253 +++ .../site-packages/sqlalchemy/sql/naming.py | 210 + .../site-packages/sqlalchemy/sql/operators.py | 1682 ++++ .../Lib/site-packages/sqlalchemy/sql/roles.py | 227 + .../site-packages/sqlalchemy/sql/schema.py | 5091 ++++++++++ .../sqlalchemy/sql/selectable.py | 6422 +++++++++++++ .../site-packages/sqlalchemy/sql/sqltypes.py | 3293 +++++++ .../sqlalchemy/sql/traversals.py | 1465 +++ .../site-packages/sqlalchemy/sql/type_api.py | 1655 ++++ venv/Lib/site-packages/sqlalchemy/sql/util.py | 1095 +++ .../site-packages/sqlalchemy/sql/visitors.py | 848 ++ .../sqlalchemy/testing/__init__.py | 84 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 2777 bytes .../__pycache__/assertions.cpython-39.pyc | Bin 0 -> 23675 bytes .../__pycache__/assertsql.cpython-39.pyc | Bin 0 -> 12108 bytes .../__pycache__/asyncio.cpython-39.pyc | Bin 0 -> 3030 bytes .../testing/__pycache__/config.cpython-39.pyc | Bin 0 -> 6743 bytes .../__pycache__/engines.cpython-39.pyc | Bin 0 -> 13452 bytes .../__pycache__/entities.cpython-39.pyc | Bin 0 -> 2899 bytes .../__pycache__/exclusions.cpython-39.pyc | Bin 0 -> 14865 bytes .../__pycache__/fixtures.cpython-39.pyc | Bin 0 -> 20925 bytes .../testing/__pycache__/mock.cpython-39.pyc | Bin 0 -> 656 bytes .../__pycache__/pickleable.cpython-39.pyc | Bin 0 -> 5104 bytes .../__pycache__/profiling.cpython-39.pyc | Bin 0 -> 8535 bytes .../__pycache__/provision.cpython-39.pyc | Bin 0 -> 11245 bytes .../__pycache__/requirements.cpython-39.pyc | Bin 0 -> 55645 bytes .../testing/__pycache__/schema.cpython-39.pyc | Bin 0 -> 5919 bytes .../testing/__pycache__/util.cpython-39.pyc | Bin 0 -> 13187 bytes .../__pycache__/warnings.cpython-39.pyc | Bin 0 -> 4262 bytes .../sqlalchemy/testing/assertions.py | 762 ++ .../sqlalchemy/testing/assertsql.py | 439 + .../sqlalchemy/testing/asyncio.py | 129 + .../sqlalchemy/testing/config.py | 209 + .../sqlalchemy/testing/engines.py | 444 + .../sqlalchemy/testing/entities.py | 111 + .../sqlalchemy/testing/exclusions.py | 465 + .../sqlalchemy/testing/fixtures.py | 808 ++ .../site-packages/sqlalchemy/testing/mock.py | 32 + .../sqlalchemy/testing/pickleable.py | 141 + .../sqlalchemy/testing/plugin/__init__.py | 0 .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 172 bytes .../__pycache__/bootstrap.cpython-39.pyc | Bin 0 -> 1755 bytes .../__pycache__/plugin_base.cpython-39.pyc | Bin 0 -> 18224 bytes .../__pycache__/pytestplugin.cpython-39.pyc | Bin 0 -> 18144 bytes .../reinvent_fixtures_py2k.cpython-39.pyc | Bin 0 -> 3171 bytes .../sqlalchemy/testing/plugin/bootstrap.py | 50 + .../sqlalchemy/testing/plugin/plugin_base.py | 789 ++ .../sqlalchemy/testing/plugin/pytestplugin.py | 774 ++ .../testing/plugin/reinvent_fixtures_py2k.py | 112 + .../sqlalchemy/testing/profiling.py | 335 + .../sqlalchemy/testing/provision.py | 414 + .../sqlalchemy/testing/requirements.py | 1426 +++ .../sqlalchemy/testing/schema.py | 218 + .../sqlalchemy/testing/suite/__init__.py | 13 + .../suite/__pycache__/__init__.cpython-39.pyc | Bin 0 -> 496 bytes .../suite/__pycache__/test_cte.cpython-39.pyc | Bin 0 -> 4541 bytes .../suite/__pycache__/test_ddl.cpython-39.pyc | Bin 0 -> 11399 bytes .../test_deprecations.cpython-39.pyc | Bin 0 -> 4380 bytes .../__pycache__/test_dialect.cpython-39.pyc | Bin 0 -> 10461 bytes .../__pycache__/test_insert.cpython-39.pyc | Bin 0 -> 8788 bytes .../test_reflection.cpython-39.pyc | Bin 0 -> 40051 bytes .../__pycache__/test_results.cpython-39.pyc | Bin 0 -> 11439 bytes .../__pycache__/test_rowcount.cpython-39.pyc | Bin 0 -> 4450 bytes .../__pycache__/test_select.cpython-39.pyc | Bin 0 -> 50704 bytes .../__pycache__/test_sequence.cpython-39.pyc | Bin 0 -> 8214 bytes .../__pycache__/test_types.cpython-39.pyc | Bin 0 -> 38783 bytes .../test_unicode_ddl.cpython-39.pyc | Bin 0 -> 4344 bytes .../test_update_delete.cpython-39.pyc | Bin 0 -> 1994 bytes .../sqlalchemy/testing/suite/test_cte.py | 204 + .../sqlalchemy/testing/suite/test_ddl.py | 381 + .../testing/suite/test_deprecations.py | 145 + .../sqlalchemy/testing/suite/test_dialect.py | 361 + .../sqlalchemy/testing/suite/test_insert.py | 367 + .../testing/suite/test_reflection.py | 1651 ++++ .../sqlalchemy/testing/suite/test_results.py | 426 + .../sqlalchemy/testing/suite/test_rowcount.py | 160 + .../sqlalchemy/testing/suite/test_select.py | 1682 ++++ .../sqlalchemy/testing/suite/test_sequence.py | 282 + .../sqlalchemy/testing/suite/test_types.py | 1377 +++ .../testing/suite/test_unicode_ddl.py | 206 + .../testing/suite/test_update_delete.py | 59 + .../site-packages/sqlalchemy/testing/util.py | 458 + .../sqlalchemy/testing/warnings.py | 166 + venv/Lib/site-packages/sqlalchemy/types.py | 115 + .../site-packages/sqlalchemy/util/__init__.py | 171 + .../util/__pycache__/__init__.cpython-39.pyc | Bin 0 -> 6032 bytes .../__pycache__/_collections.cpython-39.pyc | Bin 0 -> 37691 bytes .../_concurrency_py3k.cpython-39.pyc | Bin 0 -> 4732 bytes .../__pycache__/_preloaded.cpython-39.pyc | Bin 0 -> 2628 bytes .../util/__pycache__/compat.cpython-39.pyc | Bin 0 -> 16253 bytes .../__pycache__/concurrency.cpython-39.pyc | Bin 0 -> 1679 bytes .../__pycache__/deprecations.cpython-39.pyc | Bin 0 -> 8806 bytes .../__pycache__/langhelpers.cpython-39.pyc | Bin 0 -> 50837 bytes .../util/__pycache__/queue.cpython-39.pyc | Bin 0 -> 8490 bytes .../__pycache__/topological.cpython-39.pyc | Bin 0 -> 2205 bytes .../sqlalchemy/util/_collections.py | 1089 +++ .../sqlalchemy/util/_concurrency_py3k.py | 195 + .../sqlalchemy/util/_preloaded.py | 68 + .../site-packages/sqlalchemy/util/compat.py | 628 ++ .../sqlalchemy/util/concurrency.py | 59 + .../sqlalchemy/util/deprecations.py | 405 + .../sqlalchemy/util/langhelpers.py | 1941 ++++ .../site-packages/sqlalchemy/util/queue.py | 291 + .../sqlalchemy/util/topological.py | 100 + .../toml-0.10.2.dist-info/INSTALLER | 1 + .../toml-0.10.2.dist-info/LICENSE | 27 + .../toml-0.10.2.dist-info/METADATA | 255 + .../toml-0.10.2.dist-info/RECORD | 16 + .../site-packages/toml-0.10.2.dist-info/WHEEL | 6 + .../toml-0.10.2.dist-info/top_level.txt | 1 + venv/Lib/site-packages/toml/__init__.py | 25 + .../toml/__pycache__/__init__.cpython-39.pyc | Bin 0 -> 683 bytes .../toml/__pycache__/decoder.cpython-39.pyc | Bin 0 -> 23175 bytes .../toml/__pycache__/encoder.cpython-39.pyc | Bin 0 -> 9358 bytes .../toml/__pycache__/ordered.cpython-39.pyc | Bin 0 -> 922 bytes .../toml/__pycache__/tz.cpython-39.pyc | Bin 0 -> 1236 bytes venv/Lib/site-packages/toml/decoder.py | 1057 +++ venv/Lib/site-packages/toml/encoder.py | 304 + venv/Lib/site-packages/toml/ordered.py | 15 + venv/Lib/site-packages/toml/tz.py | 24 + venv/Lib/site-packages/werkzeug/__init__.py | 6 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 329 bytes .../__pycache__/_internal.cpython-39.pyc | Bin 0 -> 18439 bytes .../__pycache__/_reloader.cpython-39.pyc | Bin 0 -> 11950 bytes .../__pycache__/datastructures.cpython-39.pyc | Bin 0 -> 106421 bytes .../__pycache__/exceptions.cpython-39.pyc | Bin 0 -> 30214 bytes .../__pycache__/filesystem.cpython-39.pyc | Bin 0 -> 2064 bytes .../__pycache__/formparser.cpython-39.pyc | Bin 0 -> 13998 bytes .../werkzeug/__pycache__/http.cpython-39.pyc | Bin 0 -> 38032 bytes .../werkzeug/__pycache__/local.cpython-39.pyc | Bin 0 -> 22259 bytes .../__pycache__/routing.cpython-39.pyc | Bin 0 -> 73051 bytes .../__pycache__/security.cpython-39.pyc | Bin 0 -> 8150 bytes .../__pycache__/serving.cpython-39.pyc | Bin 0 -> 30702 bytes .../werkzeug/__pycache__/test.cpython-39.pyc | Bin 0 -> 39059 bytes .../__pycache__/testapp.cpython-39.pyc | Bin 0 -> 9593 bytes .../werkzeug/__pycache__/urls.cpython-39.pyc | Bin 0 -> 36609 bytes .../__pycache__/user_agent.cpython-39.pyc | Bin 0 -> 1810 bytes .../__pycache__/useragents.cpython-39.pyc | Bin 0 -> 6852 bytes .../werkzeug/__pycache__/utils.cpython-39.pyc | Bin 0 -> 32777 bytes .../werkzeug/__pycache__/wsgi.cpython-39.pyc | Bin 0 -> 30193 bytes venv/Lib/site-packages/werkzeug/_internal.py | 626 ++ venv/Lib/site-packages/werkzeug/_reloader.py | 430 + .../site-packages/werkzeug/datastructures.py | 3051 ++++++ .../site-packages/werkzeug/datastructures.pyi | 906 ++ .../site-packages/werkzeug/debug/__init__.py | 501 + .../debug/__pycache__/__init__.cpython-39.pyc | Bin 0 -> 12973 bytes .../debug/__pycache__/console.cpython-39.pyc | Bin 0 -> 7916 bytes .../debug/__pycache__/repr.cpython-39.pyc | Bin 0 -> 8845 bytes .../debug/__pycache__/tbtools.cpython-39.pyc | Bin 0 -> 18009 bytes .../site-packages/werkzeug/debug/console.py | 211 + venv/Lib/site-packages/werkzeug/debug/repr.py | 284 + .../werkzeug/debug/shared/FONT_LICENSE | 96 + .../werkzeug/debug/shared/ICON_LICENSE.md | 6 + .../werkzeug/debug/shared/console.png | Bin 0 -> 507 bytes .../werkzeug/debug/shared/debugger.js | 359 + .../werkzeug/debug/shared/less.png | Bin 0 -> 191 bytes .../werkzeug/debug/shared/more.png | Bin 0 -> 200 bytes .../werkzeug/debug/shared/source.png | Bin 0 -> 818 bytes .../werkzeug/debug/shared/style.css | 163 + .../werkzeug/debug/shared/ubuntu.ttf | Bin 0 -> 70220 bytes .../site-packages/werkzeug/debug/tbtools.py | 595 ++ venv/Lib/site-packages/werkzeug/exceptions.py | 943 ++ venv/Lib/site-packages/werkzeug/filesystem.py | 55 + venv/Lib/site-packages/werkzeug/formparser.py | 495 + venv/Lib/site-packages/werkzeug/http.py | 1388 +++ venv/Lib/site-packages/werkzeug/local.py | 666 ++ .../werkzeug/middleware/__init__.py | 22 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 674 bytes .../__pycache__/dispatcher.cpython-39.pyc | Bin 0 -> 2733 bytes .../__pycache__/http_proxy.cpython-39.pyc | Bin 0 -> 6778 bytes .../__pycache__/lint.cpython-39.pyc | Bin 0 -> 12675 bytes .../__pycache__/profiler.cpython-39.pyc | Bin 0 -> 4926 bytes .../__pycache__/proxy_fix.cpython-39.pyc | Bin 0 -> 6155 bytes .../__pycache__/shared_data.cpython-39.pyc | Bin 0 -> 9829 bytes .../werkzeug/middleware/dispatcher.py | 78 + .../werkzeug/middleware/http_proxy.py | 230 + .../site-packages/werkzeug/middleware/lint.py | 420 + .../werkzeug/middleware/profiler.py | 139 + .../werkzeug/middleware/proxy_fix.py | 187 + .../werkzeug/middleware/shared_data.py | 320 + venv/Lib/site-packages/werkzeug/py.typed | 0 venv/Lib/site-packages/werkzeug/routing.py | 2332 +++++ .../site-packages/werkzeug/sansio/__init__.py | 0 .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 162 bytes .../__pycache__/multipart.cpython-39.pyc | Bin 0 -> 6513 bytes .../sansio/__pycache__/request.cpython-39.pyc | Bin 0 -> 17158 bytes .../__pycache__/response.cpython-39.pyc | Bin 0 -> 21087 bytes .../sansio/__pycache__/utils.cpython-39.pyc | Bin 0 -> 3866 bytes .../werkzeug/sansio/multipart.py | 260 + .../site-packages/werkzeug/sansio/request.py | 548 ++ .../site-packages/werkzeug/sansio/response.py | 656 ++ .../site-packages/werkzeug/sansio/utils.py | 142 + venv/Lib/site-packages/werkzeug/security.py | 247 + venv/Lib/site-packages/werkzeug/serving.py | 1079 +++ venv/Lib/site-packages/werkzeug/test.py | 1324 +++ venv/Lib/site-packages/werkzeug/testapp.py | 240 + venv/Lib/site-packages/werkzeug/urls.py | 1211 +++ venv/Lib/site-packages/werkzeug/user_agent.py | 47 + venv/Lib/site-packages/werkzeug/useragents.py | 215 + venv/Lib/site-packages/werkzeug/utils.py | 1091 +++ .../werkzeug/wrappers/__init__.py | 16 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 885 bytes .../__pycache__/accept.cpython-39.pyc | Bin 0 -> 791 bytes .../wrappers/__pycache__/auth.cpython-39.pyc | Bin 0 -> 1269 bytes .../__pycache__/base_request.cpython-39.pyc | Bin 0 -> 1744 bytes .../__pycache__/base_response.cpython-39.pyc | Bin 0 -> 1755 bytes .../common_descriptors.cpython-39.pyc | Bin 0 -> 1346 bytes .../wrappers/__pycache__/cors.cpython-39.pyc | Bin 0 -> 1254 bytes .../wrappers/__pycache__/etag.cpython-39.pyc | Bin 0 -> 1254 bytes .../wrappers/__pycache__/json.cpython-39.pyc | Bin 0 -> 783 bytes .../__pycache__/request.cpython-39.pyc | Bin 0 -> 21272 bytes .../__pycache__/response.cpython-39.pyc | Bin 0 -> 29666 bytes .../__pycache__/user_agent.cpython-39.pyc | Bin 0 -> 804 bytes .../site-packages/werkzeug/wrappers/accept.py | 14 + .../site-packages/werkzeug/wrappers/auth.py | 26 + .../werkzeug/wrappers/base_request.py | 36 + .../werkzeug/wrappers/base_response.py | 36 + .../werkzeug/wrappers/common_descriptors.py | 26 + .../site-packages/werkzeug/wrappers/cors.py | 26 + .../site-packages/werkzeug/wrappers/etag.py | 26 + .../site-packages/werkzeug/wrappers/json.py | 13 + .../werkzeug/wrappers/request.py | 660 ++ .../werkzeug/wrappers/response.py | 890 ++ .../werkzeug/wrappers/user_agent.py | 14 + venv/Lib/site-packages/werkzeug/wsgi.py | 982 ++ venv/Scripts/Activate.ps1 | 398 + venv/Scripts/activate | 66 + venv/Scripts/activate.bat | 33 + venv/Scripts/deactivate.bat | 21 + venv/Scripts/flask.exe | Bin 0 -> 106342 bytes venv/Scripts/pip.exe | Bin 0 -> 106355 bytes venv/Scripts/pip3.9.exe | Bin 0 -> 106355 bytes venv/Scripts/pip3.exe | Bin 0 -> 106355 bytes venv/Scripts/py.test.exe | Bin 0 -> 106355 bytes venv/Scripts/pyserial-miniterm.exe | Bin 0 -> 106354 bytes venv/Scripts/pyserial-ports.exe | Bin 0 -> 106356 bytes venv/Scripts/pytest.exe | Bin 0 -> 106355 bytes venv/Scripts/python.exe | Bin 0 -> 539312 bytes venv/Scripts/pythonw.exe | Bin 0 -> 537776 bytes venv/pyvenv.cfg | 3 + 2457 files changed, 524893 insertions(+) create mode 100644 .idea/.gitignore create mode 100644 .idea/OpenHome.iml create mode 100644 .idea/inspectionProfiles/profiles_settings.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/modules.xml create mode 100644 __pycache__/cun.cpython-39.pyc create mode 100644 cun.py create mode 100644 main.py create mode 100644 venv/Include/site/python3.9/greenlet/greenlet.h create mode 100644 venv/Lib/site-packages/Flask-2.0.1.dist-info/INSTALLER create mode 100644 venv/Lib/site-packages/Flask-2.0.1.dist-info/LICENSE.rst create mode 100644 venv/Lib/site-packages/Flask-2.0.1.dist-info/METADATA create mode 100644 venv/Lib/site-packages/Flask-2.0.1.dist-info/RECORD create mode 100644 venv/Lib/site-packages/Flask-2.0.1.dist-info/WHEEL create mode 100644 venv/Lib/site-packages/Flask-2.0.1.dist-info/entry_points.txt create mode 100644 venv/Lib/site-packages/Flask-2.0.1.dist-info/top_level.txt create mode 100644 venv/Lib/site-packages/Flask_SQLAlchemy-2.5.1.dist-info/INSTALLER create mode 100644 venv/Lib/site-packages/Flask_SQLAlchemy-2.5.1.dist-info/LICENSE.rst create mode 100644 venv/Lib/site-packages/Flask_SQLAlchemy-2.5.1.dist-info/METADATA create mode 100644 venv/Lib/site-packages/Flask_SQLAlchemy-2.5.1.dist-info/RECORD create mode 100644 venv/Lib/site-packages/Flask_SQLAlchemy-2.5.1.dist-info/WHEEL create mode 100644 venv/Lib/site-packages/Flask_SQLAlchemy-2.5.1.dist-info/top_level.txt create mode 100644 venv/Lib/site-packages/Jinja2-3.0.1.dist-info/INSTALLER create mode 100644 venv/Lib/site-packages/Jinja2-3.0.1.dist-info/LICENSE.rst create mode 100644 venv/Lib/site-packages/Jinja2-3.0.1.dist-info/METADATA create mode 100644 venv/Lib/site-packages/Jinja2-3.0.1.dist-info/RECORD create mode 100644 venv/Lib/site-packages/Jinja2-3.0.1.dist-info/WHEEL create mode 100644 venv/Lib/site-packages/Jinja2-3.0.1.dist-info/entry_points.txt create mode 100644 venv/Lib/site-packages/Jinja2-3.0.1.dist-info/top_level.txt create mode 100644 venv/Lib/site-packages/MarkupSafe-2.0.1.dist-info/INSTALLER create mode 100644 venv/Lib/site-packages/MarkupSafe-2.0.1.dist-info/LICENSE.rst create mode 100644 venv/Lib/site-packages/MarkupSafe-2.0.1.dist-info/METADATA create mode 100644 venv/Lib/site-packages/MarkupSafe-2.0.1.dist-info/RECORD create mode 100644 venv/Lib/site-packages/MarkupSafe-2.0.1.dist-info/WHEEL create mode 100644 venv/Lib/site-packages/MarkupSafe-2.0.1.dist-info/top_level.txt create mode 100644 venv/Lib/site-packages/SQLAlchemy-1.4.20.dist-info/INSTALLER create mode 100644 venv/Lib/site-packages/SQLAlchemy-1.4.20.dist-info/LICENSE create mode 100644 venv/Lib/site-packages/SQLAlchemy-1.4.20.dist-info/METADATA create mode 100644 venv/Lib/site-packages/SQLAlchemy-1.4.20.dist-info/RECORD create mode 100644 venv/Lib/site-packages/SQLAlchemy-1.4.20.dist-info/WHEEL create mode 100644 venv/Lib/site-packages/SQLAlchemy-1.4.20.dist-info/top_level.txt create mode 100644 venv/Lib/site-packages/Werkzeug-2.0.1.dist-info/INSTALLER create mode 100644 venv/Lib/site-packages/Werkzeug-2.0.1.dist-info/LICENSE.rst create mode 100644 venv/Lib/site-packages/Werkzeug-2.0.1.dist-info/METADATA create mode 100644 venv/Lib/site-packages/Werkzeug-2.0.1.dist-info/RECORD create mode 100644 venv/Lib/site-packages/Werkzeug-2.0.1.dist-info/WHEEL create mode 100644 venv/Lib/site-packages/Werkzeug-2.0.1.dist-info/top_level.txt create mode 100644 venv/Lib/site-packages/__pycache__/pyparsing.cpython-39.pyc create mode 100644 venv/Lib/site-packages/_distutils_hack/__init__.py create mode 100644 venv/Lib/site-packages/_distutils_hack/__pycache__/__init__.cpython-39.pyc create mode 100644 venv/Lib/site-packages/_distutils_hack/__pycache__/override.cpython-39.pyc create mode 100644 venv/Lib/site-packages/_distutils_hack/override.py create mode 100644 venv/Lib/site-packages/_pytest/__init__.py create mode 100644 venv/Lib/site-packages/_pytest/__pycache__/__init__.cpython-39.pyc create mode 100644 venv/Lib/site-packages/_pytest/__pycache__/_argcomplete.cpython-39.pyc create mode 100644 venv/Lib/site-packages/_pytest/__pycache__/_version.cpython-39.pyc create mode 100644 venv/Lib/site-packages/_pytest/__pycache__/cacheprovider.cpython-39.pyc create mode 100644 venv/Lib/site-packages/_pytest/__pycache__/capture.cpython-39.pyc create mode 100644 venv/Lib/site-packages/_pytest/__pycache__/compat.cpython-39.pyc create mode 100644 venv/Lib/site-packages/_pytest/__pycache__/debugging.cpython-39.pyc create mode 100644 venv/Lib/site-packages/_pytest/__pycache__/deprecated.cpython-39.pyc create mode 100644 venv/Lib/site-packages/_pytest/__pycache__/doctest.cpython-39.pyc create mode 100644 venv/Lib/site-packages/_pytest/__pycache__/faulthandler.cpython-39.pyc create mode 100644 venv/Lib/site-packages/_pytest/__pycache__/fixtures.cpython-39.pyc create mode 100644 venv/Lib/site-packages/_pytest/__pycache__/freeze_support.cpython-39.pyc create mode 100644 venv/Lib/site-packages/_pytest/__pycache__/helpconfig.cpython-39.pyc create mode 100644 venv/Lib/site-packages/_pytest/__pycache__/hookspec.cpython-39.pyc create mode 100644 venv/Lib/site-packages/_pytest/__pycache__/junitxml.cpython-39.pyc create mode 100644 venv/Lib/site-packages/_pytest/__pycache__/logging.cpython-39.pyc create mode 100644 venv/Lib/site-packages/_pytest/__pycache__/main.cpython-39.pyc create mode 100644 venv/Lib/site-packages/_pytest/__pycache__/monkeypatch.cpython-39.pyc create mode 100644 venv/Lib/site-packages/_pytest/__pycache__/nodes.cpython-39.pyc create mode 100644 venv/Lib/site-packages/_pytest/__pycache__/nose.cpython-39.pyc create mode 100644 venv/Lib/site-packages/_pytest/__pycache__/outcomes.cpython-39.pyc create mode 100644 venv/Lib/site-packages/_pytest/__pycache__/pastebin.cpython-39.pyc create mode 100644 venv/Lib/site-packages/_pytest/__pycache__/pathlib.cpython-39.pyc create mode 100644 venv/Lib/site-packages/_pytest/__pycache__/pytester.cpython-39.pyc create mode 100644 venv/Lib/site-packages/_pytest/__pycache__/pytester_assertions.cpython-39.pyc create mode 100644 venv/Lib/site-packages/_pytest/__pycache__/python.cpython-39.pyc create mode 100644 venv/Lib/site-packages/_pytest/__pycache__/python_api.cpython-39.pyc create mode 100644 venv/Lib/site-packages/_pytest/__pycache__/recwarn.cpython-39.pyc create mode 100644 venv/Lib/site-packages/_pytest/__pycache__/reports.cpython-39.pyc create mode 100644 venv/Lib/site-packages/_pytest/__pycache__/runner.cpython-39.pyc create mode 100644 venv/Lib/site-packages/_pytest/__pycache__/setuponly.cpython-39.pyc create mode 100644 venv/Lib/site-packages/_pytest/__pycache__/setupplan.cpython-39.pyc create mode 100644 venv/Lib/site-packages/_pytest/__pycache__/skipping.cpython-39.pyc create mode 100644 venv/Lib/site-packages/_pytest/__pycache__/stepwise.cpython-39.pyc create mode 100644 venv/Lib/site-packages/_pytest/__pycache__/store.cpython-39.pyc create mode 100644 venv/Lib/site-packages/_pytest/__pycache__/terminal.cpython-39.pyc create mode 100644 venv/Lib/site-packages/_pytest/__pycache__/threadexception.cpython-39.pyc create mode 100644 venv/Lib/site-packages/_pytest/__pycache__/timing.cpython-39.pyc create mode 100644 venv/Lib/site-packages/_pytest/__pycache__/tmpdir.cpython-39.pyc create mode 100644 venv/Lib/site-packages/_pytest/__pycache__/unittest.cpython-39.pyc create mode 100644 venv/Lib/site-packages/_pytest/__pycache__/unraisableexception.cpython-39.pyc create mode 100644 venv/Lib/site-packages/_pytest/__pycache__/warning_types.cpython-39.pyc create mode 100644 venv/Lib/site-packages/_pytest/__pycache__/warnings.cpython-39.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-39.pyc create mode 100644 venv/Lib/site-packages/_pytest/_code/__pycache__/code.cpython-39.pyc create mode 100644 venv/Lib/site-packages/_pytest/_code/__pycache__/source.cpython-39.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-39.pyc create mode 100644 venv/Lib/site-packages/_pytest/_io/__pycache__/saferepr.cpython-39.pyc create mode 100644 venv/Lib/site-packages/_pytest/_io/__pycache__/terminalwriter.cpython-39.pyc create mode 100644 venv/Lib/site-packages/_pytest/_io/__pycache__/wcwidth.cpython-39.pyc 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/_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-39.pyc create mode 100644 venv/Lib/site-packages/_pytest/assertion/__pycache__/rewrite.cpython-39.pyc create mode 100644 venv/Lib/site-packages/_pytest/assertion/__pycache__/truncate.cpython-39.pyc create mode 100644 venv/Lib/site-packages/_pytest/assertion/__pycache__/util.cpython-39.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-39.pyc create mode 100644 venv/Lib/site-packages/_pytest/config/__pycache__/argparsing.cpython-39.pyc create mode 100644 venv/Lib/site-packages/_pytest/config/__pycache__/exceptions.cpython-39.pyc create mode 100644 venv/Lib/site-packages/_pytest/config/__pycache__/findpaths.cpython-39.pyc create mode 100644 venv/Lib/site-packages/_pytest/config/argparsing.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/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-39.pyc create mode 100644 venv/Lib/site-packages/_pytest/mark/__pycache__/expression.cpython-39.pyc create mode 100644 venv/Lib/site-packages/_pytest/mark/__pycache__/structures.cpython-39.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/nose.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/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/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/stepwise.py create mode 100644 venv/Lib/site-packages/_pytest/store.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/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/atomicwrites-1.4.0.dist-info/INSTALLER create mode 100644 venv/Lib/site-packages/atomicwrites-1.4.0.dist-info/LICENSE create mode 100644 venv/Lib/site-packages/atomicwrites-1.4.0.dist-info/METADATA create mode 100644 venv/Lib/site-packages/atomicwrites-1.4.0.dist-info/RECORD create mode 100644 venv/Lib/site-packages/atomicwrites-1.4.0.dist-info/WHEEL create mode 100644 venv/Lib/site-packages/atomicwrites-1.4.0.dist-info/top_level.txt create mode 100644 venv/Lib/site-packages/atomicwrites/__init__.py create mode 100644 venv/Lib/site-packages/atomicwrites/__pycache__/__init__.cpython-39.pyc create mode 100644 venv/Lib/site-packages/attr/__init__.py create mode 100644 venv/Lib/site-packages/attr/__init__.pyi create mode 100644 venv/Lib/site-packages/attr/__pycache__/__init__.cpython-39.pyc create mode 100644 venv/Lib/site-packages/attr/__pycache__/_cmp.cpython-39.pyc create mode 100644 venv/Lib/site-packages/attr/__pycache__/_compat.cpython-39.pyc create mode 100644 venv/Lib/site-packages/attr/__pycache__/_config.cpython-39.pyc create mode 100644 venv/Lib/site-packages/attr/__pycache__/_funcs.cpython-39.pyc create mode 100644 venv/Lib/site-packages/attr/__pycache__/_make.cpython-39.pyc create mode 100644 venv/Lib/site-packages/attr/__pycache__/_next_gen.cpython-39.pyc create mode 100644 venv/Lib/site-packages/attr/__pycache__/_version_info.cpython-39.pyc create mode 100644 venv/Lib/site-packages/attr/__pycache__/converters.cpython-39.pyc create mode 100644 venv/Lib/site-packages/attr/__pycache__/exceptions.cpython-39.pyc create mode 100644 venv/Lib/site-packages/attr/__pycache__/filters.cpython-39.pyc create mode 100644 venv/Lib/site-packages/attr/__pycache__/setters.cpython-39.pyc create mode 100644 venv/Lib/site-packages/attr/__pycache__/validators.cpython-39.pyc create mode 100644 venv/Lib/site-packages/attr/_cmp.py create mode 100644 venv/Lib/site-packages/attr/_cmp.pyi create mode 100644 venv/Lib/site-packages/attr/_compat.py create mode 100644 venv/Lib/site-packages/attr/_config.py create mode 100644 venv/Lib/site-packages/attr/_funcs.py create mode 100644 venv/Lib/site-packages/attr/_make.py create mode 100644 venv/Lib/site-packages/attr/_next_gen.py create mode 100644 venv/Lib/site-packages/attr/_version_info.py create mode 100644 venv/Lib/site-packages/attr/_version_info.pyi create mode 100644 venv/Lib/site-packages/attr/converters.py create mode 100644 venv/Lib/site-packages/attr/converters.pyi create mode 100644 venv/Lib/site-packages/attr/exceptions.py create mode 100644 venv/Lib/site-packages/attr/exceptions.pyi create mode 100644 venv/Lib/site-packages/attr/filters.py create mode 100644 venv/Lib/site-packages/attr/filters.pyi create mode 100644 venv/Lib/site-packages/attr/py.typed create mode 100644 venv/Lib/site-packages/attr/setters.py create mode 100644 venv/Lib/site-packages/attr/setters.pyi create mode 100644 venv/Lib/site-packages/attr/validators.py create mode 100644 venv/Lib/site-packages/attr/validators.pyi create mode 100644 venv/Lib/site-packages/attrs-21.2.0.dist-info/AUTHORS.rst create mode 100644 venv/Lib/site-packages/attrs-21.2.0.dist-info/INSTALLER create mode 100644 venv/Lib/site-packages/attrs-21.2.0.dist-info/LICENSE create mode 100644 venv/Lib/site-packages/attrs-21.2.0.dist-info/METADATA create mode 100644 venv/Lib/site-packages/attrs-21.2.0.dist-info/RECORD create mode 100644 venv/Lib/site-packages/attrs-21.2.0.dist-info/WHEEL create mode 100644 venv/Lib/site-packages/attrs-21.2.0.dist-info/top_level.txt create mode 100644 venv/Lib/site-packages/blinker-1.4-py3.9.egg-info/PKG-INFO create mode 100644 venv/Lib/site-packages/blinker-1.4-py3.9.egg-info/SOURCES.txt create mode 100644 venv/Lib/site-packages/blinker-1.4-py3.9.egg-info/dependency_links.txt create mode 100644 venv/Lib/site-packages/blinker-1.4-py3.9.egg-info/installed-files.txt create mode 100644 venv/Lib/site-packages/blinker-1.4-py3.9.egg-info/top_level.txt create mode 100644 venv/Lib/site-packages/blinker/__init__.py create mode 100644 venv/Lib/site-packages/blinker/_saferef.py create mode 100644 venv/Lib/site-packages/blinker/_utilities.py create mode 100644 venv/Lib/site-packages/blinker/base.py create mode 100644 venv/Lib/site-packages/click-8.0.1.dist-info/INSTALLER create mode 100644 venv/Lib/site-packages/click-8.0.1.dist-info/LICENSE.rst create mode 100644 venv/Lib/site-packages/click-8.0.1.dist-info/METADATA create mode 100644 venv/Lib/site-packages/click-8.0.1.dist-info/RECORD create mode 100644 venv/Lib/site-packages/click-8.0.1.dist-info/WHEEL create mode 100644 venv/Lib/site-packages/click-8.0.1.dist-info/top_level.txt create mode 100644 venv/Lib/site-packages/click/__init__.py create mode 100644 venv/Lib/site-packages/click/__pycache__/__init__.cpython-39.pyc create mode 100644 venv/Lib/site-packages/click/__pycache__/_compat.cpython-39.pyc create mode 100644 venv/Lib/site-packages/click/__pycache__/_termui_impl.cpython-39.pyc create mode 100644 venv/Lib/site-packages/click/__pycache__/_textwrap.cpython-39.pyc create mode 100644 venv/Lib/site-packages/click/__pycache__/_unicodefun.cpython-39.pyc create mode 100644 venv/Lib/site-packages/click/__pycache__/_winconsole.cpython-39.pyc create mode 100644 venv/Lib/site-packages/click/__pycache__/core.cpython-39.pyc create mode 100644 venv/Lib/site-packages/click/__pycache__/decorators.cpython-39.pyc create mode 100644 venv/Lib/site-packages/click/__pycache__/exceptions.cpython-39.pyc create mode 100644 venv/Lib/site-packages/click/__pycache__/formatting.cpython-39.pyc create mode 100644 venv/Lib/site-packages/click/__pycache__/globals.cpython-39.pyc create mode 100644 venv/Lib/site-packages/click/__pycache__/parser.cpython-39.pyc create mode 100644 venv/Lib/site-packages/click/__pycache__/shell_completion.cpython-39.pyc create mode 100644 venv/Lib/site-packages/click/__pycache__/termui.cpython-39.pyc create mode 100644 venv/Lib/site-packages/click/__pycache__/testing.cpython-39.pyc create mode 100644 venv/Lib/site-packages/click/__pycache__/types.cpython-39.pyc create mode 100644 venv/Lib/site-packages/click/__pycache__/utils.cpython-39.pyc create mode 100644 venv/Lib/site-packages/click/_compat.py create mode 100644 venv/Lib/site-packages/click/_termui_impl.py create mode 100644 venv/Lib/site-packages/click/_textwrap.py create mode 100644 venv/Lib/site-packages/click/_unicodefun.py create mode 100644 venv/Lib/site-packages/click/_winconsole.py create mode 100644 venv/Lib/site-packages/click/core.py create mode 100644 venv/Lib/site-packages/click/decorators.py create mode 100644 venv/Lib/site-packages/click/exceptions.py create mode 100644 venv/Lib/site-packages/click/formatting.py create mode 100644 venv/Lib/site-packages/click/globals.py create mode 100644 venv/Lib/site-packages/click/parser.py create mode 100644 venv/Lib/site-packages/click/py.typed create mode 100644 venv/Lib/site-packages/click/shell_completion.py create mode 100644 venv/Lib/site-packages/click/termui.py create mode 100644 venv/Lib/site-packages/click/testing.py create mode 100644 venv/Lib/site-packages/click/types.py create mode 100644 venv/Lib/site-packages/click/utils.py create mode 100644 venv/Lib/site-packages/colorama-0.4.4.dist-info/INSTALLER create mode 100644 venv/Lib/site-packages/colorama-0.4.4.dist-info/LICENSE.txt create mode 100644 venv/Lib/site-packages/colorama-0.4.4.dist-info/METADATA create mode 100644 venv/Lib/site-packages/colorama-0.4.4.dist-info/RECORD create mode 100644 venv/Lib/site-packages/colorama-0.4.4.dist-info/WHEEL create mode 100644 venv/Lib/site-packages/colorama-0.4.4.dist-info/top_level.txt create mode 100644 venv/Lib/site-packages/colorama/__init__.py create mode 100644 venv/Lib/site-packages/colorama/__pycache__/__init__.cpython-39.pyc create mode 100644 venv/Lib/site-packages/colorama/__pycache__/ansi.cpython-39.pyc create mode 100644 venv/Lib/site-packages/colorama/__pycache__/ansitowin32.cpython-39.pyc create mode 100644 venv/Lib/site-packages/colorama/__pycache__/initialise.cpython-39.pyc create mode 100644 venv/Lib/site-packages/colorama/__pycache__/win32.cpython-39.pyc create mode 100644 venv/Lib/site-packages/colorama/__pycache__/winterm.cpython-39.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/win32.py create mode 100644 venv/Lib/site-packages/colorama/winterm.py create mode 100644 venv/Lib/site-packages/detach-1.0-py3.9.egg-info/PKG-INFO create mode 100644 venv/Lib/site-packages/detach-1.0-py3.9.egg-info/SOURCES.txt create mode 100644 venv/Lib/site-packages/detach-1.0-py3.9.egg-info/dependency_links.txt create mode 100644 venv/Lib/site-packages/detach-1.0-py3.9.egg-info/installed-files.txt create mode 100644 venv/Lib/site-packages/detach-1.0-py3.9.egg-info/top_level.txt create mode 100644 venv/Lib/site-packages/detach.py create mode 100644 venv/Lib/site-packages/distutils-precedence.pth create mode 100644 venv/Lib/site-packages/flask/__init__.py create mode 100644 venv/Lib/site-packages/flask/__main__.py create mode 100644 venv/Lib/site-packages/flask/__pycache__/__init__.cpython-39.pyc create mode 100644 venv/Lib/site-packages/flask/__pycache__/__main__.cpython-39.pyc create mode 100644 venv/Lib/site-packages/flask/__pycache__/app.cpython-39.pyc create mode 100644 venv/Lib/site-packages/flask/__pycache__/blueprints.cpython-39.pyc create mode 100644 venv/Lib/site-packages/flask/__pycache__/cli.cpython-39.pyc create mode 100644 venv/Lib/site-packages/flask/__pycache__/config.cpython-39.pyc create mode 100644 venv/Lib/site-packages/flask/__pycache__/ctx.cpython-39.pyc create mode 100644 venv/Lib/site-packages/flask/__pycache__/debughelpers.cpython-39.pyc create mode 100644 venv/Lib/site-packages/flask/__pycache__/globals.cpython-39.pyc create mode 100644 venv/Lib/site-packages/flask/__pycache__/helpers.cpython-39.pyc create mode 100644 venv/Lib/site-packages/flask/__pycache__/logging.cpython-39.pyc create mode 100644 venv/Lib/site-packages/flask/__pycache__/scaffold.cpython-39.pyc create mode 100644 venv/Lib/site-packages/flask/__pycache__/sessions.cpython-39.pyc create mode 100644 venv/Lib/site-packages/flask/__pycache__/signals.cpython-39.pyc create mode 100644 venv/Lib/site-packages/flask/__pycache__/templating.cpython-39.pyc create mode 100644 venv/Lib/site-packages/flask/__pycache__/testing.cpython-39.pyc create mode 100644 venv/Lib/site-packages/flask/__pycache__/typing.cpython-39.pyc create mode 100644 venv/Lib/site-packages/flask/__pycache__/views.cpython-39.pyc create mode 100644 venv/Lib/site-packages/flask/__pycache__/wrappers.cpython-39.pyc create mode 100644 venv/Lib/site-packages/flask/app.py create mode 100644 venv/Lib/site-packages/flask/blueprints.py create mode 100644 venv/Lib/site-packages/flask/cli.py create mode 100644 venv/Lib/site-packages/flask/config.py create mode 100644 venv/Lib/site-packages/flask/ctx.py create mode 100644 venv/Lib/site-packages/flask/debughelpers.py create mode 100644 venv/Lib/site-packages/flask/globals.py create mode 100644 venv/Lib/site-packages/flask/helpers.py create mode 100644 venv/Lib/site-packages/flask/json/__init__.py create mode 100644 venv/Lib/site-packages/flask/json/__pycache__/__init__.cpython-39.pyc create mode 100644 venv/Lib/site-packages/flask/json/__pycache__/tag.cpython-39.pyc create mode 100644 venv/Lib/site-packages/flask/json/tag.py create mode 100644 venv/Lib/site-packages/flask/logging.py create mode 100644 venv/Lib/site-packages/flask/py.typed create mode 100644 venv/Lib/site-packages/flask/scaffold.py create mode 100644 venv/Lib/site-packages/flask/sessions.py create mode 100644 venv/Lib/site-packages/flask/signals.py create mode 100644 venv/Lib/site-packages/flask/templating.py create mode 100644 venv/Lib/site-packages/flask/testing.py create mode 100644 venv/Lib/site-packages/flask/typing.py create mode 100644 venv/Lib/site-packages/flask/views.py create mode 100644 venv/Lib/site-packages/flask/wrappers.py create mode 100644 venv/Lib/site-packages/flask_sqlalchemy/__init__.py create mode 100644 venv/Lib/site-packages/flask_sqlalchemy/__pycache__/__init__.cpython-39.pyc create mode 100644 venv/Lib/site-packages/flask_sqlalchemy/__pycache__/_compat.cpython-39.pyc create mode 100644 venv/Lib/site-packages/flask_sqlalchemy/__pycache__/model.cpython-39.pyc create mode 100644 venv/Lib/site-packages/flask_sqlalchemy/__pycache__/utils.cpython-39.pyc create mode 100644 venv/Lib/site-packages/flask_sqlalchemy/_compat.py create mode 100644 venv/Lib/site-packages/flask_sqlalchemy/model.py create mode 100644 venv/Lib/site-packages/flask_sqlalchemy/utils.py create mode 100644 venv/Lib/site-packages/greenlet-1.1.0.dist-info/AUTHORS create mode 100644 venv/Lib/site-packages/greenlet-1.1.0.dist-info/INSTALLER create mode 100644 venv/Lib/site-packages/greenlet-1.1.0.dist-info/LICENSE create mode 100644 venv/Lib/site-packages/greenlet-1.1.0.dist-info/LICENSE.PSF create mode 100644 venv/Lib/site-packages/greenlet-1.1.0.dist-info/METADATA create mode 100644 venv/Lib/site-packages/greenlet-1.1.0.dist-info/RECORD create mode 100644 venv/Lib/site-packages/greenlet-1.1.0.dist-info/WHEEL create mode 100644 venv/Lib/site-packages/greenlet-1.1.0.dist-info/top_level.txt create mode 100644 venv/Lib/site-packages/greenlet/__init__.py create mode 100644 venv/Lib/site-packages/greenlet/__pycache__/__init__.cpython-39.pyc create mode 100644 venv/Lib/site-packages/greenlet/_greenlet.cp39-win_amd64.pyd create mode 100644 venv/Lib/site-packages/greenlet/greenlet.c create mode 100644 venv/Lib/site-packages/greenlet/greenlet.h create mode 100644 venv/Lib/site-packages/greenlet/platform/setup_switch_x64_masm.cmd create mode 100644 venv/Lib/site-packages/greenlet/platform/switch_aarch64_gcc.h create mode 100644 venv/Lib/site-packages/greenlet/platform/switch_alpha_unix.h create mode 100644 venv/Lib/site-packages/greenlet/platform/switch_amd64_unix.h create mode 100644 venv/Lib/site-packages/greenlet/platform/switch_arm32_gcc.h create mode 100644 venv/Lib/site-packages/greenlet/platform/switch_arm32_ios.h create mode 100644 venv/Lib/site-packages/greenlet/platform/switch_csky_gcc.h create mode 100644 venv/Lib/site-packages/greenlet/platform/switch_m68k_gcc.h create mode 100644 venv/Lib/site-packages/greenlet/platform/switch_mips_unix.h create mode 100644 venv/Lib/site-packages/greenlet/platform/switch_ppc64_aix.h create mode 100644 venv/Lib/site-packages/greenlet/platform/switch_ppc64_linux.h create mode 100644 venv/Lib/site-packages/greenlet/platform/switch_ppc_aix.h create mode 100644 venv/Lib/site-packages/greenlet/platform/switch_ppc_linux.h create mode 100644 venv/Lib/site-packages/greenlet/platform/switch_ppc_macosx.h create mode 100644 venv/Lib/site-packages/greenlet/platform/switch_ppc_unix.h create mode 100644 venv/Lib/site-packages/greenlet/platform/switch_riscv_unix.h create mode 100644 venv/Lib/site-packages/greenlet/platform/switch_s390_unix.h create mode 100644 venv/Lib/site-packages/greenlet/platform/switch_sparc_sun_gcc.h create mode 100644 venv/Lib/site-packages/greenlet/platform/switch_x32_unix.h create mode 100644 venv/Lib/site-packages/greenlet/platform/switch_x64_masm.asm create mode 100644 venv/Lib/site-packages/greenlet/platform/switch_x64_masm.obj create mode 100644 venv/Lib/site-packages/greenlet/platform/switch_x64_msvc.h create mode 100644 venv/Lib/site-packages/greenlet/platform/switch_x86_msvc.h create mode 100644 venv/Lib/site-packages/greenlet/platform/switch_x86_unix.h create mode 100644 venv/Lib/site-packages/greenlet/slp_platformselect.h create mode 100644 venv/Lib/site-packages/greenlet/tests/__init__.py create mode 100644 venv/Lib/site-packages/greenlet/tests/__pycache__/__init__.cpython-39.pyc create mode 100644 venv/Lib/site-packages/greenlet/tests/__pycache__/test_contextvars.cpython-39.pyc create mode 100644 venv/Lib/site-packages/greenlet/tests/__pycache__/test_cpp.cpython-39.pyc create mode 100644 venv/Lib/site-packages/greenlet/tests/__pycache__/test_extension_interface.cpython-39.pyc create mode 100644 venv/Lib/site-packages/greenlet/tests/__pycache__/test_gc.cpython-39.pyc create mode 100644 venv/Lib/site-packages/greenlet/tests/__pycache__/test_generator.cpython-39.pyc create mode 100644 venv/Lib/site-packages/greenlet/tests/__pycache__/test_generator_nested.cpython-39.pyc create mode 100644 venv/Lib/site-packages/greenlet/tests/__pycache__/test_greenlet.cpython-39.pyc create mode 100644 venv/Lib/site-packages/greenlet/tests/__pycache__/test_leaks.cpython-39.pyc create mode 100644 venv/Lib/site-packages/greenlet/tests/__pycache__/test_stack_saved.cpython-39.pyc create mode 100644 venv/Lib/site-packages/greenlet/tests/__pycache__/test_throw.cpython-39.pyc create mode 100644 venv/Lib/site-packages/greenlet/tests/__pycache__/test_tracing.cpython-39.pyc create mode 100644 venv/Lib/site-packages/greenlet/tests/__pycache__/test_version.cpython-39.pyc create mode 100644 venv/Lib/site-packages/greenlet/tests/__pycache__/test_weakref.cpython-39.pyc create mode 100644 venv/Lib/site-packages/greenlet/tests/_test_extension.c create mode 100644 venv/Lib/site-packages/greenlet/tests/_test_extension.cp39-win_amd64.pyd create mode 100644 venv/Lib/site-packages/greenlet/tests/_test_extension_cpp.cp39-win_amd64.pyd create mode 100644 venv/Lib/site-packages/greenlet/tests/_test_extension_cpp.cpp create mode 100644 venv/Lib/site-packages/greenlet/tests/test_contextvars.py create mode 100644 venv/Lib/site-packages/greenlet/tests/test_cpp.py create mode 100644 venv/Lib/site-packages/greenlet/tests/test_extension_interface.py create mode 100644 venv/Lib/site-packages/greenlet/tests/test_gc.py create mode 100644 venv/Lib/site-packages/greenlet/tests/test_generator.py create mode 100644 venv/Lib/site-packages/greenlet/tests/test_generator_nested.py create mode 100644 venv/Lib/site-packages/greenlet/tests/test_greenlet.py create mode 100644 venv/Lib/site-packages/greenlet/tests/test_leaks.py create mode 100644 venv/Lib/site-packages/greenlet/tests/test_stack_saved.py create mode 100644 venv/Lib/site-packages/greenlet/tests/test_throw.py create mode 100644 venv/Lib/site-packages/greenlet/tests/test_tracing.py create mode 100644 venv/Lib/site-packages/greenlet/tests/test_version.py create mode 100644 venv/Lib/site-packages/greenlet/tests/test_weakref.py create mode 100644 venv/Lib/site-packages/iniconfig-1.1.1.dist-info/INSTALLER create mode 100644 venv/Lib/site-packages/iniconfig-1.1.1.dist-info/LICENSE create mode 100644 venv/Lib/site-packages/iniconfig-1.1.1.dist-info/METADATA create mode 100644 venv/Lib/site-packages/iniconfig-1.1.1.dist-info/RECORD create mode 100644 venv/Lib/site-packages/iniconfig-1.1.1.dist-info/WHEEL create mode 100644 venv/Lib/site-packages/iniconfig-1.1.1.dist-info/top_level.txt create mode 100644 venv/Lib/site-packages/iniconfig/__init__.py create mode 100644 venv/Lib/site-packages/iniconfig/__init__.pyi create mode 100644 venv/Lib/site-packages/iniconfig/__pycache__/__init__.cpython-39.pyc create mode 100644 venv/Lib/site-packages/iniconfig/py.typed create mode 100644 venv/Lib/site-packages/itsdangerous-2.0.1.dist-info/INSTALLER create mode 100644 venv/Lib/site-packages/itsdangerous-2.0.1.dist-info/LICENSE.rst create mode 100644 venv/Lib/site-packages/itsdangerous-2.0.1.dist-info/METADATA create mode 100644 venv/Lib/site-packages/itsdangerous-2.0.1.dist-info/RECORD create mode 100644 venv/Lib/site-packages/itsdangerous-2.0.1.dist-info/WHEEL create mode 100644 venv/Lib/site-packages/itsdangerous-2.0.1.dist-info/top_level.txt create mode 100644 venv/Lib/site-packages/itsdangerous/__init__.py create mode 100644 venv/Lib/site-packages/itsdangerous/__pycache__/__init__.cpython-39.pyc create mode 100644 venv/Lib/site-packages/itsdangerous/__pycache__/_json.cpython-39.pyc create mode 100644 venv/Lib/site-packages/itsdangerous/__pycache__/encoding.cpython-39.pyc create mode 100644 venv/Lib/site-packages/itsdangerous/__pycache__/exc.cpython-39.pyc create mode 100644 venv/Lib/site-packages/itsdangerous/__pycache__/jws.cpython-39.pyc create mode 100644 venv/Lib/site-packages/itsdangerous/__pycache__/serializer.cpython-39.pyc create mode 100644 venv/Lib/site-packages/itsdangerous/__pycache__/signer.cpython-39.pyc create mode 100644 venv/Lib/site-packages/itsdangerous/__pycache__/timed.cpython-39.pyc create mode 100644 venv/Lib/site-packages/itsdangerous/__pycache__/url_safe.cpython-39.pyc create mode 100644 venv/Lib/site-packages/itsdangerous/_json.py create mode 100644 venv/Lib/site-packages/itsdangerous/encoding.py create mode 100644 venv/Lib/site-packages/itsdangerous/exc.py create mode 100644 venv/Lib/site-packages/itsdangerous/jws.py create mode 100644 venv/Lib/site-packages/itsdangerous/py.typed create mode 100644 venv/Lib/site-packages/itsdangerous/serializer.py create mode 100644 venv/Lib/site-packages/itsdangerous/signer.py create mode 100644 venv/Lib/site-packages/itsdangerous/timed.py create mode 100644 venv/Lib/site-packages/itsdangerous/url_safe.py create mode 100644 venv/Lib/site-packages/jinja2/__init__.py create mode 100644 venv/Lib/site-packages/jinja2/__pycache__/__init__.cpython-39.pyc create mode 100644 venv/Lib/site-packages/jinja2/__pycache__/_identifier.cpython-39.pyc create mode 100644 venv/Lib/site-packages/jinja2/__pycache__/async_utils.cpython-39.pyc create mode 100644 venv/Lib/site-packages/jinja2/__pycache__/bccache.cpython-39.pyc create mode 100644 venv/Lib/site-packages/jinja2/__pycache__/compiler.cpython-39.pyc create mode 100644 venv/Lib/site-packages/jinja2/__pycache__/constants.cpython-39.pyc create mode 100644 venv/Lib/site-packages/jinja2/__pycache__/debug.cpython-39.pyc create mode 100644 venv/Lib/site-packages/jinja2/__pycache__/defaults.cpython-39.pyc create mode 100644 venv/Lib/site-packages/jinja2/__pycache__/environment.cpython-39.pyc create mode 100644 venv/Lib/site-packages/jinja2/__pycache__/exceptions.cpython-39.pyc create mode 100644 venv/Lib/site-packages/jinja2/__pycache__/ext.cpython-39.pyc create mode 100644 venv/Lib/site-packages/jinja2/__pycache__/filters.cpython-39.pyc create mode 100644 venv/Lib/site-packages/jinja2/__pycache__/idtracking.cpython-39.pyc create mode 100644 venv/Lib/site-packages/jinja2/__pycache__/lexer.cpython-39.pyc create mode 100644 venv/Lib/site-packages/jinja2/__pycache__/loaders.cpython-39.pyc create mode 100644 venv/Lib/site-packages/jinja2/__pycache__/meta.cpython-39.pyc create mode 100644 venv/Lib/site-packages/jinja2/__pycache__/nativetypes.cpython-39.pyc create mode 100644 venv/Lib/site-packages/jinja2/__pycache__/nodes.cpython-39.pyc create mode 100644 venv/Lib/site-packages/jinja2/__pycache__/optimizer.cpython-39.pyc create mode 100644 venv/Lib/site-packages/jinja2/__pycache__/parser.cpython-39.pyc create mode 100644 venv/Lib/site-packages/jinja2/__pycache__/runtime.cpython-39.pyc create mode 100644 venv/Lib/site-packages/jinja2/__pycache__/sandbox.cpython-39.pyc create mode 100644 venv/Lib/site-packages/jinja2/__pycache__/tests.cpython-39.pyc create mode 100644 venv/Lib/site-packages/jinja2/__pycache__/utils.cpython-39.pyc create mode 100644 venv/Lib/site-packages/jinja2/__pycache__/visitor.cpython-39.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/__init__.py create mode 100644 venv/Lib/site-packages/markupsafe/__pycache__/__init__.cpython-39.pyc create mode 100644 venv/Lib/site-packages/markupsafe/__pycache__/_native.cpython-39.pyc create mode 100644 venv/Lib/site-packages/markupsafe/_native.py create mode 100644 venv/Lib/site-packages/markupsafe/_speedups.cp39-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/maxcul-0.1.1-py3.9.egg-info/PKG-INFO create mode 100644 venv/Lib/site-packages/maxcul-0.1.1-py3.9.egg-info/SOURCES.txt create mode 100644 venv/Lib/site-packages/maxcul-0.1.1-py3.9.egg-info/dependency_links.txt create mode 100644 venv/Lib/site-packages/maxcul-0.1.1-py3.9.egg-info/installed-files.txt create mode 100644 venv/Lib/site-packages/maxcul-0.1.1-py3.9.egg-info/requires.txt create mode 100644 venv/Lib/site-packages/maxcul-0.1.1-py3.9.egg-info/top_level.txt create mode 100644 venv/Lib/site-packages/maxcul/__init__.py create mode 100644 venv/Lib/site-packages/maxcul/__pycache__/__init__.cpython-39.pyc create mode 100644 venv/Lib/site-packages/maxcul/communication.py create mode 100644 venv/Lib/site-packages/maxcul/exceptions.py create mode 100644 venv/Lib/site-packages/maxcul/messages.py create mode 100644 venv/Lib/site-packages/maxcul/signals.py create mode 100644 venv/Lib/site-packages/packaging-21.0.dist-info/INSTALLER create mode 100644 venv/Lib/site-packages/packaging-21.0.dist-info/LICENSE create mode 100644 venv/Lib/site-packages/packaging-21.0.dist-info/LICENSE.APACHE create mode 100644 venv/Lib/site-packages/packaging-21.0.dist-info/LICENSE.BSD create mode 100644 venv/Lib/site-packages/packaging-21.0.dist-info/METADATA create mode 100644 venv/Lib/site-packages/packaging-21.0.dist-info/RECORD create mode 100644 venv/Lib/site-packages/packaging-21.0.dist-info/WHEEL create mode 100644 venv/Lib/site-packages/packaging-21.0.dist-info/top_level.txt create mode 100644 venv/Lib/site-packages/packaging/__about__.py create mode 100644 venv/Lib/site-packages/packaging/__init__.py create mode 100644 venv/Lib/site-packages/packaging/__pycache__/__about__.cpython-39.pyc create mode 100644 venv/Lib/site-packages/packaging/__pycache__/__init__.cpython-39.pyc create mode 100644 venv/Lib/site-packages/packaging/__pycache__/_manylinux.cpython-39.pyc create mode 100644 venv/Lib/site-packages/packaging/__pycache__/_musllinux.cpython-39.pyc create mode 100644 venv/Lib/site-packages/packaging/__pycache__/_structures.cpython-39.pyc create mode 100644 venv/Lib/site-packages/packaging/__pycache__/markers.cpython-39.pyc create mode 100644 venv/Lib/site-packages/packaging/__pycache__/requirements.cpython-39.pyc create mode 100644 venv/Lib/site-packages/packaging/__pycache__/specifiers.cpython-39.pyc create mode 100644 venv/Lib/site-packages/packaging/__pycache__/tags.cpython-39.pyc create mode 100644 venv/Lib/site-packages/packaging/__pycache__/utils.cpython-39.pyc create mode 100644 venv/Lib/site-packages/packaging/__pycache__/version.cpython-39.pyc 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/_structures.py create mode 100644 venv/Lib/site-packages/packaging/markers.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-21.1.3.dist-info/INSTALLER create mode 100644 venv/Lib/site-packages/pip-21.1.3.dist-info/LICENSE.txt create mode 100644 venv/Lib/site-packages/pip-21.1.3.dist-info/METADATA create mode 100644 venv/Lib/site-packages/pip-21.1.3.dist-info/RECORD create mode 100644 venv/Lib/site-packages/pip-21.1.3.dist-info/WHEEL create mode 100644 venv/Lib/site-packages/pip-21.1.3.dist-info/entry_points.txt create mode 100644 venv/Lib/site-packages/pip-21.1.3.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/__pycache__/__init__.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/__pycache__/__main__.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/__init__.py create mode 100644 venv/Lib/site-packages/pip/_internal/__pycache__/__init__.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/__pycache__/build_env.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/__pycache__/cache.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/__pycache__/configuration.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/__pycache__/exceptions.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/__pycache__/main.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/__pycache__/pyproject.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/__pycache__/self_outdated_check.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/__pycache__/wheel_builder.cpython-39.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-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/cli/__pycache__/autocompletion.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/cli/__pycache__/base_command.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/cli/__pycache__/cmdoptions.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/cli/__pycache__/command_context.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/cli/__pycache__/main.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/cli/__pycache__/main_parser.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/cli/__pycache__/parser.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/cli/__pycache__/progress_bars.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/cli/__pycache__/req_command.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/cli/__pycache__/spinners.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/cli/__pycache__/status_codes.cpython-39.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-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/commands/__pycache__/cache.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/commands/__pycache__/check.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/commands/__pycache__/completion.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/commands/__pycache__/configuration.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/commands/__pycache__/debug.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/commands/__pycache__/download.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/commands/__pycache__/freeze.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/commands/__pycache__/hash.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/commands/__pycache__/help.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/commands/__pycache__/install.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/commands/__pycache__/list.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/commands/__pycache__/search.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/commands/__pycache__/show.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/commands/__pycache__/uninstall.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/commands/__pycache__/wheel.cpython-39.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/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-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/distributions/__pycache__/base.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/distributions/__pycache__/installed.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/distributions/__pycache__/sdist.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/distributions/__pycache__/wheel.cpython-39.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-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/index/__pycache__/collector.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/index/__pycache__/package_finder.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/index/__pycache__/sources.cpython-39.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-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/locations/__pycache__/_distutils.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/locations/__pycache__/_sysconfig.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/locations/__pycache__/base.cpython-39.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-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/metadata/__pycache__/base.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/metadata/__pycache__/pkg_resources.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/metadata/base.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-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/models/__pycache__/candidate.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/models/__pycache__/direct_url.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/models/__pycache__/format_control.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/models/__pycache__/index.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/models/__pycache__/link.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/models/__pycache__/scheme.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/models/__pycache__/search_scope.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/models/__pycache__/selection_prefs.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/models/__pycache__/target_python.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/models/__pycache__/wheel.cpython-39.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/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-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/network/__pycache__/auth.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/network/__pycache__/cache.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/network/__pycache__/download.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/network/__pycache__/lazy_wheel.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/network/__pycache__/session.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/network/__pycache__/utils.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/network/__pycache__/xmlrpc.cpython-39.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-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/operations/__pycache__/check.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/operations/__pycache__/freeze.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/operations/__pycache__/prepare.cpython-39.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-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/operations/build/__pycache__/metadata.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/operations/build/__pycache__/metadata_legacy.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/operations/build/__pycache__/wheel.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/operations/build/__pycache__/wheel_legacy.cpython-39.pyc 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_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_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-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/operations/install/__pycache__/editable_legacy.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/operations/install/__pycache__/legacy.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/operations/install/__pycache__/wheel.cpython-39.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/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-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/req/__pycache__/constructors.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/req/__pycache__/req_file.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/req/__pycache__/req_install.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/req/__pycache__/req_set.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/req/__pycache__/req_tracker.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/req/__pycache__/req_uninstall.cpython-39.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_tracker.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-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/resolution/__pycache__/base.cpython-39.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-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/resolution/legacy/__pycache__/resolver.cpython-39.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-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/resolution/resolvelib/__pycache__/base.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/resolution/resolvelib/__pycache__/candidates.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/resolution/resolvelib/__pycache__/factory.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/resolution/resolvelib/__pycache__/found_candidates.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/resolution/resolvelib/__pycache__/provider.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/resolution/resolvelib/__pycache__/reporter.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/resolution/resolvelib/__pycache__/requirements.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/resolution/resolvelib/__pycache__/resolver.cpython-39.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-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/utils/__pycache__/appdirs.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/utils/__pycache__/compat.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/utils/__pycache__/compatibility_tags.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/utils/__pycache__/datetime.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/utils/__pycache__/deprecation.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/utils/__pycache__/direct_url_helpers.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/utils/__pycache__/distutils_args.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/utils/__pycache__/encoding.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/utils/__pycache__/entrypoints.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/utils/__pycache__/filesystem.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/utils/__pycache__/filetypes.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/utils/__pycache__/glibc.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/utils/__pycache__/hashes.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/utils/__pycache__/inject_securetransport.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/utils/__pycache__/logging.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/utils/__pycache__/misc.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/utils/__pycache__/models.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/utils/__pycache__/packaging.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/utils/__pycache__/parallel.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/utils/__pycache__/pkg_resources.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/utils/__pycache__/setuptools_build.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/utils/__pycache__/subprocess.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/utils/__pycache__/temp_dir.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/utils/__pycache__/unpacking.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/utils/__pycache__/urls.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/utils/__pycache__/virtualenv.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/utils/__pycache__/wheel.cpython-39.pyc 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/distutils_args.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/inject_securetransport.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/parallel.py create mode 100644 venv/Lib/site-packages/pip/_internal/utils/pkg_resources.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-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/vcs/__pycache__/bazaar.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/vcs/__pycache__/git.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/vcs/__pycache__/mercurial.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/vcs/__pycache__/subversion.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_internal/vcs/__pycache__/versioncontrol.cpython-39.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-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/__pycache__/appdirs.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/__pycache__/distro.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/__pycache__/pyparsing.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/__pycache__/six.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/appdirs.py 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-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/cachecontrol/__pycache__/_cmd.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/cachecontrol/__pycache__/adapter.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/cachecontrol/__pycache__/cache.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/cachecontrol/__pycache__/compat.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/cachecontrol/__pycache__/controller.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/cachecontrol/__pycache__/filewrapper.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/cachecontrol/__pycache__/heuristics.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/cachecontrol/__pycache__/serialize.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/cachecontrol/__pycache__/wrapper.cpython-39.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-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/cachecontrol/caches/__pycache__/file_cache.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/cachecontrol/caches/__pycache__/redis_cache.cpython-39.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/compat.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/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-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/certifi/__pycache__/__main__.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/certifi/__pycache__/core.cpython-39.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/chardet/__init__.py create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/__pycache__/__init__.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/__pycache__/big5freq.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/__pycache__/big5prober.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/__pycache__/chardistribution.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/__pycache__/charsetgroupprober.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/__pycache__/charsetprober.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/__pycache__/codingstatemachine.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/__pycache__/compat.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/__pycache__/cp949prober.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/__pycache__/enums.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/__pycache__/escprober.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/__pycache__/escsm.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/__pycache__/eucjpprober.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/__pycache__/euckrfreq.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/__pycache__/euckrprober.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/__pycache__/euctwfreq.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/__pycache__/euctwprober.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/__pycache__/gb2312freq.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/__pycache__/gb2312prober.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/__pycache__/hebrewprober.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/__pycache__/jisfreq.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/__pycache__/jpcntx.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/__pycache__/langbulgarianmodel.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/__pycache__/langgreekmodel.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/__pycache__/langhebrewmodel.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/__pycache__/langhungarianmodel.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/__pycache__/langrussianmodel.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/__pycache__/langthaimodel.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/__pycache__/langturkishmodel.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/__pycache__/latin1prober.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/__pycache__/mbcharsetprober.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/__pycache__/mbcsgroupprober.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/__pycache__/mbcssm.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/__pycache__/sbcharsetprober.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/__pycache__/sbcsgroupprober.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/__pycache__/sjisprober.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/__pycache__/universaldetector.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/__pycache__/utf8prober.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/__pycache__/version.cpython-39.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-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/cli/__pycache__/chardetect.cpython-39.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/compat.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/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/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-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/metadata/__pycache__/languages.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/chardet/metadata/languages.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/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-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/colorama/__pycache__/ansi.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/colorama/__pycache__/ansitowin32.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/colorama/__pycache__/initialise.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/colorama/__pycache__/win32.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/colorama/__pycache__/winterm.cpython-39.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/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-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/distlib/__pycache__/compat.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/distlib/__pycache__/database.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/distlib/__pycache__/index.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/distlib/__pycache__/locators.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/distlib/__pycache__/manifest.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/distlib/__pycache__/markers.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/distlib/__pycache__/metadata.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/distlib/__pycache__/resources.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/distlib/__pycache__/scripts.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/distlib/__pycache__/util.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/distlib/__pycache__/version.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/distlib/__pycache__/wheel.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/distlib/_backport/__init__.py create mode 100644 venv/Lib/site-packages/pip/_vendor/distlib/_backport/__pycache__/__init__.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/distlib/_backport/__pycache__/misc.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/distlib/_backport/__pycache__/shutil.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/distlib/_backport/__pycache__/sysconfig.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/distlib/_backport/__pycache__/tarfile.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/distlib/_backport/misc.py create mode 100644 venv/Lib/site-packages/pip/_vendor/distlib/_backport/shutil.py create mode 100644 venv/Lib/site-packages/pip/_vendor/distlib/_backport/sysconfig.cfg create mode 100644 venv/Lib/site-packages/pip/_vendor/distlib/_backport/sysconfig.py create mode 100644 venv/Lib/site-packages/pip/_vendor/distlib/_backport/tarfile.py 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.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.exe create mode 100644 venv/Lib/site-packages/pip/_vendor/distlib/wheel.py create mode 100644 venv/Lib/site-packages/pip/_vendor/distro.py create mode 100644 venv/Lib/site-packages/pip/_vendor/html5lib/__init__.py create mode 100644 venv/Lib/site-packages/pip/_vendor/html5lib/__pycache__/__init__.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/html5lib/__pycache__/_ihatexml.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/html5lib/__pycache__/_inputstream.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/html5lib/__pycache__/_tokenizer.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/html5lib/__pycache__/_utils.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/html5lib/__pycache__/constants.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/html5lib/__pycache__/html5parser.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/html5lib/__pycache__/serializer.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/html5lib/_ihatexml.py create mode 100644 venv/Lib/site-packages/pip/_vendor/html5lib/_inputstream.py create mode 100644 venv/Lib/site-packages/pip/_vendor/html5lib/_tokenizer.py create mode 100644 venv/Lib/site-packages/pip/_vendor/html5lib/_trie/__init__.py create mode 100644 venv/Lib/site-packages/pip/_vendor/html5lib/_trie/__pycache__/__init__.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/html5lib/_trie/__pycache__/_base.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/html5lib/_trie/__pycache__/py.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/html5lib/_trie/_base.py create mode 100644 venv/Lib/site-packages/pip/_vendor/html5lib/_trie/py.py create mode 100644 venv/Lib/site-packages/pip/_vendor/html5lib/_utils.py create mode 100644 venv/Lib/site-packages/pip/_vendor/html5lib/constants.py create mode 100644 venv/Lib/site-packages/pip/_vendor/html5lib/filters/__init__.py create mode 100644 venv/Lib/site-packages/pip/_vendor/html5lib/filters/__pycache__/__init__.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/html5lib/filters/__pycache__/alphabeticalattributes.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/html5lib/filters/__pycache__/base.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/html5lib/filters/__pycache__/inject_meta_charset.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/html5lib/filters/__pycache__/lint.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/html5lib/filters/__pycache__/optionaltags.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/html5lib/filters/__pycache__/sanitizer.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/html5lib/filters/__pycache__/whitespace.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/html5lib/filters/alphabeticalattributes.py create mode 100644 venv/Lib/site-packages/pip/_vendor/html5lib/filters/base.py create mode 100644 venv/Lib/site-packages/pip/_vendor/html5lib/filters/inject_meta_charset.py create mode 100644 venv/Lib/site-packages/pip/_vendor/html5lib/filters/lint.py create mode 100644 venv/Lib/site-packages/pip/_vendor/html5lib/filters/optionaltags.py create mode 100644 venv/Lib/site-packages/pip/_vendor/html5lib/filters/sanitizer.py create mode 100644 venv/Lib/site-packages/pip/_vendor/html5lib/filters/whitespace.py create mode 100644 venv/Lib/site-packages/pip/_vendor/html5lib/html5parser.py create mode 100644 venv/Lib/site-packages/pip/_vendor/html5lib/serializer.py create mode 100644 venv/Lib/site-packages/pip/_vendor/html5lib/treeadapters/__init__.py create mode 100644 venv/Lib/site-packages/pip/_vendor/html5lib/treeadapters/__pycache__/__init__.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/html5lib/treeadapters/__pycache__/genshi.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/html5lib/treeadapters/__pycache__/sax.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/html5lib/treeadapters/genshi.py create mode 100644 venv/Lib/site-packages/pip/_vendor/html5lib/treeadapters/sax.py create mode 100644 venv/Lib/site-packages/pip/_vendor/html5lib/treebuilders/__init__.py create mode 100644 venv/Lib/site-packages/pip/_vendor/html5lib/treebuilders/__pycache__/__init__.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/html5lib/treebuilders/__pycache__/base.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/html5lib/treebuilders/__pycache__/dom.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/html5lib/treebuilders/__pycache__/etree.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/html5lib/treebuilders/__pycache__/etree_lxml.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/html5lib/treebuilders/base.py create mode 100644 venv/Lib/site-packages/pip/_vendor/html5lib/treebuilders/dom.py create mode 100644 venv/Lib/site-packages/pip/_vendor/html5lib/treebuilders/etree.py create mode 100644 venv/Lib/site-packages/pip/_vendor/html5lib/treebuilders/etree_lxml.py create mode 100644 venv/Lib/site-packages/pip/_vendor/html5lib/treewalkers/__init__.py create mode 100644 venv/Lib/site-packages/pip/_vendor/html5lib/treewalkers/__pycache__/__init__.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/html5lib/treewalkers/__pycache__/base.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/html5lib/treewalkers/__pycache__/dom.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/html5lib/treewalkers/__pycache__/etree.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/html5lib/treewalkers/__pycache__/etree_lxml.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/html5lib/treewalkers/__pycache__/genshi.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/html5lib/treewalkers/base.py create mode 100644 venv/Lib/site-packages/pip/_vendor/html5lib/treewalkers/dom.py create mode 100644 venv/Lib/site-packages/pip/_vendor/html5lib/treewalkers/etree.py create mode 100644 venv/Lib/site-packages/pip/_vendor/html5lib/treewalkers/etree_lxml.py create mode 100644 venv/Lib/site-packages/pip/_vendor/html5lib/treewalkers/genshi.py 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-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/idna/__pycache__/codec.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/idna/__pycache__/compat.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/idna/__pycache__/core.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/idna/__pycache__/idnadata.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/idna/__pycache__/intranges.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/idna/__pycache__/package_data.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/idna/__pycache__/uts46data.cpython-39.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/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-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/msgpack/__pycache__/_version.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/msgpack/__pycache__/exceptions.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/msgpack/__pycache__/ext.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/msgpack/__pycache__/fallback.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/msgpack/_version.py 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-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/packaging/__pycache__/__init__.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/packaging/__pycache__/_compat.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/packaging/__pycache__/_structures.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/packaging/__pycache__/_typing.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/packaging/__pycache__/markers.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/packaging/__pycache__/requirements.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/packaging/__pycache__/specifiers.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/packaging/__pycache__/tags.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/packaging/__pycache__/utils.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/packaging/__pycache__/version.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/packaging/_compat.py create mode 100644 venv/Lib/site-packages/pip/_vendor/packaging/_structures.py create mode 100644 venv/Lib/site-packages/pip/_vendor/packaging/_typing.py create mode 100644 venv/Lib/site-packages/pip/_vendor/packaging/markers.py 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/pep517/__init__.py create mode 100644 venv/Lib/site-packages/pip/_vendor/pep517/__pycache__/__init__.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/pep517/__pycache__/build.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/pep517/__pycache__/check.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/pep517/__pycache__/colorlog.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/pep517/__pycache__/compat.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/pep517/__pycache__/dirtools.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/pep517/__pycache__/envbuild.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/pep517/__pycache__/meta.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/pep517/__pycache__/wrappers.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/pep517/build.py create mode 100644 venv/Lib/site-packages/pip/_vendor/pep517/check.py create mode 100644 venv/Lib/site-packages/pip/_vendor/pep517/colorlog.py create mode 100644 venv/Lib/site-packages/pip/_vendor/pep517/compat.py create mode 100644 venv/Lib/site-packages/pip/_vendor/pep517/dirtools.py create mode 100644 venv/Lib/site-packages/pip/_vendor/pep517/envbuild.py create mode 100644 venv/Lib/site-packages/pip/_vendor/pep517/in_process/__init__.py create mode 100644 venv/Lib/site-packages/pip/_vendor/pep517/in_process/__pycache__/__init__.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/pep517/in_process/__pycache__/_in_process.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/pep517/in_process/_in_process.py create mode 100644 venv/Lib/site-packages/pip/_vendor/pep517/meta.py create mode 100644 venv/Lib/site-packages/pip/_vendor/pep517/wrappers.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-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/pkg_resources/__pycache__/py31compat.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/pkg_resources/py31compat.py create mode 100644 venv/Lib/site-packages/pip/_vendor/progress/__init__.py create mode 100644 venv/Lib/site-packages/pip/_vendor/progress/__pycache__/__init__.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/progress/__pycache__/bar.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/progress/__pycache__/counter.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/progress/__pycache__/spinner.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/progress/bar.py create mode 100644 venv/Lib/site-packages/pip/_vendor/progress/counter.py create mode 100644 venv/Lib/site-packages/pip/_vendor/progress/spinner.py create mode 100644 venv/Lib/site-packages/pip/_vendor/pyparsing.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-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/requests/__pycache__/__version__.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/requests/__pycache__/_internal_utils.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/requests/__pycache__/adapters.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/requests/__pycache__/api.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/requests/__pycache__/auth.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/requests/__pycache__/certs.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/requests/__pycache__/compat.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/requests/__pycache__/cookies.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/requests/__pycache__/exceptions.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/requests/__pycache__/help.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/requests/__pycache__/hooks.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/requests/__pycache__/models.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/requests/__pycache__/packages.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/requests/__pycache__/sessions.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/requests/__pycache__/status_codes.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/requests/__pycache__/structures.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/requests/__pycache__/utils.cpython-39.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-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/resolvelib/__pycache__/providers.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/resolvelib/__pycache__/reporters.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/resolvelib/__pycache__/resolvers.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/resolvelib/__pycache__/structs.cpython-39.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-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/resolvelib/compat/__pycache__/collections_abc.cpython-39.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/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/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-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/tenacity/__pycache__/_asyncio.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/tenacity/__pycache__/_utils.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/tenacity/__pycache__/after.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/tenacity/__pycache__/before.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/tenacity/__pycache__/before_sleep.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/tenacity/__pycache__/compat.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/tenacity/__pycache__/nap.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/tenacity/__pycache__/retry.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/tenacity/__pycache__/stop.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/tenacity/__pycache__/tornadoweb.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/tenacity/__pycache__/wait.cpython-39.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/compat.py create mode 100644 venv/Lib/site-packages/pip/_vendor/tenacity/nap.py 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/toml/__init__.py create mode 100644 venv/Lib/site-packages/pip/_vendor/toml/__pycache__/__init__.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/toml/__pycache__/decoder.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/toml/__pycache__/encoder.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/toml/__pycache__/ordered.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/toml/__pycache__/tz.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/toml/decoder.py create mode 100644 venv/Lib/site-packages/pip/_vendor/toml/encoder.py create mode 100644 venv/Lib/site-packages/pip/_vendor/toml/ordered.py create mode 100644 venv/Lib/site-packages/pip/_vendor/toml/tz.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-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/__pycache__/_collections.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/__pycache__/_version.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/__pycache__/connection.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/__pycache__/connectionpool.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/__pycache__/exceptions.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/__pycache__/fields.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/__pycache__/filepost.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/__pycache__/poolmanager.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/__pycache__/request.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/__pycache__/response.cpython-39.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-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/contrib/__pycache__/_appengine_environ.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/contrib/__pycache__/appengine.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/contrib/__pycache__/ntlmpool.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/contrib/__pycache__/pyopenssl.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/contrib/__pycache__/securetransport.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/contrib/__pycache__/socks.cpython-39.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-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/contrib/_securetransport/__pycache__/bindings.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/contrib/_securetransport/__pycache__/low_level.cpython-39.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-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/packages/__pycache__/six.cpython-39.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-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/packages/backports/__pycache__/makefile.cpython-39.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/six.py create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/packages/ssl_match_hostname/__init__.py create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/packages/ssl_match_hostname/__pycache__/__init__.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/packages/ssl_match_hostname/__pycache__/_implementation.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/packages/ssl_match_hostname/_implementation.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-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/util/__pycache__/connection.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/util/__pycache__/proxy.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/util/__pycache__/queue.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/util/__pycache__/request.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/util/__pycache__/response.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/util/__pycache__/retry.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/util/__pycache__/ssl_.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/util/__pycache__/ssltransport.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/util/__pycache__/timeout.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/util/__pycache__/url.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/urllib3/util/__pycache__/wait.cpython-39.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/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-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/webencodings/__pycache__/labels.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/webencodings/__pycache__/mklabels.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/webencodings/__pycache__/tests.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pip/_vendor/webencodings/__pycache__/x_user_defined.cpython-39.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/pkg_resources/__init__.py create mode 100644 venv/Lib/site-packages/pkg_resources/__pycache__/__init__.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/__init__.py create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/__pycache__/__init__.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/__pycache__/appdirs.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/__pycache__/pyparsing.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/appdirs.py create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/packaging/__about__.py create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/packaging/__init__.py create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/packaging/__pycache__/__about__.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/packaging/__pycache__/__init__.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/packaging/__pycache__/_compat.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/packaging/__pycache__/_structures.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/packaging/__pycache__/_typing.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/packaging/__pycache__/markers.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/packaging/__pycache__/requirements.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/packaging/__pycache__/specifiers.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/packaging/__pycache__/tags.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/packaging/__pycache__/utils.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/packaging/__pycache__/version.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/packaging/_compat.py create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/packaging/_structures.py create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/packaging/_typing.py create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/packaging/markers.py create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/packaging/requirements.py create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/packaging/specifiers.py create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/packaging/tags.py create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/packaging/utils.py create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/packaging/version.py create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/pyparsing.py create mode 100644 venv/Lib/site-packages/pkg_resources/extern/__init__.py create mode 100644 venv/Lib/site-packages/pkg_resources/extern/__pycache__/__init__.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pkg_resources/tests/data/my-test-package-source/__pycache__/setup.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pkg_resources/tests/data/my-test-package-source/setup.py create mode 100644 venv/Lib/site-packages/pluggy-0.13.1.dist-info/INSTALLER create mode 100644 venv/Lib/site-packages/pluggy-0.13.1.dist-info/LICENSE create mode 100644 venv/Lib/site-packages/pluggy-0.13.1.dist-info/METADATA create mode 100644 venv/Lib/site-packages/pluggy-0.13.1.dist-info/RECORD create mode 100644 venv/Lib/site-packages/pluggy-0.13.1.dist-info/WHEEL create mode 100644 venv/Lib/site-packages/pluggy-0.13.1.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-39.pyc create mode 100644 venv/Lib/site-packages/pluggy/__pycache__/_tracing.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pluggy/__pycache__/_version.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pluggy/__pycache__/callers.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pluggy/__pycache__/hooks.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pluggy/__pycache__/manager.cpython-39.pyc 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/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/py-1.10.0.dist-info/INSTALLER create mode 100644 venv/Lib/site-packages/py-1.10.0.dist-info/LICENSE create mode 100644 venv/Lib/site-packages/py-1.10.0.dist-info/METADATA create mode 100644 venv/Lib/site-packages/py-1.10.0.dist-info/RECORD create mode 100644 venv/Lib/site-packages/py-1.10.0.dist-info/WHEEL create mode 100644 venv/Lib/site-packages/py-1.10.0.dist-info/top_level.txt create mode 100644 venv/Lib/site-packages/py/__init__.py create mode 100644 venv/Lib/site-packages/py/__init__.pyi create mode 100644 venv/Lib/site-packages/py/__metainfo.py create mode 100644 venv/Lib/site-packages/py/__pycache__/__init__.cpython-39.pyc create mode 100644 venv/Lib/site-packages/py/__pycache__/__metainfo.cpython-39.pyc create mode 100644 venv/Lib/site-packages/py/__pycache__/_builtin.cpython-39.pyc create mode 100644 venv/Lib/site-packages/py/__pycache__/_error.cpython-39.pyc create mode 100644 venv/Lib/site-packages/py/__pycache__/_std.cpython-39.pyc create mode 100644 venv/Lib/site-packages/py/__pycache__/_version.cpython-39.pyc create mode 100644 venv/Lib/site-packages/py/__pycache__/_xmlgen.cpython-39.pyc create mode 100644 venv/Lib/site-packages/py/__pycache__/test.cpython-39.pyc create mode 100644 venv/Lib/site-packages/py/_builtin.py create mode 100644 venv/Lib/site-packages/py/_code/__init__.py create mode 100644 venv/Lib/site-packages/py/_code/__pycache__/__init__.cpython-39.pyc create mode 100644 venv/Lib/site-packages/py/_code/__pycache__/_assertionnew.cpython-39.pyc create mode 100644 venv/Lib/site-packages/py/_code/__pycache__/_assertionold.cpython-39.pyc create mode 100644 venv/Lib/site-packages/py/_code/__pycache__/_py2traceback.cpython-39.pyc create mode 100644 venv/Lib/site-packages/py/_code/__pycache__/assertion.cpython-39.pyc create mode 100644 venv/Lib/site-packages/py/_code/__pycache__/code.cpython-39.pyc create mode 100644 venv/Lib/site-packages/py/_code/__pycache__/source.cpython-39.pyc create mode 100644 venv/Lib/site-packages/py/_code/_assertionnew.py create mode 100644 venv/Lib/site-packages/py/_code/_assertionold.py create mode 100644 venv/Lib/site-packages/py/_code/_py2traceback.py create mode 100644 venv/Lib/site-packages/py/_code/assertion.py create mode 100644 venv/Lib/site-packages/py/_code/code.py create mode 100644 venv/Lib/site-packages/py/_code/source.py create mode 100644 venv/Lib/site-packages/py/_error.py create mode 100644 venv/Lib/site-packages/py/_io/__init__.py create mode 100644 venv/Lib/site-packages/py/_io/__pycache__/__init__.cpython-39.pyc create mode 100644 venv/Lib/site-packages/py/_io/__pycache__/capture.cpython-39.pyc create mode 100644 venv/Lib/site-packages/py/_io/__pycache__/saferepr.cpython-39.pyc create mode 100644 venv/Lib/site-packages/py/_io/__pycache__/terminalwriter.cpython-39.pyc create mode 100644 venv/Lib/site-packages/py/_io/capture.py create mode 100644 venv/Lib/site-packages/py/_io/saferepr.py create mode 100644 venv/Lib/site-packages/py/_io/terminalwriter.py create mode 100644 venv/Lib/site-packages/py/_log/__init__.py create mode 100644 venv/Lib/site-packages/py/_log/__pycache__/__init__.cpython-39.pyc create mode 100644 venv/Lib/site-packages/py/_log/__pycache__/log.cpython-39.pyc create mode 100644 venv/Lib/site-packages/py/_log/__pycache__/warning.cpython-39.pyc create mode 100644 venv/Lib/site-packages/py/_log/log.py create mode 100644 venv/Lib/site-packages/py/_log/warning.py create mode 100644 venv/Lib/site-packages/py/_path/__init__.py create mode 100644 venv/Lib/site-packages/py/_path/__pycache__/__init__.cpython-39.pyc create mode 100644 venv/Lib/site-packages/py/_path/__pycache__/cacheutil.cpython-39.pyc create mode 100644 venv/Lib/site-packages/py/_path/__pycache__/common.cpython-39.pyc create mode 100644 venv/Lib/site-packages/py/_path/__pycache__/local.cpython-39.pyc create mode 100644 venv/Lib/site-packages/py/_path/__pycache__/svnurl.cpython-39.pyc create mode 100644 venv/Lib/site-packages/py/_path/__pycache__/svnwc.cpython-39.pyc create mode 100644 venv/Lib/site-packages/py/_path/cacheutil.py create mode 100644 venv/Lib/site-packages/py/_path/common.py create mode 100644 venv/Lib/site-packages/py/_path/local.py create mode 100644 venv/Lib/site-packages/py/_path/svnurl.py create mode 100644 venv/Lib/site-packages/py/_path/svnwc.py create mode 100644 venv/Lib/site-packages/py/_process/__init__.py create mode 100644 venv/Lib/site-packages/py/_process/__pycache__/__init__.cpython-39.pyc create mode 100644 venv/Lib/site-packages/py/_process/__pycache__/cmdexec.cpython-39.pyc create mode 100644 venv/Lib/site-packages/py/_process/__pycache__/forkedfunc.cpython-39.pyc create mode 100644 venv/Lib/site-packages/py/_process/__pycache__/killproc.cpython-39.pyc create mode 100644 venv/Lib/site-packages/py/_process/cmdexec.py create mode 100644 venv/Lib/site-packages/py/_process/forkedfunc.py create mode 100644 venv/Lib/site-packages/py/_process/killproc.py create mode 100644 venv/Lib/site-packages/py/_std.py create mode 100644 venv/Lib/site-packages/py/_vendored_packages/__init__.py create mode 100644 venv/Lib/site-packages/py/_vendored_packages/__pycache__/__init__.cpython-39.pyc create mode 100644 venv/Lib/site-packages/py/_vendored_packages/apipkg-1.5.dist-info/INSTALLER create mode 100644 venv/Lib/site-packages/py/_vendored_packages/apipkg-1.5.dist-info/METADATA create mode 100644 venv/Lib/site-packages/py/_vendored_packages/apipkg-1.5.dist-info/RECORD create mode 100644 venv/Lib/site-packages/py/_vendored_packages/apipkg-1.5.dist-info/REQUESTED create mode 100644 venv/Lib/site-packages/py/_vendored_packages/apipkg-1.5.dist-info/WHEEL create mode 100644 venv/Lib/site-packages/py/_vendored_packages/apipkg-1.5.dist-info/top_level.txt create mode 100644 venv/Lib/site-packages/py/_vendored_packages/apipkg/__init__.py create mode 100644 venv/Lib/site-packages/py/_vendored_packages/apipkg/__pycache__/__init__.cpython-39.pyc create mode 100644 venv/Lib/site-packages/py/_vendored_packages/apipkg/__pycache__/version.cpython-39.pyc create mode 100644 venv/Lib/site-packages/py/_vendored_packages/apipkg/version.py create mode 100644 venv/Lib/site-packages/py/_vendored_packages/iniconfig-1.1.1.dist-info/INSTALLER create mode 100644 venv/Lib/site-packages/py/_vendored_packages/iniconfig-1.1.1.dist-info/LICENSE create mode 100644 venv/Lib/site-packages/py/_vendored_packages/iniconfig-1.1.1.dist-info/METADATA create mode 100644 venv/Lib/site-packages/py/_vendored_packages/iniconfig-1.1.1.dist-info/RECORD create mode 100644 venv/Lib/site-packages/py/_vendored_packages/iniconfig-1.1.1.dist-info/REQUESTED create mode 100644 venv/Lib/site-packages/py/_vendored_packages/iniconfig-1.1.1.dist-info/WHEEL create mode 100644 venv/Lib/site-packages/py/_vendored_packages/iniconfig-1.1.1.dist-info/top_level.txt create mode 100644 venv/Lib/site-packages/py/_vendored_packages/iniconfig/__init__.py create mode 100644 venv/Lib/site-packages/py/_vendored_packages/iniconfig/__init__.pyi create mode 100644 venv/Lib/site-packages/py/_vendored_packages/iniconfig/__pycache__/__init__.cpython-39.pyc create mode 100644 venv/Lib/site-packages/py/_vendored_packages/iniconfig/py.typed create mode 100644 venv/Lib/site-packages/py/_version.py create mode 100644 venv/Lib/site-packages/py/_xmlgen.py create mode 100644 venv/Lib/site-packages/py/error.pyi create mode 100644 venv/Lib/site-packages/py/iniconfig.pyi create mode 100644 venv/Lib/site-packages/py/io.pyi create mode 100644 venv/Lib/site-packages/py/path.pyi create mode 100644 venv/Lib/site-packages/py/py.typed create mode 100644 venv/Lib/site-packages/py/test.py create mode 100644 venv/Lib/site-packages/py/xml.pyi create mode 100644 venv/Lib/site-packages/pyparsing-2.4.7.dist-info/INSTALLER create mode 100644 venv/Lib/site-packages/pyparsing-2.4.7.dist-info/LICENSE create mode 100644 venv/Lib/site-packages/pyparsing-2.4.7.dist-info/METADATA create mode 100644 venv/Lib/site-packages/pyparsing-2.4.7.dist-info/RECORD create mode 100644 venv/Lib/site-packages/pyparsing-2.4.7.dist-info/WHEEL create mode 100644 venv/Lib/site-packages/pyparsing-2.4.7.dist-info/top_level.txt create mode 100644 venv/Lib/site-packages/pyparsing.py create mode 100644 venv/Lib/site-packages/pyserial-3.5.dist-info/DESCRIPTION.rst create mode 100644 venv/Lib/site-packages/pyserial-3.5.dist-info/INSTALLER create mode 100644 venv/Lib/site-packages/pyserial-3.5.dist-info/METADATA create mode 100644 venv/Lib/site-packages/pyserial-3.5.dist-info/RECORD create mode 100644 venv/Lib/site-packages/pyserial-3.5.dist-info/WHEEL create mode 100644 venv/Lib/site-packages/pyserial-3.5.dist-info/entry_points.txt create mode 100644 venv/Lib/site-packages/pyserial-3.5.dist-info/metadata.json create mode 100644 venv/Lib/site-packages/pyserial-3.5.dist-info/top_level.txt create mode 100644 venv/Lib/site-packages/pytest-6.2.4.dist-info/INSTALLER create mode 100644 venv/Lib/site-packages/pytest-6.2.4.dist-info/LICENSE create mode 100644 venv/Lib/site-packages/pytest-6.2.4.dist-info/METADATA create mode 100644 venv/Lib/site-packages/pytest-6.2.4.dist-info/RECORD create mode 100644 venv/Lib/site-packages/pytest-6.2.4.dist-info/WHEEL create mode 100644 venv/Lib/site-packages/pytest-6.2.4.dist-info/entry_points.txt create mode 100644 venv/Lib/site-packages/pytest-6.2.4.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-39.pyc create mode 100644 venv/Lib/site-packages/pytest/__pycache__/__main__.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pytest/__pycache__/collect.cpython-39.pyc create mode 100644 venv/Lib/site-packages/pytest/collect.py create mode 100644 venv/Lib/site-packages/pytest/py.typed create mode 100644 venv/Lib/site-packages/serial/__init__.py create mode 100644 venv/Lib/site-packages/serial/__main__.py create mode 100644 venv/Lib/site-packages/serial/__pycache__/__init__.cpython-39.pyc create mode 100644 venv/Lib/site-packages/serial/__pycache__/__main__.cpython-39.pyc create mode 100644 venv/Lib/site-packages/serial/__pycache__/rfc2217.cpython-39.pyc create mode 100644 venv/Lib/site-packages/serial/__pycache__/rs485.cpython-39.pyc create mode 100644 venv/Lib/site-packages/serial/__pycache__/serialcli.cpython-39.pyc create mode 100644 venv/Lib/site-packages/serial/__pycache__/serialjava.cpython-39.pyc create mode 100644 venv/Lib/site-packages/serial/__pycache__/serialposix.cpython-39.pyc create mode 100644 venv/Lib/site-packages/serial/__pycache__/serialutil.cpython-39.pyc create mode 100644 venv/Lib/site-packages/serial/__pycache__/serialwin32.cpython-39.pyc create mode 100644 venv/Lib/site-packages/serial/__pycache__/win32.cpython-39.pyc create mode 100644 venv/Lib/site-packages/serial/rfc2217.py create mode 100644 venv/Lib/site-packages/serial/rs485.py create mode 100644 venv/Lib/site-packages/serial/serialcli.py create mode 100644 venv/Lib/site-packages/serial/serialjava.py create mode 100644 venv/Lib/site-packages/serial/serialposix.py create mode 100644 venv/Lib/site-packages/serial/serialutil.py create mode 100644 venv/Lib/site-packages/serial/serialwin32.py create mode 100644 venv/Lib/site-packages/serial/threaded/__init__.py create mode 100644 venv/Lib/site-packages/serial/threaded/__pycache__/__init__.cpython-39.pyc create mode 100644 venv/Lib/site-packages/serial/tools/__init__.py create mode 100644 venv/Lib/site-packages/serial/tools/__pycache__/__init__.cpython-39.pyc create mode 100644 venv/Lib/site-packages/serial/tools/__pycache__/hexlify_codec.cpython-39.pyc create mode 100644 venv/Lib/site-packages/serial/tools/__pycache__/list_ports.cpython-39.pyc create mode 100644 venv/Lib/site-packages/serial/tools/__pycache__/list_ports_common.cpython-39.pyc create mode 100644 venv/Lib/site-packages/serial/tools/__pycache__/list_ports_linux.cpython-39.pyc create mode 100644 venv/Lib/site-packages/serial/tools/__pycache__/list_ports_osx.cpython-39.pyc create mode 100644 venv/Lib/site-packages/serial/tools/__pycache__/list_ports_posix.cpython-39.pyc create mode 100644 venv/Lib/site-packages/serial/tools/__pycache__/list_ports_windows.cpython-39.pyc create mode 100644 venv/Lib/site-packages/serial/tools/__pycache__/miniterm.cpython-39.pyc create mode 100644 venv/Lib/site-packages/serial/tools/hexlify_codec.py create mode 100644 venv/Lib/site-packages/serial/tools/list_ports.py create mode 100644 venv/Lib/site-packages/serial/tools/list_ports_common.py create mode 100644 venv/Lib/site-packages/serial/tools/list_ports_linux.py create mode 100644 venv/Lib/site-packages/serial/tools/list_ports_osx.py create mode 100644 venv/Lib/site-packages/serial/tools/list_ports_posix.py create mode 100644 venv/Lib/site-packages/serial/tools/list_ports_windows.py create mode 100644 venv/Lib/site-packages/serial/tools/miniterm.py create mode 100644 venv/Lib/site-packages/serial/urlhandler/__init__.py create mode 100644 venv/Lib/site-packages/serial/urlhandler/__pycache__/__init__.cpython-39.pyc create mode 100644 venv/Lib/site-packages/serial/urlhandler/__pycache__/protocol_alt.cpython-39.pyc create mode 100644 venv/Lib/site-packages/serial/urlhandler/__pycache__/protocol_cp2110.cpython-39.pyc create mode 100644 venv/Lib/site-packages/serial/urlhandler/__pycache__/protocol_hwgrep.cpython-39.pyc create mode 100644 venv/Lib/site-packages/serial/urlhandler/__pycache__/protocol_loop.cpython-39.pyc create mode 100644 venv/Lib/site-packages/serial/urlhandler/__pycache__/protocol_rfc2217.cpython-39.pyc create mode 100644 venv/Lib/site-packages/serial/urlhandler/__pycache__/protocol_socket.cpython-39.pyc create mode 100644 venv/Lib/site-packages/serial/urlhandler/__pycache__/protocol_spy.cpython-39.pyc create mode 100644 venv/Lib/site-packages/serial/urlhandler/protocol_alt.py create mode 100644 venv/Lib/site-packages/serial/urlhandler/protocol_cp2110.py create mode 100644 venv/Lib/site-packages/serial/urlhandler/protocol_hwgrep.py create mode 100644 venv/Lib/site-packages/serial/urlhandler/protocol_loop.py create mode 100644 venv/Lib/site-packages/serial/urlhandler/protocol_rfc2217.py create mode 100644 venv/Lib/site-packages/serial/urlhandler/protocol_socket.py create mode 100644 venv/Lib/site-packages/serial/urlhandler/protocol_spy.py create mode 100644 venv/Lib/site-packages/serial/win32.py create mode 100644 venv/Lib/site-packages/setuptools-57.0.0.dist-info/INSTALLER create mode 100644 venv/Lib/site-packages/setuptools-57.0.0.dist-info/LICENSE create mode 100644 venv/Lib/site-packages/setuptools-57.0.0.dist-info/METADATA create mode 100644 venv/Lib/site-packages/setuptools-57.0.0.dist-info/RECORD create mode 100644 venv/Lib/site-packages/setuptools-57.0.0.dist-info/WHEEL create mode 100644 venv/Lib/site-packages/setuptools-57.0.0.dist-info/dependency_links.txt create mode 100644 venv/Lib/site-packages/setuptools-57.0.0.dist-info/entry_points.txt create mode 100644 venv/Lib/site-packages/setuptools-57.0.0.dist-info/top_level.txt create mode 100644 venv/Lib/site-packages/setuptools/__init__.py create mode 100644 venv/Lib/site-packages/setuptools/__pycache__/__init__.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/__pycache__/_deprecation_warning.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/__pycache__/_imp.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/__pycache__/archive_util.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/__pycache__/build_meta.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/__pycache__/config.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/__pycache__/dep_util.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/__pycache__/depends.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/__pycache__/dist.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/__pycache__/errors.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/__pycache__/extension.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/__pycache__/glob.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/__pycache__/installer.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/__pycache__/launch.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/__pycache__/lib2to3_ex.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/__pycache__/monkey.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/__pycache__/msvc.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/__pycache__/namespaces.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/__pycache__/package_index.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/__pycache__/py34compat.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/__pycache__/sandbox.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/__pycache__/ssl_support.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/__pycache__/unicode_utils.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/__pycache__/version.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/__pycache__/wheel.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/__pycache__/windows_support.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/_deprecation_warning.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/__init__.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/__pycache__/__init__.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/__pycache__/_msvccompiler.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/__pycache__/archive_util.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/__pycache__/bcppcompiler.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/__pycache__/ccompiler.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/__pycache__/cmd.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/__pycache__/config.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/__pycache__/core.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/__pycache__/cygwinccompiler.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/__pycache__/debug.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/__pycache__/dep_util.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/__pycache__/dir_util.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/__pycache__/dist.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/__pycache__/errors.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/__pycache__/extension.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/__pycache__/fancy_getopt.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/__pycache__/file_util.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/__pycache__/filelist.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/__pycache__/log.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/__pycache__/msvc9compiler.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/__pycache__/msvccompiler.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/__pycache__/py35compat.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/__pycache__/py38compat.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/__pycache__/spawn.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/__pycache__/sysconfig.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/__pycache__/text_file.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/__pycache__/unixccompiler.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/__pycache__/util.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/__pycache__/version.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/__pycache__/versionpredicate.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/_msvccompiler.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/archive_util.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/bcppcompiler.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/ccompiler.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/cmd.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/command/__init__.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/command/__pycache__/__init__.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/command/__pycache__/bdist.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/command/__pycache__/bdist_dumb.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/command/__pycache__/bdist_msi.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/command/__pycache__/bdist_rpm.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/command/__pycache__/bdist_wininst.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/command/__pycache__/build.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/command/__pycache__/build_clib.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/command/__pycache__/build_ext.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/command/__pycache__/build_py.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/command/__pycache__/build_scripts.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/command/__pycache__/check.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/command/__pycache__/clean.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/command/__pycache__/config.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/command/__pycache__/install.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/command/__pycache__/install_data.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/command/__pycache__/install_egg_info.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/command/__pycache__/install_headers.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/command/__pycache__/install_lib.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/command/__pycache__/install_scripts.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/command/__pycache__/py37compat.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/command/__pycache__/register.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/command/__pycache__/sdist.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/command/__pycache__/upload.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/command/bdist.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/command/bdist_dumb.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/command/bdist_msi.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/command/bdist_rpm.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/command/bdist_wininst.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/command/build.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/command/build_clib.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/command/build_ext.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/command/build_py.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/command/build_scripts.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/command/check.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/command/clean.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/command/config.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/command/install.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/command/install_data.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/command/install_egg_info.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/command/install_headers.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/command/install_lib.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/command/install_scripts.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/command/py37compat.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/command/register.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/command/sdist.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/command/upload.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/config.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/core.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/cygwinccompiler.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/debug.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/dep_util.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/dir_util.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/dist.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/errors.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/extension.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/fancy_getopt.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/file_util.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/filelist.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/log.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/msvc9compiler.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/msvccompiler.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/py35compat.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/py38compat.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/spawn.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/sysconfig.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/text_file.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/unixccompiler.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/util.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/version.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/versionpredicate.py create mode 100644 venv/Lib/site-packages/setuptools/_imp.py create mode 100644 venv/Lib/site-packages/setuptools/_vendor/__init__.py create mode 100644 venv/Lib/site-packages/setuptools/_vendor/__pycache__/__init__.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/_vendor/__pycache__/ordered_set.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/_vendor/__pycache__/pyparsing.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/_vendor/more_itertools/__init__.py create mode 100644 venv/Lib/site-packages/setuptools/_vendor/more_itertools/__pycache__/__init__.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/_vendor/more_itertools/__pycache__/more.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/_vendor/more_itertools/__pycache__/recipes.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/_vendor/more_itertools/more.py create mode 100644 venv/Lib/site-packages/setuptools/_vendor/more_itertools/recipes.py create mode 100644 venv/Lib/site-packages/setuptools/_vendor/ordered_set.py create mode 100644 venv/Lib/site-packages/setuptools/_vendor/packaging/__about__.py create mode 100644 venv/Lib/site-packages/setuptools/_vendor/packaging/__init__.py create mode 100644 venv/Lib/site-packages/setuptools/_vendor/packaging/__pycache__/__about__.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/_vendor/packaging/__pycache__/__init__.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/_vendor/packaging/__pycache__/_compat.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/_vendor/packaging/__pycache__/_structures.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/_vendor/packaging/__pycache__/_typing.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/_vendor/packaging/__pycache__/markers.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/_vendor/packaging/__pycache__/requirements.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/_vendor/packaging/__pycache__/specifiers.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/_vendor/packaging/__pycache__/tags.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/_vendor/packaging/__pycache__/utils.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/_vendor/packaging/__pycache__/version.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/_vendor/packaging/_compat.py create mode 100644 venv/Lib/site-packages/setuptools/_vendor/packaging/_structures.py create mode 100644 venv/Lib/site-packages/setuptools/_vendor/packaging/_typing.py create mode 100644 venv/Lib/site-packages/setuptools/_vendor/packaging/markers.py create mode 100644 venv/Lib/site-packages/setuptools/_vendor/packaging/requirements.py create mode 100644 venv/Lib/site-packages/setuptools/_vendor/packaging/specifiers.py create mode 100644 venv/Lib/site-packages/setuptools/_vendor/packaging/tags.py create mode 100644 venv/Lib/site-packages/setuptools/_vendor/packaging/utils.py create mode 100644 venv/Lib/site-packages/setuptools/_vendor/packaging/version.py create mode 100644 venv/Lib/site-packages/setuptools/_vendor/pyparsing.py create mode 100644 venv/Lib/site-packages/setuptools/archive_util.py create mode 100644 venv/Lib/site-packages/setuptools/build_meta.py create mode 100644 venv/Lib/site-packages/setuptools/cli-32.exe create mode 100644 venv/Lib/site-packages/setuptools/cli-64.exe create mode 100644 venv/Lib/site-packages/setuptools/cli.exe create mode 100644 venv/Lib/site-packages/setuptools/command/__init__.py create mode 100644 venv/Lib/site-packages/setuptools/command/__pycache__/__init__.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/command/__pycache__/alias.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/command/__pycache__/bdist_egg.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/command/__pycache__/bdist_rpm.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/command/__pycache__/build_clib.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/command/__pycache__/build_ext.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/command/__pycache__/build_py.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/command/__pycache__/develop.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/command/__pycache__/dist_info.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/command/__pycache__/easy_install.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/command/__pycache__/egg_info.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/command/__pycache__/install.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/command/__pycache__/install_egg_info.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/command/__pycache__/install_lib.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/command/__pycache__/install_scripts.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/command/__pycache__/py36compat.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/command/__pycache__/register.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/command/__pycache__/rotate.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/command/__pycache__/saveopts.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/command/__pycache__/sdist.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/command/__pycache__/setopt.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/command/__pycache__/test.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/command/__pycache__/upload.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/command/__pycache__/upload_docs.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/command/alias.py create mode 100644 venv/Lib/site-packages/setuptools/command/bdist_egg.py create mode 100644 venv/Lib/site-packages/setuptools/command/bdist_rpm.py create mode 100644 venv/Lib/site-packages/setuptools/command/build_clib.py create mode 100644 venv/Lib/site-packages/setuptools/command/build_ext.py create mode 100644 venv/Lib/site-packages/setuptools/command/build_py.py create mode 100644 venv/Lib/site-packages/setuptools/command/develop.py create mode 100644 venv/Lib/site-packages/setuptools/command/dist_info.py create mode 100644 venv/Lib/site-packages/setuptools/command/easy_install.py create mode 100644 venv/Lib/site-packages/setuptools/command/egg_info.py create mode 100644 venv/Lib/site-packages/setuptools/command/install.py create mode 100644 venv/Lib/site-packages/setuptools/command/install_egg_info.py create mode 100644 venv/Lib/site-packages/setuptools/command/install_lib.py create mode 100644 venv/Lib/site-packages/setuptools/command/install_scripts.py create mode 100644 venv/Lib/site-packages/setuptools/command/launcher manifest.xml create mode 100644 venv/Lib/site-packages/setuptools/command/py36compat.py create mode 100644 venv/Lib/site-packages/setuptools/command/register.py create mode 100644 venv/Lib/site-packages/setuptools/command/rotate.py create mode 100644 venv/Lib/site-packages/setuptools/command/saveopts.py create mode 100644 venv/Lib/site-packages/setuptools/command/sdist.py create mode 100644 venv/Lib/site-packages/setuptools/command/setopt.py create mode 100644 venv/Lib/site-packages/setuptools/command/test.py create mode 100644 venv/Lib/site-packages/setuptools/command/upload.py create mode 100644 venv/Lib/site-packages/setuptools/command/upload_docs.py create mode 100644 venv/Lib/site-packages/setuptools/config.py create mode 100644 venv/Lib/site-packages/setuptools/dep_util.py create mode 100644 venv/Lib/site-packages/setuptools/depends.py create mode 100644 venv/Lib/site-packages/setuptools/dist.py create mode 100644 venv/Lib/site-packages/setuptools/errors.py create mode 100644 venv/Lib/site-packages/setuptools/extension.py create mode 100644 venv/Lib/site-packages/setuptools/extern/__init__.py create mode 100644 venv/Lib/site-packages/setuptools/extern/__pycache__/__init__.cpython-39.pyc create mode 100644 venv/Lib/site-packages/setuptools/glob.py create mode 100644 venv/Lib/site-packages/setuptools/gui-32.exe create mode 100644 venv/Lib/site-packages/setuptools/gui-64.exe create mode 100644 venv/Lib/site-packages/setuptools/gui.exe create mode 100644 venv/Lib/site-packages/setuptools/installer.py create mode 100644 venv/Lib/site-packages/setuptools/launch.py create mode 100644 venv/Lib/site-packages/setuptools/lib2to3_ex.py create mode 100644 venv/Lib/site-packages/setuptools/monkey.py create mode 100644 venv/Lib/site-packages/setuptools/msvc.py create mode 100644 venv/Lib/site-packages/setuptools/namespaces.py create mode 100644 venv/Lib/site-packages/setuptools/package_index.py create mode 100644 venv/Lib/site-packages/setuptools/py34compat.py create mode 100644 venv/Lib/site-packages/setuptools/sandbox.py create mode 100644 venv/Lib/site-packages/setuptools/script (dev).tmpl create mode 100644 venv/Lib/site-packages/setuptools/script.tmpl create mode 100644 venv/Lib/site-packages/setuptools/ssl_support.py create mode 100644 venv/Lib/site-packages/setuptools/unicode_utils.py create mode 100644 venv/Lib/site-packages/setuptools/version.py create mode 100644 venv/Lib/site-packages/setuptools/wheel.py create mode 100644 venv/Lib/site-packages/setuptools/windows_support.py create mode 100644 venv/Lib/site-packages/sqlalchemy/__init__.py create mode 100644 venv/Lib/site-packages/sqlalchemy/__pycache__/__init__.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/__pycache__/events.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/__pycache__/exc.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/__pycache__/inspection.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/__pycache__/log.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/__pycache__/processors.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/__pycache__/schema.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/__pycache__/types.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/cimmutabledict.cp39-win_amd64.pyd create mode 100644 venv/Lib/site-packages/sqlalchemy/connectors/__init__.py create mode 100644 venv/Lib/site-packages/sqlalchemy/connectors/__pycache__/__init__.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/connectors/__pycache__/mxodbc.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/connectors/__pycache__/pyodbc.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/connectors/mxodbc.py create mode 100644 venv/Lib/site-packages/sqlalchemy/connectors/pyodbc.py create mode 100644 venv/Lib/site-packages/sqlalchemy/cprocessors.cp39-win_amd64.pyd create mode 100644 venv/Lib/site-packages/sqlalchemy/cresultproxy.cp39-win_amd64.pyd create mode 100644 venv/Lib/site-packages/sqlalchemy/databases/__init__.py create mode 100644 venv/Lib/site-packages/sqlalchemy/databases/__pycache__/__init__.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/__init__.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/__pycache__/__init__.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/firebird/__init__.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/firebird/__pycache__/__init__.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/firebird/__pycache__/base.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/firebird/__pycache__/fdb.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/firebird/__pycache__/kinterbasdb.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/firebird/base.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/firebird/fdb.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/firebird/kinterbasdb.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/mssql/__init__.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/mssql/__pycache__/__init__.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/mssql/__pycache__/base.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/mssql/__pycache__/information_schema.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/mssql/__pycache__/json.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/mssql/__pycache__/mxodbc.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/mssql/__pycache__/provision.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/mssql/__pycache__/pymssql.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/mssql/__pycache__/pyodbc.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/mssql/base.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/mssql/information_schema.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/mssql/json.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/mssql/mxodbc.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/mssql/provision.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/mssql/pymssql.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/mssql/pyodbc.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/mysql/__init__.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/mysql/__pycache__/__init__.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/mysql/__pycache__/aiomysql.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/mysql/__pycache__/base.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/mysql/__pycache__/cymysql.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/mysql/__pycache__/dml.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/mysql/__pycache__/enumerated.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/mysql/__pycache__/expression.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/mysql/__pycache__/json.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/mysql/__pycache__/mariadb.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/mysql/__pycache__/mariadbconnector.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/mysql/__pycache__/mysqlconnector.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/mysql/__pycache__/mysqldb.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/mysql/__pycache__/oursql.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/mysql/__pycache__/provision.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/mysql/__pycache__/pymysql.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/mysql/__pycache__/pyodbc.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/mysql/__pycache__/reflection.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/mysql/__pycache__/types.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/mysql/aiomysql.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/mysql/base.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/mysql/cymysql.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/mysql/dml.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/mysql/enumerated.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/mysql/expression.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/mysql/json.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/mysql/mariadb.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/mysql/mariadbconnector.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/mysql/mysqlconnector.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/mysql/mysqldb.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/mysql/oursql.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/mysql/provision.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/mysql/pymysql.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/mysql/pyodbc.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/mysql/reflection.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/mysql/types.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/oracle/__init__.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/oracle/__pycache__/__init__.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/oracle/__pycache__/base.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/oracle/__pycache__/cx_oracle.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/oracle/__pycache__/provision.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/oracle/base.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/oracle/cx_oracle.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/oracle/provision.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/postgresql/__init__.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/postgresql/__pycache__/__init__.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/postgresql/__pycache__/array.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/postgresql/__pycache__/asyncpg.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/postgresql/__pycache__/base.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/postgresql/__pycache__/dml.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/postgresql/__pycache__/ext.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/postgresql/__pycache__/hstore.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/postgresql/__pycache__/json.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/postgresql/__pycache__/pg8000.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/postgresql/__pycache__/provision.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/postgresql/__pycache__/psycopg2.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/postgresql/__pycache__/psycopg2cffi.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/postgresql/__pycache__/pygresql.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/postgresql/__pycache__/pypostgresql.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/postgresql/__pycache__/ranges.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/postgresql/array.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/postgresql/asyncpg.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/postgresql/base.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/postgresql/dml.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/postgresql/ext.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/postgresql/hstore.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/postgresql/json.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/postgresql/pg8000.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/postgresql/provision.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/postgresql/psycopg2.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/postgresql/psycopg2cffi.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/postgresql/pygresql.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/postgresql/pypostgresql.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/postgresql/ranges.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/sqlite/__init__.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/sqlite/__pycache__/__init__.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/sqlite/__pycache__/aiosqlite.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/sqlite/__pycache__/base.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/sqlite/__pycache__/dml.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/sqlite/__pycache__/json.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/sqlite/__pycache__/provision.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/sqlite/__pycache__/pysqlcipher.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/sqlite/__pycache__/pysqlite.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/sqlite/aiosqlite.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/sqlite/base.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/sqlite/dml.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/sqlite/json.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/sqlite/provision.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/sqlite/pysqlcipher.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/sqlite/pysqlite.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/sybase/__init__.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/sybase/__pycache__/__init__.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/sybase/__pycache__/base.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/sybase/__pycache__/mxodbc.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/sybase/__pycache__/pyodbc.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/sybase/__pycache__/pysybase.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/sybase/base.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/sybase/mxodbc.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/sybase/pyodbc.py create mode 100644 venv/Lib/site-packages/sqlalchemy/dialects/sybase/pysybase.py create mode 100644 venv/Lib/site-packages/sqlalchemy/engine/__init__.py create mode 100644 venv/Lib/site-packages/sqlalchemy/engine/__pycache__/__init__.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/engine/__pycache__/base.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/engine/__pycache__/characteristics.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/engine/__pycache__/create.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/engine/__pycache__/cursor.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/engine/__pycache__/default.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/engine/__pycache__/events.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/engine/__pycache__/interfaces.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/engine/__pycache__/mock.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/engine/__pycache__/reflection.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/engine/__pycache__/result.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/engine/__pycache__/row.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/engine/__pycache__/strategies.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/engine/__pycache__/url.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/engine/__pycache__/util.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/engine/base.py create mode 100644 venv/Lib/site-packages/sqlalchemy/engine/characteristics.py create mode 100644 venv/Lib/site-packages/sqlalchemy/engine/create.py create mode 100644 venv/Lib/site-packages/sqlalchemy/engine/cursor.py create mode 100644 venv/Lib/site-packages/sqlalchemy/engine/default.py create mode 100644 venv/Lib/site-packages/sqlalchemy/engine/events.py create mode 100644 venv/Lib/site-packages/sqlalchemy/engine/interfaces.py create mode 100644 venv/Lib/site-packages/sqlalchemy/engine/mock.py create mode 100644 venv/Lib/site-packages/sqlalchemy/engine/reflection.py create mode 100644 venv/Lib/site-packages/sqlalchemy/engine/result.py create mode 100644 venv/Lib/site-packages/sqlalchemy/engine/row.py create mode 100644 venv/Lib/site-packages/sqlalchemy/engine/strategies.py create mode 100644 venv/Lib/site-packages/sqlalchemy/engine/url.py create mode 100644 venv/Lib/site-packages/sqlalchemy/engine/util.py create mode 100644 venv/Lib/site-packages/sqlalchemy/event/__init__.py create mode 100644 venv/Lib/site-packages/sqlalchemy/event/__pycache__/__init__.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/event/__pycache__/api.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/event/__pycache__/attr.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/event/__pycache__/base.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/event/__pycache__/legacy.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/event/__pycache__/registry.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/event/api.py create mode 100644 venv/Lib/site-packages/sqlalchemy/event/attr.py create mode 100644 venv/Lib/site-packages/sqlalchemy/event/base.py create mode 100644 venv/Lib/site-packages/sqlalchemy/event/legacy.py create mode 100644 venv/Lib/site-packages/sqlalchemy/event/registry.py create mode 100644 venv/Lib/site-packages/sqlalchemy/events.py create mode 100644 venv/Lib/site-packages/sqlalchemy/exc.py create mode 100644 venv/Lib/site-packages/sqlalchemy/ext/__init__.py create mode 100644 venv/Lib/site-packages/sqlalchemy/ext/__pycache__/__init__.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/ext/__pycache__/associationproxy.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/ext/__pycache__/automap.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/ext/__pycache__/baked.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/ext/__pycache__/compiler.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/ext/__pycache__/horizontal_shard.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/ext/__pycache__/hybrid.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/ext/__pycache__/indexable.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/ext/__pycache__/instrumentation.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/ext/__pycache__/mutable.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/ext/__pycache__/orderinglist.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/ext/__pycache__/serializer.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/ext/associationproxy.py create mode 100644 venv/Lib/site-packages/sqlalchemy/ext/asyncio/__init__.py create mode 100644 venv/Lib/site-packages/sqlalchemy/ext/asyncio/__pycache__/__init__.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/ext/asyncio/__pycache__/base.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/ext/asyncio/__pycache__/engine.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/ext/asyncio/__pycache__/events.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/ext/asyncio/__pycache__/exc.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/ext/asyncio/__pycache__/result.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/ext/asyncio/__pycache__/scoping.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/ext/asyncio/__pycache__/session.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/ext/asyncio/base.py create mode 100644 venv/Lib/site-packages/sqlalchemy/ext/asyncio/engine.py create mode 100644 venv/Lib/site-packages/sqlalchemy/ext/asyncio/events.py create mode 100644 venv/Lib/site-packages/sqlalchemy/ext/asyncio/exc.py create mode 100644 venv/Lib/site-packages/sqlalchemy/ext/asyncio/result.py create mode 100644 venv/Lib/site-packages/sqlalchemy/ext/asyncio/scoping.py create mode 100644 venv/Lib/site-packages/sqlalchemy/ext/asyncio/session.py create mode 100644 venv/Lib/site-packages/sqlalchemy/ext/automap.py create mode 100644 venv/Lib/site-packages/sqlalchemy/ext/baked.py create mode 100644 venv/Lib/site-packages/sqlalchemy/ext/compiler.py create mode 100644 venv/Lib/site-packages/sqlalchemy/ext/declarative/__init__.py create mode 100644 venv/Lib/site-packages/sqlalchemy/ext/declarative/__pycache__/__init__.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/ext/declarative/__pycache__/extensions.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/ext/declarative/extensions.py create mode 100644 venv/Lib/site-packages/sqlalchemy/ext/horizontal_shard.py create mode 100644 venv/Lib/site-packages/sqlalchemy/ext/hybrid.py create mode 100644 venv/Lib/site-packages/sqlalchemy/ext/indexable.py create mode 100644 venv/Lib/site-packages/sqlalchemy/ext/instrumentation.py create mode 100644 venv/Lib/site-packages/sqlalchemy/ext/mutable.py create mode 100644 venv/Lib/site-packages/sqlalchemy/ext/mypy/__init__.py create mode 100644 venv/Lib/site-packages/sqlalchemy/ext/mypy/__pycache__/__init__.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/ext/mypy/__pycache__/apply.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/ext/mypy/__pycache__/decl_class.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/ext/mypy/__pycache__/infer.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/ext/mypy/__pycache__/names.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/ext/mypy/__pycache__/plugin.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/ext/mypy/__pycache__/util.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/ext/mypy/apply.py create mode 100644 venv/Lib/site-packages/sqlalchemy/ext/mypy/decl_class.py create mode 100644 venv/Lib/site-packages/sqlalchemy/ext/mypy/infer.py create mode 100644 venv/Lib/site-packages/sqlalchemy/ext/mypy/names.py create mode 100644 venv/Lib/site-packages/sqlalchemy/ext/mypy/plugin.py create mode 100644 venv/Lib/site-packages/sqlalchemy/ext/mypy/util.py create mode 100644 venv/Lib/site-packages/sqlalchemy/ext/orderinglist.py create mode 100644 venv/Lib/site-packages/sqlalchemy/ext/serializer.py create mode 100644 venv/Lib/site-packages/sqlalchemy/future/__init__.py create mode 100644 venv/Lib/site-packages/sqlalchemy/future/__pycache__/__init__.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/future/__pycache__/engine.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/future/engine.py create mode 100644 venv/Lib/site-packages/sqlalchemy/future/orm/__init__.py create mode 100644 venv/Lib/site-packages/sqlalchemy/future/orm/__pycache__/__init__.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/inspection.py create mode 100644 venv/Lib/site-packages/sqlalchemy/log.py create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/__init__.py create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/__pycache__/__init__.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/__pycache__/attributes.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/__pycache__/base.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/__pycache__/clsregistry.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/__pycache__/collections.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/__pycache__/context.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/__pycache__/decl_api.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/__pycache__/decl_base.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/__pycache__/dependency.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/__pycache__/descriptor_props.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/__pycache__/dynamic.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/__pycache__/evaluator.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/__pycache__/events.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/__pycache__/exc.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/__pycache__/identity.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/__pycache__/instrumentation.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/__pycache__/interfaces.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/__pycache__/loading.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/__pycache__/mapper.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/__pycache__/path_registry.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/__pycache__/persistence.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/__pycache__/properties.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/__pycache__/query.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/__pycache__/relationships.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/__pycache__/scoping.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/__pycache__/session.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/__pycache__/state.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/__pycache__/strategies.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/__pycache__/strategy_options.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/__pycache__/sync.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/__pycache__/unitofwork.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/__pycache__/util.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/attributes.py create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/base.py create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/clsregistry.py create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/collections.py create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/context.py create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/decl_api.py create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/decl_base.py create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/dependency.py create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/descriptor_props.py create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/dynamic.py create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/evaluator.py create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/events.py create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/exc.py create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/identity.py create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/instrumentation.py create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/interfaces.py create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/loading.py create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/mapper.py create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/path_registry.py create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/persistence.py create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/properties.py create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/query.py create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/relationships.py create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/scoping.py create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/session.py create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/state.py create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/strategies.py create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/strategy_options.py create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/sync.py create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/unitofwork.py create mode 100644 venv/Lib/site-packages/sqlalchemy/orm/util.py create mode 100644 venv/Lib/site-packages/sqlalchemy/pool/__init__.py create mode 100644 venv/Lib/site-packages/sqlalchemy/pool/__pycache__/__init__.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/pool/__pycache__/base.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/pool/__pycache__/dbapi_proxy.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/pool/__pycache__/events.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/pool/__pycache__/impl.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/pool/base.py create mode 100644 venv/Lib/site-packages/sqlalchemy/pool/dbapi_proxy.py create mode 100644 venv/Lib/site-packages/sqlalchemy/pool/events.py create mode 100644 venv/Lib/site-packages/sqlalchemy/pool/impl.py create mode 100644 venv/Lib/site-packages/sqlalchemy/processors.py create mode 100644 venv/Lib/site-packages/sqlalchemy/schema.py create mode 100644 venv/Lib/site-packages/sqlalchemy/sql/__init__.py create mode 100644 venv/Lib/site-packages/sqlalchemy/sql/__pycache__/__init__.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/sql/__pycache__/annotation.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/sql/__pycache__/base.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/sql/__pycache__/coercions.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/sql/__pycache__/compiler.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/sql/__pycache__/crud.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/sql/__pycache__/ddl.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/sql/__pycache__/default_comparator.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/sql/__pycache__/dml.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/sql/__pycache__/elements.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/sql/__pycache__/events.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/sql/__pycache__/expression.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/sql/__pycache__/functions.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/sql/__pycache__/lambdas.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/sql/__pycache__/naming.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/sql/__pycache__/operators.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/sql/__pycache__/roles.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/sql/__pycache__/schema.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/sql/__pycache__/selectable.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/sql/__pycache__/sqltypes.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/sql/__pycache__/traversals.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/sql/__pycache__/type_api.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/sql/__pycache__/util.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/sql/__pycache__/visitors.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/sql/annotation.py create mode 100644 venv/Lib/site-packages/sqlalchemy/sql/base.py create mode 100644 venv/Lib/site-packages/sqlalchemy/sql/coercions.py create mode 100644 venv/Lib/site-packages/sqlalchemy/sql/compiler.py create mode 100644 venv/Lib/site-packages/sqlalchemy/sql/crud.py create mode 100644 venv/Lib/site-packages/sqlalchemy/sql/ddl.py create mode 100644 venv/Lib/site-packages/sqlalchemy/sql/default_comparator.py create mode 100644 venv/Lib/site-packages/sqlalchemy/sql/dml.py create mode 100644 venv/Lib/site-packages/sqlalchemy/sql/elements.py create mode 100644 venv/Lib/site-packages/sqlalchemy/sql/events.py create mode 100644 venv/Lib/site-packages/sqlalchemy/sql/expression.py create mode 100644 venv/Lib/site-packages/sqlalchemy/sql/functions.py create mode 100644 venv/Lib/site-packages/sqlalchemy/sql/lambdas.py create mode 100644 venv/Lib/site-packages/sqlalchemy/sql/naming.py create mode 100644 venv/Lib/site-packages/sqlalchemy/sql/operators.py create mode 100644 venv/Lib/site-packages/sqlalchemy/sql/roles.py create mode 100644 venv/Lib/site-packages/sqlalchemy/sql/schema.py create mode 100644 venv/Lib/site-packages/sqlalchemy/sql/selectable.py create mode 100644 venv/Lib/site-packages/sqlalchemy/sql/sqltypes.py create mode 100644 venv/Lib/site-packages/sqlalchemy/sql/traversals.py create mode 100644 venv/Lib/site-packages/sqlalchemy/sql/type_api.py create mode 100644 venv/Lib/site-packages/sqlalchemy/sql/util.py create mode 100644 venv/Lib/site-packages/sqlalchemy/sql/visitors.py create mode 100644 venv/Lib/site-packages/sqlalchemy/testing/__init__.py create mode 100644 venv/Lib/site-packages/sqlalchemy/testing/__pycache__/__init__.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/testing/__pycache__/assertions.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/testing/__pycache__/assertsql.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/testing/__pycache__/asyncio.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/testing/__pycache__/config.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/testing/__pycache__/engines.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/testing/__pycache__/entities.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/testing/__pycache__/exclusions.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/testing/__pycache__/fixtures.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/testing/__pycache__/mock.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/testing/__pycache__/pickleable.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/testing/__pycache__/profiling.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/testing/__pycache__/provision.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/testing/__pycache__/requirements.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/testing/__pycache__/schema.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/testing/__pycache__/util.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/testing/__pycache__/warnings.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/testing/assertions.py create mode 100644 venv/Lib/site-packages/sqlalchemy/testing/assertsql.py create mode 100644 venv/Lib/site-packages/sqlalchemy/testing/asyncio.py create mode 100644 venv/Lib/site-packages/sqlalchemy/testing/config.py create mode 100644 venv/Lib/site-packages/sqlalchemy/testing/engines.py create mode 100644 venv/Lib/site-packages/sqlalchemy/testing/entities.py create mode 100644 venv/Lib/site-packages/sqlalchemy/testing/exclusions.py create mode 100644 venv/Lib/site-packages/sqlalchemy/testing/fixtures.py create mode 100644 venv/Lib/site-packages/sqlalchemy/testing/mock.py create mode 100644 venv/Lib/site-packages/sqlalchemy/testing/pickleable.py create mode 100644 venv/Lib/site-packages/sqlalchemy/testing/plugin/__init__.py create mode 100644 venv/Lib/site-packages/sqlalchemy/testing/plugin/__pycache__/__init__.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/testing/plugin/__pycache__/bootstrap.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/testing/plugin/__pycache__/plugin_base.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/testing/plugin/__pycache__/pytestplugin.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/testing/plugin/__pycache__/reinvent_fixtures_py2k.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/testing/plugin/bootstrap.py create mode 100644 venv/Lib/site-packages/sqlalchemy/testing/plugin/plugin_base.py create mode 100644 venv/Lib/site-packages/sqlalchemy/testing/plugin/pytestplugin.py create mode 100644 venv/Lib/site-packages/sqlalchemy/testing/plugin/reinvent_fixtures_py2k.py create mode 100644 venv/Lib/site-packages/sqlalchemy/testing/profiling.py create mode 100644 venv/Lib/site-packages/sqlalchemy/testing/provision.py create mode 100644 venv/Lib/site-packages/sqlalchemy/testing/requirements.py create mode 100644 venv/Lib/site-packages/sqlalchemy/testing/schema.py create mode 100644 venv/Lib/site-packages/sqlalchemy/testing/suite/__init__.py create mode 100644 venv/Lib/site-packages/sqlalchemy/testing/suite/__pycache__/__init__.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/testing/suite/__pycache__/test_cte.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/testing/suite/__pycache__/test_ddl.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/testing/suite/__pycache__/test_deprecations.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/testing/suite/__pycache__/test_dialect.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/testing/suite/__pycache__/test_insert.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/testing/suite/__pycache__/test_reflection.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/testing/suite/__pycache__/test_results.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/testing/suite/__pycache__/test_rowcount.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/testing/suite/__pycache__/test_select.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/testing/suite/__pycache__/test_sequence.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/testing/suite/__pycache__/test_types.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/testing/suite/__pycache__/test_unicode_ddl.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/testing/suite/__pycache__/test_update_delete.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/testing/suite/test_cte.py create mode 100644 venv/Lib/site-packages/sqlalchemy/testing/suite/test_ddl.py create mode 100644 venv/Lib/site-packages/sqlalchemy/testing/suite/test_deprecations.py create mode 100644 venv/Lib/site-packages/sqlalchemy/testing/suite/test_dialect.py create mode 100644 venv/Lib/site-packages/sqlalchemy/testing/suite/test_insert.py create mode 100644 venv/Lib/site-packages/sqlalchemy/testing/suite/test_reflection.py create mode 100644 venv/Lib/site-packages/sqlalchemy/testing/suite/test_results.py create mode 100644 venv/Lib/site-packages/sqlalchemy/testing/suite/test_rowcount.py create mode 100644 venv/Lib/site-packages/sqlalchemy/testing/suite/test_select.py create mode 100644 venv/Lib/site-packages/sqlalchemy/testing/suite/test_sequence.py create mode 100644 venv/Lib/site-packages/sqlalchemy/testing/suite/test_types.py create mode 100644 venv/Lib/site-packages/sqlalchemy/testing/suite/test_unicode_ddl.py create mode 100644 venv/Lib/site-packages/sqlalchemy/testing/suite/test_update_delete.py create mode 100644 venv/Lib/site-packages/sqlalchemy/testing/util.py create mode 100644 venv/Lib/site-packages/sqlalchemy/testing/warnings.py create mode 100644 venv/Lib/site-packages/sqlalchemy/types.py create mode 100644 venv/Lib/site-packages/sqlalchemy/util/__init__.py create mode 100644 venv/Lib/site-packages/sqlalchemy/util/__pycache__/__init__.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/util/__pycache__/_collections.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/util/__pycache__/_concurrency_py3k.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/util/__pycache__/_preloaded.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/util/__pycache__/compat.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/util/__pycache__/concurrency.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/util/__pycache__/deprecations.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/util/__pycache__/langhelpers.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/util/__pycache__/queue.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/util/__pycache__/topological.cpython-39.pyc create mode 100644 venv/Lib/site-packages/sqlalchemy/util/_collections.py create mode 100644 venv/Lib/site-packages/sqlalchemy/util/_concurrency_py3k.py create mode 100644 venv/Lib/site-packages/sqlalchemy/util/_preloaded.py create mode 100644 venv/Lib/site-packages/sqlalchemy/util/compat.py create mode 100644 venv/Lib/site-packages/sqlalchemy/util/concurrency.py create mode 100644 venv/Lib/site-packages/sqlalchemy/util/deprecations.py create mode 100644 venv/Lib/site-packages/sqlalchemy/util/langhelpers.py create mode 100644 venv/Lib/site-packages/sqlalchemy/util/queue.py create mode 100644 venv/Lib/site-packages/sqlalchemy/util/topological.py create mode 100644 venv/Lib/site-packages/toml-0.10.2.dist-info/INSTALLER create mode 100644 venv/Lib/site-packages/toml-0.10.2.dist-info/LICENSE create mode 100644 venv/Lib/site-packages/toml-0.10.2.dist-info/METADATA create mode 100644 venv/Lib/site-packages/toml-0.10.2.dist-info/RECORD create mode 100644 venv/Lib/site-packages/toml-0.10.2.dist-info/WHEEL create mode 100644 venv/Lib/site-packages/toml-0.10.2.dist-info/top_level.txt create mode 100644 venv/Lib/site-packages/toml/__init__.py create mode 100644 venv/Lib/site-packages/toml/__pycache__/__init__.cpython-39.pyc create mode 100644 venv/Lib/site-packages/toml/__pycache__/decoder.cpython-39.pyc create mode 100644 venv/Lib/site-packages/toml/__pycache__/encoder.cpython-39.pyc create mode 100644 venv/Lib/site-packages/toml/__pycache__/ordered.cpython-39.pyc create mode 100644 venv/Lib/site-packages/toml/__pycache__/tz.cpython-39.pyc create mode 100644 venv/Lib/site-packages/toml/decoder.py create mode 100644 venv/Lib/site-packages/toml/encoder.py create mode 100644 venv/Lib/site-packages/toml/ordered.py create mode 100644 venv/Lib/site-packages/toml/tz.py create mode 100644 venv/Lib/site-packages/werkzeug/__init__.py create mode 100644 venv/Lib/site-packages/werkzeug/__pycache__/__init__.cpython-39.pyc create mode 100644 venv/Lib/site-packages/werkzeug/__pycache__/_internal.cpython-39.pyc create mode 100644 venv/Lib/site-packages/werkzeug/__pycache__/_reloader.cpython-39.pyc create mode 100644 venv/Lib/site-packages/werkzeug/__pycache__/datastructures.cpython-39.pyc create mode 100644 venv/Lib/site-packages/werkzeug/__pycache__/exceptions.cpython-39.pyc create mode 100644 venv/Lib/site-packages/werkzeug/__pycache__/filesystem.cpython-39.pyc create mode 100644 venv/Lib/site-packages/werkzeug/__pycache__/formparser.cpython-39.pyc create mode 100644 venv/Lib/site-packages/werkzeug/__pycache__/http.cpython-39.pyc create mode 100644 venv/Lib/site-packages/werkzeug/__pycache__/local.cpython-39.pyc create mode 100644 venv/Lib/site-packages/werkzeug/__pycache__/routing.cpython-39.pyc create mode 100644 venv/Lib/site-packages/werkzeug/__pycache__/security.cpython-39.pyc create mode 100644 venv/Lib/site-packages/werkzeug/__pycache__/serving.cpython-39.pyc create mode 100644 venv/Lib/site-packages/werkzeug/__pycache__/test.cpython-39.pyc create mode 100644 venv/Lib/site-packages/werkzeug/__pycache__/testapp.cpython-39.pyc create mode 100644 venv/Lib/site-packages/werkzeug/__pycache__/urls.cpython-39.pyc create mode 100644 venv/Lib/site-packages/werkzeug/__pycache__/user_agent.cpython-39.pyc create mode 100644 venv/Lib/site-packages/werkzeug/__pycache__/useragents.cpython-39.pyc create mode 100644 venv/Lib/site-packages/werkzeug/__pycache__/utils.cpython-39.pyc create mode 100644 venv/Lib/site-packages/werkzeug/__pycache__/wsgi.cpython-39.pyc create mode 100644 venv/Lib/site-packages/werkzeug/_internal.py create mode 100644 venv/Lib/site-packages/werkzeug/_reloader.py create mode 100644 venv/Lib/site-packages/werkzeug/datastructures.py create mode 100644 venv/Lib/site-packages/werkzeug/datastructures.pyi create mode 100644 venv/Lib/site-packages/werkzeug/debug/__init__.py create mode 100644 venv/Lib/site-packages/werkzeug/debug/__pycache__/__init__.cpython-39.pyc create mode 100644 venv/Lib/site-packages/werkzeug/debug/__pycache__/console.cpython-39.pyc create mode 100644 venv/Lib/site-packages/werkzeug/debug/__pycache__/repr.cpython-39.pyc create mode 100644 venv/Lib/site-packages/werkzeug/debug/__pycache__/tbtools.cpython-39.pyc create mode 100644 venv/Lib/site-packages/werkzeug/debug/console.py create mode 100644 venv/Lib/site-packages/werkzeug/debug/repr.py create mode 100644 venv/Lib/site-packages/werkzeug/debug/shared/FONT_LICENSE create mode 100644 venv/Lib/site-packages/werkzeug/debug/shared/ICON_LICENSE.md create mode 100644 venv/Lib/site-packages/werkzeug/debug/shared/console.png create mode 100644 venv/Lib/site-packages/werkzeug/debug/shared/debugger.js create mode 100644 venv/Lib/site-packages/werkzeug/debug/shared/less.png create mode 100644 venv/Lib/site-packages/werkzeug/debug/shared/more.png create mode 100644 venv/Lib/site-packages/werkzeug/debug/shared/source.png create mode 100644 venv/Lib/site-packages/werkzeug/debug/shared/style.css create mode 100644 venv/Lib/site-packages/werkzeug/debug/shared/ubuntu.ttf create mode 100644 venv/Lib/site-packages/werkzeug/debug/tbtools.py create mode 100644 venv/Lib/site-packages/werkzeug/exceptions.py create mode 100644 venv/Lib/site-packages/werkzeug/filesystem.py create mode 100644 venv/Lib/site-packages/werkzeug/formparser.py create mode 100644 venv/Lib/site-packages/werkzeug/http.py create mode 100644 venv/Lib/site-packages/werkzeug/local.py create mode 100644 venv/Lib/site-packages/werkzeug/middleware/__init__.py create mode 100644 venv/Lib/site-packages/werkzeug/middleware/__pycache__/__init__.cpython-39.pyc create mode 100644 venv/Lib/site-packages/werkzeug/middleware/__pycache__/dispatcher.cpython-39.pyc create mode 100644 venv/Lib/site-packages/werkzeug/middleware/__pycache__/http_proxy.cpython-39.pyc create mode 100644 venv/Lib/site-packages/werkzeug/middleware/__pycache__/lint.cpython-39.pyc create mode 100644 venv/Lib/site-packages/werkzeug/middleware/__pycache__/profiler.cpython-39.pyc create mode 100644 venv/Lib/site-packages/werkzeug/middleware/__pycache__/proxy_fix.cpython-39.pyc create mode 100644 venv/Lib/site-packages/werkzeug/middleware/__pycache__/shared_data.cpython-39.pyc create mode 100644 venv/Lib/site-packages/werkzeug/middleware/dispatcher.py create mode 100644 venv/Lib/site-packages/werkzeug/middleware/http_proxy.py create mode 100644 venv/Lib/site-packages/werkzeug/middleware/lint.py create mode 100644 venv/Lib/site-packages/werkzeug/middleware/profiler.py create mode 100644 venv/Lib/site-packages/werkzeug/middleware/proxy_fix.py create mode 100644 venv/Lib/site-packages/werkzeug/middleware/shared_data.py create mode 100644 venv/Lib/site-packages/werkzeug/py.typed create mode 100644 venv/Lib/site-packages/werkzeug/routing.py create mode 100644 venv/Lib/site-packages/werkzeug/sansio/__init__.py create mode 100644 venv/Lib/site-packages/werkzeug/sansio/__pycache__/__init__.cpython-39.pyc create mode 100644 venv/Lib/site-packages/werkzeug/sansio/__pycache__/multipart.cpython-39.pyc create mode 100644 venv/Lib/site-packages/werkzeug/sansio/__pycache__/request.cpython-39.pyc create mode 100644 venv/Lib/site-packages/werkzeug/sansio/__pycache__/response.cpython-39.pyc create mode 100644 venv/Lib/site-packages/werkzeug/sansio/__pycache__/utils.cpython-39.pyc create mode 100644 venv/Lib/site-packages/werkzeug/sansio/multipart.py create mode 100644 venv/Lib/site-packages/werkzeug/sansio/request.py create mode 100644 venv/Lib/site-packages/werkzeug/sansio/response.py create mode 100644 venv/Lib/site-packages/werkzeug/sansio/utils.py create mode 100644 venv/Lib/site-packages/werkzeug/security.py create mode 100644 venv/Lib/site-packages/werkzeug/serving.py create mode 100644 venv/Lib/site-packages/werkzeug/test.py create mode 100644 venv/Lib/site-packages/werkzeug/testapp.py create mode 100644 venv/Lib/site-packages/werkzeug/urls.py create mode 100644 venv/Lib/site-packages/werkzeug/user_agent.py create mode 100644 venv/Lib/site-packages/werkzeug/useragents.py create mode 100644 venv/Lib/site-packages/werkzeug/utils.py create mode 100644 venv/Lib/site-packages/werkzeug/wrappers/__init__.py create mode 100644 venv/Lib/site-packages/werkzeug/wrappers/__pycache__/__init__.cpython-39.pyc create mode 100644 venv/Lib/site-packages/werkzeug/wrappers/__pycache__/accept.cpython-39.pyc create mode 100644 venv/Lib/site-packages/werkzeug/wrappers/__pycache__/auth.cpython-39.pyc create mode 100644 venv/Lib/site-packages/werkzeug/wrappers/__pycache__/base_request.cpython-39.pyc create mode 100644 venv/Lib/site-packages/werkzeug/wrappers/__pycache__/base_response.cpython-39.pyc create mode 100644 venv/Lib/site-packages/werkzeug/wrappers/__pycache__/common_descriptors.cpython-39.pyc create mode 100644 venv/Lib/site-packages/werkzeug/wrappers/__pycache__/cors.cpython-39.pyc create mode 100644 venv/Lib/site-packages/werkzeug/wrappers/__pycache__/etag.cpython-39.pyc create mode 100644 venv/Lib/site-packages/werkzeug/wrappers/__pycache__/json.cpython-39.pyc create mode 100644 venv/Lib/site-packages/werkzeug/wrappers/__pycache__/request.cpython-39.pyc create mode 100644 venv/Lib/site-packages/werkzeug/wrappers/__pycache__/response.cpython-39.pyc create mode 100644 venv/Lib/site-packages/werkzeug/wrappers/__pycache__/user_agent.cpython-39.pyc create mode 100644 venv/Lib/site-packages/werkzeug/wrappers/accept.py create mode 100644 venv/Lib/site-packages/werkzeug/wrappers/auth.py create mode 100644 venv/Lib/site-packages/werkzeug/wrappers/base_request.py create mode 100644 venv/Lib/site-packages/werkzeug/wrappers/base_response.py create mode 100644 venv/Lib/site-packages/werkzeug/wrappers/common_descriptors.py create mode 100644 venv/Lib/site-packages/werkzeug/wrappers/cors.py create mode 100644 venv/Lib/site-packages/werkzeug/wrappers/etag.py create mode 100644 venv/Lib/site-packages/werkzeug/wrappers/json.py create mode 100644 venv/Lib/site-packages/werkzeug/wrappers/request.py create mode 100644 venv/Lib/site-packages/werkzeug/wrappers/response.py create mode 100644 venv/Lib/site-packages/werkzeug/wrappers/user_agent.py create mode 100644 venv/Lib/site-packages/werkzeug/wsgi.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/deactivate.bat create mode 100644 venv/Scripts/flask.exe create mode 100644 venv/Scripts/pip.exe create mode 100644 venv/Scripts/pip3.9.exe create mode 100644 venv/Scripts/pip3.exe create mode 100644 venv/Scripts/py.test.exe create mode 100644 venv/Scripts/pyserial-miniterm.exe create mode 100644 venv/Scripts/pyserial-ports.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/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..73f69e0 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml +# Editor-based HTTP Client requests +/httpRequests/ diff --git a/.idea/OpenHome.iml b/.idea/OpenHome.iml new file mode 100644 index 0000000..74d515a --- /dev/null +++ b/.idea/OpenHome.iml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 0000000..105ce2d --- /dev/null +++ b/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..8823e5a --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..4c3883c --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/__pycache__/cun.cpython-39.pyc b/__pycache__/cun.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4cf173d7b4ceff1cbe656a7c105d3768f08214bb GIT binary patch literal 8134 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 {}".format(req)) + else: + print("Request while not connected!") + + def response(self): + if self.client is not None: + response = self.client.read(100).decode() + if DEBUG: + print("<<< {}".format(response)) + return response + else: + print("Waiting for response while not connected!") + return None + + def version_string(self): + self.request("V") + return self.response() + + def is_connected(self): + return self.client is not None + + def set_moritz_mode(self, moritz_mode_enable): + if moritz_mode_enable: + self.request("Zr") + #return self.response() + else: + self.request("Zx") + + +class CUN: + def __init__(self): + self.client: Telnet = None + + def connect(self, host, ip): + if self.client is None: + self.client = Telnet(host, ip) + + def disconnect(self): + if self.client is not None: + self.client.close() + self.client = None + + def version_string(self): + self.request("V") + return self.response() + + def request(self, req): + if self.client is not None: + self.client.write(req.encode()) + self.client.write(b"\n") + if DEBUG: + print(">>> {}".format(req)) + else: + print("Request while not connected!") + + def response(self): + if self.client is not None: + response = self.client.read_some().decode() + if DEBUG: + print("<<< {}".format(response)) + return response + else: + print("Waiting for response while not connected!") + return None + + def set_moritz_mode(self, moritz_mode_enable): + if moritz_mode_enable: + self.request("Zr") + #return self.response() + else: + self.request("Zx") + + def configure(self, ip, netmask, gateway, ): + # ToDo: NYI + pass + + def h_request(self): + self.request("H?") + return self.response() + + def send_moritz(self, packet: MAXPacket): + # llnnccttssssssddddddpp... + # ll - length + # nn - msg counter + # cc - control byte + # tt - msg type + # ss - sender address(3 byte) + # dd - destination address(3 byte - 000000 for broadcast) + # pp - payload... + len_str = packet.length + msg_counter_str = packet.ms + control_byte_str = "00" + message_type_str = "02" + sender_addr_str = "000000" + dest_addr_str = "000000" + payload = "l:" + self.request("Zs{}{}{}{}{}{}{}".format(packet.length, packet.message_counter, control_byte_str, + message_type_str, sender_addr_str, + dest_addr_str, payload)) + #self.request("l:") + return self.response() diff --git a/main.py b/main.py new file mode 100644 index 0000000..8e8b284 --- /dev/null +++ b/main.py @@ -0,0 +1,35 @@ +from cun import CUL, MAXPacketFactory, MAXPacket, MAXPairPingPacket, MAXPairPongPacket +import time + +if __name__ == '__main__': + #cun = CUN() + #cun.connect("192.168.0.244", 2323) + #cun.request(b"V\n") + #print(cun.response()) + #version = cun.version_string() + #print(version) + #cun.set_moritz_mode(True) + #print(cun.send_moritz()) + #cun.request("Zsl:") + #print(cun.response()) + #cun.disconnect() + + cul = CUL() + cul.connect("COM8") + print(cul.version_string()) + cul.set_moritz_mode(True) + while cul.is_connected(): + resp = cul.response() + if resp is not None and resp[0:1] == "Z": + pkt = MAXPacketFactory.create_packet(resp) + print(pkt.to_string()) + if isinstance(pkt, MAXPairPingPacket): + print("Sending Pong!") + pong = MAXPairPongPacket(message_counter="00", message_flag="00", sender_address="FDF7CA", + dest_address=pkt.sender_address, group_id="00") + pong_str = pong.serialize() + cul.request(pong_str) + + time.sleep(1) + cul.disconnect() + diff --git a/venv/Include/site/python3.9/greenlet/greenlet.h b/venv/Include/site/python3.9/greenlet/greenlet.h new file mode 100644 index 0000000..830bef8 --- /dev/null +++ b/venv/Include/site/python3.9/greenlet/greenlet.h @@ -0,0 +1,146 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; -*- */ + +/* Greenlet object interface */ + +#ifndef Py_GREENLETOBJECT_H +#define Py_GREENLETOBJECT_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* This is deprecated and undocumented. It does not change. */ +#define GREENLET_VERSION "1.0.0" + +typedef struct _greenlet { + PyObject_HEAD + char* stack_start; + char* stack_stop; + char* stack_copy; + intptr_t stack_saved; + struct _greenlet* stack_prev; + struct _greenlet* parent; + PyObject* run_info; + struct _frame* top_frame; + int recursion_depth; + PyObject* weakreflist; +#if PY_VERSION_HEX >= 0x030700A3 + _PyErr_StackItem* exc_info; + _PyErr_StackItem exc_state; +#else + PyObject* exc_type; + PyObject* exc_value; + PyObject* exc_traceback; +#endif + PyObject* dict; +#if PY_VERSION_HEX >= 0x030700A3 + PyObject* context; +#endif +#if PY_VERSION_HEX >= 0x30A00B1 + CFrame* cframe; +#endif +} PyGreenlet; + +#define PyGreenlet_Check(op) PyObject_TypeCheck(op, &PyGreenlet_Type) +#define PyGreenlet_MAIN(op) (((PyGreenlet*)(op))->stack_stop == (char*)-1) +#define PyGreenlet_STARTED(op) (((PyGreenlet*)(op))->stack_stop != NULL) +#define PyGreenlet_ACTIVE(op) (((PyGreenlet*)(op))->stack_start != NULL) +#define PyGreenlet_GET_PARENT(op) (((PyGreenlet*)(op))->parent) + +/* C API functions */ + +/* Total number of symbols that are exported */ +#define PyGreenlet_API_pointers 8 + +#define PyGreenlet_Type_NUM 0 +#define PyExc_GreenletError_NUM 1 +#define PyExc_GreenletExit_NUM 2 + +#define PyGreenlet_New_NUM 3 +#define PyGreenlet_GetCurrent_NUM 4 +#define PyGreenlet_Throw_NUM 5 +#define PyGreenlet_Switch_NUM 6 +#define PyGreenlet_SetParent_NUM 7 + +#ifndef GREENLET_MODULE +/* This section is used by modules that uses the greenlet C API */ +static void** _PyGreenlet_API = NULL; + +# define PyGreenlet_Type \ + (*(PyTypeObject*)_PyGreenlet_API[PyGreenlet_Type_NUM]) + +# define PyExc_GreenletError \ + ((PyObject*)_PyGreenlet_API[PyExc_GreenletError_NUM]) + +# define PyExc_GreenletExit \ + ((PyObject*)_PyGreenlet_API[PyExc_GreenletExit_NUM]) + +/* + * PyGreenlet_New(PyObject *args) + * + * greenlet.greenlet(run, parent=None) + */ +# define PyGreenlet_New \ + (*(PyGreenlet * (*)(PyObject * run, PyGreenlet * parent)) \ + _PyGreenlet_API[PyGreenlet_New_NUM]) + +/* + * PyGreenlet_GetCurrent(void) + * + * greenlet.getcurrent() + */ +# define PyGreenlet_GetCurrent \ + (*(PyGreenlet * (*)(void)) _PyGreenlet_API[PyGreenlet_GetCurrent_NUM]) + +/* + * PyGreenlet_Throw( + * PyGreenlet *greenlet, + * PyObject *typ, + * PyObject *val, + * PyObject *tb) + * + * g.throw(...) + */ +# define PyGreenlet_Throw \ + (*(PyObject * (*)(PyGreenlet * self, \ + PyObject * typ, \ + PyObject * val, \ + PyObject * tb)) \ + _PyGreenlet_API[PyGreenlet_Throw_NUM]) + +/* + * PyGreenlet_Switch(PyGreenlet *greenlet, PyObject *args) + * + * g.switch(*args, **kwargs) + */ +# define PyGreenlet_Switch \ + (*(PyObject * \ + (*)(PyGreenlet * greenlet, PyObject * args, PyObject * kwargs)) \ + _PyGreenlet_API[PyGreenlet_Switch_NUM]) + +/* + * PyGreenlet_SetParent(PyObject *greenlet, PyObject *new_parent) + * + * g.parent = new_parent + */ +# define PyGreenlet_SetParent \ + (*(int (*)(PyGreenlet * greenlet, PyGreenlet * nparent)) \ + _PyGreenlet_API[PyGreenlet_SetParent_NUM]) + +/* Macro that imports greenlet and initializes C API */ +/* NOTE: This has actually moved to ``greenlet._greenlet._C_API``, but we + keep the older definition to be sure older code that might have a copy of + the header still works. */ +# define PyGreenlet_Import() \ + { \ + _PyGreenlet_API = (void**)PyCapsule_Import("greenlet._C_API", 0); \ + } + +#endif /* GREENLET_MODULE */ + +#ifdef __cplusplus +} +#endif +#endif /* !Py_GREENLETOBJECT_H */ diff --git a/venv/Lib/site-packages/Flask-2.0.1.dist-info/INSTALLER b/venv/Lib/site-packages/Flask-2.0.1.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/venv/Lib/site-packages/Flask-2.0.1.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/Lib/site-packages/Flask-2.0.1.dist-info/LICENSE.rst b/venv/Lib/site-packages/Flask-2.0.1.dist-info/LICENSE.rst new file mode 100644 index 0000000..9d227a0 --- /dev/null +++ b/venv/Lib/site-packages/Flask-2.0.1.dist-info/LICENSE.rst @@ -0,0 +1,28 @@ +Copyright 2010 Pallets + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/venv/Lib/site-packages/Flask-2.0.1.dist-info/METADATA b/venv/Lib/site-packages/Flask-2.0.1.dist-info/METADATA new file mode 100644 index 0000000..2b881c2 --- /dev/null +++ b/venv/Lib/site-packages/Flask-2.0.1.dist-info/METADATA @@ -0,0 +1,124 @@ +Metadata-Version: 2.1 +Name: Flask +Version: 2.0.1 +Summary: A simple framework for building complex web applications. +Home-page: https://palletsprojects.com/p/flask +Author: Armin Ronacher +Author-email: armin.ronacher@active-4.com +Maintainer: Pallets +Maintainer-email: contact@palletsprojects.com +License: BSD-3-Clause +Project-URL: Donate, https://palletsprojects.com/donate +Project-URL: Documentation, https://flask.palletsprojects.com/ +Project-URL: Changes, https://flask.palletsprojects.com/changes/ +Project-URL: Source Code, https://github.com/pallets/flask/ +Project-URL: Issue Tracker, https://github.com/pallets/flask/issues/ +Project-URL: Twitter, https://twitter.com/PalletsTeam +Project-URL: Chat, https://discord.gg/pallets +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Web Environment +Classifier: Framework :: Flask +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 :: Internet :: WWW/HTTP :: WSGI +Classifier: Topic :: Internet :: WWW/HTTP :: WSGI :: Application +Classifier: Topic :: Software Development :: Libraries :: Application Frameworks +Requires-Python: >=3.6 +Description-Content-Type: text/x-rst +Requires-Dist: Werkzeug (>=2.0) +Requires-Dist: Jinja2 (>=3.0) +Requires-Dist: itsdangerous (>=2.0) +Requires-Dist: click (>=7.1.2) +Provides-Extra: async +Requires-Dist: asgiref (>=3.2) ; extra == 'async' +Provides-Extra: dotenv +Requires-Dist: python-dotenv ; extra == 'dotenv' + +Flask +===== + +Flask is a lightweight `WSGI`_ web application framework. It is designed +to make getting started quick and easy, with the ability to scale up to +complex applications. It began as a simple wrapper around `Werkzeug`_ +and `Jinja`_ and has become one of the most popular Python web +application frameworks. + +Flask offers suggestions, but doesn't enforce any dependencies or +project layout. It is up to the developer to choose the tools and +libraries they want to use. There are many extensions provided by the +community that make adding new functionality easy. + +.. _WSGI: https://wsgi.readthedocs.io/ +.. _Werkzeug: https://werkzeug.palletsprojects.com/ +.. _Jinja: https://jinja.palletsprojects.com/ + + +Installing +---------- + +Install and update using `pip`_: + +.. code-block:: text + + $ pip install -U Flask + +.. _pip: https://pip.pypa.io/en/stable/quickstart/ + + +A Simple Example +---------------- + +.. code-block:: python + + # save this as app.py + from flask import Flask + + app = Flask(__name__) + + @app.route("/") + def hello(): + return "Hello, World!" + +.. code-block:: text + + $ flask run + * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit) + + +Contributing +------------ + +For guidance on setting up a development environment and how to make a +contribution to Flask, see the `contributing guidelines`_. + +.. _contributing guidelines: https://github.com/pallets/flask/blob/main/CONTRIBUTING.rst + + +Donate +------ + +The Pallets organization develops and supports Flask and the libraries +it uses. In order to grow the community of contributors and users, and +allow the maintainers to devote more time to the projects, `please +donate today`_. + +.. _please donate today: https://palletsprojects.com/donate + + +Links +----- + +- Documentation: https://flask.palletsprojects.com/ +- Changes: https://flask.palletsprojects.com/changes/ +- PyPI Releases: https://pypi.org/project/Flask/ +- Source Code: https://github.com/pallets/flask/ +- Issue Tracker: https://github.com/pallets/flask/issues/ +- Website: https://palletsprojects.com/p/flask/ +- Twitter: https://twitter.com/PalletsTeam +- Chat: https://discord.gg/pallets + + diff --git a/venv/Lib/site-packages/Flask-2.0.1.dist-info/RECORD b/venv/Lib/site-packages/Flask-2.0.1.dist-info/RECORD new file mode 100644 index 0000000..537c566 --- /dev/null +++ b/venv/Lib/site-packages/Flask-2.0.1.dist-info/RECORD @@ -0,0 +1,51 @@ +../../Scripts/flask.exe,sha256=jAsR3j2HDZxKuPxMa4-KTV_CkpVSHVaFcQeGKvncEIE,106342 +Flask-2.0.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +Flask-2.0.1.dist-info/LICENSE.rst,sha256=SJqOEQhQntmKN7uYPhHg9-HTHwvY-Zp5yESOf_N9B-o,1475 +Flask-2.0.1.dist-info/METADATA,sha256=50Jm1647RKw98p4RF64bCqRh0wajk-n3hQ7av2-pniA,3808 +Flask-2.0.1.dist-info/RECORD,, +Flask-2.0.1.dist-info/WHEEL,sha256=OqRkF0eY5GHssMorFjlbTIq072vpHpF60fIQA6lS9xA,92 +Flask-2.0.1.dist-info/entry_points.txt,sha256=gBLA1aKg0OYR8AhbAfg8lnburHtKcgJLDU52BBctN0k,42 +Flask-2.0.1.dist-info/top_level.txt,sha256=dvi65F6AeGWVU0TBpYiC04yM60-FX1gJFkK31IKQr5c,6 +flask/__init__.py,sha256=w5v6GCNm8eLDMNWqs2ue7HLHo75aslAwz1h3k3YO9HY,2251 +flask/__main__.py,sha256=bYt9eEaoRQWdejEHFD8REx9jxVEdZptECFsV7F49Ink,30 +flask/__pycache__/__init__.cpython-39.pyc,, +flask/__pycache__/__main__.cpython-39.pyc,, +flask/__pycache__/app.cpython-39.pyc,, +flask/__pycache__/blueprints.cpython-39.pyc,, +flask/__pycache__/cli.cpython-39.pyc,, +flask/__pycache__/config.cpython-39.pyc,, +flask/__pycache__/ctx.cpython-39.pyc,, +flask/__pycache__/debughelpers.cpython-39.pyc,, +flask/__pycache__/globals.cpython-39.pyc,, +flask/__pycache__/helpers.cpython-39.pyc,, +flask/__pycache__/logging.cpython-39.pyc,, +flask/__pycache__/scaffold.cpython-39.pyc,, +flask/__pycache__/sessions.cpython-39.pyc,, +flask/__pycache__/signals.cpython-39.pyc,, +flask/__pycache__/templating.cpython-39.pyc,, +flask/__pycache__/testing.cpython-39.pyc,, +flask/__pycache__/typing.cpython-39.pyc,, +flask/__pycache__/views.cpython-39.pyc,, +flask/__pycache__/wrappers.cpython-39.pyc,, +flask/app.py,sha256=q6lpiiWVxjljQRwjjneUBpfllXYPEq0CFAUpTQ5gIeA,82376 +flask/blueprints.py,sha256=OjI-dkwx96ZNMUGDDFMKzgcpUJf240WRuMlHkmgI1Lc,23541 +flask/cli.py,sha256=iN1pL2SevE5Nmvey-0WwnxG3nipZXIiE__Ed4lx3IuM,32036 +flask/config.py,sha256=jj_7JGen_kYuTlKrx8ZPBsZddb8mihC0ODg4gcjXBX8,11068 +flask/ctx.py,sha256=EM3W0v1ctuFQAGk_HWtQdoJEg_r2f5Le4xcmElxFwwk,17428 +flask/debughelpers.py,sha256=wk5HtLwENsQ4e8tkxfBn6ykUeVRDuMbQCKgtEVe6jxk,6171 +flask/globals.py,sha256=cWd-R2hUH3VqPhnmQNww892tQS6Yjqg_wg8UvW1M7NM,1723 +flask/helpers.py,sha256=00WqA3wYeyjMrnAOPZTUyrnUf7H8ik3CVT0kqGl_qjk,30589 +flask/json/__init__.py,sha256=d-db2DJMASq0G7CI-JvobehRE1asNRGX1rIDQ1GF9WM,11580 +flask/json/__pycache__/__init__.cpython-39.pyc,, +flask/json/__pycache__/tag.cpython-39.pyc,, +flask/json/tag.py,sha256=fys3HBLssWHuMAIJuTcf2K0bCtosePBKXIWASZEEjnU,8857 +flask/logging.py,sha256=1o_hirVGqdj7SBdETnhX7IAjklG89RXlrwz_2CjzQQE,2273 +flask/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +flask/scaffold.py,sha256=EhQuiFrdcmJHxqPGQkEpqLsEUZ7ULZD0rtED2NrduvM,32400 +flask/sessions.py,sha256=Kb7zY4qBIOU2cw1xM5mQ_KmgYUBDFbUYWjlkq0EFYis,15189 +flask/signals.py,sha256=HQWgBEXlrLbHwLBoWqAStJKcN-rsB1_AMO8-VZ7LDOo,2126 +flask/templating.py,sha256=l96VD39JQ0nue4Bcj7wZ4-FWWs-ppLxvgBCpwDQ4KAk,5626 +flask/testing.py,sha256=OsHT-2B70abWH3ulY9IbhLchXIeyj3L-cfcDa88wv5E,10281 +flask/typing.py,sha256=zVqhz53KklncAv-WxbpxGZfaRGOqeWAsLdP1tTMaCuE,1684 +flask/views.py,sha256=F2PpWPloe4x0906IUjnPcsOqg5YvmQIfk07_lFeAD4s,5865 +flask/wrappers.py,sha256=VndbHPRBSUUOejmd2Y3ydkoCVUtsS2OJIdJEVIkBVD8,5604 diff --git a/venv/Lib/site-packages/Flask-2.0.1.dist-info/WHEEL b/venv/Lib/site-packages/Flask-2.0.1.dist-info/WHEEL new file mode 100644 index 0000000..385faab --- /dev/null +++ b/venv/Lib/site-packages/Flask-2.0.1.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.36.2) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/venv/Lib/site-packages/Flask-2.0.1.dist-info/entry_points.txt b/venv/Lib/site-packages/Flask-2.0.1.dist-info/entry_points.txt new file mode 100644 index 0000000..1eb0252 --- /dev/null +++ b/venv/Lib/site-packages/Flask-2.0.1.dist-info/entry_points.txt @@ -0,0 +1,3 @@ +[console_scripts] +flask = flask.cli:main + diff --git a/venv/Lib/site-packages/Flask-2.0.1.dist-info/top_level.txt b/venv/Lib/site-packages/Flask-2.0.1.dist-info/top_level.txt new file mode 100644 index 0000000..7e10602 --- /dev/null +++ b/venv/Lib/site-packages/Flask-2.0.1.dist-info/top_level.txt @@ -0,0 +1 @@ +flask diff --git a/venv/Lib/site-packages/Flask_SQLAlchemy-2.5.1.dist-info/INSTALLER b/venv/Lib/site-packages/Flask_SQLAlchemy-2.5.1.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/venv/Lib/site-packages/Flask_SQLAlchemy-2.5.1.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/Lib/site-packages/Flask_SQLAlchemy-2.5.1.dist-info/LICENSE.rst b/venv/Lib/site-packages/Flask_SQLAlchemy-2.5.1.dist-info/LICENSE.rst new file mode 100644 index 0000000..9d227a0 --- /dev/null +++ b/venv/Lib/site-packages/Flask_SQLAlchemy-2.5.1.dist-info/LICENSE.rst @@ -0,0 +1,28 @@ +Copyright 2010 Pallets + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/venv/Lib/site-packages/Flask_SQLAlchemy-2.5.1.dist-info/METADATA b/venv/Lib/site-packages/Flask_SQLAlchemy-2.5.1.dist-info/METADATA new file mode 100644 index 0000000..a1971dc --- /dev/null +++ b/venv/Lib/site-packages/Flask_SQLAlchemy-2.5.1.dist-info/METADATA @@ -0,0 +1,94 @@ +Metadata-Version: 2.1 +Name: Flask-SQLAlchemy +Version: 2.5.1 +Summary: Adds SQLAlchemy support to your Flask application. +Home-page: https://github.com/pallets/flask-sqlalchemy +Author: Armin Ronacher +Author-email: armin.ronacher@active-4.com +Maintainer: Pallets +Maintainer-email: contact@palletsprojects.com +License: BSD-3-Clause +Project-URL: Documentation, https://flask-sqlalchemy.palletsprojects.com/ +Project-URL: Code, https://github.com/pallets/flask-sqlalchemy +Project-URL: Issue tracker, https://github.com/pallets/flask-sqlalchemy/issues +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Web Environment +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.4 +Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Requires-Python: >= 2.7, != 3.0.*, != 3.1.*, != 3.2.*, != 3.3.* +Requires-Dist: Flask (>=0.10) +Requires-Dist: SQLAlchemy (>=0.8.0) + +Flask-SQLAlchemy +================ + +Flask-SQLAlchemy is an extension for `Flask`_ that adds support for +`SQLAlchemy`_ to your application. It aims to simplify using SQLAlchemy +with Flask by providing useful defaults and extra helpers that make it +easier to accomplish common tasks. + + +Installing +---------- + +Install and update using `pip`_: + +.. code-block:: text + + $ pip install -U Flask-SQLAlchemy + + +A Simple Example +---------------- + +.. code-block:: python + + from flask import Flask + from flask_sqlalchemy import SQLAlchemy + + app = Flask(__name__) + app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///example.sqlite" + db = SQLAlchemy(app) + + + class User(db.Model): + id = db.Column(db.Integer, primary_key=True) + username = db.Column(db.String, unique=True, nullable=False) + email = db.Column(db.String, unique=True, nullable=False) + + + db.session.add(User(name="Flask", email="example@example.com")) + db.session.commit() + + users = User.query.all() + + +Links +----- + +- Documentation: https://flask-sqlalchemy.palletsprojects.com/ +- Releases: https://pypi.org/project/Flask-SQLAlchemy/ +- Code: https://github.com/pallets/flask-sqlalchemy +- Issue tracker: https://github.com/pallets/flask-sqlalchemy/issues +- Test status: https://travis-ci.org/pallets/flask-sqlalchemy +- Test coverage: https://codecov.io/gh/pallets/flask-sqlalchemy + +.. _Flask: https://palletsprojects.com/p/flask/ +.. _SQLAlchemy: https://www.sqlalchemy.org +.. _pip: https://pip.pypa.io/en/stable/quickstart/ + + diff --git a/venv/Lib/site-packages/Flask_SQLAlchemy-2.5.1.dist-info/RECORD b/venv/Lib/site-packages/Flask_SQLAlchemy-2.5.1.dist-info/RECORD new file mode 100644 index 0000000..c5121f5 --- /dev/null +++ b/venv/Lib/site-packages/Flask_SQLAlchemy-2.5.1.dist-info/RECORD @@ -0,0 +1,14 @@ +Flask_SQLAlchemy-2.5.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +Flask_SQLAlchemy-2.5.1.dist-info/LICENSE.rst,sha256=SJqOEQhQntmKN7uYPhHg9-HTHwvY-Zp5yESOf_N9B-o,1475 +Flask_SQLAlchemy-2.5.1.dist-info/METADATA,sha256=vVCeMtTM_xOrUVoVyemeNaTUI5L9iXa16NsiMDDOgFU,3128 +Flask_SQLAlchemy-2.5.1.dist-info/RECORD,, +Flask_SQLAlchemy-2.5.1.dist-info/WHEEL,sha256=Z-nyYpwrcSqxfdux5Mbn_DQ525iP7J2DG3JgGvOYyTQ,110 +Flask_SQLAlchemy-2.5.1.dist-info/top_level.txt,sha256=w2K4fNNoTh4HItoFfz2FRQShSeLcvHYrzU_sZov21QU,17 +flask_sqlalchemy/__init__.py,sha256=IaupgTRkQnY05KPLYvfiNnJdrmwoyfsxaiyGtrEYfO4,40738 +flask_sqlalchemy/__pycache__/__init__.cpython-39.pyc,, +flask_sqlalchemy/__pycache__/_compat.cpython-39.pyc,, +flask_sqlalchemy/__pycache__/model.cpython-39.pyc,, +flask_sqlalchemy/__pycache__/utils.cpython-39.pyc,, +flask_sqlalchemy/_compat.py,sha256=yua0ZSgVWwi56QpEgwaPInzkNQ9PFb7YQdvEk3dImXo,821 +flask_sqlalchemy/model.py,sha256=bd2mIv9LA1A2MZkQObgnMUCSrxNvyqplaSkCxyxKNxY,4988 +flask_sqlalchemy/utils.py,sha256=4eHqAbYElnJ3NbSAHhuINckoAHDABoxjleMJD0iKgyg,1390 diff --git a/venv/Lib/site-packages/Flask_SQLAlchemy-2.5.1.dist-info/WHEEL b/venv/Lib/site-packages/Flask_SQLAlchemy-2.5.1.dist-info/WHEEL new file mode 100644 index 0000000..01b8fc7 --- /dev/null +++ b/venv/Lib/site-packages/Flask_SQLAlchemy-2.5.1.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.36.2) +Root-Is-Purelib: true +Tag: py2-none-any +Tag: py3-none-any + diff --git a/venv/Lib/site-packages/Flask_SQLAlchemy-2.5.1.dist-info/top_level.txt b/venv/Lib/site-packages/Flask_SQLAlchemy-2.5.1.dist-info/top_level.txt new file mode 100644 index 0000000..8a5538e --- /dev/null +++ b/venv/Lib/site-packages/Flask_SQLAlchemy-2.5.1.dist-info/top_level.txt @@ -0,0 +1 @@ +flask_sqlalchemy diff --git a/venv/Lib/site-packages/Jinja2-3.0.1.dist-info/INSTALLER b/venv/Lib/site-packages/Jinja2-3.0.1.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/venv/Lib/site-packages/Jinja2-3.0.1.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/Lib/site-packages/Jinja2-3.0.1.dist-info/LICENSE.rst b/venv/Lib/site-packages/Jinja2-3.0.1.dist-info/LICENSE.rst new file mode 100644 index 0000000..c37cae4 --- /dev/null +++ b/venv/Lib/site-packages/Jinja2-3.0.1.dist-info/LICENSE.rst @@ -0,0 +1,28 @@ +Copyright 2007 Pallets + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/venv/Lib/site-packages/Jinja2-3.0.1.dist-info/METADATA b/venv/Lib/site-packages/Jinja2-3.0.1.dist-info/METADATA new file mode 100644 index 0000000..afd84cb --- /dev/null +++ b/venv/Lib/site-packages/Jinja2-3.0.1.dist-info/METADATA @@ -0,0 +1,112 @@ +Metadata-Version: 2.1 +Name: Jinja2 +Version: 3.0.1 +Summary: A very fast and expressive template engine. +Home-page: https://palletsprojects.com/p/jinja/ +Author: Armin Ronacher +Author-email: armin.ronacher@active-4.com +Maintainer: Pallets +Maintainer-email: contact@palletsprojects.com +License: BSD-3-Clause +Project-URL: Donate, https://palletsprojects.com/donate +Project-URL: Documentation, https://jinja.palletsprojects.com/ +Project-URL: Changes, https://jinja.palletsprojects.com/changes/ +Project-URL: Source Code, https://github.com/pallets/jinja/ +Project-URL: Issue Tracker, https://github.com/pallets/jinja/issues/ +Project-URL: Twitter, https://twitter.com/PalletsTeam +Project-URL: Chat, https://discord.gg/pallets +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Web Environment +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content +Classifier: Topic :: Text Processing :: Markup :: HTML +Requires-Python: >=3.6 +Description-Content-Type: text/x-rst +Requires-Dist: MarkupSafe (>=2.0) +Provides-Extra: i18n +Requires-Dist: Babel (>=2.7) ; extra == 'i18n' + +Jinja +===== + +Jinja is a fast, expressive, extensible templating engine. Special +placeholders in the template allow writing code similar to Python +syntax. Then the template is passed data to render the final document. + +It includes: + +- Template inheritance and inclusion. +- Define and import macros within templates. +- HTML templates can use autoescaping to prevent XSS from untrusted + user input. +- A sandboxed environment can safely render untrusted templates. +- AsyncIO support for generating templates and calling async + functions. +- I18N support with Babel. +- Templates are compiled to optimized Python code just-in-time and + cached, or can be compiled ahead-of-time. +- Exceptions point to the correct line in templates to make debugging + easier. +- Extensible filters, tests, functions, and even syntax. + +Jinja's philosophy is that while application logic belongs in Python if +possible, it shouldn't make the template designer's job difficult by +restricting functionality too much. + + +Installing +---------- + +Install and update using `pip`_: + +.. code-block:: text + + $ pip install -U Jinja2 + +.. _pip: https://pip.pypa.io/en/stable/quickstart/ + + +In A Nutshell +------------- + +.. code-block:: jinja + + {% extends "base.html" %} + {% block title %}Members{% endblock %} + {% block content %} + + {% endblock %} + + +Donate +------ + +The Pallets organization develops and supports Jinja and other popular +packages. In order to grow the community of contributors and users, and +allow the maintainers to devote more time to the projects, `please +donate today`_. + +.. _please donate today: https://palletsprojects.com/donate + + +Links +----- + +- Documentation: https://jinja.palletsprojects.com/ +- Changes: https://jinja.palletsprojects.com/changes/ +- PyPI Releases: https://pypi.org/project/Jinja2/ +- Source Code: https://github.com/pallets/jinja/ +- Issue Tracker: https://github.com/pallets/jinja/issues/ +- Website: https://palletsprojects.com/p/jinja/ +- Twitter: https://twitter.com/PalletsTeam +- Chat: https://discord.gg/pallets + + diff --git a/venv/Lib/site-packages/Jinja2-3.0.1.dist-info/RECORD b/venv/Lib/site-packages/Jinja2-3.0.1.dist-info/RECORD new file mode 100644 index 0000000..48496ab --- /dev/null +++ b/venv/Lib/site-packages/Jinja2-3.0.1.dist-info/RECORD @@ -0,0 +1,58 @@ +Jinja2-3.0.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +Jinja2-3.0.1.dist-info/LICENSE.rst,sha256=O0nc7kEF6ze6wQ-vG-JgQI_oXSUrjp3y4JefweCUQ3s,1475 +Jinja2-3.0.1.dist-info/METADATA,sha256=k6STiOONbGESP2rEKmjhznuG10vm9sNCHCUQL9AQFM4,3508 +Jinja2-3.0.1.dist-info/RECORD,, +Jinja2-3.0.1.dist-info/WHEEL,sha256=OqRkF0eY5GHssMorFjlbTIq072vpHpF60fIQA6lS9xA,92 +Jinja2-3.0.1.dist-info/entry_points.txt,sha256=Qy_DkVo6Xj_zzOtmErrATe8lHZhOqdjpt3e4JJAGyi8,61 +Jinja2-3.0.1.dist-info/top_level.txt,sha256=PkeVWtLb3-CqjWi1fO29OCbj55EhX_chhKrCdrVe_zs,7 +jinja2/__init__.py,sha256=fd8jaCRsCATgC7ahuUTD8CyfQoc4aRfALEIny4mwfog,2205 +jinja2/__pycache__/__init__.cpython-39.pyc,, +jinja2/__pycache__/_identifier.cpython-39.pyc,, +jinja2/__pycache__/async_utils.cpython-39.pyc,, +jinja2/__pycache__/bccache.cpython-39.pyc,, +jinja2/__pycache__/compiler.cpython-39.pyc,, +jinja2/__pycache__/constants.cpython-39.pyc,, +jinja2/__pycache__/debug.cpython-39.pyc,, +jinja2/__pycache__/defaults.cpython-39.pyc,, +jinja2/__pycache__/environment.cpython-39.pyc,, +jinja2/__pycache__/exceptions.cpython-39.pyc,, +jinja2/__pycache__/ext.cpython-39.pyc,, +jinja2/__pycache__/filters.cpython-39.pyc,, +jinja2/__pycache__/idtracking.cpython-39.pyc,, +jinja2/__pycache__/lexer.cpython-39.pyc,, +jinja2/__pycache__/loaders.cpython-39.pyc,, +jinja2/__pycache__/meta.cpython-39.pyc,, +jinja2/__pycache__/nativetypes.cpython-39.pyc,, +jinja2/__pycache__/nodes.cpython-39.pyc,, +jinja2/__pycache__/optimizer.cpython-39.pyc,, +jinja2/__pycache__/parser.cpython-39.pyc,, +jinja2/__pycache__/runtime.cpython-39.pyc,, +jinja2/__pycache__/sandbox.cpython-39.pyc,, +jinja2/__pycache__/tests.cpython-39.pyc,, +jinja2/__pycache__/utils.cpython-39.pyc,, +jinja2/__pycache__/visitor.cpython-39.pyc,, +jinja2/_identifier.py,sha256=EdgGJKi7O1yvr4yFlvqPNEqV6M1qHyQr8Gt8GmVTKVM,1775 +jinja2/async_utils.py,sha256=bY2nCUfBA_4FSnNUsIsJgljBq3hACr6fzLi7LiyMTn8,1751 +jinja2/bccache.py,sha256=smAvSDgDSvXdvJzCN_9s0XfkVpQEu8be-QwgeMlrwiM,12677 +jinja2/compiler.py,sha256=qq0Fo9EpDAEwHPLAs3sAP7dindUvDrFrbx4AcB8xV5M,72046 +jinja2/constants.py,sha256=GMoFydBF_kdpaRKPoM5cl5MviquVRLVyZtfp5-16jg0,1433 +jinja2/debug.py,sha256=uBmrsiwjYH5l14R9STn5mydOOyriBYol5lDGvEqAb3A,9238 +jinja2/defaults.py,sha256=boBcSw78h-lp20YbaXSJsqkAI2uN_mD_TtCydpeq5wU,1267 +jinja2/environment.py,sha256=T6U4be9mY1CUXXin_EQFwpvpFqCiryweGqzXGRYIoSA,61573 +jinja2/exceptions.py,sha256=ioHeHrWwCWNaXX1inHmHVblvc4haO7AXsjCp3GfWvx0,5071 +jinja2/ext.py,sha256=44SjDjeYkkxQTpmC2BetOTxEFMgQ42p2dfSwXmPFcSo,32122 +jinja2/filters.py,sha256=LslRsJd0JVFBHtdfU_WraM1eQitotciwawiW-seR42U,52577 +jinja2/idtracking.py,sha256=KdFVohVNK-baOwt_INPMco19D7AfLDEN8i3_JoiYnGQ,10713 +jinja2/lexer.py,sha256=D5qOKB3KnRqK9gPAZFQvRguomYsQok5-14TKiWTN8Jw,29923 +jinja2/loaders.py,sha256=ePpWB0xDrILgLVqNFcxqqSbPizsI0T-JlkNEUFqq9fo,22350 +jinja2/meta.py,sha256=GNPEvifmSaU3CMxlbheBOZjeZ277HThOPUTf1RkppKQ,4396 +jinja2/nativetypes.py,sha256=62hvvsAxAj0YaxylOHoREYVogJ5JqOlJISgGY3OKd_o,3675 +jinja2/nodes.py,sha256=LHF97fu6GW4r2Z9UaOX92MOT1wZpdS9Nx4N-5Fp5ti8,34509 +jinja2/optimizer.py,sha256=tHkMwXxfZkbfA1KmLcqmBMSaz7RLIvvItrJcPoXTyD8,1650 +jinja2/parser.py,sha256=kHnU8v92GwMYkfr0MVakWv8UlSf_kJPx8LUsgQMof70,39767 +jinja2/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +jinja2/runtime.py,sha256=bSWdawLjReKpKHhF3-96OIuWYpUy1yxFJCN3jBYyoXc,35013 +jinja2/sandbox.py,sha256=-8zxR6TO9kUkciAVFsIKu8Oq-C7PTeYEdZ5TtA55-gw,14600 +jinja2/tests.py,sha256=Am5Z6Lmfr2XaH_npIfJJ8MdXtWsbLjMULZJulTAj30E,5905 +jinja2/utils.py,sha256=0wGkxDbxlW10y0ac4-kEiy1Bn0AsWXqz8uomK9Ugy1Q,26961 +jinja2/visitor.py,sha256=ZmeLuTj66ic35-uFH-1m0EKXiw4ObDDb_WuE6h5vPFg,3572 diff --git a/venv/Lib/site-packages/Jinja2-3.0.1.dist-info/WHEEL b/venv/Lib/site-packages/Jinja2-3.0.1.dist-info/WHEEL new file mode 100644 index 0000000..385faab --- /dev/null +++ b/venv/Lib/site-packages/Jinja2-3.0.1.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.36.2) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/venv/Lib/site-packages/Jinja2-3.0.1.dist-info/entry_points.txt b/venv/Lib/site-packages/Jinja2-3.0.1.dist-info/entry_points.txt new file mode 100644 index 0000000..3619483 --- /dev/null +++ b/venv/Lib/site-packages/Jinja2-3.0.1.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.0.1.dist-info/top_level.txt b/venv/Lib/site-packages/Jinja2-3.0.1.dist-info/top_level.txt new file mode 100644 index 0000000..7f7afbf --- /dev/null +++ b/venv/Lib/site-packages/Jinja2-3.0.1.dist-info/top_level.txt @@ -0,0 +1 @@ +jinja2 diff --git a/venv/Lib/site-packages/MarkupSafe-2.0.1.dist-info/INSTALLER b/venv/Lib/site-packages/MarkupSafe-2.0.1.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/venv/Lib/site-packages/MarkupSafe-2.0.1.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/Lib/site-packages/MarkupSafe-2.0.1.dist-info/LICENSE.rst b/venv/Lib/site-packages/MarkupSafe-2.0.1.dist-info/LICENSE.rst new file mode 100644 index 0000000..9d227a0 --- /dev/null +++ b/venv/Lib/site-packages/MarkupSafe-2.0.1.dist-info/LICENSE.rst @@ -0,0 +1,28 @@ +Copyright 2010 Pallets + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/venv/Lib/site-packages/MarkupSafe-2.0.1.dist-info/METADATA b/venv/Lib/site-packages/MarkupSafe-2.0.1.dist-info/METADATA new file mode 100644 index 0000000..ef44e2b --- /dev/null +++ b/venv/Lib/site-packages/MarkupSafe-2.0.1.dist-info/METADATA @@ -0,0 +1,100 @@ +Metadata-Version: 2.1 +Name: MarkupSafe +Version: 2.0.1 +Summary: Safely add untrusted strings to HTML/XML markup. +Home-page: https://palletsprojects.com/p/markupsafe/ +Author: Armin Ronacher +Author-email: armin.ronacher@active-4.com +Maintainer: Pallets +Maintainer-email: contact@palletsprojects.com +License: BSD-3-Clause +Project-URL: Donate, https://palletsprojects.com/donate +Project-URL: Documentation, https://markupsafe.palletsprojects.com/ +Project-URL: Changes, https://markupsafe.palletsprojects.com/changes/ +Project-URL: Source Code, https://github.com/pallets/markupsafe/ +Project-URL: Issue Tracker, https://github.com/pallets/markupsafe/issues/ +Project-URL: Twitter, https://twitter.com/PalletsTeam +Project-URL: Chat, https://discord.gg/pallets +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Web Environment +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content +Classifier: Topic :: Text Processing :: Markup :: HTML +Requires-Python: >=3.6 +Description-Content-Type: text/x-rst + +MarkupSafe +========== + +MarkupSafe implements a text object that escapes characters so it is +safe to use in HTML and XML. Characters that have special meanings are +replaced so that they display as the actual characters. This mitigates +injection attacks, meaning untrusted user input can safely be displayed +on a page. + + +Installing +---------- + +Install and update using `pip`_: + +.. code-block:: text + + pip install -U MarkupSafe + +.. _pip: https://pip.pypa.io/en/stable/quickstart/ + + +Examples +-------- + +.. code-block:: pycon + + >>> from markupsafe import Markup, escape + + >>> # escape replaces special characters and wraps in Markup + >>> escape("") + Markup('<script>alert(document.cookie);</script>') + + >>> # wrap in Markup to mark text "safe" and prevent escaping + >>> Markup("Hello") + Markup('hello') + + >>> escape(Markup("Hello")) + Markup('hello') + + >>> # Markup is a str subclass + >>> # methods and operators escape their arguments + >>> template = Markup("Hello {name}") + >>> template.format(name='"World"') + Markup('Hello "World"') + + +Donate +------ + +The Pallets organization develops and supports MarkupSafe and other +popular packages. In order to grow the community of contributors and +users, and allow the maintainers to devote more time to the projects, +`please donate today`_. + +.. _please donate today: https://palletsprojects.com/donate + + +Links +----- + +- Documentation: https://markupsafe.palletsprojects.com/ +- Changes: https://markupsafe.palletsprojects.com/changes/ +- PyPI Releases: https://pypi.org/project/MarkupSafe/ +- Source Code: https://github.com/pallets/markupsafe/ +- Issue Tracker: https://github.com/pallets/markupsafe/issues/ +- Website: https://palletsprojects.com/p/markupsafe/ +- Twitter: https://twitter.com/PalletsTeam +- Chat: https://discord.gg/pallets + + diff --git a/venv/Lib/site-packages/MarkupSafe-2.0.1.dist-info/RECORD b/venv/Lib/site-packages/MarkupSafe-2.0.1.dist-info/RECORD new file mode 100644 index 0000000..c5acda2 --- /dev/null +++ b/venv/Lib/site-packages/MarkupSafe-2.0.1.dist-info/RECORD @@ -0,0 +1,13 @@ +MarkupSafe-2.0.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +MarkupSafe-2.0.1.dist-info/LICENSE.rst,sha256=RjHsDbX9kKVH4zaBcmTGeYIUM4FG-KyUtKV_lu6MnsQ,1503 +MarkupSafe-2.0.1.dist-info/METADATA,sha256=FmPpxBdaqCCjF-XKqoxeEzqAzhetQnrkkSsd3V3X-Jc,3211 +MarkupSafe-2.0.1.dist-info/RECORD,, +MarkupSafe-2.0.1.dist-info/WHEEL,sha256=jr7ubY0Lkz_yXH9FfFe9PTtLhGOsf62dZkNvTYrJINE,100 +MarkupSafe-2.0.1.dist-info/top_level.txt,sha256=qy0Plje5IJuvsCBjejJyhDCjEAdcDLK_2agVcex8Z6U,11 +markupsafe/__init__.py,sha256=s08KbuFRV3zh4Wh7xjsIphXgp1xf0EUB79wlPj-4scc,9211 +markupsafe/__pycache__/__init__.cpython-39.pyc,, +markupsafe/__pycache__/_native.cpython-39.pyc,, +markupsafe/_native.py,sha256=JMXegJtk1ZcnRKrgyCA-CEXmRnOpaIXLyDAM98GbshY,2061 +markupsafe/_speedups.cp39-win_amd64.pyd,sha256=hPTsANj9bt7hLYxWLuZcC7E3-EnJQ4GlvIf9Vx0p1p0,16384 +markupsafe/_speedups.pyi,sha256=f5QtwIOP0eLrxh2v5p6SmaYmlcHIGIfmz0DovaqL0OU,238 +markupsafe/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 diff --git a/venv/Lib/site-packages/MarkupSafe-2.0.1.dist-info/WHEEL b/venv/Lib/site-packages/MarkupSafe-2.0.1.dist-info/WHEEL new file mode 100644 index 0000000..d1267fc --- /dev/null +++ b/venv/Lib/site-packages/MarkupSafe-2.0.1.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.36.2) +Root-Is-Purelib: false +Tag: cp39-cp39-win_amd64 + diff --git a/venv/Lib/site-packages/MarkupSafe-2.0.1.dist-info/top_level.txt b/venv/Lib/site-packages/MarkupSafe-2.0.1.dist-info/top_level.txt new file mode 100644 index 0000000..75bf729 --- /dev/null +++ b/venv/Lib/site-packages/MarkupSafe-2.0.1.dist-info/top_level.txt @@ -0,0 +1 @@ +markupsafe diff --git a/venv/Lib/site-packages/SQLAlchemy-1.4.20.dist-info/INSTALLER b/venv/Lib/site-packages/SQLAlchemy-1.4.20.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/venv/Lib/site-packages/SQLAlchemy-1.4.20.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/Lib/site-packages/SQLAlchemy-1.4.20.dist-info/LICENSE b/venv/Lib/site-packages/SQLAlchemy-1.4.20.dist-info/LICENSE new file mode 100644 index 0000000..0d9fb6d --- /dev/null +++ b/venv/Lib/site-packages/SQLAlchemy-1.4.20.dist-info/LICENSE @@ -0,0 +1,19 @@ +Copyright 2005-2021 SQLAlchemy authors and contributors . + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/venv/Lib/site-packages/SQLAlchemy-1.4.20.dist-info/METADATA b/venv/Lib/site-packages/SQLAlchemy-1.4.20.dist-info/METADATA new file mode 100644 index 0000000..2b60d8b --- /dev/null +++ b/venv/Lib/site-packages/SQLAlchemy-1.4.20.dist-info/METADATA @@ -0,0 +1,234 @@ +Metadata-Version: 2.1 +Name: SQLAlchemy +Version: 1.4.20 +Summary: Database Abstraction Library +Home-page: http://www.sqlalchemy.org +Author: Mike Bayer +Author-email: mike_mp@zzzcomputing.com +License: MIT +Project-URL: Documentation, https://docs.sqlalchemy.org +Project-URL: Issue Tracker, https://github.com/sqlalchemy/sqlalchemy/ +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: MIT License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Topic :: Database :: Front-Ends +Requires-Python: !=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7 +Description-Content-Type: text/x-rst +Requires-Dist: importlib-metadata ; python_version < "3.8" +Requires-Dist: greenlet (!=0.4.17) ; python_version >= "3" +Provides-Extra: aiomysql +Requires-Dist: greenlet (!=0.4.17) ; (python_version >= "3") and extra == 'aiomysql' +Requires-Dist: aiomysql ; (python_version >= "3") and extra == 'aiomysql' +Provides-Extra: aiosqlite +Requires-Dist: greenlet (!=0.4.17) ; (python_version >= "3") and extra == 'aiosqlite' +Requires-Dist: aiosqlite ; (python_version >= "3") and extra == 'aiosqlite' +Provides-Extra: asyncio +Requires-Dist: greenlet (!=0.4.17) ; (python_version >= "3") and extra == 'asyncio' +Provides-Extra: mariadb_connector +Requires-Dist: mariadb (>=1.0.1) ; (python_version >= "3") and extra == 'mariadb_connector' +Provides-Extra: mssql +Requires-Dist: pyodbc ; extra == 'mssql' +Provides-Extra: mssql_pymssql +Requires-Dist: pymssql ; extra == 'mssql_pymssql' +Provides-Extra: mssql_pyodbc +Requires-Dist: pyodbc ; extra == 'mssql_pyodbc' +Provides-Extra: mypy +Requires-Dist: sqlalchemy2-stubs ; extra == 'mypy' +Requires-Dist: mypy (>=0.800) ; (python_version >= "3") and extra == 'mypy' +Provides-Extra: mysql +Requires-Dist: mysqlclient (<2,>=1.4.0) ; (python_version < "3") and extra == 'mysql' +Requires-Dist: mysqlclient (>=1.4.0) ; (python_version >= "3") and extra == 'mysql' +Provides-Extra: mysql_connector +Requires-Dist: mysqlconnector ; extra == 'mysql_connector' +Provides-Extra: oracle +Requires-Dist: cx-oracle (<8,>=7) ; (python_version < "3") and extra == 'oracle' +Requires-Dist: cx-oracle (>=7) ; (python_version >= "3") and extra == 'oracle' +Provides-Extra: postgresql +Requires-Dist: psycopg2 (>=2.7) ; extra == 'postgresql' +Provides-Extra: postgresql_asyncpg +Requires-Dist: greenlet (!=0.4.17) ; (python_version >= "3") and extra == 'postgresql_asyncpg' +Requires-Dist: asyncpg ; (python_version >= "3") and extra == 'postgresql_asyncpg' +Provides-Extra: postgresql_pg8000 +Requires-Dist: pg8000 (>=1.16.6) ; extra == 'postgresql_pg8000' +Provides-Extra: postgresql_psycopg2binary +Requires-Dist: psycopg2-binary ; extra == 'postgresql_psycopg2binary' +Provides-Extra: postgresql_psycopg2cffi +Requires-Dist: psycopg2cffi ; extra == 'postgresql_psycopg2cffi' +Provides-Extra: pymysql +Requires-Dist: pymysql (<1) ; (python_version < "3") and extra == 'pymysql' +Requires-Dist: pymysql ; (python_version >= "3") and extra == 'pymysql' +Provides-Extra: sqlcipher +Requires-Dist: sqlcipher3-binary ; (python_version >= "3") and extra == 'sqlcipher' + +SQLAlchemy +========== + +|PyPI| |Python| |Downloads| + +.. |PyPI| image:: https://img.shields.io/pypi/v/sqlalchemy + :target: https://pypi.org/project/sqlalchemy + :alt: PyPI + +.. |Python| image:: https://img.shields.io/pypi/pyversions/sqlalchemy + :target: https://pypi.org/project/sqlalchemy + :alt: PyPI - Python Version + +.. |Downloads| image:: https://img.shields.io/pypi/dm/sqlalchemy + :target: https://pypi.org/project/sqlalchemy + :alt: PyPI - Downloads + + +The Python SQL Toolkit and Object Relational Mapper + +Introduction +------------- + +SQLAlchemy is the Python SQL toolkit and Object Relational Mapper +that gives application developers the full power and +flexibility of SQL. SQLAlchemy provides a full suite +of well known enterprise-level persistence patterns, +designed for efficient and high-performing database +access, adapted into a simple and Pythonic domain +language. + +Major SQLAlchemy features include: + +* An industrial strength ORM, built + from the core on the identity map, unit of work, + and data mapper patterns. These patterns + allow transparent persistence of objects + using a declarative configuration system. + Domain models + can be constructed and manipulated naturally, + and changes are synchronized with the + current transaction automatically. +* A relationally-oriented query system, exposing + the full range of SQL's capabilities + explicitly, including joins, subqueries, + correlation, and most everything else, + in terms of the object model. + Writing queries with the ORM uses the same + techniques of relational composition you use + when writing SQL. While you can drop into + literal SQL at any time, it's virtually never + needed. +* A comprehensive and flexible system + of eager loading for related collections and objects. + Collections are cached within a session, + and can be loaded on individual access, all + at once using joins, or by query per collection + across the full result set. +* A Core SQL construction system and DBAPI + interaction layer. The SQLAlchemy Core is + separate from the ORM and is a full database + abstraction layer in its own right, and includes + an extensible Python-based SQL expression + language, schema metadata, connection pooling, + type coercion, and custom types. +* All primary and foreign key constraints are + assumed to be composite and natural. Surrogate + integer primary keys are of course still the + norm, but SQLAlchemy never assumes or hardcodes + to this model. +* Database introspection and generation. Database + schemas can be "reflected" in one step into + Python structures representing database metadata; + those same structures can then generate + CREATE statements right back out - all within + the Core, independent of the ORM. + +SQLAlchemy's philosophy: + +* SQL databases behave less and less like object + collections the more size and performance start to + matter; object collections behave less and less like + tables and rows the more abstraction starts to matter. + SQLAlchemy aims to accommodate both of these + principles. +* An ORM doesn't need to hide the "R". A relational + database provides rich, set-based functionality + that should be fully exposed. SQLAlchemy's + ORM provides an open-ended set of patterns + that allow a developer to construct a custom + mediation layer between a domain model and + a relational schema, turning the so-called + "object relational impedance" issue into + a distant memory. +* The developer, in all cases, makes all decisions + regarding the design, structure, and naming conventions + of both the object model as well as the relational + schema. SQLAlchemy only provides the means + to automate the execution of these decisions. +* With SQLAlchemy, there's no such thing as + "the ORM generated a bad query" - you + retain full control over the structure of + queries, including how joins are organized, + how subqueries and correlation is used, what + columns are requested. Everything SQLAlchemy + does is ultimately the result of a developer- + initiated decision. +* Don't use an ORM if the problem doesn't need one. + SQLAlchemy consists of a Core and separate ORM + component. The Core offers a full SQL expression + language that allows Pythonic construction + of SQL constructs that render directly to SQL + strings for a target database, returning + result sets that are essentially enhanced DBAPI + cursors. +* Transactions should be the norm. With SQLAlchemy's + ORM, nothing goes to permanent storage until + commit() is called. SQLAlchemy encourages applications + to create a consistent means of delineating + the start and end of a series of operations. +* Never render a literal value in a SQL statement. + Bound parameters are used to the greatest degree + possible, allowing query optimizers to cache + query plans effectively and making SQL injection + attacks a non-issue. + +Documentation +------------- + +Latest documentation is at: + +http://www.sqlalchemy.org/docs/ + +Installation / Requirements +--------------------------- + +Full documentation for installation is at +`Installation `_. + +Getting Help / Development / Bug reporting +------------------------------------------ + +Please refer to the `SQLAlchemy Community Guide `_. + +Code of Conduct +--------------- + +Above all, SQLAlchemy places great emphasis on polite, thoughtful, and +constructive communication between users and developers. +Please see our current Code of Conduct at +`Code of Conduct `_. + +License +------- + +SQLAlchemy is distributed under the `MIT license +`_. + + + diff --git a/venv/Lib/site-packages/SQLAlchemy-1.4.20.dist-info/RECORD b/venv/Lib/site-packages/SQLAlchemy-1.4.20.dist-info/RECORD new file mode 100644 index 0000000..e2b67a6 --- /dev/null +++ b/venv/Lib/site-packages/SQLAlchemy-1.4.20.dist-info/RECORD @@ -0,0 +1,479 @@ +SQLAlchemy-1.4.20.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +SQLAlchemy-1.4.20.dist-info/LICENSE,sha256=YBpAuebmf1_VblyrYHwFdqeATqzBxJ-T6h8-e4s2zW4,1119 +SQLAlchemy-1.4.20.dist-info/METADATA,sha256=Jw3VHRkzzDevEV7BfHjLoAigwu92NLmbpX1abbrS8w4,9386 +SQLAlchemy-1.4.20.dist-info/RECORD,, +SQLAlchemy-1.4.20.dist-info/WHEEL,sha256=jr7ubY0Lkz_yXH9FfFe9PTtLhGOsf62dZkNvTYrJINE,100 +SQLAlchemy-1.4.20.dist-info/top_level.txt,sha256=rp-ZgB7D8G11ivXON5VGPjupT1voYmWqkciDt5Uaw_Q,11 +sqlalchemy/__init__.py,sha256=ExIPxvWel4JwxLhYY4m_-B3qZdcD0IIJGfcSP8c11gw,4241 +sqlalchemy/__pycache__/__init__.cpython-39.pyc,, +sqlalchemy/__pycache__/events.cpython-39.pyc,, +sqlalchemy/__pycache__/exc.cpython-39.pyc,, +sqlalchemy/__pycache__/inspection.cpython-39.pyc,, +sqlalchemy/__pycache__/log.cpython-39.pyc,, +sqlalchemy/__pycache__/processors.cpython-39.pyc,, +sqlalchemy/__pycache__/schema.cpython-39.pyc,, +sqlalchemy/__pycache__/types.cpython-39.pyc,, +sqlalchemy/cimmutabledict.cp39-win_amd64.pyd,sha256=dFj0Cbv6yOjMa3hJzlY5LcEsJlrQC0p5Sonj8AZsYS4,14848 +sqlalchemy/connectors/__init__.py,sha256=TBW6z0L_8lga2UXWZBC2kXFkmBj1tyyHGj2zPiwVTnA,288 +sqlalchemy/connectors/__pycache__/__init__.cpython-39.pyc,, +sqlalchemy/connectors/__pycache__/mxodbc.cpython-39.pyc,, +sqlalchemy/connectors/__pycache__/pyodbc.cpython-39.pyc,, +sqlalchemy/connectors/mxodbc.py,sha256=YPVxoPpPZyCGgZuNG-PXh6vF_y4BT2IJTbhSoSUFgG0,5948 +sqlalchemy/connectors/pyodbc.py,sha256=-9snrm-iRg68Zf-3HBGDvJqFQlK17lku5zL03KJbB0I,7005 +sqlalchemy/cprocessors.cp39-win_amd64.pyd,sha256=qLenj6vwMu-MHzCk6kaLlSuQf1FQhVHZNmCHhLy5OcE,17408 +sqlalchemy/cresultproxy.cp39-win_amd64.pyd,sha256=1tj6189v39vpUK7vBUYkM80Tk72094dU3wzaOtEiL6M,20992 +sqlalchemy/databases/__init__.py,sha256=Wr0SaNwZ_U8v65tQ9b0kL8hcpsCMCQ9f8dnfLjg9z1M,1047 +sqlalchemy/databases/__pycache__/__init__.cpython-39.pyc,, +sqlalchemy/dialects/__init__.py,sha256=Q9SoXJ9mpShD_4lbIXI6wjJqJsmpmDgS-yndzS1aauU,2156 +sqlalchemy/dialects/__pycache__/__init__.cpython-39.pyc,, +sqlalchemy/dialects/firebird/__init__.py,sha256=99IsIXbvMTkG2KkeQDdsuxdM5eonmd8kN_7_Hwew1TA,1193 +sqlalchemy/dialects/firebird/__pycache__/__init__.cpython-39.pyc,, +sqlalchemy/dialects/firebird/__pycache__/base.cpython-39.pyc,, +sqlalchemy/dialects/firebird/__pycache__/fdb.cpython-39.pyc,, +sqlalchemy/dialects/firebird/__pycache__/kinterbasdb.cpython-39.pyc,, +sqlalchemy/dialects/firebird/base.py,sha256=SFPEpzpyUTKy4zwIZe1rnM4V8wjuwk0A7RV-XGMGCXc,32167 +sqlalchemy/dialects/firebird/fdb.py,sha256=qQf8oSpPCWYrXQCVSZlGMLGn2XiqNloP6opoSrMGkxY,4229 +sqlalchemy/dialects/firebird/kinterbasdb.py,sha256=fchGnET7sqXuiNcj-Bim_rmRjhaMPcXyrSkUSuBKVdw,6675 +sqlalchemy/dialects/mssql/__init__.py,sha256=8fhQ2dxcuRwcdYrdbI9nzVAel5nNJotnV1btviNhIag,1872 +sqlalchemy/dialects/mssql/__pycache__/__init__.cpython-39.pyc,, +sqlalchemy/dialects/mssql/__pycache__/base.cpython-39.pyc,, +sqlalchemy/dialects/mssql/__pycache__/information_schema.cpython-39.pyc,, +sqlalchemy/dialects/mssql/__pycache__/json.cpython-39.pyc,, +sqlalchemy/dialects/mssql/__pycache__/mxodbc.cpython-39.pyc,, +sqlalchemy/dialects/mssql/__pycache__/provision.cpython-39.pyc,, +sqlalchemy/dialects/mssql/__pycache__/pymssql.cpython-39.pyc,, +sqlalchemy/dialects/mssql/__pycache__/pyodbc.cpython-39.pyc,, +sqlalchemy/dialects/mssql/base.py,sha256=_xMHlPWaOgx7EyTZOklfxSQBQ2Q4ckxm5m9vFfrwnVo,110143 +sqlalchemy/dialects/mssql/information_schema.py,sha256=yjE4A6JS0MQnC7mdIFFoh8wIz4TXAfvzBcMSKJvRkYM,7815 +sqlalchemy/dialects/mssql/json.py,sha256=bAi9z-htrC6adJUiBRetVj3RwtJ2GNN5I_KGeHFL8_s,4683 +sqlalchemy/dialects/mssql/mxodbc.py,sha256=EwR2i5KeL0O_QOoC_w8bB8-Ibmmca07p_Pk-B9V6oXs,4956 +sqlalchemy/dialects/mssql/provision.py,sha256=oTGw0l5RNcXIWkA0s2erVgk2XKiNw26IRzZE84tN2uE,4371 +sqlalchemy/dialects/mssql/pymssql.py,sha256=KXNWqN_gcJNs4eOcGMUuK2JS0zo3QIZpLtK0Cc2lo10,4979 +sqlalchemy/dialects/mssql/pyodbc.py,sha256=dt8bzeusRcfPSrW14tfK1auaLHbIiKyIAL455R_n66c,21198 +sqlalchemy/dialects/mysql/__init__.py,sha256=zGUaA54PNMKz_foJOomYcDZ0nRcM0FOBTXeZC4DkjkQ,2261 +sqlalchemy/dialects/mysql/__pycache__/__init__.cpython-39.pyc,, +sqlalchemy/dialects/mysql/__pycache__/aiomysql.cpython-39.pyc,, +sqlalchemy/dialects/mysql/__pycache__/base.cpython-39.pyc,, +sqlalchemy/dialects/mysql/__pycache__/cymysql.cpython-39.pyc,, +sqlalchemy/dialects/mysql/__pycache__/dml.cpython-39.pyc,, +sqlalchemy/dialects/mysql/__pycache__/enumerated.cpython-39.pyc,, +sqlalchemy/dialects/mysql/__pycache__/expression.cpython-39.pyc,, +sqlalchemy/dialects/mysql/__pycache__/json.cpython-39.pyc,, +sqlalchemy/dialects/mysql/__pycache__/mariadb.cpython-39.pyc,, +sqlalchemy/dialects/mysql/__pycache__/mariadbconnector.cpython-39.pyc,, +sqlalchemy/dialects/mysql/__pycache__/mysqlconnector.cpython-39.pyc,, +sqlalchemy/dialects/mysql/__pycache__/mysqldb.cpython-39.pyc,, +sqlalchemy/dialects/mysql/__pycache__/oursql.cpython-39.pyc,, +sqlalchemy/dialects/mysql/__pycache__/provision.cpython-39.pyc,, +sqlalchemy/dialects/mysql/__pycache__/pymysql.cpython-39.pyc,, +sqlalchemy/dialects/mysql/__pycache__/pyodbc.cpython-39.pyc,, +sqlalchemy/dialects/mysql/__pycache__/reflection.cpython-39.pyc,, +sqlalchemy/dialects/mysql/__pycache__/types.cpython-39.pyc,, +sqlalchemy/dialects/mysql/aiomysql.py,sha256=IIaMMlE5Sa6tNHdBF2_w-quqjSckbSLJLO6yGkqWO4I,9458 +sqlalchemy/dialects/mysql/base.py,sha256=PgA-SPgvPsAvwovzNM0jL8MygoeJW1xAzdexHszrM14,121335 +sqlalchemy/dialects/mysql/cymysql.py,sha256=u_2t1j9HsNYjHN1W5mcer3VzXKWpy9Ht0U23fmVv4TA,2352 +sqlalchemy/dialects/mysql/dml.py,sha256=rRmYkeT-R8ZgfqwSG2uuCPwVMN60AMCJ3IMhVdQNjbw,6355 +sqlalchemy/dialects/mysql/enumerated.py,sha256=7Gpd__GD6cqATpJG_exsGzADlV7PmS7Krr4K1KaAYHM,9396 +sqlalchemy/dialects/mysql/expression.py,sha256=rinSVZR7GK6S6OpqNwoUIrbNuT671gObXv5OD_Y6qwk,3867 +sqlalchemy/dialects/mysql/json.py,sha256=iEKILiRmIpakQcqJt2OzfmVGFsgSyi7gBg63khPggu4,2396 +sqlalchemy/dialects/mysql/mariadb.py,sha256=jE1Hpmka_nKJjWsFHcCVRKWvjg7l1mNCjCzvVADMD-k,523 +sqlalchemy/dialects/mysql/mariadbconnector.py,sha256=PONgd15UBS5TUZL8OB6hLj0Za_RpRA4VN6vgZoMYnlY,7755 +sqlalchemy/dialects/mysql/mysqlconnector.py,sha256=DhI1eAhINj_iWsC61fOCMSvJJOW_D9PKQBjOJUZX7gM,7929 +sqlalchemy/dialects/mysql/mysqldb.py,sha256=UFaJzBGFO9ix5oo22IAMQLnVRsETILV8b_zNSAq8drI,10853 +sqlalchemy/dialects/mysql/oursql.py,sha256=lJexKOuqiuzYlbbc4qd6oyzn2jQ8QbocDcgixKGnxEU,8794 +sqlalchemy/dialects/mysql/provision.py,sha256=-gCG7MwEMtztSmNU107Q563t-nN-oqTVkCiay-HRRQI,2727 +sqlalchemy/dialects/mysql/pymysql.py,sha256=QtNLSJ_etMgSoo31A47gyt8lMRXbSOHYPaVyjtS8tjE,2867 +sqlalchemy/dialects/mysql/pyodbc.py,sha256=95nwWGTVR-Ym4-R_QrDRuxcH8-pwu21-ZWBVNvZeTcM,4637 +sqlalchemy/dialects/mysql/reflection.py,sha256=gCWmTOsvrRDGKAmXh4AGGnO03rClVuXPswASmzALgg0,19110 +sqlalchemy/dialects/mysql/types.py,sha256=zMqX3xlwcFf1N4AZhZUeguI3UAn8vEDLJiVBj1TbIYg,25361 +sqlalchemy/dialects/oracle/__init__.py,sha256=z_sm511AIB30Vuv1Es-hfkRMHe5-yeqNGpnzE_XkPPo,1286 +sqlalchemy/dialects/oracle/__pycache__/__init__.cpython-39.pyc,, +sqlalchemy/dialects/oracle/__pycache__/base.cpython-39.pyc,, +sqlalchemy/dialects/oracle/__pycache__/cx_oracle.cpython-39.pyc,, +sqlalchemy/dialects/oracle/__pycache__/provision.cpython-39.pyc,, +sqlalchemy/dialects/oracle/base.py,sha256=yMK4KDqaFhfntVEXtYXOU3RKhgp8yo8vTiIamog9YEU,89010 +sqlalchemy/dialects/oracle/cx_oracle.py,sha256=9CBbppRxvJEkptdy6iRBRpKRC8LDEyAvYPd7_q8Va5E,50460 +sqlalchemy/dialects/oracle/provision.py,sha256=HCi5hs5fv6rXv-lVQir6gsKrhes9NoAWlChzR3ueq74,5965 +sqlalchemy/dialects/postgresql/__init__.py,sha256=WraAOkI7m26v85Pf28M2ONh-3j4SduvqFdefDPpc3EM,2625 +sqlalchemy/dialects/postgresql/__pycache__/__init__.cpython-39.pyc,, +sqlalchemy/dialects/postgresql/__pycache__/array.cpython-39.pyc,, +sqlalchemy/dialects/postgresql/__pycache__/asyncpg.cpython-39.pyc,, +sqlalchemy/dialects/postgresql/__pycache__/base.cpython-39.pyc,, +sqlalchemy/dialects/postgresql/__pycache__/dml.cpython-39.pyc,, +sqlalchemy/dialects/postgresql/__pycache__/ext.cpython-39.pyc,, +sqlalchemy/dialects/postgresql/__pycache__/hstore.cpython-39.pyc,, +sqlalchemy/dialects/postgresql/__pycache__/json.cpython-39.pyc,, +sqlalchemy/dialects/postgresql/__pycache__/pg8000.cpython-39.pyc,, +sqlalchemy/dialects/postgresql/__pycache__/provision.cpython-39.pyc,, +sqlalchemy/dialects/postgresql/__pycache__/psycopg2.cpython-39.pyc,, +sqlalchemy/dialects/postgresql/__pycache__/psycopg2cffi.cpython-39.pyc,, +sqlalchemy/dialects/postgresql/__pycache__/pygresql.cpython-39.pyc,, +sqlalchemy/dialects/postgresql/__pycache__/pypostgresql.cpython-39.pyc,, +sqlalchemy/dialects/postgresql/__pycache__/ranges.cpython-39.pyc,, +sqlalchemy/dialects/postgresql/array.py,sha256=3WQ2yBDAhms40MhJ683FELKmN9TL8XLpXDj3-TIaASo,12615 +sqlalchemy/dialects/postgresql/asyncpg.py,sha256=SmsjkLFctg51nrJKv3zrPWFNXhp0wRf8vWRa06FvC8A,34474 +sqlalchemy/dialects/postgresql/base.py,sha256=m28uVDT3IYxGRKZjeEyilwOAo8o8l_isjF145-Q8zuU,155990 +sqlalchemy/dialects/postgresql/dml.py,sha256=Id1QgpOnhLNOpIiF0igYYovdOcPvpHG0KJsD1VvQH60,9828 +sqlalchemy/dialects/postgresql/ext.py,sha256=K5huWreKbPLRVuwF7gFRhSPG0CY5sdXF0LiFFmDpMbY,8656 +sqlalchemy/dialects/postgresql/hstore.py,sha256=obkZOC84OYVWUC8O4PyMlEGifJAA-AjsgINtrkOj0Ac,12837 +sqlalchemy/dialects/postgresql/json.py,sha256=szREjVPlVfpwAc5D6tHqNCBL2UHIfr6yk_CgZqZQ10A,10777 +sqlalchemy/dialects/postgresql/pg8000.py,sha256=-KFdYLjDPQapVZ-nth9-NxcDETC7m2wwg3NdXuHtV_g,17390 +sqlalchemy/dialects/postgresql/provision.py,sha256=EgdZRObrTzwZ7jaBA_oPkWx3Ly1Cathw4pOfWZm6Pn0,4543 +sqlalchemy/dialects/postgresql/psycopg2.py,sha256=hwJ1yjtBiekK2WHr5NHCF6XQWRuL_Wj65oUEBGBgQVs,39900 +sqlalchemy/dialects/postgresql/psycopg2cffi.py,sha256=DgxWc4LrxnS3ay2KYr4sYZ76wC2ff1HFc-61Nf9TkpY,1753 +sqlalchemy/dialects/postgresql/pygresql.py,sha256=i7H04i1vuc0I8f3nnUmNinsfGGCku_BKGu_6i1j083A,8861 +sqlalchemy/dialects/postgresql/pypostgresql.py,sha256=MIEJhCHUwj3JF1PC4dB8dAOuoRaoiF8W0yzAU_L-a-g,3817 +sqlalchemy/dialects/postgresql/ranges.py,sha256=b04r-iDteuTMhmVAc5Yf6cGraGTFggZTi0hx61_P3ng,4762 +sqlalchemy/dialects/sqlite/__init__.py,sha256=rPgXiMXoA6X1_aeqq4ABU6QDqhGfIHcBaUY89utTQQw,1255 +sqlalchemy/dialects/sqlite/__pycache__/__init__.cpython-39.pyc,, +sqlalchemy/dialects/sqlite/__pycache__/aiosqlite.cpython-39.pyc,, +sqlalchemy/dialects/sqlite/__pycache__/base.cpython-39.pyc,, +sqlalchemy/dialects/sqlite/__pycache__/dml.cpython-39.pyc,, +sqlalchemy/dialects/sqlite/__pycache__/json.cpython-39.pyc,, +sqlalchemy/dialects/sqlite/__pycache__/provision.cpython-39.pyc,, +sqlalchemy/dialects/sqlite/__pycache__/pysqlcipher.cpython-39.pyc,, +sqlalchemy/dialects/sqlite/__pycache__/pysqlite.cpython-39.pyc,, +sqlalchemy/dialects/sqlite/aiosqlite.py,sha256=pUeydy7OicEYmuYE59LIvcyaN2pPZHEQgq8JCeMtCtw,10179 +sqlalchemy/dialects/sqlite/base.py,sha256=J6JqD1JGTYfDWQZZOOKIKdBhb8MQ1naSX44A8RbzTp0,90650 +sqlalchemy/dialects/sqlite/dml.py,sha256=9CKXKKpA2e1Xqo6U1gvvHedyfAZvpkRBpZiRQJVF9ZU,7037 +sqlalchemy/dialects/sqlite/json.py,sha256=bz_1axFG5YI9kLszE-oiCN3-z95zIPPcLgVwug_-AT4,2602 +sqlalchemy/dialects/sqlite/provision.py,sha256=3F5ZX2dYGMFAGra99UVavCihth1_XazJXX9XAet8gbw,4818 +sqlalchemy/dialects/sqlite/pysqlcipher.py,sha256=stRIWDxLkq0QATChLf1p3v1P0xlHfGm_sYIrye83vxM,5768 +sqlalchemy/dialects/sqlite/pysqlite.py,sha256=ZL7OxYuaX9nf_3QGutZL29x1RRgqCdPo6d_wR4ZKu_w,23896 +sqlalchemy/dialects/sybase/__init__.py,sha256=L1T7DkOKeeT4rs_-HMQtT8a6L_yeHNJqtTi8MGd_pbw,1430 +sqlalchemy/dialects/sybase/__pycache__/__init__.cpython-39.pyc,, +sqlalchemy/dialects/sybase/__pycache__/base.cpython-39.pyc,, +sqlalchemy/dialects/sybase/__pycache__/mxodbc.cpython-39.pyc,, +sqlalchemy/dialects/sybase/__pycache__/pyodbc.cpython-39.pyc,, +sqlalchemy/dialects/sybase/__pycache__/pysybase.cpython-39.pyc,, +sqlalchemy/dialects/sybase/base.py,sha256=4aLf2TrGblF0ojJR54tHifgNkfTD0adC9j_wR_GYU1Y,33519 +sqlalchemy/dialects/sybase/mxodbc.py,sha256=5QXZjzxHpne8MKAAwPR32x6YXU5IQPDPKCLaSW23wp8,971 +sqlalchemy/dialects/sybase/pyodbc.py,sha256=BNaSGyeAroOrHLc7rLIZ9kkeZxULVDxqHZtzNIjbG6A,2321 +sqlalchemy/dialects/sybase/pysybase.py,sha256=AEIbV2egEfWKrXYMkTj74HlB7owPSn7hs8U7M_b_EpE,3474 +sqlalchemy/engine/__init__.py,sha256=x8TozgrDsPtS2j-9ByAv0ACrcpUMiFJMpxc0lsGQmo8,2092 +sqlalchemy/engine/__pycache__/__init__.cpython-39.pyc,, +sqlalchemy/engine/__pycache__/base.cpython-39.pyc,, +sqlalchemy/engine/__pycache__/characteristics.cpython-39.pyc,, +sqlalchemy/engine/__pycache__/create.cpython-39.pyc,, +sqlalchemy/engine/__pycache__/cursor.cpython-39.pyc,, +sqlalchemy/engine/__pycache__/default.cpython-39.pyc,, +sqlalchemy/engine/__pycache__/events.cpython-39.pyc,, +sqlalchemy/engine/__pycache__/interfaces.cpython-39.pyc,, +sqlalchemy/engine/__pycache__/mock.cpython-39.pyc,, +sqlalchemy/engine/__pycache__/reflection.cpython-39.pyc,, +sqlalchemy/engine/__pycache__/result.cpython-39.pyc,, +sqlalchemy/engine/__pycache__/row.cpython-39.pyc,, +sqlalchemy/engine/__pycache__/strategies.cpython-39.pyc,, +sqlalchemy/engine/__pycache__/url.cpython-39.pyc,, +sqlalchemy/engine/__pycache__/util.cpython-39.pyc,, +sqlalchemy/engine/base.py,sha256=cQVJm7LS3VvsdznS_EmthCan6qCyjENX0LFsGFJ5Gls,121303 +sqlalchemy/engine/characteristics.py,sha256=DrhLcmpnkMpmgo_kgQpcujoD4nW81vTt-GkuWqW9QaY,1873 +sqlalchemy/engine/create.py,sha256=lMQRneFu5OcwkQNxmOgonrgwU-9E1oGh0h7ZlmKk_IQ,31143 +sqlalchemy/engine/cursor.py,sha256=B3rcfKd1WHfOQtxkIUCVWZaKFT6AFxKqfNLZHbPYBuU,69686 +sqlalchemy/engine/default.py,sha256=WhPeP06ZwFzR6TECsP7AKDhDt2ABOJN1eH017p1m2eA,66314 +sqlalchemy/engine/events.py,sha256=Hv0GAy-w8m9xdq8E6vZz0fSuLgbZ_9uXMVO3N-HavN4,33483 +sqlalchemy/engine/interfaces.py,sha256=h0i-LWMoBq5f2PFDG1qYQTt87TSAzl4BvIypVgq_MLc,60760 +sqlalchemy/engine/mock.py,sha256=eXuMRQawEZFJWpdTELQcljPq5guokWSW9koqySGL-90,3743 +sqlalchemy/engine/reflection.py,sha256=dRuXqeVrQXa-24YA45ZuiHDPprdr0d-kGNnt4WZp_v8,39755 +sqlalchemy/engine/result.py,sha256=jJ3QGGPeINyfbEnYkaRSvm7e5UArvWaw_u3dYlxVKto,54923 +sqlalchemy/engine/row.py,sha256=J0ku18YCc8_DAu-B8oFTTdeLj-5AbylRSrN7eY16AGo,18799 +sqlalchemy/engine/strategies.py,sha256=Qqx7Nw6TPJe6KzM58oPHavzbSqaVST4DQGrhTO42R14,430 +sqlalchemy/engine/url.py,sha256=kpLkdISaFyNChYyUViXS05hg5m4SYx6vrqj3LEkeClc,26200 +sqlalchemy/engine/util.py,sha256=R2fGBk_tpc5LR05Q-mIauF5mGgPDm5IJ9TBl5Brhitg,7875 +sqlalchemy/event/__init__.py,sha256=UEArBlnnF0If5n_aohWAmYx3Jjoym-8bBgyZ0Fp7hMI,533 +sqlalchemy/event/__pycache__/__init__.cpython-39.pyc,, +sqlalchemy/event/__pycache__/api.cpython-39.pyc,, +sqlalchemy/event/__pycache__/attr.cpython-39.pyc,, +sqlalchemy/event/__pycache__/base.cpython-39.pyc,, +sqlalchemy/event/__pycache__/legacy.cpython-39.pyc,, +sqlalchemy/event/__pycache__/registry.cpython-39.pyc,, +sqlalchemy/event/api.py,sha256=zmVEc1OZ2IHUHxRk7QBIQj12NKkXA1_tedtPq1xWpu4,6994 +sqlalchemy/event/attr.py,sha256=W_Yy6A5oChf1S1cafSm_lKflMjfxYtZbjEQNTDHU03Y,15092 +sqlalchemy/event/base.py,sha256=uL2Vvau-A6b5W9IREO2_jfSz78aP_QH8cjglIbaTBNY,11280 +sqlalchemy/event/legacy.py,sha256=spFIf45jAL4hi4ah9_ttIbj0hzpgq5bJlv4lpoA7eRs,6454 +sqlalchemy/event/registry.py,sha256=p1ixAawLN7Lssif73SjTsPKHNU6fhTJOwrVtCrSfifE,8567 +sqlalchemy/events.py,sha256=08BdPP_ASWqos10aJ_grM8qKKsjKFIupUIwqt3kaH-c,480 +sqlalchemy/exc.py,sha256=EzLFC6lCWPchAYYfGFLWCXkNhqxtH7n_FovNx9qgmG8,20959 +sqlalchemy/ext/__init__.py,sha256=FFnko4I2iMY2OaeAIJdsu200D68cAfubuqC_2Eeg8BM,332 +sqlalchemy/ext/__pycache__/__init__.cpython-39.pyc,, +sqlalchemy/ext/__pycache__/associationproxy.cpython-39.pyc,, +sqlalchemy/ext/__pycache__/automap.cpython-39.pyc,, +sqlalchemy/ext/__pycache__/baked.cpython-39.pyc,, +sqlalchemy/ext/__pycache__/compiler.cpython-39.pyc,, +sqlalchemy/ext/__pycache__/horizontal_shard.cpython-39.pyc,, +sqlalchemy/ext/__pycache__/hybrid.cpython-39.pyc,, +sqlalchemy/ext/__pycache__/indexable.cpython-39.pyc,, +sqlalchemy/ext/__pycache__/instrumentation.cpython-39.pyc,, +sqlalchemy/ext/__pycache__/mutable.cpython-39.pyc,, +sqlalchemy/ext/__pycache__/orderinglist.cpython-39.pyc,, +sqlalchemy/ext/__pycache__/serializer.cpython-39.pyc,, +sqlalchemy/ext/associationproxy.py,sha256=QoEz-UR5j43ZnqNl13gZp-cMpdBS7ldv1Wtay-EkTNo,51568 +sqlalchemy/ext/asyncio/__init__.py,sha256=D_PoNbVNTdnK6h7keS-ppBSFcjXFs9WhmyuOyvx9EBU,798 +sqlalchemy/ext/asyncio/__pycache__/__init__.cpython-39.pyc,, +sqlalchemy/ext/asyncio/__pycache__/base.cpython-39.pyc,, +sqlalchemy/ext/asyncio/__pycache__/engine.cpython-39.pyc,, +sqlalchemy/ext/asyncio/__pycache__/events.cpython-39.pyc,, +sqlalchemy/ext/asyncio/__pycache__/exc.cpython-39.pyc,, +sqlalchemy/ext/asyncio/__pycache__/result.cpython-39.pyc,, +sqlalchemy/ext/asyncio/__pycache__/scoping.cpython-39.pyc,, +sqlalchemy/ext/asyncio/__pycache__/session.cpython-39.pyc,, +sqlalchemy/ext/asyncio/base.py,sha256=DQjKnvQPq6d_WwCbks3NSRVt9mcU2u8xojaV3E60Zno,2291 +sqlalchemy/ext/asyncio/engine.py,sha256=g1rq30UYH54G9LnXX8xxtmNbtaHgzsGQM-EF8gDCNgY,23751 +sqlalchemy/ext/asyncio/events.py,sha256=mXQgkDLsnS6qkQeB_GV-i6VhtmNAve85B-jIGs7cRyg,1270 +sqlalchemy/ext/asyncio/exc.py,sha256=0gqT7n-U-p7rLbkzAQKfzQ6_jBZxHweK-2mXFWVYvec,659 +sqlalchemy/ext/asyncio/result.py,sha256=aYLMEM5TNsmyAWG4659wyperZrDttKUlToxqo_eOHkE,20531 +sqlalchemy/ext/asyncio/scoping.py,sha256=WaTlSFO_J7RNPF7pKVgVZs84D6MmDMZThrFVlyk9P0g,2947 +sqlalchemy/ext/asyncio/session.py,sha256=6G4_B-gnbIyMlSaoBNQN_KpUasIwVVGa0lATEpwt0fY,15392 +sqlalchemy/ext/automap.py,sha256=s0hAWEawAv94ETxRhXqvAwrKUc3bGBX1PEFEcVv2wbU,46422 +sqlalchemy/ext/baked.py,sha256=Yc9t2UY5c8Fu4v_yDWInPFZBeLi657uwXVpGr8g8dwo,20616 +sqlalchemy/ext/compiler.py,sha256=Xfq_ONWDQ_mhHNSHH_AY5eIhDF4OEQwqc5018l9fQxU,18496 +sqlalchemy/ext/declarative/__init__.py,sha256=4lGjX9yq22BJ3K7ZGvFF_46oWoYStN9pkIKypXNavNQ,1905 +sqlalchemy/ext/declarative/__pycache__/__init__.cpython-39.pyc,, +sqlalchemy/ext/declarative/__pycache__/extensions.cpython-39.pyc,, +sqlalchemy/ext/declarative/extensions.py,sha256=hwtcP7O8i9le8Lhg_nVH1lDnRxE0psG-oRoq2Kov-kU,16866 +sqlalchemy/ext/horizontal_shard.py,sha256=qREdIj09TeY5axbuWsFfrfCZuk9zgjDzcqREd-DxoAo,8990 +sqlalchemy/ext/hybrid.py,sha256=MKlGMqD2-7uyYZx05Zq3znS0pCpToyTN-ScQRNE4uek,43102 +sqlalchemy/ext/indexable.py,sha256=7GhAsHMFk_q4YiCAbMU0J9_jbQeJa73E-iVzDNB9DNU,11606 +sqlalchemy/ext/instrumentation.py,sha256=kx2zn8mN30F-CbWqiBxb7HgnMSJChLcvQhJ8Tb8VIXo,14802 +sqlalchemy/ext/mutable.py,sha256=cuiyl1bgu6DSCMMCZcQIHObul1BLUh-jSTXGtZUgwm4,32947 +sqlalchemy/ext/mypy/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +sqlalchemy/ext/mypy/__pycache__/__init__.cpython-39.pyc,, +sqlalchemy/ext/mypy/__pycache__/apply.cpython-39.pyc,, +sqlalchemy/ext/mypy/__pycache__/decl_class.cpython-39.pyc,, +sqlalchemy/ext/mypy/__pycache__/infer.cpython-39.pyc,, +sqlalchemy/ext/mypy/__pycache__/names.cpython-39.pyc,, +sqlalchemy/ext/mypy/__pycache__/plugin.cpython-39.pyc,, +sqlalchemy/ext/mypy/__pycache__/util.cpython-39.pyc,, +sqlalchemy/ext/mypy/apply.py,sha256=AWjlXxc1ZSEKs-KpJfPZ60gGDOzdinls2cc6keW3pxI,9296 +sqlalchemy/ext/mypy/decl_class.py,sha256=LUzP227RsiT_Em2wQas-mLdrRxg0Mzqn7bXQcw8hiMY,17040 +sqlalchemy/ext/mypy/infer.py,sha256=r9Ceg7FiaUL1HGQCvZRK7RHmGrkmCvFVba_TWLLPwGo,17690 +sqlalchemy/ext/mypy/names.py,sha256=Z8tpYE8i6xGBkuRXpme7Yd58wryMILd_TAAWHKy0OFQ,7902 +sqlalchemy/ext/mypy/plugin.py,sha256=bjxzxej4w80N315Md3Y_gJECXqaYhN3RwN1jwfNLoNs,9955 +sqlalchemy/ext/mypy/util.py,sha256=U5IzfJart5sqq2eM9zhFQQHxoA4w9XJXQmWSImVX_ak,6236 +sqlalchemy/ext/orderinglist.py,sha256=sa2PZ06PH8Cs6Zwcgzog3vUg6WjUbGxdGkGkoMCpJxc,14262 +sqlalchemy/ext/serializer.py,sha256=rae10NTl3_fm9I_SrXflEioL8GNQnShlH7cUxfSUnds,6132 +sqlalchemy/future/__init__.py,sha256=uXl3ElTaXdjRoOBshDEitc07u6IGLfCzLONWerWRF88,542 +sqlalchemy/future/__pycache__/__init__.cpython-39.pyc,, +sqlalchemy/future/__pycache__/engine.cpython-39.pyc,, +sqlalchemy/future/engine.py,sha256=2Qc97YYmaKSp0jyOF4X6S6efF4btcsuj3c0Wpu1PlNU,16993 +sqlalchemy/future/orm/__init__.py,sha256=_g7j2aIWEydf4JHJuJDetdAVACyheROjqXmBkKb2OaQ,298 +sqlalchemy/future/orm/__pycache__/__init__.cpython-39.pyc,, +sqlalchemy/inspection.py,sha256=vfF9qC0gJSRzn4JLu9CjNENzqCtDdzAIthoLxl8rW50,3143 +sqlalchemy/log.py,sha256=MWkG0aStnbomwsE9wqkuQqwQiRyTykirq9_oY2032tg,6994 +sqlalchemy/orm/__init__.py,sha256=-IMaSfF-yrtmMGrlFYkXjcA8iAnEAR6UkROHHAXYSpU,10769 +sqlalchemy/orm/__pycache__/__init__.cpython-39.pyc,, +sqlalchemy/orm/__pycache__/attributes.cpython-39.pyc,, +sqlalchemy/orm/__pycache__/base.cpython-39.pyc,, +sqlalchemy/orm/__pycache__/clsregistry.cpython-39.pyc,, +sqlalchemy/orm/__pycache__/collections.cpython-39.pyc,, +sqlalchemy/orm/__pycache__/context.cpython-39.pyc,, +sqlalchemy/orm/__pycache__/decl_api.cpython-39.pyc,, +sqlalchemy/orm/__pycache__/decl_base.cpython-39.pyc,, +sqlalchemy/orm/__pycache__/dependency.cpython-39.pyc,, +sqlalchemy/orm/__pycache__/descriptor_props.cpython-39.pyc,, +sqlalchemy/orm/__pycache__/dynamic.cpython-39.pyc,, +sqlalchemy/orm/__pycache__/evaluator.cpython-39.pyc,, +sqlalchemy/orm/__pycache__/events.cpython-39.pyc,, +sqlalchemy/orm/__pycache__/exc.cpython-39.pyc,, +sqlalchemy/orm/__pycache__/identity.cpython-39.pyc,, +sqlalchemy/orm/__pycache__/instrumentation.cpython-39.pyc,, +sqlalchemy/orm/__pycache__/interfaces.cpython-39.pyc,, +sqlalchemy/orm/__pycache__/loading.cpython-39.pyc,, +sqlalchemy/orm/__pycache__/mapper.cpython-39.pyc,, +sqlalchemy/orm/__pycache__/path_registry.cpython-39.pyc,, +sqlalchemy/orm/__pycache__/persistence.cpython-39.pyc,, +sqlalchemy/orm/__pycache__/properties.cpython-39.pyc,, +sqlalchemy/orm/__pycache__/query.cpython-39.pyc,, +sqlalchemy/orm/__pycache__/relationships.cpython-39.pyc,, +sqlalchemy/orm/__pycache__/scoping.cpython-39.pyc,, +sqlalchemy/orm/__pycache__/session.cpython-39.pyc,, +sqlalchemy/orm/__pycache__/state.cpython-39.pyc,, +sqlalchemy/orm/__pycache__/strategies.cpython-39.pyc,, +sqlalchemy/orm/__pycache__/strategy_options.cpython-39.pyc,, +sqlalchemy/orm/__pycache__/sync.cpython-39.pyc,, +sqlalchemy/orm/__pycache__/unitofwork.cpython-39.pyc,, +sqlalchemy/orm/__pycache__/util.cpython-39.pyc,, +sqlalchemy/orm/attributes.py,sha256=wGWhtVsCdcev3nl_ucxsAIS7w_iV5_pAccf-iBxHmCM,77884 +sqlalchemy/orm/base.py,sha256=JR1bHf9tmjd5Ucb_b4-Yx20p210hkeklUjYeA0ea_XY,15473 +sqlalchemy/orm/clsregistry.py,sha256=T_cR_8J6_k8zBAtISYPnjWpKbMKQAbzREdPhBDzG6Z8,13731 +sqlalchemy/orm/collections.py,sha256=WxqkreJpyfguYOZtbIqKgx01pdL3ihmNzNNprnUobl8,56416 +sqlalchemy/orm/context.py,sha256=ujp5554a0aM-NI6tw99tw03M6DKbMPNRSO_YH74UxwM,106564 +sqlalchemy/orm/decl_api.py,sha256=Lbj6w87SXY2q8dzoWFYgZbdStVZ0Ordnw9O6yXH6nuk,35949 +sqlalchemy/orm/decl_base.py,sha256=BR85kxbPUf0CDzbiF-_tgfqMIafC0Esl-4B8OTzQcMk,43849 +sqlalchemy/orm/dependency.py,sha256=HPQE5POiLq8yUPuFtQ-IrMS9jPw67Z1-05c7xuZbm4Y,48268 +sqlalchemy/orm/descriptor_props.py,sha256=S0vw_wnNFB0p3lBM-0RRGcxvyzgGoDYXK4kb4xn_FUo,26701 +sqlalchemy/orm/dynamic.py,sha256=INcHvTXxXb17gGW_3HbSuQ53VVUPRANexuUn79g-X0k,16306 +sqlalchemy/orm/evaluator.py,sha256=ymKsLcqOwf7OZ98PZINfgp2WUlB-Hv1pAXuoAbWITHU,7092 +sqlalchemy/orm/events.py,sha256=MCsdnTPUnhW4ORFAZDrBk4pvGZw6Lr1l8h2jJOLVKtY,113098 +sqlalchemy/orm/exc.py,sha256=XQNh69fkGLyeZ3apPiTTRwSO6y_ULbVOBLUACOfWNFc,6735 +sqlalchemy/orm/identity.py,sha256=9D0FTwPOsUQ21mTlOkzdMSoIKYPcyn1gvbxee6MqHyI,7040 +sqlalchemy/orm/instrumentation.py,sha256=tE5A13NbKUfNvpOEaJuHFbh4BrrQ9JmqA14SKVXBNdA,20998 +sqlalchemy/orm/interfaces.py,sha256=Fn0Q-aojEwBlFiad5moTR8OkAgYsHAVO1bIngxqRoGE,29347 +sqlalchemy/orm/loading.py,sha256=WNkS0444FA49IzPEvoejbW9DZ6W-pmSz07yzHWfKFCk,50987 +sqlalchemy/orm/mapper.py,sha256=Hxn0jmpxhgk_yOlHcUzUVP32nqKHIolcFYpaDh5JW34,139805 +sqlalchemy/orm/path_registry.py,sha256=Qe86D6vzluH4rkfnmb3ZpHnTCt7eBri5PtyuhlcCbUQ,15588 +sqlalchemy/orm/persistence.py,sha256=rhqAtyFKgJMin6hGFgTAdLgK2kpMowAfBiytkERurkg,81401 +sqlalchemy/orm/properties.py,sha256=H0cSlyMOIDvaizxmCoWeKdQWiqImyQb5VLd9I4Sy3pc,15319 +sqlalchemy/orm/query.py,sha256=gIAWefHFhHJqfuofCGb6RaJMVm-a-S7Sy1XRPVUk6q8,125217 +sqlalchemy/orm/relationships.py,sha256=PHsSUNrlqjrz95pu36DhC10ZHx884nRiDlLXkJR6dls,146477 +sqlalchemy/orm/scoping.py,sha256=GPFT5NinEnV49r0gidhRxdzc3LcFWx52VNFZqz-SGQs,6880 +sqlalchemy/orm/session.py,sha256=vuX_VvIBp5Enx-a1h7lSSlSOlPiYd0m1ILy-lZjyGJ8,159850 +sqlalchemy/orm/state.py,sha256=9jv6AlV8RwoEBkN6Mti3Z84C0hHSTBfHX4Hh8VZEyl8,34431 +sqlalchemy/orm/strategies.py,sha256=Cr0PC-PG8X3_L2K3Ieu_EZMOofeZ8OePf-bA1J_1vxc,109896 +sqlalchemy/orm/strategy_options.py,sha256=09iyh4ILS7yBxYK8IZWsFuYYWpnYdwALPyqyZY7se8g,63532 +sqlalchemy/orm/sync.py,sha256=3WNF_rv2bvq-8w-Umo6WPPMpZR81CCC6lR0LVsdUyyQ,5990 +sqlalchemy/orm/unitofwork.py,sha256=XZdTDFw3CHBZstRU1jWDtZ-op0h-Sh_UZUx7h8moQZc,27873 +sqlalchemy/orm/util.py,sha256=DZAiAIKYeGtoa8IWS75W7Te4p2v2WvNy9YzFzzEdLFU,70601 +sqlalchemy/pool/__init__.py,sha256=vPypHYNtKMNIc2Yrgw6qyVWHvvisQfIC0Gn3AhKi654,1658 +sqlalchemy/pool/__pycache__/__init__.cpython-39.pyc,, +sqlalchemy/pool/__pycache__/base.cpython-39.pyc,, +sqlalchemy/pool/__pycache__/dbapi_proxy.cpython-39.pyc,, +sqlalchemy/pool/__pycache__/events.cpython-39.pyc,, +sqlalchemy/pool/__pycache__/impl.cpython-39.pyc,, +sqlalchemy/pool/base.py,sha256=BoVrinbry7_706Y6z5HuCUeTevH3tZF6d91JGQRaabY,36657 +sqlalchemy/pool/dbapi_proxy.py,sha256=EECXJ-pyG1436lAs5H02gN3ByZIRd54yeVG6EInHmQU,4375 +sqlalchemy/pool/events.py,sha256=e9pXO_HEZzsg-8V6jdtV-u6BjW4GCvdvSIJYeNYh8GU,8942 +sqlalchemy/pool/impl.py,sha256=ebFDPqQEggH7W43Ld13wd2UV3geMkJEDD66EruvmF1k,16284 +sqlalchemy/processors.py,sha256=S-h949YV8k_f3HMoV2ChfwZE5L-e1sDPYTko5M1Gj_o,5920 +sqlalchemy/schema.py,sha256=ntWzaOocNzjjFTTGcH2W-OHElZYiAxLFcWUhxVQJ-oE,2471 +sqlalchemy/sql/__init__.py,sha256=Ie0LsqfpzMNZN621dtp3LFeF0ySw-FJmMoLrqrL81Z8,4810 +sqlalchemy/sql/__pycache__/__init__.cpython-39.pyc,, +sqlalchemy/sql/__pycache__/annotation.cpython-39.pyc,, +sqlalchemy/sql/__pycache__/base.cpython-39.pyc,, +sqlalchemy/sql/__pycache__/coercions.cpython-39.pyc,, +sqlalchemy/sql/__pycache__/compiler.cpython-39.pyc,, +sqlalchemy/sql/__pycache__/crud.cpython-39.pyc,, +sqlalchemy/sql/__pycache__/ddl.cpython-39.pyc,, +sqlalchemy/sql/__pycache__/default_comparator.cpython-39.pyc,, +sqlalchemy/sql/__pycache__/dml.cpython-39.pyc,, +sqlalchemy/sql/__pycache__/elements.cpython-39.pyc,, +sqlalchemy/sql/__pycache__/events.cpython-39.pyc,, +sqlalchemy/sql/__pycache__/expression.cpython-39.pyc,, +sqlalchemy/sql/__pycache__/functions.cpython-39.pyc,, +sqlalchemy/sql/__pycache__/lambdas.cpython-39.pyc,, +sqlalchemy/sql/__pycache__/naming.cpython-39.pyc,, +sqlalchemy/sql/__pycache__/operators.cpython-39.pyc,, +sqlalchemy/sql/__pycache__/roles.cpython-39.pyc,, +sqlalchemy/sql/__pycache__/schema.cpython-39.pyc,, +sqlalchemy/sql/__pycache__/selectable.cpython-39.pyc,, +sqlalchemy/sql/__pycache__/sqltypes.cpython-39.pyc,, +sqlalchemy/sql/__pycache__/traversals.cpython-39.pyc,, +sqlalchemy/sql/__pycache__/type_api.cpython-39.pyc,, +sqlalchemy/sql/__pycache__/util.cpython-39.pyc,, +sqlalchemy/sql/__pycache__/visitors.cpython-39.pyc,, +sqlalchemy/sql/annotation.py,sha256=EvDBXH-wt9RRqHNY-8hlXiXYJfl9antb8M3IvB91cqM,11592 +sqlalchemy/sql/base.py,sha256=Aue1owXMTXOzO3WXF_QcMtlkBtEJCvRputH7WjbYQnY,56651 +sqlalchemy/sql/coercions.py,sha256=PcuTab2Rs91JX3qZQixKjmmTT-oj_A-HIjbmdw_4CrA,33560 +sqlalchemy/sql/compiler.py,sha256=BtiVfBp-T7V8FQEmG6EASPwaD_b8EtO9Nz2RcjuqeXg,176837 +sqlalchemy/sql/crud.py,sha256=dkdesuCs9Lt5QanQg3vvVT8glavy-b5LK5ABYFm3Dfo,35833 +sqlalchemy/sql/ddl.py,sha256=uvUIePaU1oObVGquGcEdrqebIQ8DLz6qstns1Ag5_R0,45336 +sqlalchemy/sql/default_comparator.py,sha256=7iC7YGjoXyICcnxjovGA37T_smTSSnMlsf4RJGprF2U,11233 +sqlalchemy/sql/dml.py,sha256=lDzS--PzSyEldp0zjjZMbNhUiGy9jjkH5AmefyrsZTg,52695 +sqlalchemy/sql/elements.py,sha256=AdCKqNXv2zjCYH3i9HjwkON7LM8T0YiGn_LTpMBkK4Q,175679 +sqlalchemy/sql/events.py,sha256=pQJPAeybf1jel2LMq59CU5cYe-_duV9pkUGbDxkSelc,13412 +sqlalchemy/sql/expression.py,sha256=xA7Kg_G_oobzVANzXaXRC9Ms96U0g80UxgxH2GrPdmo,9105 +sqlalchemy/sql/functions.py,sha256=l9RCbwROk-KtEpzTUsOI72xxO1saJtbNRWx502IEU0E,48502 +sqlalchemy/sql/lambdas.py,sha256=kC56163Xqy_FPpZcMUQQ89rJuEN0cGeV6lMOzg2SAuY,44237 +sqlalchemy/sql/naming.py,sha256=frPyvaq5BALvRCUcaH7s30e8lTZMoWEBHLkZILUIdi4,6995 +sqlalchemy/sql/operators.py,sha256=MexbaWdAztb_Xd6dCLU3oFbe-imrWS96izD8iSVxsKw,49446 +sqlalchemy/sql/roles.py,sha256=eiVyxFCACcmCnxf4z5dXxhIHgZLRuETY8xV1zd8fCYc,5625 +sqlalchemy/sql/schema.py,sha256=aouTvkn9-Zs7gRPyRDklmidF-4K8RLqa0GRW_BHpugg,191714 +sqlalchemy/sql/selectable.py,sha256=2YPclquuN66CXtuD9d9Dcf1EU1j3ww6h1yGmuT7jy9k,224427 +sqlalchemy/sql/sqltypes.py,sha256=-0di1pjJubEtr7G5fbiRQoiMxFC4rF979N5EI9dqA3U,113934 +sqlalchemy/sql/traversals.py,sha256=eTRuqYptAJ33ihgLPizlc_S5m9zvKqC3V2t2YYtE6Fo,50825 +sqlalchemy/sql/type_api.py,sha256=ChLSlTQAeN2beCz7XQeoKkZfvYEBM4_-RlGJoMve-1I,59372 +sqlalchemy/sql/util.py,sha256=UhOSuPJ4yGVN8Qal2tDvDMNMd9eOI05DGVUcACmZ8bo,35520 +sqlalchemy/sql/visitors.py,sha256=-Yuc-yAKUZBf9XjJ6bvOP8Sy5Zv7mB-gdXKo9JOzHLQ,28063 +sqlalchemy/testing/__init__.py,sha256=XCdyR7e5mC5m4fhcmPgjxAGsIe2Vgca7rygudyUryJY,2851 +sqlalchemy/testing/__pycache__/__init__.cpython-39.pyc,, +sqlalchemy/testing/__pycache__/assertions.cpython-39.pyc,, +sqlalchemy/testing/__pycache__/assertsql.cpython-39.pyc,, +sqlalchemy/testing/__pycache__/asyncio.cpython-39.pyc,, +sqlalchemy/testing/__pycache__/config.cpython-39.pyc,, +sqlalchemy/testing/__pycache__/engines.cpython-39.pyc,, +sqlalchemy/testing/__pycache__/entities.cpython-39.pyc,, +sqlalchemy/testing/__pycache__/exclusions.cpython-39.pyc,, +sqlalchemy/testing/__pycache__/fixtures.cpython-39.pyc,, +sqlalchemy/testing/__pycache__/mock.cpython-39.pyc,, +sqlalchemy/testing/__pycache__/pickleable.cpython-39.pyc,, +sqlalchemy/testing/__pycache__/profiling.cpython-39.pyc,, +sqlalchemy/testing/__pycache__/provision.cpython-39.pyc,, +sqlalchemy/testing/__pycache__/requirements.cpython-39.pyc,, +sqlalchemy/testing/__pycache__/schema.cpython-39.pyc,, +sqlalchemy/testing/__pycache__/util.cpython-39.pyc,, +sqlalchemy/testing/__pycache__/warnings.cpython-39.pyc,, +sqlalchemy/testing/assertions.py,sha256=s2UhwsBzCPa75W8UQRxlR3gfWm9WIfTksdG-zWPtOYg,24466 +sqlalchemy/testing/assertsql.py,sha256=2UQGJYRLD4IgFzYm1IwdUDoeJgIDIIeJIcrOit-XGLE,14689 +sqlalchemy/testing/asyncio.py,sha256=4qUnZPDR7omIo3FLoRWy6hBJbne1KbyMELbdIWEZddk,3800 +sqlalchemy/testing/config.py,sha256=IxwIEAW7_1b88yUOX_wjivjuS9PvlCJVEpIScmtQh7Y,6751 +sqlalchemy/testing/engines.py,sha256=TU32-pY1rHRiWR3zrXr6_sRXYMuh1CkF9AyoPCbNm8s,13098 +sqlalchemy/testing/entities.py,sha256=BzhnwvfZ_NkNn4xtvFbLzPvQw2T9gEBNc7hMay2cG5s,3363 +sqlalchemy/testing/exclusions.py,sha256=2X5m5QCLO0W0D6GmI7cw6BuMIclsBoMnnjaqAY2Hclc,13793 +sqlalchemy/testing/fixtures.py,sha256=DyIAvYvavTI3Qs3oYtHT8TGqwFaFevEguc4gNApUGi0,25977 +sqlalchemy/testing/mock.py,sha256=krEfc7Vz1EEjkPb1D07yRZXX1ehaCCT2uFoQiXz9EOM,925 +sqlalchemy/testing/pickleable.py,sha256=5Dlu3OP7s5DKAI1c4XP6UCz_17MOJsM3E0zqQWesZFA,2847 +sqlalchemy/testing/plugin/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +sqlalchemy/testing/plugin/__pycache__/__init__.cpython-39.pyc,, +sqlalchemy/testing/plugin/__pycache__/bootstrap.cpython-39.pyc,, +sqlalchemy/testing/plugin/__pycache__/plugin_base.cpython-39.pyc,, +sqlalchemy/testing/plugin/__pycache__/pytestplugin.cpython-39.pyc,, +sqlalchemy/testing/plugin/__pycache__/reinvent_fixtures_py2k.cpython-39.pyc,, +sqlalchemy/testing/plugin/bootstrap.py,sha256=0104b_dYFUT5WsZU-NL44wlMo1LrKByBtPQYaFt6CTQ,1738 +sqlalchemy/testing/plugin/plugin_base.py,sha256=t0PKr1XE2GiH5zb6Z5lXV_abu3rZva6v8DLQglTTF00,22349 +sqlalchemy/testing/plugin/pytestplugin.py,sha256=Tj-wscMJ9yuPsS74U1AiJ-4UTrxNT-ZI89qHAAcv0-Y,24973 +sqlalchemy/testing/plugin/reinvent_fixtures_py2k.py,sha256=b9fWp5RXdePykrNviZPXaGDIjOEOfovchez2Ovr4IRQ,3400 +sqlalchemy/testing/profiling.py,sha256=K4Kn3o4GMbdb-1YMu1E4tPkpuHxbnJIWEqypsv_nY2s,10900 +sqlalchemy/testing/provision.py,sha256=J9PWi7i7Xmr-sALBrmK4OTXH9ur4rpFXKHhnhrjcQcs,12461 +sqlalchemy/testing/requirements.py,sha256=-69hbEEuVSCVdNC3KiHJL2xC7fbSguoL66f_li1Nq50,42209 +sqlalchemy/testing/schema.py,sha256=2MNf-qPPUqEtxtCfVjZpwaoOBZEToUOHfW-egM2Nov4,6761 +sqlalchemy/testing/suite/__init__.py,sha256=u3lEc0j47s7Dad_2SVWOZ6EU2aOMRWqE_WrQ17HmBsA,489 +sqlalchemy/testing/suite/__pycache__/__init__.cpython-39.pyc,, +sqlalchemy/testing/suite/__pycache__/test_cte.cpython-39.pyc,, +sqlalchemy/testing/suite/__pycache__/test_ddl.cpython-39.pyc,, +sqlalchemy/testing/suite/__pycache__/test_deprecations.cpython-39.pyc,, +sqlalchemy/testing/suite/__pycache__/test_dialect.cpython-39.pyc,, +sqlalchemy/testing/suite/__pycache__/test_insert.cpython-39.pyc,, +sqlalchemy/testing/suite/__pycache__/test_reflection.cpython-39.pyc,, +sqlalchemy/testing/suite/__pycache__/test_results.cpython-39.pyc,, +sqlalchemy/testing/suite/__pycache__/test_rowcount.cpython-39.pyc,, +sqlalchemy/testing/suite/__pycache__/test_select.cpython-39.pyc,, +sqlalchemy/testing/suite/__pycache__/test_sequence.cpython-39.pyc,, +sqlalchemy/testing/suite/__pycache__/test_types.cpython-39.pyc,, +sqlalchemy/testing/suite/__pycache__/test_unicode_ddl.cpython-39.pyc,, +sqlalchemy/testing/suite/__pycache__/test_update_delete.cpython-39.pyc,, +sqlalchemy/testing/suite/test_cte.py,sha256=shi2WJZpzAzDCdkmzx0IDEu-URsLLBsdyFdzDqsfpyw,6387 +sqlalchemy/testing/suite/test_ddl.py,sha256=BZuYkKaG_tNUkZcFLNd6o1qnfbzU0IYiedCw_NhOJBg,12143 +sqlalchemy/testing/suite/test_deprecations.py,sha256=0LUmXIiK8hHUr6tY8cJJC7VErOj9YNGNN-c324k08Dw,5204 +sqlalchemy/testing/suite/test_dialect.py,sha256=XpwZxIXlMVsZlP74khuPW2wZskinT0IOT9S6PM5PyE8,11256 +sqlalchemy/testing/suite/test_insert.py,sha256=QlMovNeDU-GB5UvTEumce3LLK94PAduKlWe2TqCcLF4,11501 +sqlalchemy/testing/suite/test_reflection.py,sha256=kZI4xcVR7_Ft_8kYYF58FjM7IIeuFzS0yXDj7lVfwbA,56815 +sqlalchemy/testing/suite/test_results.py,sha256=qa_V37VKFRfi2xoI8d5bW9EzB_E_oPFhShBo9VPjCss,14396 +sqlalchemy/testing/suite/test_rowcount.py,sha256=M6E-d4JuoVVfbBC-z59hB1accmoh4yARRJsD4yVFP60,4944 +sqlalchemy/testing/suite/test_select.py,sha256=hZfNo0bTFQodHg2PB4_dqdnJ2mnXq3Fs_mdp17409uk,54167 +sqlalchemy/testing/suite/test_sequence.py,sha256=2KBcs0FtoL3gk37IN2PnRZSnQwt7RKkShAbYQHFTBcw,8713 +sqlalchemy/testing/suite/test_types.py,sha256=kiVqATGP79gKi3LNvjCFpWF2qFNCjvy8svv68peQU8E,45445 +sqlalchemy/testing/suite/test_unicode_ddl.py,sha256=T9Bkkj7Cm2mIQW5BQFU2NnJVnvBhZyEQXcuHRO3AhKc,6937 +sqlalchemy/testing/suite/test_update_delete.py,sha256=q3AoCPMKhxYVUi4n7YBjf8v8nb9obXmUXqnFsVxpZLY,1646 +sqlalchemy/testing/util.py,sha256=pF1e_qH8eH1LVN4xB0hgL3XNfsbb5A2iRvNG8c_IgVc,12960 +sqlalchemy/testing/warnings.py,sha256=n7poYJsx6YspiD9RIp2QiYxWbnWG7OQMLkjrmWqn85g,6010 +sqlalchemy/types.py,sha256=xd4i1Yi0zY9lb6jZuy70TOkdKwP_deLpB20IQ3nx_zw,2997 +sqlalchemy/util/__init__.py,sha256=Eklvf318BEPscpatTBry6K7hQU9JfragXEX_clr-Fm0,6434 +sqlalchemy/util/__pycache__/__init__.cpython-39.pyc,, +sqlalchemy/util/__pycache__/_collections.cpython-39.pyc,, +sqlalchemy/util/__pycache__/_concurrency_py3k.cpython-39.pyc,, +sqlalchemy/util/__pycache__/_preloaded.cpython-39.pyc,, +sqlalchemy/util/__pycache__/compat.cpython-39.pyc,, +sqlalchemy/util/__pycache__/concurrency.cpython-39.pyc,, +sqlalchemy/util/__pycache__/deprecations.cpython-39.pyc,, +sqlalchemy/util/__pycache__/langhelpers.cpython-39.pyc,, +sqlalchemy/util/__pycache__/queue.cpython-39.pyc,, +sqlalchemy/util/__pycache__/topological.cpython-39.pyc,, +sqlalchemy/util/_collections.py,sha256=vQ5XrglAq0tVxIQtay9WVuQSgnKJ-L_bF4bRflNgEew,30227 +sqlalchemy/util/_concurrency_py3k.py,sha256=iE8ZnXzkIVXWIwR7In5bwIK1esv_vrlP2Nm2fhBose8,6728 +sqlalchemy/util/_preloaded.py,sha256=B7sQrjHOamdgOWZ1EAFWPOvlXH59ose5KoOwFMkhFe0,2463 +sqlalchemy/util/compat.py,sha256=S4vHlw295cr9gH4dmUYxCjKjCh9poB7e7-5hlF1Mwc8,18786 +sqlalchemy/util/concurrency.py,sha256=tdD998NG1XOOC4m8Ml0mB2xuHzHy9UlVGMpNftMelbk,1826 +sqlalchemy/util/deprecations.py,sha256=oz5oLKUYL7WOMq8WudNdr4olB9KJycR1B88FuyN_BFU,11894 +sqlalchemy/util/langhelpers.py,sha256=5eFZacBOGQoWvnBjVU5ItoOgkWjz7Q05PSMi0vbLDSk,58174 +sqlalchemy/util/queue.py,sha256=SoUrtd6cw5ncTUk2FN1-YEvetBHcZzo5Zbr2l-9VJGw,9583 +sqlalchemy/util/topological.py,sha256=w9_UvLP0Gf8wsCjpBxNwspXdoll_7kqO-rYVb-I5AU8,2957 diff --git a/venv/Lib/site-packages/SQLAlchemy-1.4.20.dist-info/WHEEL b/venv/Lib/site-packages/SQLAlchemy-1.4.20.dist-info/WHEEL new file mode 100644 index 0000000..d1267fc --- /dev/null +++ b/venv/Lib/site-packages/SQLAlchemy-1.4.20.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.36.2) +Root-Is-Purelib: false +Tag: cp39-cp39-win_amd64 + diff --git a/venv/Lib/site-packages/SQLAlchemy-1.4.20.dist-info/top_level.txt b/venv/Lib/site-packages/SQLAlchemy-1.4.20.dist-info/top_level.txt new file mode 100644 index 0000000..39fb2be --- /dev/null +++ b/venv/Lib/site-packages/SQLAlchemy-1.4.20.dist-info/top_level.txt @@ -0,0 +1 @@ +sqlalchemy diff --git a/venv/Lib/site-packages/Werkzeug-2.0.1.dist-info/INSTALLER b/venv/Lib/site-packages/Werkzeug-2.0.1.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/venv/Lib/site-packages/Werkzeug-2.0.1.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/Lib/site-packages/Werkzeug-2.0.1.dist-info/LICENSE.rst b/venv/Lib/site-packages/Werkzeug-2.0.1.dist-info/LICENSE.rst new file mode 100644 index 0000000..c37cae4 --- /dev/null +++ b/venv/Lib/site-packages/Werkzeug-2.0.1.dist-info/LICENSE.rst @@ -0,0 +1,28 @@ +Copyright 2007 Pallets + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/venv/Lib/site-packages/Werkzeug-2.0.1.dist-info/METADATA b/venv/Lib/site-packages/Werkzeug-2.0.1.dist-info/METADATA new file mode 100644 index 0000000..f991d5a --- /dev/null +++ b/venv/Lib/site-packages/Werkzeug-2.0.1.dist-info/METADATA @@ -0,0 +1,128 @@ +Metadata-Version: 2.1 +Name: Werkzeug +Version: 2.0.1 +Summary: The comprehensive WSGI web application library. +Home-page: https://palletsprojects.com/p/werkzeug/ +Author: Armin Ronacher +Author-email: armin.ronacher@active-4.com +Maintainer: Pallets +Maintainer-email: contact@palletsprojects.com +License: BSD-3-Clause +Project-URL: Donate, https://palletsprojects.com/donate +Project-URL: Documentation, https://werkzeug.palletsprojects.com/ +Project-URL: Changes, https://werkzeug.palletsprojects.com/changes/ +Project-URL: Source Code, https://github.com/pallets/werkzeug/ +Project-URL: Issue Tracker, https://github.com/pallets/werkzeug/issues/ +Project-URL: Twitter, https://twitter.com/PalletsTeam +Project-URL: Chat, https://discord.gg/pallets +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Web Environment +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content +Classifier: Topic :: Internet :: WWW/HTTP :: WSGI +Classifier: Topic :: Internet :: WWW/HTTP :: WSGI :: Application +Classifier: Topic :: Internet :: WWW/HTTP :: WSGI :: Middleware +Classifier: Topic :: Software Development :: Libraries :: Application Frameworks +Requires-Python: >=3.6 +Description-Content-Type: text/x-rst +Requires-Dist: dataclasses ; python_version < "3.7" +Provides-Extra: watchdog +Requires-Dist: watchdog ; extra == 'watchdog' + +Werkzeug +======== + +*werkzeug* German noun: "tool". Etymology: *werk* ("work"), *zeug* ("stuff") + +Werkzeug is a comprehensive `WSGI`_ web application library. It began as +a simple collection of various utilities for WSGI applications and has +become one of the most advanced WSGI utility libraries. + +It includes: + +- An interactive debugger that allows inspecting stack traces and + source code in the browser with an interactive interpreter for any + frame in the stack. +- A full-featured request object with objects to interact with + headers, query args, form data, files, and cookies. +- A response object that can wrap other WSGI applications and handle + streaming data. +- A routing system for matching URLs to endpoints and generating URLs + for endpoints, with an extensible system for capturing variables + from URLs. +- HTTP utilities to handle entity tags, cache control, dates, user + agents, cookies, files, and more. +- A threaded WSGI server for use while developing applications + locally. +- A test client for simulating HTTP requests during testing without + requiring running a server. + +Werkzeug doesn't enforce any dependencies. It is up to the developer to +choose a template engine, database adapter, and even how to handle +requests. It can be used to build all sorts of end user applications +such as blogs, wikis, or bulletin boards. + +`Flask`_ wraps Werkzeug, using it to handle the details of WSGI while +providing more structure and patterns for defining powerful +applications. + +.. _WSGI: https://wsgi.readthedocs.io/en/latest/ +.. _Flask: https://www.palletsprojects.com/p/flask/ + + +Installing +---------- + +Install and update using `pip`_: + +.. code-block:: text + + pip install -U Werkzeug + +.. _pip: https://pip.pypa.io/en/stable/quickstart/ + + +A Simple Example +---------------- + +.. code-block:: python + + from werkzeug.wrappers import Request, Response + + @Request.application + def application(request): + return Response('Hello, World!') + + if __name__ == '__main__': + from werkzeug.serving import run_simple + run_simple('localhost', 4000, application) + + +Donate +------ + +The Pallets organization develops and supports Werkzeug and other +popular packages. In order to grow the community of contributors and +users, and allow the maintainers to devote more time to the projects, +`please donate today`_. + +.. _please donate today: https://palletsprojects.com/donate + + +Links +----- + +- Documentation: https://werkzeug.palletsprojects.com/ +- Changes: https://werkzeug.palletsprojects.com/changes/ +- PyPI Releases: https://pypi.org/project/Werkzeug/ +- Source Code: https://github.com/pallets/werkzeug/ +- Issue Tracker: https://github.com/pallets/werkzeug/issues/ +- Website: https://palletsprojects.com/p/werkzeug/ +- Twitter: https://twitter.com/PalletsTeam +- Chat: https://discord.gg/pallets + + diff --git a/venv/Lib/site-packages/Werkzeug-2.0.1.dist-info/RECORD b/venv/Lib/site-packages/Werkzeug-2.0.1.dist-info/RECORD new file mode 100644 index 0000000..32e9492 --- /dev/null +++ b/venv/Lib/site-packages/Werkzeug-2.0.1.dist-info/RECORD @@ -0,0 +1,111 @@ +Werkzeug-2.0.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +Werkzeug-2.0.1.dist-info/LICENSE.rst,sha256=O0nc7kEF6ze6wQ-vG-JgQI_oXSUrjp3y4JefweCUQ3s,1475 +Werkzeug-2.0.1.dist-info/METADATA,sha256=8-W33EMnGqnCCi-d8Dv63IQQuyELRIsXhwOJNXbNgL0,4421 +Werkzeug-2.0.1.dist-info/RECORD,, +Werkzeug-2.0.1.dist-info/WHEEL,sha256=OqRkF0eY5GHssMorFjlbTIq072vpHpF60fIQA6lS9xA,92 +Werkzeug-2.0.1.dist-info/top_level.txt,sha256=QRyj2VjwJoQkrwjwFIOlB8Xg3r9un0NtqVHQF-15xaw,9 +werkzeug/__init__.py,sha256=_CCsfdeqNllFNRJx8cvqYrwBsQQQXJaMmnk2sAZnDng,188 +werkzeug/__pycache__/__init__.cpython-39.pyc,, +werkzeug/__pycache__/_internal.cpython-39.pyc,, +werkzeug/__pycache__/_reloader.cpython-39.pyc,, +werkzeug/__pycache__/datastructures.cpython-39.pyc,, +werkzeug/__pycache__/exceptions.cpython-39.pyc,, +werkzeug/__pycache__/filesystem.cpython-39.pyc,, +werkzeug/__pycache__/formparser.cpython-39.pyc,, +werkzeug/__pycache__/http.cpython-39.pyc,, +werkzeug/__pycache__/local.cpython-39.pyc,, +werkzeug/__pycache__/routing.cpython-39.pyc,, +werkzeug/__pycache__/security.cpython-39.pyc,, +werkzeug/__pycache__/serving.cpython-39.pyc,, +werkzeug/__pycache__/test.cpython-39.pyc,, +werkzeug/__pycache__/testapp.cpython-39.pyc,, +werkzeug/__pycache__/urls.cpython-39.pyc,, +werkzeug/__pycache__/user_agent.cpython-39.pyc,, +werkzeug/__pycache__/useragents.cpython-39.pyc,, +werkzeug/__pycache__/utils.cpython-39.pyc,, +werkzeug/__pycache__/wsgi.cpython-39.pyc,, +werkzeug/_internal.py,sha256=_QKkvdaG4pDFwK68c0EpPzYJGe9Y7toRAT1cBbC-CxU,18572 +werkzeug/_reloader.py,sha256=B1hEfgsUOz2IginBQM5Zak_eaIF7gr3GS5-0x2OHvAE,13950 +werkzeug/datastructures.py,sha256=KahVPSLOapbNbKh1ppr9K8_DgWJv1EGgA9DhTEGMHcg,97886 +werkzeug/datastructures.pyi,sha256=5DTPF8P8Zvi458eK27Qcj7eNUlLM_AC0jBNkj6uQpds,33774 +werkzeug/debug/__init__.py,sha256=CUFrPEYAaotHRkmjOieqd3EasXDii2JVC1HdmEzMwqM,17924 +werkzeug/debug/__pycache__/__init__.cpython-39.pyc,, +werkzeug/debug/__pycache__/console.cpython-39.pyc,, +werkzeug/debug/__pycache__/repr.cpython-39.pyc,, +werkzeug/debug/__pycache__/tbtools.cpython-39.pyc,, +werkzeug/debug/console.py,sha256=E1nBMEvFkX673ShQjPtVY-byYatfX9MN-dBMjRI8a8E,5897 +werkzeug/debug/repr.py,sha256=QCSHENKsChEZDCIApkVi_UNjhJ77v8BMXK1OfxO189M,9483 +werkzeug/debug/shared/FONT_LICENSE,sha256=LwAVEI1oYnvXiNMT9SnCH_TaLCxCpeHziDrMg0gPkAI,4673 +werkzeug/debug/shared/ICON_LICENSE.md,sha256=DhA6Y1gUl5Jwfg0NFN9Rj4VWITt8tUx0IvdGf0ux9-s,222 +werkzeug/debug/shared/console.png,sha256=bxax6RXXlvOij_KeqvSNX0ojJf83YbnZ7my-3Gx9w2A,507 +werkzeug/debug/shared/debugger.js,sha256=dYbUmFmb3YZb5YpWpYPOQArdrN7NPeY0ODawL7ihzDI,10524 +werkzeug/debug/shared/less.png,sha256=-4-kNRaXJSONVLahrQKUxMwXGm9R4OnZ9SxDGpHlIR4,191 +werkzeug/debug/shared/more.png,sha256=GngN7CioHQoV58rH6ojnkYi8c_qED2Aka5FO5UXrReY,200 +werkzeug/debug/shared/source.png,sha256=RoGcBTE4CyCB85GBuDGTFlAnUqxwTBiIfDqW15EpnUQ,818 +werkzeug/debug/shared/style.css,sha256=vyp1RnB227Fuw8LIyM5C-bBCBQN5hvZSCApY2oeJ9ik,6705 +werkzeug/debug/shared/ubuntu.ttf,sha256=1eaHFyepmy4FyDvjLVzpITrGEBu_CZYY94jE0nED1c0,70220 +werkzeug/debug/tbtools.py,sha256=TfReusPbM3yjm3xvOFkH45V7-5JnNqB9x1EQPnVC6Xo,19189 +werkzeug/exceptions.py,sha256=CUwx0pBiNbk4f9cON17ekgKnmLi6HIVFjUmYZc2x0wM,28681 +werkzeug/filesystem.py,sha256=JS2Dv2QF98WILxY4_thHl-WMcUcwluF_4igkDPaP1l4,1956 +werkzeug/formparser.py,sha256=GIKfzuQ_khuBXnf3N7_LzOEruYwNc3m4bI02BgtT5jg,17385 +werkzeug/http.py,sha256=oUCXFFMnkOQ-cHbUY_aiqitshcrSzNDq3fEMf1VI_yk,45141 +werkzeug/local.py,sha256=WsR6H-2XOtPigpimjORbLsS3h9WI0lCdZjGI2LHDDxA,22733 +werkzeug/middleware/__init__.py,sha256=qfqgdT5npwG9ses3-FXQJf3aB95JYP1zchetH_T3PUw,500 +werkzeug/middleware/__pycache__/__init__.cpython-39.pyc,, +werkzeug/middleware/__pycache__/dispatcher.cpython-39.pyc,, +werkzeug/middleware/__pycache__/http_proxy.cpython-39.pyc,, +werkzeug/middleware/__pycache__/lint.cpython-39.pyc,, +werkzeug/middleware/__pycache__/profiler.cpython-39.pyc,, +werkzeug/middleware/__pycache__/proxy_fix.cpython-39.pyc,, +werkzeug/middleware/__pycache__/shared_data.cpython-39.pyc,, +werkzeug/middleware/dispatcher.py,sha256=Fh_w-KyWnTSYF-Lfv5dimQ7THSS7afPAZMmvc4zF1gg,2580 +werkzeug/middleware/http_proxy.py,sha256=HE8VyhS7CR-E1O6_9b68huv8FLgGGR1DLYqkS3Xcp3Q,7558 +werkzeug/middleware/lint.py,sha256=yMzMdm4xI2_N-Wv2j1yoaVI3ltHOYS6yZyA-wUv1sKw,13962 +werkzeug/middleware/profiler.py,sha256=G2JieUMv4QPamtCY6ibIK7P-piPRdPybav7bm2MSFvs,4898 +werkzeug/middleware/proxy_fix.py,sha256=uRgQ3dEvFV8JxUqajHYYYOPEeA_BFqaa51Yp8VW0uzA,6849 +werkzeug/middleware/shared_data.py,sha256=eOCGr-i6BCexDfL7xdPRWMwPJPgp0NE2B416Gl67Q78,10959 +werkzeug/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +werkzeug/routing.py,sha256=FDRtvCfaZSmXnQ0cUYxowb3P0y0dxlUlMSUmerY5sb0,84147 +werkzeug/sansio/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +werkzeug/sansio/__pycache__/__init__.cpython-39.pyc,, +werkzeug/sansio/__pycache__/multipart.cpython-39.pyc,, +werkzeug/sansio/__pycache__/request.cpython-39.pyc,, +werkzeug/sansio/__pycache__/response.cpython-39.pyc,, +werkzeug/sansio/__pycache__/utils.cpython-39.pyc,, +werkzeug/sansio/multipart.py,sha256=bJMCNC2f5xyAaylahNViJ0JqmV4ThLRbDVGVzKwcqrQ,8751 +werkzeug/sansio/request.py,sha256=aA9rABkWiG4MhYMByanst2NXkEclsq8SIxhb0LQf0e0,20228 +werkzeug/sansio/response.py,sha256=HSG6t-tyPZd3awzWqr7qL9IV4HYAvDgON1c0YPU2RXw,24117 +werkzeug/sansio/utils.py,sha256=V5v-UUnX8pm4RehP9Tt_NiUSOJGJGUvKjlW0eOIQldM,4164 +werkzeug/security.py,sha256=gPDRuCjkjWrcqj99tBMq8_nHFZLFQjgoW5Ga5XIw9jo,8158 +werkzeug/serving.py,sha256=_RG2dCclOQcdjJ2NE8tzCRybGePlwcs8kTypiWRP2gY,38030 +werkzeug/test.py,sha256=EJXJy-b_JriHrlfs5VNCkwbki8Kn_xUDkOYOCx_6Q7Q,48096 +werkzeug/testapp.py,sha256=f48prWSGJhbSrvYb8e1fnAah4BkrLb0enHSdChgsjBY,9471 +werkzeug/urls.py,sha256=3o_aUcr5Ou13XihSU6VvX6RHMhoWkKpXuCCia9SSAb8,41021 +werkzeug/user_agent.py,sha256=WclZhpvgLurMF45hsioSbS75H1Zb4iMQGKN3_yZ2oKo,1420 +werkzeug/useragents.py,sha256=G8tmv_6vxJaPrLQH3eODNgIYe0_V6KETROQlJI-WxDE,7264 +werkzeug/utils.py,sha256=WrU-LbwemyGd8zBHBgQyLaIxing4QLEChiP0qnzr2sc,36771 +werkzeug/wrappers/__init__.py,sha256=-s75nPbyXHzU_rwmLPDhoMuGbEUk0jZT_n0ZQAOFGf8,654 +werkzeug/wrappers/__pycache__/__init__.cpython-39.pyc,, +werkzeug/wrappers/__pycache__/accept.cpython-39.pyc,, +werkzeug/wrappers/__pycache__/auth.cpython-39.pyc,, +werkzeug/wrappers/__pycache__/base_request.cpython-39.pyc,, +werkzeug/wrappers/__pycache__/base_response.cpython-39.pyc,, +werkzeug/wrappers/__pycache__/common_descriptors.cpython-39.pyc,, +werkzeug/wrappers/__pycache__/cors.cpython-39.pyc,, +werkzeug/wrappers/__pycache__/etag.cpython-39.pyc,, +werkzeug/wrappers/__pycache__/json.cpython-39.pyc,, +werkzeug/wrappers/__pycache__/request.cpython-39.pyc,, +werkzeug/wrappers/__pycache__/response.cpython-39.pyc,, +werkzeug/wrappers/__pycache__/user_agent.cpython-39.pyc,, +werkzeug/wrappers/accept.py,sha256=_oZtAQkahvsrPRkNj2fieg7_St9P0NFC3SgZbJKS6xU,429 +werkzeug/wrappers/auth.py,sha256=rZPCzGxHk9R55PRkmS90kRywUVjjuMWzCGtH68qCq8U,856 +werkzeug/wrappers/base_request.py,sha256=saz9RyNQkvI_XLPYVm29KijNHmD1YzgxDqa0qHTbgss,1174 +werkzeug/wrappers/base_response.py,sha256=q_-TaYywT5G4zA-DWDRDJhJSat2_4O7gOPob6ye4_9A,1186 +werkzeug/wrappers/common_descriptors.py,sha256=v_kWLH3mvCiSRVJ1FNw7nO3w2UJfzY57UKKB5J4zCvE,898 +werkzeug/wrappers/cors.py,sha256=c5UndlZsZvYkbPrp6Gj5iSXxw_VOJDJHskO6-jRmNyQ,846 +werkzeug/wrappers/etag.py,sha256=XHWQQs7Mdd1oWezgBIsl-bYe8ydKkRZVil2Qd01D0Mo,846 +werkzeug/wrappers/json.py,sha256=HM1btPseGeXca0vnwQN_MvZl6h-qNsFY5YBKXKXFwus,410 +werkzeug/wrappers/request.py,sha256=0zAkCUwJbUBzioGy2UKxE6XpuXPAZbEhhML4WErzeBo,24818 +werkzeug/wrappers/response.py,sha256=95hXIysZTeNC0bqhvGB2fLBRKxedR_cgI5szZZWfyzw,35177 +werkzeug/wrappers/user_agent.py,sha256=Wl1-A0-1r8o7cHIZQTB55O4Ged6LpCKENaQDlOY5pXA,435 +werkzeug/wsgi.py,sha256=7psV3SHLtCzk1KSuGmIK5uP2QTDXyfCCDclyqCmIUO4,33715 diff --git a/venv/Lib/site-packages/Werkzeug-2.0.1.dist-info/WHEEL b/venv/Lib/site-packages/Werkzeug-2.0.1.dist-info/WHEEL new file mode 100644 index 0000000..385faab --- /dev/null +++ b/venv/Lib/site-packages/Werkzeug-2.0.1.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.36.2) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/venv/Lib/site-packages/Werkzeug-2.0.1.dist-info/top_level.txt b/venv/Lib/site-packages/Werkzeug-2.0.1.dist-info/top_level.txt new file mode 100644 index 0000000..6fe8da8 --- /dev/null +++ b/venv/Lib/site-packages/Werkzeug-2.0.1.dist-info/top_level.txt @@ -0,0 +1 @@ +werkzeug diff --git a/venv/Lib/site-packages/__pycache__/pyparsing.cpython-39.pyc b/venv/Lib/site-packages/__pycache__/pyparsing.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c66e5bd11105dee7365bb3cceac596b5c3e95e62 GIT binary patch literal 240369 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

0GPzSOX_u|OkI2%6xcwsTb( z4R&kN?r{q+GBT0vtEnlGF^+$7*ZL=SuTM+|zY0>S^#G;fDXzWf64!eY@2`Pxf}=d( zu$#FT#0sZx>+YWi)q?%ZV{Tqg#@wc1KjV~d+BoIg+?-q~Q`0%FT1` z^U+CCNjE=PSzSPOR9;O=b2qN&Iwg;fwkc{_0zc-Bi!QXRshZxNB~%c&W@pQqswULg zB!NfkKJ6Y`pp59FbInR4BAQ5`&qRO=Q`aNDmLQb`rZc3k3_M0N$PmIzOfSU31VTH2P7DhNerJN{He zr*t*yAyB$Uqzo0-QkGhTgI&RgmP1A*B#sKo$*m_6k7BjkpXPC^+GrIJ~Z%Z-^r22ry;oKLlw0lfU62gT01L8V9qAHS_+mT|5&zr>N#A zf{4;TUXd1tEZwHQ!J<}Kj=-qwI+axxUEGODEYvDcGq@~<;f3Nskb9RUVQ51rHa4N{ zcxmyu2ppf*V8}MIBaeyKU8(u8v+3Cu1}2)7GDh{IqOn2w$&o+sW488a1kB^m(E%}R z+!XaDQ;BZbIK&zxORW*7llU);G%L;agMZI?^CBB&)yEw>05+`rxO%50(~*;x7d=cH zc?nT8y>jzYulD>+QYWyqAVb~$zi(#hsr}l$xoDG7Y6qK*muPG_86zDC7vfPp zmPi;UO`dzNIsAlicA8hNkkD^Q?kg7m!5i{u021+qY%Q=~`1U21LtH;HLg%s3ZdMVU z4a2VIeC&mvNzJLI=;x^?TV_-w(mTqS#oUO*rc!#_6)D&sk`c{v_NC6?8GSorLKMH| zjDD3s*aV@$MecQEz6w>tJnWn&m`nmwEH0U0Q_LYSCM-DuGk_hSVitFvg#yL^%EaCi znx!ZmN~;P9b7CWHP$4#+JMqx|1O~c8TeI;UcKa`64RM8e!>u zLpQAiO|ZefswXiGj49frEn$-#ERUwF0mrCoe!2L-_OQRj?GF@JDi@Zqq{HHWaz94w z_DIsmkNwjCvD1NUeBoquZDsL*1K9ViLQD%?Lt=Ya((A-hLfdZ4Vbz?7EWTemv3}mQ z@^5HrT1Lb{lnf?z9wZ!Jtx4eQ9!}Yi3r9wD;V3`qotN^=$IPXE1pL`c!vwG=xallh zEJvc)i8f5w9l1D9@+Cu~&)%@abWpl-%_pDX45PZ%NfvEX$)(s8{1YL-f2M<`wM+b< zJP?@t3qdye%sz6;qKP#Ie4rfvb5o?5br+fu=_at^K{d|ZuKXhbt)Ego7X5A>pQz@& zqUoKDSGJ?6&8o}7{#Qv5!^*|JK8D-;Uxr4Cmc#cEbSEv#J6>dG=i-<^LeUmdEKcwq z#!b8Nx@&P&)Se#Xp)ep!T*){s65UsP2p20n2g-|kkrfb-cJIUK!ZxV(Lsa`{nbLQq=E`cjzQKE6ziA5G>3B^{(!FHdX71O>yob>z3 z`^RyDEidlhv5yJ1e_}KtQUneLke%`b1}rXomytQB1VL+2Rqzz;N4M2Qu@A%YH2M!{<(qP4V z?_n$>{-Hr3!=Hs@cB*pd5lSV^;qHmN|udAh*E(MRX{GyY$) z19`1^ONQX@aHk!+9sD~U{HE#?lZFXHi%trw6QmN&6-6vvQqbV6m%c(dZL9h!H@=mq zs;N_`8W%5Jx2_=yaO`7=&M7o7-MgKOuzQNi75v^ty_~1L|6Ns^OvSNyO#zDOC&px2 zeJwPJI&V-FaTnQExsltfDx1VOg9JTVxNw3fyl`u)}x~{a{PQ*|1`fqKFgm zJvt!^;$2XI-_D!9IGh8_|m^CxTYy8tJUfk3$GyZfCaI}A}Fv@ zOaqb#7ASwRp96-TuWk%jneZ$#h!TAQi%8hoD1>+4je4GIp2FcH3+iL|(dhK?XTbjZ zoYcr{r$F`VS>kLRM73m5wxnDV>xP#_=Et_lmNtB*lnDcrmxp$DRG!+!llrG%;!X)w zQ;afVkt#Q#cbdkq&@Kn3-rabW?Wa6huV5SBFQI)TSST|3%r_Z^*)`eV z<60sOvr|)EVAK_WJh)Sfq$|OAt_U;-4OA~7{Ag>7g00iy<=NRg)G1>+v1^kvrTz`I zFrDt6Mb=ElzWY?QN?-?Q;&I&r?_mSLNGx-{)NTim ze4Dtn=dg)L)Yq~NxJNebptc;>!EkrmuzRwO5@@$4>TB8I?1_!5dr$CGg#zg0bt}{p zDa0ovsBRhJ+rvq1n|5zGOW;VBDRsU@5E8N_FvUQEnYA4uT9ZdPp#tSh&^7;0*^45) z<8o#hmM4@g+XI4a3$z2>=7SLt#rLeleR4I|UGHzcv-tGts-uo9h(}B}pC|b&ZhjZf zF9bx-tKjTPVYt7fwj3v%P*`Gfokb6T4*-0qcRWn6%FiaE#f!7F&!jcwq=nXlq!Y!c zriCpvHATF`S^s3p4Hp@8o!1OgOs%YNI_^1z$D!Ap@=9^$MSH1mskZbT(t!4&s~tU< zDDB{ii&JPFjv18=z9}hhF!guv(c-jxHj6eP4q_~oU=<4P*b;KFoP+rl%37Aoo~Q9o%b%VPe&hxf`~TZ zKcuJ#4USebWRJk3&V}y2+KuECS8J2EX3ZYtAuKd-ks;W30c|K5LkWf1t9&NQed|30 zHS6WCkIk|l#~Yrz+FuR^@7-0K6Su5}X79_;vR=EY0vn{eilPW3Z{4kzJk zDSs_f`N?`;@P+!O$`|T=S2x#rhXgxSet(H$U+@n~xl-=xaJ}yuCe~Pvbk{eX&8!cp zR&t>XT)m~f>FP**@ESEzddKn>(%9lM^;TwmGX%!BSzB8Pf6>a(oyQi!|;|{ zjdUw-bI<6B^c}lCz;-e$^KdJTprWg<)${B9^Rb_v=B%t^BQh1vC>g zCeZ4Z`WAn6FEH6sy%U&Zg6*oUzUAs&*IeHQ^_#l4Xhf0K+3))1@IDZpvG!)xK2LkM z(%!vR!sg|>>bcUUx9lzJ!O+#a3D*7t)na|?>z&I#P~Unjs}em7w!XR*lB;u_Z?&E@ z&}v7zlrGx$TO?}E1ixk_677ibA?z!&gV3w8R;u@E2Guu{;f7z#AA-SJvfa(|m4O5% zy%7aBPo#$6yz>V#Tm`9(oB(cIEVtwfix?Y53tW#l=G1UaoL~de4}@cpc)XW(aS7c$ zQA>+IWEk9-;rYrMLD7+ctVSh*p+c0Nz{C;Cj9t0W$;&0ex9{dKv1BTV5?qXr#pHfL zM`=1pSPBc2^EM9}YpS1w*^YG1rX zm2?wLIAA6IUZp5A4feY@c2syz%z|m8%e0SXBeKvi#4%eOOb{8F>4-86pmDb1{TnJe z**25adLxPr+ULZ%Zh*ZN0X?r&OZ-R{r6~;8IYZMlm6E%07!C&T8tl~x6Ki4jAyB*U zyNnZYrsx*Mt1#Q!lX5spy%eUlZ>m89qS20m)kx!ZhOh%hH^g}X9K8?%`@<&ip~m@G z$?QNlw^SZi%ZS~rCGI1PLu1+?O*o^p(3Jwb;HVfHj>-^UcGrn;CVAtF6tGzC$u5M} zWr-e$Q<_{ke*sqU%rbxxSe)!k4GRrA!rV1-N`t3rGZmaqo;??eQYi@u*IH$#8%2bV zQQJffiIl-USXX;P@4EgmbTzrj4|9ijeE5L4d)yJ$sul}Tx5DNtL3~}-sx`d zlp#~IZp5_<`Y%z$PSCT>{_(M~u?V0cnz|PWrUa%;jZj|`-U`2=58J#JIlXQ4+GP9p zmR45LXqa(WW#K&RHZimpBKz2uiXPdyYtQ1I2V1V+8#edmi`u<7xqDAbK>#Hte${0| zEaRgmCDX1{Ml~ZGSP9ligb{GV=wX{HL|BT4Qg6N==(9xGOe4ddmgWJ(7g{df3k!sp zo2>%V7vpkD=g$(c%UfHF?Q9+A$(`+oqq-_B0(rpNz4YMD-4E^D{qSQg4FG6_5lmoK zm*F7w-1~#0L+j02x*N>W=B}OM!i(v#xi|LGY&T(BChI9(X({79Ox|`A_mo}y;GPFt z3ifTfxyjr*kx$vBrc(V3Zid74W*mKPnQiGPrp9}mzpZokx^4O-;=z^Bt9m0ugMK>sSe>W>NhG))_hwgfZ_o=j^=89GwBZ7} zZU@=|wVnDm)r^+mw(JYGDSJ8~bV5ez3vB@UV6)eLwNu{Zak8UO;t`P#a&N~DjNoLU zO;A*^ZFflYfhAIWFUB$IK!TFyWvm@7+>XYBQIn-040f-bwy4Cesb1&mD&@w5*3L>2 z;f}{<>~T54{L5jhiC{DoGu67LIQCm%i+KL z!+&zv#RC5O$RTRo6Y zIh~oEyE*lIox2maI@_Ht&8>0p_Y z59#cf4$tafIRho<3qGo!me4iI9_TU~eL^?pbdcN;Y5lRO=Vy;h&sZo)o4!F+cmK9Z zQ5dyAF};Gg>mL_z4~Uk`8}aho98pujueoI>;9-kY7gd)B5>29e!4a zzpBIMb+D!M*L5bfxaq2cuc~tCb4?)`d{JkAONYO$!!PRacXar>I{ZByY#IK#&f=*U z%}3MCJdHhRMPgRzUHF$_5^ds-K(f7Ey|)(bDs1Y#wYR7Dmcs5rZ{fDW0RQ@XvxQvm zbA^1NFtnBFc-PQf$`Qe}TkZXvCT;&{@7}?W6n6C&2X8BUps=aX&%cipvi?lJ`YEBS za9{68p}Wvy|F#u+dq2oCS*~Y$^SxWSs$cx;9YK-V+3{U#^#Tn}eWae&ry{TV#f}$n z8+;*C>itndOO*&?^hVDM_}Z1a-spHCSIU>V`R;PzqF%`VD3Zh%y3r!^1@hlN+@hUO z*+R(`DaoqROd$JpS%pb0ff`Kd8IkA`ZLNfA74>```6_=z7?;IffwU7Hvo2+Ob~@NW zDeq)uHqqPY!kiz2Z;KmX%fV2vo(O(P2h-}`!vtvLiMxa2Oz;tS%G(RK+r+es}uhBt7+?hAYa~Vvd zBhI~ne%gTSbK7$V{qpOIU5eNe;jcT-UF&>`QWO`z)pNL-X^8Ikafn)Qpq- zS*Uqwg#wamE}L(W@;MWNiy#rD%;V_9t}@Cl>AX3Nk~#Yy@}fnVVKw_8jsWhQ!%h(WmSE>BCblxQYhz7tv*>(rh?pocM5+svV-rWnzan@1xbaFotypB% z3FXD2`3m1x%2l&K2*rSlfJXPS`#P*k`RU-VsI}HFb)(i3vSRy!&(Il^uT8zXLtXrr zJc{wc&+smLnu}(cP*K{>)FW}NLFwCZ$F(utsN>OlRQ`-wXQ*5dw0aGJb4v!d!9`jV zw!lr~;5YT4LEdcU)fU69-&8B)4cx&dCd|^G8|v(RH`AGUHS{R6p8yd@eRqvqz3Khc5%cYH(4n0txJD%pxOn~LUoOE1%N=~`RgUuN-DS8!ufLO-M zHN{dZ>gNnJuuEhjlGXX<(qxfEZ+q8{e?e8vlU5sIF46L>DHxf!T>(3^fft%LY#|G` zseM&vDylI6Rv;d7Wq~-lD0br|@_%Z`Y@md-{-Xw}zzc=!V0U4tkTqP^f}Vvw^{Es& zMCiHm1&2F?pbIbL3=tFk69es|JXZVo)ENlIdE|VdhVohYP(&d-rGB+GDy;xHNMr*f zC=%5eHLdXprsvbO*h@gtl9sum_8E$on@))0moq}>Z*>w~1}yY;c3N&jf6O?5M*fAM zpjHM3Je$1+83UAoM>qNtBQiL%dSS5~;oP_L&BI`HlNI-`Rl1dAhZy9lv7j=A8I@_t z+VDMtTN_SoY>Iee*m+97LPO<(iQIC21&xcM<)LMECNUcW?>>qNf#>!|Gg-roi2W~D zlI?$2h@Dj>-b;B0N&Rw=)Mgj_p$`95hm8Pxi-4ec*I{-qVCca^YI_9Ups^7QRo74g zh8f$DH-_PMp5JK7lH^GnU*`*5HhGn{un>HR7i#xTIWQu#HJKxzEHC2Rb#stZoi~Ie zMmV#1gJ;*YRXTIHw)vxF8!FSm*f^3-+a{}8)fUA6?Rs#&OMqgHM{owxL({9rY&wMG z!_0!FWBAdzXov&+84efyAQEUQdAob#g=B2PhO0>+xn0F;hhQ~j`nwH|-^aNirg{F$K-d<)6c&rGfYH!F*)SF7 zk#2+r*-rl8Rg-Px^HKb6T#?Q-r;c^kqO7s@Gj<5N;j2rltKVA*xIq=o4=S>)PKfG8 zRraV8T^$BhDR?Hn#vsIW$LuC7K00QH?6{e$@Hf!MpQ{d=sh%uSK3Ba(B^k!( znKsh6wx_K8%MKc*$aQ);4{`H;E*fGbnTpY*^ z3d-{(N88JFP~MZ2CxoZbT_*Id0{xP)3YJNRxBL<+%xcV*GqSnRtgvh3T1lhFeDFJh zglLlB-|6ftI@}m#ek8%4IupCXeb?s_}dysy}95<|?+Y@bpWkE?vX*+{I*Ezk=0$jVV3il7CY z7IUS{trAEKMx1od3;EI}6Hs)%&}~1 z9|rl{^fn`_iun+Y$|eUYC5$7BIxm#hpU1hkbRGdaJ66*EVji_=c{0Spkq!|b{{v*P zBXWh2-=+Zxv8bPfbDHwcW(CqxPpWKJ9i>rq@s-DS^V^Jl?85lk?K9Y13GEYz<2p!QCq@US7RIjxr1`%*7z=j+LNv(QLew1Nnj5kfrXNm)R2B z5e<=zf_eU-zQ?3%!2*TMj>_yHENApSpGQS7))ub4nMU!dqH|woQxB~y%yB3k!=(+K z6Ahm^=n|{M(3V5vL>Ez#ZW4_+YU7m8auSm|H}N)IjtYp#mG@1Z_zL|5d?zz|-}K=Y zf;gfLex8-uL>pvEN&$3dwR1U3R4+*kb5-n2l}2MWpP&@Fp%g};6bfsnJ*9w!DU?EA zNGXIg0y|62=!1Tv4`hScF*{(C!Jr+nF6GJ)Y2QE3-n99e?hlg!m$UT@R02Xiu8bI^ zP`Ey&;Cp`qN+I}dFrylK`6`kk;ayThYnAD^ytj&4<7pXbH`5{oIUW&)QOXB z$FrzMw(S|NZ$~J!p$r;@aC$bnEJt8tAiQ9UKKNrQZ}i4X%lm`}UsE%`uEYPMLmQf* zA~ZkF>)J+4X@it3rW^X98;oiY6~UbXw1WrH&Cm{c2q8wVBdFV^XH3(rCMpy^ExG}E z;*aW?kdj;`r1FKX%2B^XvTfdmDAAtmBJu-I5_2w7S=RkhW?Rc$N_1OoUR!PXB|T?! zi&7r^k?xid*3Wavy1y3J9p6#Ow#8e$)gJ#oDXbz2=)-^l zk-UwAO@RNX5&2I#*rc#gvb{ehl5R`|ys6>aX~U<0%R}#GGF!i!$>#qglN2a#d3V^-_KyrOG z-m~myjJM{OfKNn-IVxkmh0iB*o&u?8`xS1IgxDoX^}dI)G1!~pS)IyRd8J~x9DBVt z&PKq z7;4d=~_-<_-(%m#b+8=Cq5G%YVUS1QP!gz6w4e1HdQ|H>wR*tQ~aS9v3AjfE2`nn;fBYeR;%PI~{& z`2Wcr1tkOY(SDkTwOt~ZHNKtx;UZZ8T=(Y5vX#$xaOit~lAH+3f&v@Nn`cQOURP@Wu- zLr@Ah>8{2OIVu$I2j{SFTZh{UqrM989;)6zKhb^CjV~h7Ac%&0UVvpgCfd~eiosOH zhZkJ8!m=!2A!CE77*L3OMCZLnX>(^CG_iAJ#?0<`--1e6O$E3YDhG#Gc9|e^?W^F@4C<#{F1J$omY8Ur*yQYt{xQWmgW_HW0~*^nJ)dsMUt;}BJ5G*vZJk;p+tXC7$Yh38JG_Xz}ltbJzn2}HjLrSc9@8-8s zH6ak<>ahJPWNIUcAy1IYPJ?zms##O?*Hm4e6KJU7V2=?!3;axdbYGu}ayF!>r~#v* zWC01)TpEOq>UGu&(9(USepC#xJ>;g~IIqt?kL9f6X2p;h%!HSuX+`ME7HGj!Agx1! zA`&v=99;F>>_H!($JU`2V0*Gbg*oXnVq{1FnIxW<{5&G6cCb$x;)-PJZA-AX&#aab zK24S4IMVwtFt?TA=_HGM;CAY!>eOoQZ^!l=RY`^~rOb1oWjuYQhLs)wDzL1WDG|Ho zZ1tSQIbM`GlEmvi37bHt?O!44eX8l*Y*Sz&E7d=>k9`cP09A#P;7=&QUsF*Q*vNWf zgdpWf!>yDHG(o>0Ye))kadF;^p~~{Ha>@R}cG}CXt@0Z~J!&y933Qg|t_mcib;)_d4RgwkM~SAQL%MkW>^XUZInpW=PB_|cZcd|Pav>kx&5dI+$Tnb8oQ-*> z$#=8L`NhekMN$2wYB7T^c^t30q=izv#4U05U{Y5rS*53!LYllncIdOjYXi>EaiU~3 zHdd`>qK}~$6{gt`G64p-)YEfw7C0>#IStKzn$MBasQu<+K@pmEdvim{5O%m*8; z6@)0}K`n;TNg69y<+^CA5}7$c-9)8%hr$~$D(Qy)^ijY}EI>4P9I9*(szhYMws|%qkQ`~e#%J(%& zS9~+IhPC<1snzEpB|3Cv?c$l$6-Abi$$Vu2_oS+{nCIs?r7z_2-BE1c5QMB%!yf89 zlS$3NZyX;Y3wIyQ9`9V&Miz8$h>()|ojJb7GV;)mG8wZLqCU>xpv9umA0ORCoY;pR ze&j=sK8AFZnPpcUV&di`R}+4IQQfw8!u~EgzuSfROXsWdGSl>~kImCpxBP;e zj6H$F_tRDTDm7urN-;KxGj!D8dy5Dh*nUkL4KXmTJrVq7o0XG2kiex5wm!7N= ziqLAt$OX1wlS$u)O0X)kn_*(mMwie95^bqtx%<|RmpX|#^cAa9>Bzrg^|&*Y?ap{o zZ`iLVNu!Rf+j5t;lN%{NQ{Ccz({4RqCYAcutGDXfwN87Met33rq(cZom{Vlp)?V-# zh#f7XbnGFK>}gi0WM^)0+?(2r8~&WYLUG9u;iP}iupJZ!`-Mx*<4qO+K;7;-b${i> z57xTxujpiZtw&$(G;{jQ#NAFRAP&e$1!OW1e2qh6Kpe^eNSn%WU9ubd1meS^OMB;ZBQmyco$DV~(?;QtVKzNA_tfQV%bjh^|6Cpj8hXBR8r^2HNG zH-F`XHfn1}qYx~fbY_mmF#m*_nRkyi_t;+qAm%+^0&}^+Ne${3;N)ErVzV|7qVIz54~b z{|NVc6tBzj|NSrAM>{D?4V7Q{P5<6+CEoijy;m;;zu(#$z3l`t61zym^7Fvp@Qf`l z(p}rI##%m`EE?rNNG2%g@UDJ-R|gF=A{d(wX_-p!P2D$%N{{BaHrz_^M_ghRIDO&xmC11(JB&da`$%fi zf|{Z|vr6%`q2#XVL*8cB@cz?3XDtQvb7p^4mck5oMhO=+2l9x)K7r4Q{bsMycRt!9 za^)eS;rV+egw%Y=8O&3S5;6yl&W-UtP9(1**`sAf$Gea1+j#=YH&RiZz5Md)(vvyY z$R~j-+Ph!S1w)?^4sG-r)!n16>uXrZ!6)1^t&sds)_Ez#D}fnk&xQESEL`$=z3yZZg= zwRTP5)TkNi(7#|s>_<#*TU^w>p@)QEoaKi#Z9{txwSJQtVN_IS?xDfQdhax0)&h4@ z9}Ufu{9WIKxtqt*ct$uT!ZQ!?0~b!iHP?huaxlxym?kF+1ZC|cWOMFXht_9k3X~Th zqgC0+5`?4Cbv9VNaw&A3Y+8Q5rlCkr1%t~ksMF3{ryqPf1m?t9GQ#jq-XGdG2fv#x zC7M-%y8E|O6c5OpS-%%JO?@`j%p*aB8kM(oQZwHjoaFu5PfRH^U{IY@20-)POUqxp zctX=OG*q>1=keVLDxC1mLX$=U8?Uky$!-KZLL6d?MDI%iH6N4p#$ZG@y>RmI^g`Gh zby4S9Nc7t6Wg!-kVjbl}21+#ZRu0vAwu=1$uFgh9dRh}3xnO_EFo;u5RJ)z+oY94% zQM;E#=l0q{_wyeWLNC?Dd+R$nks{{nTg#t5bAJVrq|cEg0;5|B8NKo~{WQFu)lY5x zL6^?VtV2`F8#h7-2u&|m<64?muNK%p*1+?}>fP44m03ihNcP+Vyc_Mg6Pd%;QT3~V zzpPRONW|1R8~Ftlnf!zxugq{YC8XY*oIs^voa_n`QkQP=;_7^9#DvwMZTvuKP`2?{ z%n#w%-(z<1y`bVP!P68ye~y~k2(Upw%oT~XB({{ysyOe%k$VxHO_o?(wX=AzjTGCH zsgfN^6xBw$=Z)40QBK-B9#~jCTRvYy((TiE5KU#}{F&mjYq$fKi^VxuY?tY-xbIxG zda1Jafd@)tXWV(o9@x1WoPFTI2Y4fx57r(C7I&VjUR>EfH|s=fsF~Fa7gn9dEWRQ` zbeG!9sn`v%AQ*RYyONhy&drTK@$5c5v0tGHFd}e^jlwWaGm|rfx^hTf3|^w`2oqg# zwiio|tXPd+)=DG1vrs6Z^PTfujeBUJf6iCVg_6yA1UgGgsOGUpIC03s!Nx(c_ZT53 zh6G0Rj2s`95fI^p%2B2h9JE)I!cY;(){2XGSR;pvWN$p>Nzf)ET)u*cA4{0Z1A89%(1VXX_5hy#lcm*3U@>X4ep1d5lO{l!G%3lX7rIm?1)0ft z5}BO6LLlqOcqv43noz6~_V?H)ckOs+ef+?&`hkP>gHQ15!S#vp=MU7MKUzQf#Pk05 zk>~42K34zO6Gzjz91`}YCIS_g}m;?u~1%Sgv{Xukp|&M=Mh<| z$P?jq;`G~MI|w0?3Err8R(4d$!cgk0qRTF2-^zB1q=*8D)b6hKUd0(uT2!(zz{1Q* zNW!n#{^vR_|0Bc=1IvSJuQ~G8N`ge)ifdt5+NU6TFaIOL20vQe#8w7V6Z~B}%j?YQ z>aBM!7jlp@ge{S{Mz!>m3soEoP0-V^+`Ei>;q0&kBPPNbF%b@;5Uy@95e`8*t|Gu8 zT1ej&wf|OgH`r!kq1$NjHUuaVpO^rIZ-gYl-F!6pZ>BiqQKL9#X=%R!F1U-!+P%cEB#OA^PmBVEt%?f%iSKbepGXXX3>L;&dq0 z%US**Gk<#GE=RU#D|TedU)9+IjBm5c=hiF?WS0RTLC1)!AwM4?3v0)`vSf4-G>2d~ zKcqr->ktt;?Ib7`PoU8^UwK+I^-U#ck%*U^if?$Du$4-zHy712OXa?+6>L~bGZ2acs#NjoIX5M9MpTupn)ubAf-VU9R=l8_~2be4ig zHI@(S(1yF|BQm+?D?{$4ALzj60LR)U2cu}rQY5{|`G{SUQN+!{$T4fcC)M+=c0F(8 z9c4vek9H!HsGrSUyAxP#Q`a2P9d?ajzrCqTi5a?a@7@zLnrrS^u3gW9do-|3BXSql z?rCjyj*ML`&9>FEz_3OiokyGEL4Ld^SV@3(=$(DenWoVN*=sl@$(ecH+k+uf7beg=2IZ zx}t@Bs*`(N!IRazG&p?g83I4Gd1l5~As1XLQy%=$jvqx!0Yj93qd*w0&a22Im6~Uw zF#l;l8f{-LX0<1W9Lk0bu0kB11Bcrh@p%Ka4d8oGt{8KoEz|CLqgXZ|N76bq^ zv3Gpu_9rIxDz7~a3P$&x-o5|yo_!DSl_0uRgz4nQqk?uueLrdJ;P~jd-Fq*R83rwP zb!CwfV-=!azt zJ=Xq3NLWB`)IzEtTUoA%Va67YwY(;s-4x$6;;x1%8 zTp+*Ir4_f-^=k0XIsBj&1GkX~)V8<&!id)Tuyy9 zw)yLeaENUF`a^MwU6uV`(&ld0uM@C*0xR*ptP8Pw< zWLs|3(m0LDWbw@EDpnn2TaA;0Ab173fd`7oTNEZa(Zn!Fvx}LWf=ih3B6@m~@c-7u z6Haync&<~YX3RxV>`b%Bw}ffJ2YIp&d*BNsH$B2FSzjg+UnS~AzHS5J$Nl}ztuR(L zEpy`;F9cs?vKek(6pN4VFOrL9kqtF!k)Nh&PIBJB?jXpiJ{MUk9WvdoW%_lu{49kl zi=Igoic`?o=_)}gb}&iCPM7V>ETlABkgi{0CboaG4fw3!Hv97B3Y)QkvvksJp<|;7 z`IOslFOygU@m3g&XPk`n%cZ5_s2K#xt}%q+XtAhRUA#9_q)qIxUe{VEe_SBAEA2ESdsRYp>m zzEbL5%5K#g+sgY&DsfAxaJ@H?bKUOV{cPu@JL9*jJ*oq(aLmcyp;Okju(1jTiCR887+kbXjJxd9n@g)qYHHyT$HM}e1r(f}R zH``qpi14m&J4xb7!?&tmclalPjlrWoezrEwwL59|HvYC;6Ksdwui3l!9$vniD?fl4 z)K27*#pQcGn_1iGC6gNW5j~4+G8%o!Cd-*!9o11WMZ9}&X$#NXYwftN#AwXkUt%m~ zKVXWpZPT^;Y;x(B+@klesz%*19J2m7;{1{TJ;-0h@#Qb>AjU6iLiDg0!@6nznPrOl zi+WMBOlD}_tjmRJkc|@UcySs{-cL|gkYjaW-IdtJ0@DT7MM@h_8rixOuI)gT9BdSz3x*Ltek6L5_} zh=d!1X~LqhZKIvkpxNj-M(~*f9}sSA@l~F1 z%BE+XRN84>vwA6(MsCy3+jRJnUcW~JWKry5?=f$2HKB(lb=b+F(G$^P!2`Oe&Ih}6 zFiE2gvN11Trrb4+Z{&K+4KSp=yS|+nZB7g8t5} z-z{V%bsWl!e7C?n$-JBIG7B8^z{6x1kjxVQCbQ@edxWf#w}=P2rR`iwNvKjXO0^hW zgEz|}ZUK4#0_1V!S_Z7xVFna?n&ZBCNu10lkXf3UR-Och5(7!8PE=gk(X4lY@4KqK z=rewe)zyWW)#tg}7kpm3jEoXW`Ja_}SN>|h$v8*6jI-X+5gJ{|qXq zWlk^g7u32=pWU~6Kd}-&sNrlULzVc{4f}ST?a(2Xn%gXpMAgNJw>e{I!8pnX^{}Rp zO_>`CUGp#F>NbCgTpksCMjHHF&{{M65hIar(y@P_Nv41(9i4Y;S~0mW-^&govL0k| z*uU@O^FwgTOd|b}a3VD|V%aQ>k3Kq&M1hC-@t#D1Pcq8hV9T5DCWL`I4WES3J|&&P z6Hr{B$x!ac7X{`f{ux?78Rlw|8QSu+MYxg7@ZSkO%`(Gb-2GB8n3YTE>@0>)P)^g5 z8w5idtQg?7zHMo%9Vb+(B*QG_4`8+S?%-7Ky~d<3a543X3@Tc@{+tBBTjbnO0x(Xg z^D^{m!~DOD>1EpMPt(O&>J+!Lp>B5uH*o|S`A^|~OURlq2~Ny%=-H5|OO%5dueN6S zm%;6M^9gP2mx9%o2-;PW1^x;`NX#WvJbX1;Jj{f|C``09(=d{mZ9s+)8gV%sDpjS= zC>dAooEPAG|9|B8=iDyZ@OJlx&RwQKzc@cx@i|aSvKP1_CRRpQ)pG)=51C;@H(%Vy zG2#Hzr82!*JyIU$k(kSKd}72wOG!5#IaD;o7b;!A*Jq_ty0E;oqq2KX@xh07JbBN( zJNE2;Fmjw}!yS$3HMxYjH+AM-)*0{Xiz3Dx!xWn(wkY#iu8zI=vz(-r`9emsW-}^h zbz-M=1ly}!%J(c=WA5d#9n0Yj@uaE5yYZ2bn!Vdw|Gr=8jjri z&)jPiSix4X8M`~HVnMB~nR^t{H`K81)01Y7+@rHz9olGE|3t{KNX6c%URzSO)vlcU z-?zk_jHunT8dZdP7k>x%t9+J2Ork(UT-S1(F%jXB)wnI^ZtZZ<8?hM27O*ZqnKQmD z#l<@lS&Sd#uCo@`Gq1H+jKU63qPf?b()AutGFz0v<_Q|;2uXvILDsCfWxmZr?KF_z zFHRHU z)dl`ipKbnEWsZ}S(PTv4O1>{cNtsBNFpZR1WN9;Il(%3uxuvwVbZco_>9(J}6rs%5q# z)B?(FNKcRhA}$i;X@Pi}21zL*0#BrTSo@*m%2p6fL%v=- zT|FI~E+c2BuBnxZ(zjfoArbxH-ToaMywAR)GLlaFX9>HDWeYbde$&Mi2qPj|W|f5w z!7}EabFgtnLbcq!lh5w2b?uv(dTKvEjvm(WzzH1>bDrNf{gfUU-go@zXQqzLo_u!d zz#-C`o%q51wLA75J3jsN@uNqkre{BP{JBF%XHOn_cIw2`%yC}t*>`Aq_J=+?b3`j&mqesuc4%txn`%`m*!PaQ`Rzn+;oaw5DpyzhlWCyviPcjVyAGvT%FebcV* z`F+QZ2?n?9J96;Q@uyErJ^Rd&1O6#e3jP6;Ja|}p%(ry*A)P(K;lF|~JO0gGbK3f$VSD%NBW$BqtmTCFH4c$K<@nWU0LnGu;p~Rg$FG(-$nV(Lgf*W_@CGmLO#qsPz(8jy->QPZv}Dcq^BMKeY? zxqV^4QBNwvQBPBveSa0OILrP&0QWkvkr7EIJ2Droh?w#;QzP?or7g*n78QMrMwlb1 zBbkaMk~FtG$IWw8DxEaI(sYva%p1{3H}J4C`iRU2&`LRkG#29;D#=7B2`UL{3W_VH zlb(8ibdmuwrjra_Ml8j2QhQ4+(Mjga){Zn<7W|Zv!AK+IAPVpUArO*An;h&SQ3oR1 zCpDsy6x)c0V%QgmQpY`$gOjM9sMxp>!mi{b0QkqkOcFQh{ z&#`b`Q3x*_;DS4WKhUwLTnBmGf#ct3AsEC=h#IjB8>Wc*q@W{;N+Tt~XYJN>b0duK zc31OI*Z^#3o1sS4RJxEC{!#k9po)?ouGy!uhp!NhrKXf;5=i2*(gnTdoLpkst2cOn z7i;rQ2qT4U=_Q4;G2Lw(=w)Gb<;q2iY#ImhjpFG*Kp62r^a~mA%SKBk?gY;u9z^-9 zBOp>%Z0eC(u0KP5O`~S~tvF8SyOwjKq?Wo-lIySc4{UVNHXD8Il1J;jdNESlyQ6BTn{T<-Da+(7^lrTrH!vvFHB^x|1)(IeMghQYTN!ssYGN$^BQ_Zt-%u3_g6hC?}(P@dtt>Zb;Sew{fascPetn0`L z;3~nYk~nmkP@J%Eb&Ir4jP2STGlb?eDGky$?7v(#bvKVN!O{qtGh%x#veAvSr^3^X ze0lL@MNRvnUTE6xgu8xRU6b`TG?N(PLXrYajl1VZHk86y9`(nIV`F2dZ8aD9OifKu2X!eocj~Hq2wLyBH_qU5 zDaoqc$4)7F@hK}%ufpIco`Lecw0)9R#8Fy|L6H!5{3y*hDd{?vj>K}Id1HLTkX4T_ z=^loeN&63VX<~0wy?W#XfK?v@rk(swg(9Pid*?-UdYh1={%)%OJYkh=yfcZwOXD~v z!!xNGt7Yqln3IT*n9Crt^5`HV7aCqB>={zXmA2;mJ!(2DR~dhP4Uh{lr2 zB}7@u=0FbI%vtjvZk8AsUN-cVjhC^L5^a=S@9(K$BI&eAVsd^<+_dEC+&GfA0Pr(m zA5B&b2R77Xhx$mqxT$Hp5rra8l_tYNTARL2x*aM2)JyyWFOcqX-VPf2A(a;1_Mub z2gvrJJhTuN9=hE^9X8p|_xJz5w{wwXV$-MWk+}h<}NpziA z371Lp@`=z}@Bv06=mblCUmf6Uu*Bd9u!OL?o8qqA+>+6N{cd8(WI=7Z*0N-5^Kiow z3nf{-a(*Ubtg~co60wKrf@3zXFZg!eEH0<1O0A2Vrz|BCcH(6bKM;kY+~6vv%hHu& zl&`=TkxX0kS{Ud++VjO314C0&((RinV~RM52@7DO#a#TvDYWYoSej#;BnS2ou})3( z@y^-ux#G)n$gH7}0_QR={sJOUntDXrIK%j=)M7_2hQS1@IOpyvxac2thXQ?~8${|C zTzCd2ch}&wfYz#kEv`#9Ve?CbN;%=S(&E+zM~~l#10H5>(qups4bO7O_k!6Z*?BJ{HbU!X4skOy_&&%;Heh7NY4zq-I;e3 zcY2G4Ru!?hYCz(ub}}n%vkmGSn+XI;gf{Z9I~e5Jq@U_! zPDP2SVo&z2@3Cv0_hg+1ljyUw#gM1Z@g&ntla)$R+9fSDQKp>#noE)E;@9`tbr%w- zA6+-PZ*#;eR0m~?9Qv%s8|?*W@37;jH+ zMEme&^nIe>B(ypLy$7#pBt!mnXm|W*C4obi5qVHTJ?hIQ$U;pT_S89|ilMdV-UoM*(&xHL` zQ;|Uil=E4_YCH1_hs+5(z6eGAGF!a;yNeaWa1uB1k=?1cJ25lXaJ5!<9dGH97UgNa z7V~0O%hENx>sr^}%U(nS=f>~6M)IWQzG*mb4dD#i>Q5X%w2&-)g`i+J=WgjHq`& zffY+-BbPG3lolJ;F=D`Mfar=`Gsl|S@N*|J^8J+^Z{z6?+kzcuKv90!cKiTM2uuNF z;@&6p^M|#!H6#p=jm8DdfDK6#-L5AOYe+OyP7R_>w;60Y!0@y#Zf6*kS_zaLqlgpy zcw3fq$F&va2`W`HCy6+IDOXuRZzy8My;T=6!zV&|f;yas`cJE0wp(N6>yPWMtp|fI zO&yk9-MBjJ$cH#~##Xei&FjG1%B6a%dDptP`4<>W6MM88vy{{Z-i$2)S1qKfoZA$G z8)SwnNEXB;6`_#j)kb!{x2j{GxfklJ-+t$?D>T;;doQO{MrLWtbwCCIws6H;!_#X{ z%DoQzTYeK3Yktsv587M#bZyoO>ieEdb!%9ozt(Tp)WWV(Ub;E%>)cxKcIqv4E4sSU zKG8~oX#vEDCHZO1j#iAbH~4;?$t#Q0Nb&kDhU?eSinh*eg8zf&`)fM<-#XaL$9i^~ zd5NdPWp69q)_&6}Pz4{>D%el`u&dyjfo?5%V%tDU_K{jSPg>`+3e+bF~XgZsRw>LOovB2#C0ozl{3c zidQY@9)gtA7T{G|{2OBAP$M51D2KM;VeV zbNa{%QCSHwv#OuRbdAXWn7WnnpUi?M25W0->}7Du zS}|Y>QbP3Xw_bd8_sTx`n3&fPl%;}PTHwpVt7CTuD@3Cxyt-#4A?5Z66CPj!kzlCgZ!(}ty7Ie#UGT-HUL6@%ID8A~yleb4v8 zGK$5_5M5U6CDCPNQqA81A8i)bs(jb;gDjU#!M#*dypx_7#dk`XUsy6m^SOAep%HJ> z5}zs%0m1ByEgz`_w33~;caSQS11A=VnVTvSQ9_X@gvb)6%0BjrnIIa?>YjhbJ;Txi zY+@h{#pxn9sdPd@Jg|Lg3OnU81uXh1^oo(Rl-=>Yoyqse0CR4R$gGkDS-;88V^pn0 zN)aP{-b{_`UK<~yc4>3$S*T^(X_Ml93I_@&E9H~HGlYF{=^k9VnlSFB9`?^2LTq)$ zmpU>$R~jZftU}dVIU~b5)urf(;w-;kj6IRIx!5x0Vs#E9h|44wf-%4>J2qGF6-6be z4y+woD&bo6jMZZOYFVZN^!nU{#a(@f-tC&SC`YGji%TiQp>nk3wGYj>MTi5Za-iBg zNMa6J<8zr$Wn|_H%>dPO6-QcriFU}2zm?e;3aYh^gO6!P+V;I$aKyIn8*N~?9{IfX zri0-!C#~O_Ze_%Z2ghh*f=w~H!B|w)_M<9=zcQ)Q?}-%D5AC7U%j&=7;00))z~EC7 zSz|N$Dbii$Bp|cYmQ%R)D!^TLEki(wmr0e$D2Ptn+J&HPL6O*i^@!hYCELQSP{~F$ z;-Pg_)G|RM6#eb=tLR~st^8>D~|Hqp)*5th9REPPvbd^R<%`V3vjbuvj&;PgqcJe zUn=QGXr^Z6nDpbc7Fmy$s-#A%1J(1S3R|2k2D8odhf4nmt-1myl6G3~MU#4Ng-aXA z${1lI#=XitQXfqAH8rwYlM&bYYy4nNk zD911EWxZyNj@b*B&=uJ4dCIvB4>PD6yQ*KRjg^S!nbxtME#>x8V|<<9oqMCzXl-DX z5qv{jA#1izaG)j|R5D=bt!TZnrhyK9?TJfUNV}}NFWwhR%ogtM+&ff1OD)v|NY?fts zV*RuD*SgZP{JBimN?)d{PP~am-(+vCn$~_>_jfnzrPjav zE3OPl!vXaM&JhfB$?%q_+FvEA_Quv0GvhHEFh<83wBUdaZ|U$`I_wdkGdpE79XrF%_CX(32OR&R1-aU9 z0hC2T7;~^XAezSMDT3cru^XeqCY?zy^y^v#Bb;>Q?!fUurWI^O`)=V}5sZ6ab(QBu z_mrtg3jqSPT2|7L6vM8?FR*Fr;4V%gLEmPv;S%hzCC!rC0#<4>!r0h=M3QmGS+o-+ z&oB&sr1m461~UkwZm@*9BWs5`(Qti2*!hwz?HcV9&*^W}Ko% zvY%`tN?2e>D`dwu(xlaO=#);Ci8xrEJ})2lw$cN=`lo8VNm3bTH6OBSqlS|Rc+=-8 z;!+^1iWE?xf>_7+1V0#4_7?jD(qbOS%h4g1eUgn|wRk87@bd!r*C2#SJtj~Ra?<+j z9XH>j)y%8rB$ui!oIwL&Tc{0kV#r~EE6sY6;H%v42*kclUmZDdmwM>sf^9(hYGwsr z4U771M5#%reosZ)kjMHKTfMe$f0w)eIUY;K5qVP~TVIo?Nx?h$zqTz0YeY&+3>)y& zZV0r*OeExlr5#PvwSB)&Ef8xA^SWVu&(P+ ztjoiwx~z|)G5cU)H_7W)-n;Vrg+c9=_b-3@#6>9&MKvKHm zm*`Ezv05kxj9E|-R51`D;$}zA6>B47T6Yzmc6LNc#Vlart$v*84goY_IpfRxq2g+-FtRh@wk+d?F)Sr@>EVm4ITRk#r|`w~p4@mDcJ+ll4e7PGd6lY!@nx4*PK z;)P=icT69Y7CDvE^`*iQ)BJiy)T|?5^|tSl$-8vqBX%j~D4l7$D=KXVKTIOn!b7{p z_KrPLuxzlM&EL;#=h^Kq?!eUDz2MGyD-6YVJjE{wxN05gZ*?DsSd18QC~WdvKp4KX zNYVI|?n;mR>%DzbzLoyd?*CSam(A=5zwb}R$%WeN`-zY<)lzj#X-w=GWAN>CvmJH6 zfhxQZb|WBkD;qyiRSKR7dTOw2z(m!?17_qg6o&=#2<5M+oIFzp5zR(wBZ0lSL4w=* zW5JS6G~RonVPdsjpXLqS=4_k{RNX^IR<}qfiJ#9T>c>d{-=xR%Ml9M$S_9f?bX3UJ zco2o+q43tG8j^Ni0Mx}49z+uu2ucozB$#tjyPDwrGb_DyqA!L~zgGINEO;3zE4y5R zF6)ObBg$W+AK7+4PJ#-U@@%6YT998Wc~7DBzt(?oBjxhfD75b7&w2{2$EY*C^#c+H zkib#YTCd`ST|pq=ey`-A+LVg5N0eWqcg86Jpy5!}{G&$CJPdt_D376txRI;=KCvlb z;S>vUWuT#4EJm?j%aOIOXN7o^(5F8F{}V4=ZDpE62+eNeIH5pof5{|_WqB<-qVY|#^CvGo~&~=G?R7paqe1?5B}I)Us-eU zkGV#2L+d37fb_g5O8E72%ngywl%n@znaYqx4p!90>|+@dNZ`M1vIy;z2~@#Ft=fY+ zh;j`+!p?aM+tWy0UR+SN7P6kaEFkZ#I79mO5f+xSA~ag|S5-n2Ei0+OYQBogVjxHs z7)ktP&QNE4MHkWrO~lM20Qf7@Cl;qI-)fwMazS;8w^_~Ov9>1|(`o-%@Qn!tS9?A7 zvD;(L-eN5HCH1Tg9ekIz!vytrLHp|8093H-q$ESnletn-gUy6NTW{gf@IpWZW_i?z znU`hzD9WB_=Q|)p29Yr+=SQBeBKFk>*;?-$$sD{Wz}SXDUhSKloO@-FP-JJ+odlWe z0i@k)0g#e%9N}Aig2OvSUItS%vYsovly=Brk{mErecuv+S*D8)(cEd!9|EJjw5q7l zI;%q`T5?LG<>>N=mK=x+?aWq)j_k-*q!p1dGl8fiW+q-zyPDRcScSQG0>vuSH_)6W zE`c|rkIth=5zq2N>a*&>8badWV1gL%LQAemeU?LghG-;IpIysIoFvs*8^u{~E4iTk zYvjTWt-t7U%=LCci5H^@y;ozO)=9M?Al~})+gIdC!=lt^*s>E^#6IIl3v0RQZvbJ<&jMUsNHHfbmqxD0*gFOQWq}l81Hm+n^Pb5EDM*95qFh-ID z6#plN?cHXKX66FVXK8t$rh}`}iV^4KQW1vAHDj4>%nUV>8akhpv5oF6$q1R>(s65u zQxa}(v}1_26)u}EhBZ&BNiF7>v5WD1tix-=LN(q^7OJqVP&i(kTe!Qh^I(CX=P}>p zSSE2>Dds6^D%oA|khatk)v^d?D3?U%3N}yC(-CiYbL?QDkzfv^Lf#H-E>gAqL{?JMxF0js|Tc(Sz&iMnXQ(M$N!?jCW#eb6uwJ`{MEQOyMnUYfm$WDe=EZ z`H(sEjNzwNdg@z~V)T{X24Z~2&KQGu)(h?njXt#6V)DiteXxf7idaJl?}rk?`~31} zV&Ofkpx*klv4&>o4zmYiYOJCCHr8-xch|)s#~hIw6DvYkBiN$^Yb4D&!hd6paC^4{91Td5#$oE23yO zmRc}kMb_t)MiAy=b7G4AI+%=Jm<;h4KciMAc#MsScKAnHcG|ngYP^*L7N_cCaNi+T zj-5Dh^0~s3#OE9@96s^EV})lwcs?su>IOMj2jP2Ro?k&IUY{&}_?{@vw>nv#Z_QSh( zJeGddRpC~?y|`z$>-D~9_1dd@NL!zNv$^RVHFIyjPiMBfcNERa4Ta8JQ*~zBQYY3l zPT*m8@79GKIxsw;%1`4;;zI3wKaSDB?et^P=H2KSz5L2P4SMR*GM5rlKzaqif^gaf z;xF!dTLn-86Zt2jMA z?kO9ez3s=xN=|gawdBn9rZ=M}`~W3Zg|4tr61yfEps-QV+u;!T*VBf*5D>AV`XE1I zt~yjylNveHx|*emQ&F3O+Nr6LPou2vGgj^RX1RMeW8SRr4y0igc4?M zXI!DFt7W~qIwY%EQ45B-s!j>qNs&E3^^rZ$7Nob>1FaQSi5ik(Xei$p8Vzon=&S7q z^jlM7U*Ax1Q#5?T*6QnvCwiPqi;bU&Cz{5ulcsuuM#=G+W{J42sjieMBF}b79${l7 zS%+j0ILgOXyV*#^pOYHcOr`7^3T*;qCw_Nh+}CZUQ~474IpRBJL($uGr3YJ`^fRbg z@hjvw-(zX~dQ12zO=8PyD)ZhFCW#iwQ%|5wcfUS??dnQjX<((lfnQ;8PU#HQhIwnr zJf>VL13a;&F@R#awlavyeGp|gDHaKh+JoZxE+i_vxjf{gBaJmz2O6aK%U}X|@pq(D z<~KGXSLGW;dP44*k{soB@5^1(cn}pns&2D}|D6WGzp=sp9o*%kYqg=qnkyNO0|cGR z20vfwU+KU2^9?*BgYPA|;W})TWO3Z9amL_pE$SsHo!2(jZt1#`BWH?><}k!)VHYp| zt}7W#DEsKuP@|7t4X@BL(sX%6JSz3<%DTo7?Ka!`@5x;JLTy81sJ3xd#v0j`^_X6+ zH`B{?*HKd4$Es+G0+5xsNN%PLqU1G40q}N-@8z}N3lh{=xPnf6Bc2q!f~hoaS2ki5t?{JNe->}9zJJu+btqpO36C2{K;l$hUVq)Jqyw*<}S?jFT z-)VU73adUo5xfdCY#C*PlT`k#>Ea&`tEu1r4B?bz=qEK7tgGo=LUyKr{>3t46h=ro z+0qux_$IX_x!}^FJ#eg~@YQ zd*P3}2KwNOAI=k<5BB4#*o?&NePJe2^+$Lpgxoai>b*M zVe{@uhA~39@7@r|(Re_l;jc@SILHac1CL=m&^<6l&~M$D=9~INV#Ikar^JZ2B*lni zN9rl{Vny9;*3@B?DOptG3~WzYD&~Y$^{vXS6h)XK$C4sUMNYw6193Fv}zk|W0)NgieSM@U-5{y+kxYQFkwLp2+dT|xlgVO~DlXAbP;Hii&Bow1jXL7C~ z^-BrT$kJ>pzQU(L@;Jc|7b8Pd406)(1|IbDq>#q5F*Co8YwP%n0|M!`GxHnx%Xfa` z)lK}`sHeoVxoiTP9o4T9b6k_5FL`^c4gjBgG%kyY6r9jQdv!1jZk_;9PF7qpN0i7fxnHEjrno! zF!V*OJAL{q=BUDG4tuLbl{s%!fFoVYMcG~0qHF|(|X zdGw)l=03Zcli{?u%HXFMa$0I$o7UU~&BkZd$aa=?b|VIHGM6+Lo^9<~+Z4zs1^Lw8 zXb%MZh-ievTFsr~30W^HJkgVmDAYclP^YJ@@7IYZWJ}%R3E7~Sou)ZO$Lmmcmo*f+ z)Fy_I50T=irEu+ah$AFOV9-DeP7Dp)EGZ5RGdE11b1pcRwcdQ1d`J&#uJPm6+)iBh z-%)d&X@&IAm?YlSZr)-MKx)duM%oTJOhX$05*W)ZnmXr>W$83oXpRj|xM;b>chD9e zjP+pVn*{ zxVNr1MW$-EB=y>7)aP_is?FpzufM0=RE9|$725>c$MFuHVQ*5d%hF`v%C@m>4y~KO z=EPduk%m#HKBR_eewd2f(8htDzFxwba2)6}(qWogsgKr@J{I92Ny!*>0=j_yAOV}q zb4AA(CDMb+voF}mTSXZ+pIBTxUp!YXmYfHcv0yG-tJ8ljSyqzRsLw?cnVLF|7s=w~ za_R8$6fyA@(NnpM&rxO~OcwR73PhkhCmj-2uw)x?N>bSjx-3O(eICMCR^DZp*uwvi zu+dz>!WhbEsB{TUms(?@zIGKIac&$s2 z+mH{N>D`>8tPAwxQc7A{l(Gv}i*imqS%^k4ZkPPr7E$-zFW3s<|tms^=vbxH;ji|ioy#n40d6mc(%OYMvfFT45E4A`CQhD_g&b&FIK#jinZe8 zWwg_w2G)m8iNy1+xC4dw!3Po*k5E>uKa{dCjL70NuI}vz3a5SC7keJ1>&YlyOH{5h zurDs1tQ@d+loO_A`|l8zmWw2W#)ocxajudK>Jpa{)!dW~I;EUS>B77QjCNr$xlo>| z4fCoqxa_FS!(P-1ltRVp=FI2L6gs--lyi1XN)ug*D+u*+DTD?zh-LA&^CQN*E`FiW zQD9%_B;AHiGMK0R>gZre$*_x$3DzbWL6GQ+0wl)fz*g5c#S5ncAu1v?Vx;BOEnE{h z;b%T67kZ1H;;_>JWYLXolbmM9gT3?#2OxvMB0TZWv&Ju;{67^*+>jC*8~sq zUU7*M1TzV6KZokwcHvC}&1Zca{upQ~@b^uCOaU3&qJKq@nJ`?oVyxMKST+El2xuZ8 z))sK4n(Tn4mEd4m8^GyJVE~@MyF`pMz>)`?0IR4!4#^~erh{N1sPr%q$N+%E*qO*n z4pi;5E}?gf-=$DF5?{i6-T}wt2iOBX;H<`9h0dz4V2>RMOOm7j_grP+k^5SB3*C!iATY5Y-Ym5cB+Lhk5mKgh|VpFr!2$%iL8sNqd9KbJg;WX!L9f&bkE~*T{%ywrNFJWxMeU2hJjf6=s6P z3)ZRB`ZSUxA}S1-CI{Ol|9Z&uLx*=(Ys+}=yj%?Cl+41yb@?cLX61y-`pn!b;|A%_u~XGOm%`_Znb)5^Xt0!dY>|W0j*wY<#y6@4Vl|E?LRiGrg4$*&P&o($54l z2xZnr_x9Pnq1rmT_gxa>)HaaV$IhasHrrDhdHZT97haXjVscX{FNK~DQo7k%atml? z%hg+rJdHwa5@%xKcp$!_zFyFgsfcoyw2VWf>{-wA1pFn)nNZXfn50mUlnsuHOL#Csxkfisu+WlIupVTL=^=8tF9g8Y_-?#JePUEL?srHRSJWUSG797Tko|Y z0e)ZDP=XnWtDK-pE~yex=~o{NkB)Lul@LqU>YLhh-ew{#wi3IzhVK!+)!*kJY9Sz< z4}X@Mdn%&(m3ZS6W)C>5JGuQ<(({Ix6;MfXZi<*Kd`!X8~U*8Q0pu{m8iTbaa0 z*7V$c$?{hEiNBU*DOssXRd$60COV=49$y)#-_hu$jy{%l9}3!mtzBVV=(tDFaj#ka z3SI+ia4TQmShLda9mvlV><-0Q3>>d2QJ==qp zg#fD&MG`w2i_6WrdGAbdY3cup>Ji-r#r2+{Ta7@PR<+3w9kWk$4}X#rSBgjN4|!Ch zyG+)Od2TPf>Ok-qFBbn9<;1)yxTG{!#Y%VavcvTv!Ngq}_Pjpl3PL8!SUP*YFCinP(fNO;$I^AXY#!?(`zNPVSKM2US<1h zu`6I_BztK5=nhPdcNE4?P81F>M@u6|h?c(q%Nx-v!#u<@dSrN4b!lOagrNx=(rtxW z@jO&%5%*ak0_Ggeq+L7jNFIfo2?{Z`O`vOGVCsEsM0GS&OGEW2G^a&nX=Z!RE-o^5 zl}Tu-3%qwSw0ZUKSRJvg<2DH?NTU^8DwPZV)g&RhQ;k*ZQ)O6&cA_!o%2mj>*%Ye5hBW9n~yXj`N%m)hGH;%ZrSorl^4RMiZG<9;ZJM%1BK_y!J-e1 z8ueYhPCk|zWN)l2CsS6S27AVfc9bn_c-OF9gIK)8c3UpYEG!mliGi-v%CjuIR8X7; z3a^Ru9N}drEa=Pv?`Cf|(=VO{&O$X1l-a4HJl%lt6CeSd$3%V0@x@CpV3#VFi?o3} zw{oK1Q3$=+1T-9KHsono`Nwt5fVHVbJ}H>n26f&KH1#5DqBaOIu5&vtq6pIXP{dfo zJ>SOVX#{?c z@U4pSKx&Sn2I>qITq*|GE>J33CF1BQ?pH1q_ls<%cY|%}+1Fk*Wpr!Qt$18bjjKr_o+*lc!%}pw!j}x%XmcibEZbJ;yTu6O1 z8~RiwQH}5onSIFlBZ+AL^FGuWlF_vxmtPvP`_k=N&}WZ(){)iz8Nz(eEiIIvk-RNU ziHB6U#<(gE_Vz2ektGmqo-pJJ)Va%4syS4uj5j%ZIP+>x6ZiPMu>U{<@g^lYL(qSy zvnbNtYLAngAsaG!!WPh{d4pgj70f)^t?4H;cuvmm3^Gg=b8EVH(fV$?UyrDlJFJ%w zYFb6f=zLDwGTg|l>#7h}j=f9&LK?g|R^MuWWf4L~jfK*%PJF+598bN?y884~IO|T` zMWWkVni}l}BfTZnQ<-yGoBuf{+LH6c9^$WyBWVgQ3@Uqqd$>kCC&R=ROsW2#6h zBKB`*%v$31-_JzP_G;I|E&v zR_l@C>%^k>YPkUBpHTtmM@|zkx-@V!s1&~3L`$E zjHL!3&WF#E=pQUxUc9tG?!*9s9x)Z9mtzyu+aMYaqv2g~arK_QYZK%n(!PWQTQeQ} z*QWltDTR+x7SmGi3K+VIIscSc(UQ{h3`Zk!Lv4{IM2?g|qQqUK5G25*g7duDPON`Z zg+YrGjD_p0w~AWj2FiurXi^fn_DKB`wZ%F9`Sla|_8I#UHB&r8@#V$264UFrI9G=g z0jME>J{K>DC7_v%ijUM`4A#4-h;!?#kkIBu6{q!ZO(VN49jT;gxLl-?cKS0pyEW_D zQiD2eTO`a;5vON}24*B`VB!A4$k>k2>$TI~diMxu7uhVoLMZ;=?+d1OB9^~TK(vWi z-ViMlh%}>@$AOOuuNbV?0ZEL{vLbRjK}b=SH)0boT~^VCDl_@p47|3$X5H1NDw;b5 zn_b>a`MwZuwuD_H6tyfQmCRc4W|LBz!Qd3H7te#?LS8xnwX}=sFsi11fPkCs-TG+d6UO~EL>q-5OwuUu{a`PF4B~`mRsa_wT~ZlY`gFi zL7=P1-=e^;<({K?(8$ni(8znegHm?3YbM)+d(vqHwz1)8w-e1<(?xT}^svhk-_&M3 z@)K&asFp6sP+70nHz(R15uEQLXsEMK-paptL(uRuYH1T_*qCT*WUWLRrt7fN8l2CD zorawr7|IXz*p6)h2N9`O;GGy8$W`n4Tsa3IFlRVGKsQUMXLVq597CE331eEui%vR@ zOp{)chDN5K4p_AVhVSR@KUXu=h%Q!BTsEso5-no4tI+VHbA6BSH85wD{A+%7u0-(i zONmN0rYiBEwx=1?s%gQ`>d+D1{s=qiiz?Vj$opVx0$pnv;t1kOgb)ug!<)a-^?Y}^ zdnS{RUi1*i!r626dRcj26v|@2Jg16JQcDDNjcRPMP>6}eZ-r9s3}aFC7ZIF5<9bP| zwC7k5UMdTGC2gTR*&9BW6r&k5g=nYZv(1&KbQFxdvU?WN; zxo>QT==Xh(c*!+eDf;MKn^k{PG2etx<&`dk>ZD1MU4y%%c}Mnu=3NL@w)+1Euxx@n zMP}7y-gs|Pnv?68YXULNT&{=|x_v=cn^2ct{6hg4hq^W+JwaBzAzYjt5UwQEk!Y;W z4>YDJYcDw)n81qQ+LZ~=t~qQjqjA(fo|O=-8|(0M_Fhl$lI4@l&!XISI$i`{DzS%5 zLkoI-mg|)~_RWOZu4M5g_9;(-A7msE*vCRGlYmQh6=)b%@0<)fGdUS+ zYkKf7U4RreA!6F(%*0d)QaI9&{;38{?)$>`cbKrrJkUt;gnjzPu+CT`+Ja=X0*>(g zmZj=}Fn_S3!*zLr|5r_JLj3EahDKovwxKWwTrr5#2 z5k2p#a=lilpVk7Tu?wx`BqS&n_U!cjvKC-?)LCq+p(dx!H4o*2PM#s36E13_n*E6}IhI zIDoyAr{@+@$&j>e-!zvBLYIYW9HH(}dwQ@x=~jiPzFp1(AiuXx~Hu_!});=Q>^t@3R6+52d!(Bx!$ zl{-ry&JYtleEQh4+=vb68wrDZCr0$kjm?TgU?~y|YY8mNzhbsb6SY`kPMSPB|S|$C&1cJ+{7o6XRcAZ=CVb@iG!84p3Y#mzm9sv;`42+@j zmdq`Uj2f^nD;k;yb-U`=}lLlvHJEld#cEUxoqXM06;G6R;`11V*-PbV>CHA~LqD2IdL z{u0E_T4D_*ie35TxA%g^;f8NSttzptA(+ZUwoBN0NGVY}uP}v$M0e6jh}=3Jjq}^8 z`Q|2}S9g^zU0~iE2(qiyeW5`%&ndjvn4BQOO|J8fyIePky&WyfoyfF(v-cI^ZO&!- zzOOU1NE8swhT@owC2la@`y$X2T|cw^@x9hu4?Z{_z9IY=rrBeAJGDCjO2UTUu*9f6 zpy+Tp?E|nSuDka8W=7Y~Y=87|wcj9>PHq}b>EXSPcIrd|bVZ%GSqK<4F4dVwA5UDj z&RCo6;_>lm@g(6#=Q;naPH~bH2bw3j7%FGhOj1a(SjK3@#?2TSW1Mgt^9oe(*JS>@ zrqUyxWO5DWM0lmoul43c#DzAtY~px*(|G7on~OJR`i{-eSYiG{KLnBx84EffTYW7gR{EFT~|V|2#v`j^BF%CdRWavf~K<5#$+5 zrTRJt2Rm?na(Rq6zhUacNFgsrTKG2k{AVn_6hVV9g}@}jJb-uTY(6iOjurFFw5x}? z+CAT6SFyxZ0;ERw`AqO@jXt|SgLwnCx6H!K#jiGq5&*aO>H1JZ5uLcYGC(rU3Y=x{ z^7n=Y4;Md8oT&zr(Lv4=lnJ%BYv#-{V_}8c2Yz&lg7*GuG*86$us~6ud z({rtac`5IS#oZVne=fGh-imll4Fe2%s`(!z=G5)Zc*6`l@}A>Ws;0G+I!HMf!Lv5H z$B$#7G62&akhjSvH&TL5{6Nj1JLX+&%FK@IbDBJ}$~d6;j+t6BI}^hi{3wr%4z=Z4 z|0OdMY*S?_+hybQmKs~lQp2sKNyJ?6HP!Ms9MP(~6R*xm3^wep1%$!PWwoygjuiLg zk{~0_;NpsckVVkHK0B1l z=hx?y*{Cnu$CbWpeZ60eMhchB7Zck9=Ndx>cA*H>TX3|o zF_w#O}aF*}1qgad#jVxIa5tk*AW|8SIdD~34zx#gUNO$nmrQr-IHra7As7J*`iR0{E7WMABZPfHJO!z8!0nJfTNI~ z;395P7s81uHzfh}k)gaT#gx(Ywg-yvCpOo`c!|4}AjlQK?%{1m$K1k8EFZVH(~%PZ z8nO6Q7Ed8%kQBd~BJh9bQN*W)ytP-I)T>=Y4T;0PUjRpiqhea`cSeLlJDWnz^FkCs zBxMR|^odL$B?ryTgIl3dbVg>0NxqnU>K7@dy|`HM9r~?UJr*ZL=_%8-d<=eLHHHHVR5;G&PtqYCfg2NQ+6O<1iDdQ;%4 zg~HJk(-gDr%?1BSQ(!bhu~V~j<0sWDC+-!AXU4kG$iqpNix;GZU2@ zw9Tdw_EG#1Z`8cnk6`w+@%_z1W?E+rnX}xK=7w*s2MP*HSb{P2WJjRq!}aI|{4HZg z(_(EDeQ-0mwMLXpv!K8aRB@xOSZJ9>*9Yt`qo@`62Jd3%Va<;rrNv_UgjQ*jwn;aB zBVLw{;~?JEVA#g##iix6O|Dv}P?h>yeS(A8tyfSeRr@Tk>9IhD@l6D5Upql@{xLxG&mR1&%aGt19;`oS*#E{6@I=A6tl?1%yoh`;5=&N+7G1zm3atxI zi%#FFAa6)1$fQUZ)w>vJ8^{DDw0BYy9j#!h*zeG=Hf;k9`X6by9MS+I-fD^~PhOSd z+|^86u&PxNL(7=aQB8jk7Q?9LYk=ue{VL$TN8}(a`Crl zO4La4B!Cso0+FmTp z7pKt|95DTwlA(Bg4%N`eOVhQ*ptGq6cypnUl)7Wf(VVK?YC@Rlq@E*s#VLXvARh@c zu8)oukaLq%aQj1&rtQ+fWfP1_AU`6Zf1G|#>WE62QHbRABEOxhUD&_t*IKaBuTyPQ zukU7y4U0@yn!6i|#!a4kQ~mV`2BZ4639#BSaM}@Lk<4vS5^1tNFEEo#TkJO2xvq3C zqqNNl`;F)JjqJv*1pMsU9(u!Cuv-el!zo(|GpY@G7-ATc#NdC8ZV%xB z`HRKn8whYOB`^oFozOrzwoRQfIZ1duHe627VWA+Xjndp4utU(@?6iXVExg5PH3n(Y zFLTGe+)%up2xiS(swnG`yOpr$E0q^8vqmOyg1(NZfP@y%fi1n7LJ7k&q+cTL5!s4% z??&Y&oxEX&84C$k6lyqY_<_tl31hgXHaO3X(5Qz1M6E4}yY^RV%9-s=ZE@PS6dTEs zFqmR+&@f1}R!E=`>IjJ@GTBfJXM~B8KU+bz z|3fM(=9vT#=4uF<%ar}W2p}LHK6^<{3Qm!1C7{7CnmLyVjHA*aKo>4usDyN%$-=GG z?7jLSpNajlOJ+0N_AnGz(-6ShUtXYfZ{h?X42xyJN)Iq^}J^+^1L zgPZmouy{;3skXS&X((KXuL%4*<`D$Ywye0}?HtCULP9E5e5f~#NcG2u_*869 zVt&q*7fOalwJWhgMlMvYDr%cl2g)_hu4r`6{I6*CaSO-Z7NAxh*;D1Vb>_@ zLT!;~u)@45)r*^j92kJB=#Ea-HB^AYBOyq!tlRMpB+~VvZ@(D_YE0QCfaA!6SPyh^=qOpuZ#N zT*&Aq(D{c`=zOgfVS8*YN>&*C2;b_Ta6r!tuV8FWa8Z1|2>V=8YN)#|_K|Em zb6eL+4|2?&8tH6;Ctzgk+7;x5un|4}`7F=BP*di&JORUUjUJ57`~1B;_r8M?U?*))=N9G=X2GcSe3|!qlke)c zTAI8D(=rhJfumL$InS(gj|U=Vg8yCz@d&}EIgG9e{!-^=O7IVL_JcZ@H2L4@Y?FAD z!#XqO#sWNijcYI%U(wYx4-@ooYc&VTUd_*enOFM-P)v*1j!t?(lXnlrC`Q~Mf@kqI zI$KX5fz8P9dx#gdK8r9~+|H0qZbV20KMRGJC^eRCY3Yn52&MZVlyt3yP!fsu1m9|b zLy~ui!vHwGrKF(My}<`~taygEVgd;s#2Ne<`+-Cy+z!&hd!y zu4wC|)Hs3)`X1qHj8P<`adotFjQk8`e=1SOhNzAzsE9349VZHNI|+obIC?#?bB%*C z=1CjuQex-c(wDPM*LmlT@W%CbE>dY6!%Kba;QnAAkJpbr2S1=N+}ga3u5)(6P(!Rm zWY3TpT7j%tPAp1>j6xuy5Lpu@U2D1p$!WN43~9fR3s!AN@jxgXQ8$H>{#@`Y+z&SK z_-cZd3?_$3{FNQXa9 zPpLKf2f6kGYV-h4n9L4IsT*F$`*5 zucKJ}M3)J~K!A!r!Q6)#fAQ7m?_>|&{E zxt7XjFU{gl;D9aw#Te{Ra1|M2C0DilLfk@=zJwnbi}?~wJzxOO*7VfW@WF%P4DjWk z2Dcn{ihXddiTizo?n#F&z&w@m!5xMDR@-D4%UV~w@NUux?+%U*PffXP)4qk%i=sUV zJQ^C^cW&VR{gmbP7HmieP@xJZ0_3;R8phPfE28|$blG{L80LzQO6V7WRU2+cosn`Y z-i^@E6Zp+em!Fjx(IK0I=nl#$+M_&rlch^CszSjvDOZN-h#QV3>vk{m-0t!Xj8t*B zDVIiA#$+tF6a?XGa_Wt0xePByoX9Az0l2Q&p`d*uT2W#(g$;+R3+GGo)k2lvKYTfq z7G`vqpNXvbTXE#UgVC~SU#(j~@F5*b2cME}eFR3)9x4OU zjjm61t#ktq_L*h!XNeLFIb9$2Z;}U1k$~OvAHyd3BVE`;_q^6q8E}6UH`qU`AadkK zaL+cR{_YzOeiNc zj(|I8LNJO=9H3L`B1XtR)!PQ01(;Ve^)Cu6J4==x41Wf(|0U2ce}_2R%D4WW=It5> z)vjZyXE@pL!oQ(J5VBSAixDdLmpU^naE1HVRNW9KUAN)4yH*tqg4zPzdn>bJnunv0 z5O#!!r}!ZYBk`HHdLn^%mxPaYS2-s>9FZBb!C@^ZL(8l^xeHv$prj&NBMK<4!;Kn& z;qHZZT-dUr%!ZySFNnl}p79=w#fXqEnO~;(`BISaP6%iSmCidmBd9B6Kfp<9LRqwm$M92wcyuB_9-e9goL|sAq zC5h8OPE%7Ljk6%21M+SKCA87QlS0Qzh@RDBKsFq18JDRk_hyq7frHVa{OsKbvzTbu znyL^~U1Lno^mKyFX;bF8@++qN@(kkvGW95u zK|DuIwH-RN@9^P$M~>_}dUW5hV{x77>%Pu}y4MsBQf-_4O!P5GwYIz2lQa_|_s%&;;J|>isNPOS06?n| zvhqUGh+8{VA(aIj;sfRmj`%={;<%$I*mw9s4bNAqvykXUeEIjC9hHJ*D_**z11G|5 zK>PhJa63&xhBCV^Kw{t_RQN!=GYnSQ&I09Iz;PQ3_n>WY&xS_(;^`UoI}JTXr#uu0 zQ<0dLLiL=5a>FK{@mUr@D-&FJ%NJ4#1rzisf}c(i_>;ej7QhJ7?+|(=E4H*N;mt zli(x1A!sBOuS`9LGxZ0O0~Av&ZQmWArfJbf=K@%#`I_;Fhz#}giqzQFi;R=^eugKS zA>(KD_AOkeJze#?+P7lAPe_6>k{0)ga%BSg>@8}p_l%IyXS858%us`pLAZ1yRh@lK z2NNk9pAJV6=R@0E?>xt2h+vN8Mh=U(v-<}oq({WLN# ze}+I!cWF^pGbdb*F^R#O$5YP@FrMCwN1kj5yR(CtKzC9N=68&3)Zh@?VAtve2*~)s zC84GA>!jl>bt}rSFo&A_Iua4+N0o& z;jw*VNF5w`s&E5#X%ClZ?&0!u5pg)(o^zULH$sMJ!~T(4!sW_}?J-^1!gU^O+XzK< z3GBKbyR91Vm(j&clp26bI+F#DM$PtOC{wm#!x2y~Y2wjEpBq!nG%6Bf$n(gQ410j* ztON{kW%lZ&o7ywsM=XrK1}~rG#Ml##p}GvXiR|#UE*Jkj(d?T}(V;i`}X-ro)$#tQru5C$B0VAvYIobNy z+ivqj@V_vCZ}pC7uZ|XIltE$xxCVL+>iYDFZ}O;X*H?6Az;=liZc<}Rio1^V*8@sD z9O!xqYH6M#8u>Us9MqD?S(Gn}B=Z5InIkBc1&WbmDFMZDKrtDNrlHux|Bs=V_V9l- z5C*@kRqhFRak@Z7m-D9ue#LOoz)1>z$-+MAA?zs2*!$Jsu#*MwV8sfMOc?qT7)MYT zfkNP*@Kku+K{DNN5Hbnntblfq4&nAqLAm>F`2xpSx=phC-0MUrz-!+-33>Q6t<@iX zQULQFu8PQTMIqY(D3Z6XP&2-fY$&nH#p&e*1R;ZBh$8>}R$#J*k*byrYE%I!YivT? z&je`MtXzoC2rW*;j~~-^b|fb5@|pUH6jafQiy>Vb&=lWnSGWT>sSbRuT?c}HBM@V7 z=C>kXBLH+H*}B1eeb?J-ovT0i4N3wy8?ZAvvkBsPIx}0|6SRYRi!^ym4H@F33#g~W zb%VVI`E)M@`9!a2IFInPparE2#N}p*iLV0nK!}28*_Ez~z_zQ%`${?evU4n>b&jNu zg(jM9yx#r7OFPfJ82l$n3;r2p2M=p}9#e;M!56r?+B(^{tCXALKPe7W9Lm6NDYtkK0=YgybJAv;SsC#VSaIl%_{!ycJO z5W6E6?5z=_qt^Y2uF4&VG;}qOBT#tvRE=02wcbyN^6sHzI4n$RDhz-OUwSI{*tLGH zeaxl4;M#htx4*W=YR=&&(Nq0wZSV?&Z6o(Rnd+|6fJ|mch|vQbvSw@7`7WYr>}p^T z125NC8`4>64T6+Gxn!ED&p4(&{gxO-+4-`;TY9F^&oexCK;*>dFa9QJRQfD-$Zw|a zHhQkE=i{{v zr6FB&5g!^oqKmF&ws5?7I z3*}VKWs7_iW#k>QHTD7{1NOzpXw87#aWM-n4!~KeD zOLB0!X+E>6*!+feM&eE+=SImpyGgYfvj7)kY|+V@5jOa@!$++;ik07scEe@k^0H=bLyqf=a(D@0y#jYqZQCNlq*a*-Zlpk9t_*+NujywV#U%a@t8gRfZcHIdcZD4ZZ4;2pYKXB*}=Lg63Tu1$j{q13CW)J8vw#TK^ICLnwwC5pr$sXEo zPjS~QZrxJ{!xx%MNx$ne}5(2fL=^{KDipzNdu-C0!DZz6L7P@jU+j&TBmr{Cl2+(_92ou4c|Q zqg~0)UIpzs+1ahOG-9eL6q>cvEQD+|3yZrtG1svcNE6`d&RX5U79I&!X=}T>8)4lo zRj7kjg^<1c^rB9=aypgxw>p*>4VU;sHM(JH3+E=oTb*0^(Xh<=BTxJ2;L!x5pR-X@ zL~|cEI|RxYTnYvwtcWdIohM$fiN>^0W@=znUk%yyg3rF(orMU%Dmc%HsrG?bg^$_30V2N?R(%e4R95oEyV}ofSjuD`T zy?F$})cY`QQV-P7p9mW|dP^dJOdQqVK0UZyhcO-Y>hRM#XiWtsYyBuIH+WP(P2OTw z%WA!IwR}=HOq?NmfpPB>fP>sSrM2D&$=A^5j&>7ipDsYD7wuZ)rQOGKloc+Ug zIseTbe)nYc%tr3sVRC8(Vo(6H9-@A4B#7D8%>CK>xuWMWSXM19ySJN*NBCMS2k9xq zHi)^9p2FCNOz{tSD5l`b#i~+wS%@eEjX{!_lWjIpdeI2A@pT6$LSV@O-l)9Dw_fW> z_pt7^Ql09nP?!+m2ay6b=P4nRB5NV)@lZY~8*T)t;m?G7rqPdvsKT4(S2GoA|GXXw z^_%dmk%35GCwxEE90nJ+9SF%V=55O`Y5es_OQ&1` zH3=uZBSvT4qgn+*zsVG4v3rSi=f9Hgm&hf39JO*XvT=*uqEZ%X>f>|C2rKeA5oN zbZIt_D%ft>Br!Ch?eYi~3zl3XsxNZ$W0EZO-T^DFMa8+Ds0Q-BPnhRzOJ+;bqIWEd z1x#l7${y1c@5R{N%FQ+n(!PY0jN8u|l_#4uIIaD(u1DQ+Ysz>f?1e`!m5%SFhH?WTXfEz&E?;OJ6PBR=tb|5WYpl_42P|o_1|iC z%3?5X)}?F3W*vc$M3t6F%@op@t`&u|jz);f=mtYvXL<6U3UP@D?7BD5?-YuW3`-=6 zp_rFAkp%y$(>X%aVc}wmofU+ijU4%Pv!%R@+%a)PZZDZV0TVL86NrV#&a~29>2qN( z5fJ4ea=Ojxg)j?^Jb~%*h#UHg-%oWhvT_W);fuw9%sB+^K*L4MK>*!cUG%KRt)rXfc32Tj6wPF>;5J3nukCN#yn2h6#LDZe3j@wOls3x~&IG~Gi7NHV2ov4~F3XyCLQ{&DWhz3}V3gd4oi@X+y zv@KddbYl^9ZL(Z%mD3s5R|AEV7!DQ160{ZepFLQBvMm+%&mGhP+yCGk;)w^jN_K$@ zM8>f-9=nR~4X-!(O7M($WgLJc>z?$GrAbliP8}0Z5oLp4pxl z9e;f~2LKE|ItQXU)>$C35%S~49RP;S_~%!-UTGdMXPAw`0BdJ z@FCJ|0;E>g#urEn)6zP%@HAg9wP;{`&Nm~`cS3T&Q-R_M2EVA5wrS*l4jb80fp`zA zK+GCkM;1i}e5?J%N=6l7Gle={cJcc;0^o^|^Y6nd36nD=q|~P7)io?k+$n0>bbk?H zs7(Eya18uzj0Y<50$Lua+k?;PV5%O4KME9a6el#ZCh7|T-)cV^{AY|2SRG^-<6r#- zNTmA;{!mSB8k^TC{5RFI&75@IsVvt$*pm@Sh2dG?Wm~shs#xpdnF*y^P$Cx2PI@=y z-r^WG4eBST{u|MB=L7vt4WZf>5hRidG!Z1?e=&j*5h7AVz7~q9B~x2RkT{iE@nZsfp}ABqAXl z;SrA$eIHg*L*ld$bjjneHFa6f8x;LgAf-{;uePrJ)&MNghXJ zkz?$hgPNKso~=%Gl)SfkW*M5`AOvOu#@x7pEL%M85$7RQ45rUHB4}iIVZK_LA(Y#~ z`D$r?-kqJFpC8+^2U&NU6eJ^7Zf9I`2LGk)Rw&Z!hsnPkw&~27$8^V4!@aTe%`tbE zs=2r4A^&0v1lEqV-P($P+CkMoNaxOIr`~Cc%8fTT;YfvyB49p-hI)8S(}{A~{Pp%>1)xbyy9_dM{@ z`$k4zD_}}G04#192!Vwoe9e>+lmpn3`5B<=y%Fg8MHSivZ!{ny;egoi)HlEI!tVE- zdF8_|p4q+g@#4;zLpzVZ_&`gAk=#vkTZ`pqm11-W<^%p<2?sFGyATWY+zY#33H~ph z9L=rvT&m2Glf#+W{sIl}I8&ox!|MICiRL_dy@uSLY6vQFkS?Rp)D*L-`$GYDX6io* ztyy@7PGfU_)P(E{F9sjd^K}jApHjeTPX&!^t-P978YIgYm;%*27aI^9<%e0SV((QS zF6FK|=T}96BQ}Nt1YXO`=bYl(=T^=yA9TuV+}em)K}KbZxlrt5vB#j>i!+^pYN!Rr{*`OSMKhaIq-0F)=$AIFEB#r%pqfe`GLfsWn+nuXj1 z`5kx19f!xP>#&8o#n|u^w<3^kHUN@#SxIm##gJL^SWq%qE5eWqd{d%c6{)&`&<7h^WGoguFW8(FcN01n3{TFr%x}z{D2d< z@FEJ~HpSw(6j$qLfVgO4zP|7ZabDefg~ABsys~FZ*1)*2ks-ru7kKJ!9*T<@wFh^< zvTa-Zs-9G;MG2P8$=8;w+O8ZP-m0vH_BDX{lRhrEat)SqFlE-91i&n5sc&1{io#%0p0Ie-kR9(-6q`?s*CH~+Nn;nIBeVL zhbZ8tlz?Csv$xJ85R+|lzibfutMeb1Nl(GBSf0d)Tr? zk8@lM1ly<%8Nd_JQP2!qPXE1CJ7S$Myh1R3l@%mjMpWvis@)MwGzTSIF!Bdor zq~d?n!pV!e3x0{y;O}Z^qm{GTk9>1(X8FkC!f6erXw5FvlJ&c-bVbuRRO4p6TBGN! z5SD`+pMq~_UaV9r-)5*nQ75B9^MkoSS5kAM1+VVwYvf%#H=38CQr@>t4M64s@TJYf z_zaZ}!x^BHWr*wt7MwT@>EJP&BY5ZKFs_lAra?=TV!3X}tstkURN*l$I3{9$ntN%w zdvK~SLjGH;w~hG_UMz5IvC&U+0Ef_Z4j&rtJ;#}(U9o_zqU@7qhcGfc{@|g)_@Utz z^%Twq3&g4rQzZ`O+^$Nok|=_~P%|jWMW_uiT_U)I9!V>nh9BCq7gl9&L6!11^55mU zco+`UVrM?8f@U=Q=v*^x3tU=<*O480F_l|1zsN;^2{DnE78vPxeHvsdvxuB+&7FzP z#Y-}jLAckxNp*=cTZ$``|6wgf)@oPR+CaZs$bL2R2wx{t^L$1r=VWT5`Qk0S>=$El z=^3=J7!-m8$^+`qpA^uLsghCKyW? z(jFSYxpciDx_%FkuG{`QWU!d{9*Vl+N{_e5JJ#o$7@=Xe*ki+_;QLuW@sf6D5wHzj zwQ zl+y;872Yo5Fc?LNG>qiSSez_K{RB14qIJ3_M?XStx{zDISCPcPUuqE%>O7dEx@F*( zD$A?_om9)h%-KtZ2H|ofth07bO+o5UYH`cI-9z3mZb38fwYQaym*()ZvBh>}lT_U@0`$RXabQH`Wz zO?YN1=_u*@m<|tQNG9xR3x;UzYXmXE(sq$@dIE(~m8eA9qq`Dt?cQA&8!PO=dA(RT ze!Q@|SlC@Ej1^PQ@J_-w!orneLC_r4u;UcKS?;oz5@;(n3yF#w?cB%<%Z2kS;1aM` z0>UC;3LxUH)5aD7<8>QXBXM52ZR6U+Ws1y2IyaZ>i6Jn)oC3yVC-K0TJxNCF*#%T2 z^1!WLxG6}N^v6twpE`N!^fAAAC}s z{uGC4W4UOUKEnO_Y8h2u<8CPB2#DqQ33D53WCoFTd=e)zvP_ePj;r}vHzA1$D@Cqj zndE$`)+1p|(CQms&TJ)|3W6nLR9@uq=|6R2|g5@mi*~&ehehnyy>h+S0(s z5fq2D)i%{OmxiS4bN*VG8Lgw&z4&F}(@969ZMu3ZK@zr> z$fbKNd-XQP;`VD+HvPJ#w8g!>(u=Kg&*XbJ_D;U1b_ao&84a%7DNiuV$9!x3)YG!? z5e;)L3b7T5nUz!4Omie!mAF+zlPrmliJ(!dN@B3XQ!_=oA%l_NkJ!+~> zLuKn`UX!>ZUIi2);=>J>3H3@}TP#FTVrq@N9S{DDcAJpvt;{|PAcaZ5K6U8#G)7Yq{pJc$QYC~6kAd@HLNe|hJ@<44M z_&2lJ+rN>`-R@Hn-tMmrWrn`l_l<1+_P@+_XTP5B*_s{7Zp`ce;uC%#6Wm5EEsI1D z@(5oO{|QRsTCGKpEq;Joo=u0gep`Fhh((z;IR?eCA@RoD1$lsLwRlz>gLKes{ zBT1!oaJi$WA^lwmks+7= zQgy?Ez)ZkZ?1$P#d4FK|T&=cL-S^;wvo!M3*FBH9?3;JCHvcwB4I9 z%L04wH?-w}yj@*e0{y=E&07EBx3W_|e3Kj1LV7dDT8wT_+lGBG-mPgaeP7a=Kjg@h zOVFtYCj!JSUBP?WjLHQ@;cL;T^aT1n%{Tf0qm0NCZCrhfLxYhO5G$E!9gg)1NQD>T zCoF!z#}S!IdF8`On`T}1*#Nwr;-gTEiD8siG0RCnWDe$*TuAM}uaZxpUFg z8j?$1v+30;7zBrJI!K6RGF!#c!_Uak*Vd)eejtP*rIXVK7*LKC!uuzV$~cKVj-}Gk zr$59GKg~yEIM>=n-wKRe$&_Pi(Wn^)w*|7TYNzSmRXAj|o_i^Sm>XNg=b^}m!KZImuT|G1RO2R%{1|xz3KK7R&F-YJ7TA^sM z`m!XfbW2PY6jj2qBFmJW_M*e~t-u4Rr)9$k10D#wOikIKF(D(iy11mjn1rx=r-@WDTL%j+d6L9jr<)f4 zu0!J=nf%b96CXTAOIcbPBAc-$xELC6Fl^^)7nIw!Svt7CH23nsaD{1jF~(<^35}+% z>bXK~d8vG0c$R3t!w1I-dkaVw3%mC}*iuC5;o{Ocp5F0LVGrN29b+AyKWK6>pM_f$ zwR_H06*Vl`85*&JZ(I6<>rf!kYoty=P3l$)JCT0D0TKn-4ZZ44tW;YDZa27|N5!Z% zjRm91DT>4=J@Vy&qCTQF)Qgz1r4L;p!E{k@$lIDWwH#1)Rgq1{em$ zjLJ(>MVkC3s>o3&iPv`-_mLc>m=rJi!Lq@S@rhb?ol%opOmln?8^hG#QP8A~_RSAq zYE0u@kQh-LEx36%E0a+jZ)Z?dMm(lri8nc<@yp2(Z58K68z>&~_>%2J4vBqZLzk9C z=Ac6$W}Ku!wvQ%@T>t-d_bt$IRp+_$(r7dqNn;`82gZzLY|Yq`EXl@zFt+uuvB3xj zix4E6@p#TijM2kBGcxvJ6i7@UNkaln6K>l)4A&%(hMS}}X=#!sclGA>w)dtiny!_k zlhxL%G+CuLU45>$D^2nJzJH(RJY+kU^sZHd_UyCIK70TB{r~^|AF!fBWiZ?!O$RFZ zkouFPbSrQ|G9I@=N<`h+uJ?AbV^q)PIA&R~^^eR@vcQA`Fo_5;pQDEW1kCQ!l>TYI zE(U}s6G6rlWg_U>_XK=s=`i70f-(M5h;fpI$VES0X6kfM$xxhvQ()o68l9FeX<4oI zfp2VacGqs0$j5pvs4X$U#~xeEj!%%X*bPASClQpJ?7bAiEni^r?=a{Gehjmzs9>D* zbrWOnBykPaLjxb&O7vul_M-!pIV#Y=5%|dJL>Rkdtgh3YCo|xx>?U(1DBNIG8Ff*0 z;j+pY-9TJbcpHTEABdrnqvX2<1UgjNp`hL~J2AKAZSMG2F=%dh_!=&jidXKG5vkS9PLo znl0XjF4;5#%RT1lCI!9i$D6ffaR1@9zTB}iO3W~+&NJ+t_O5InALi~Ua@0xQ~xWk8FdF1;3$2=#D$i{+Nw&ezyZrE$MgFxiHNuG(zmYZyh z)=cHTR(Uvl2%imcZ{tbh+W;j2ru@X8)RWjTJhvg-vKsLx8_yT9f!;v^8Op{Qwj4-4 zVADUxy9aJPxL@F5fV&7fam0MHR zuungx!73a!Gq?@o?^27T2lQVmHEE$mU*QRuT5f_2X%h~W4;4dN^qe0@P7}&*U1}o? z=Nn2bxM_)(nv3BRjbit$h%%bt5b`^v5N;D-bMBnK2s>=WCYHn}+^DRWKaX5Zl7F+* z1-o*3%=)4fc7LUw>@U|!E_aTwtK9vn1t{%=Shy>Esu*LNmYPsg(}|W++=RMG#iX5|FxwdblY@QA zcK)N?PU{Ql9qTKzhg3V=IpVH%j+yNY=;L-fkyqMje^X-XJ*fhd(?-TxWK3gd<%iI= zSh2O(HVv;mcz$nb#ff++_F;&y%|}bL*cX$Vr#Rv=?xpaE?=BxkKfV+`)dzcmVLUa6 zAD-6CENt4C%o&$(sf`Nrn45uVItYsi%fWSVq)xlVlQ=SEL$yeHWsQL!;#d=--0-DM zhuVpU@*w)5@B|mxiF-^2fF*km<}xh5sDdCjpgaRWqJFyAOBRWc*YsVykj;ZKqTMmDCOjyTzu>*F903Iu$QNK%#ZXi^ zsdL~5$O-8)+AkHMG1DQ8@R5b5L9*U#vXTLU_bjR|h(}u>1z_u0QkdhAJcBHy=v|u% z+(aqpe$s}SXsF2|fR+M_Lf4TiK=XkDWxh~Y1T3bQsOov}6;QZH`hgLY^HT59bGG!3 zGD3R@(#-&0St$eol!NNSe7*n$c4&bD%=u!TGO4PIr~#9PwEf%y1dot6pG{aU+DZK3l`1Han1}37 zPI0?X*22ky=p;leo{q)Hq2?*S%wZ}xJ?e}PCfMnmk{SbqCJWXDnK|`uQw2z=WJzHX z5KImNSSZjPXPeZG3W|Xo?F1xL!S9e+dk}MYm=o9Z+O4DmgvMC~_)$JKuAU7Pu9|oQBJF>Ka$y`d^A_2HtN4gxgvADuUXHMA6t|aMw+$>8bdgn6uU)w% zX9QqqgpfVWjOJwx``+9*5=ol-C98PA?)e0mF5E4)*?3QH#TZz{n?6R67h?c`loctv zY}6xTXTc{bJKT)GeX_S8a>c9}YY!ESb54sgl5C-{qF}c%fvK`^Qf5Nmy$9~tKS5p= zF4>&agM0TOQAyxq9O1*8qWx;VSb$5CmsM_oM5ZWsFb>SF@lgy71+BXioh*qY$HiT| zQ;-_d!`{X2+-r3)Zul@aupMJh z(o#3k2Il3tADs`6#9LsdAb-+AjSIHJ6yL-X;7~qcOT4pKlCrPEA z(7idawHu6WZWv~6F26dNw0|K~S6~)t9QgY0I|FJG>1*QmP`Ycd)m2V8D|N zeBd6eo!Qy3#krHWi*4DucdOn?rluew*}VmyPhn&4-i*kOqfqd+i%(WSFy(x)4HmJi zFPsl5z(al+gh6KJXsE}50oKBf$%0<;{0rh~5B@~E%l4fNuk=us^G`yahIfd@8qc(F z#DdcMMkYKo8L2j#M+p8ASZpUuFT>BnQg$gY?@vZmbW7QeZd$j?#F_KcTjp%8hg}1Er;Um&f#pGg*7%=9LVmITJ-mf9rjUYeJS{NA~}FHp|I~n zau9LshNw%dGA6gBj6v^r<}nJLU0wn*bHyP^)zKAtFIuN;-~W`cA29e3`-dWt`XV07 zA+A0pWppc_8{~*oa!Vp{BqQ|;%r6EwFCbH@y&SPH%5qpCflwvGo>~rZ_f=i{Sr!)o z2S};z(HW8m4C!OJX@0dASPWR4*dEW8TfG>Ya3&$Egn<|*0|9bI%spU529<+Qrs;pp zv8$LrA4Js}hx!;IzWworF!-eHu&ucYywVOx7X>cCZ!`F(gr1I=g1Lx zbLMF2jN7^Z-1u((iNDJ*@kNh*w6?Iw<3OZ-3dZ(sDWKa`H00EU=wE`i$4QQ;*n@B9<&=D1h{32_#q;;>hM*23tx*C--T{RL6$`EuH zaYd6Zb~V-}YTT_I2WI8Me}Njp;34w+jleW{Q@Yr zd;hydDKumJackjxTRhzpBpB~CQ~gjo$OP!|qI{LdNJS!|Vo2`o-x>r!jr1FOuGNJ2Y7V zgnu>%8XXu?wFnevVZM-F&@@uli8HChqgCLT23C`2HV&qg&u|OSC8ruDu>|yMy0`!d zk~2x3R*8og)+!atFDWnSjiei%em8AWndzXU`&O8{u7-q_SkOS?Pko>2WfUc(DxhQT zCGd=~nJ!<{K>wD?(uQl7=INUy`^hbf#VwH&jSOVhD0u|6U1L&HGO250k~1v@TxFvO zPo)Z!%ASF#g^fx6rV3q&jZzJcI{~9w#zqY*oZes=m>+{4#}gOPnQoT}IA_c%b93;R zT?Wef#OSjQ1=m4sV&62gKX08RIKc{NhUq}_??4k}fqL8j7$@0#7=KDv2*~(q9|eg) zlhe{8#JJzU%eoqBw?Xbl$?dFlwh4zHV5tdRWv%)UiST+_aeJ&f-_}y^__>sN&??oq zr`C{RCMo4~TR=#WP>|`Bv(ZA0oI{ydlnA9m6iH#b!jW(?q}mlNF9w0OVh9y|Pn95L z2t3Ky7=u6KTT<@2#z2&fXh&1=1{@Wmdzr?Gl4>A@P9(e>R;Y2g(F9SyfXSu&8&^=> zr||XP*i`p0JzzvlnmYnbp8&LcLT?2Efww}JA`Rhx4L3&3v~V!|Z;=M(k01{;euA-o zi-f{I4TnL4BPI4$1UbUUL2ntG12=;5)>&H?pxFPK5u*_irWH1!#juEAi_M!Vw`6Wb zJ`i}y59gywSUxE6u-5oQWGN)^h}ML71MU0a20x%dSCWcl^i-=gL(?!^Y&hrR-}!JU zL{+o%jT&x#mm1BqAo4eAy!VfoDKL4ClvboP!DsHbixJ%R<5s@Z2tv+Ris1fV5p|M^ zG_xk$PK$iNQgpu0`sT{=m!jvRPX*A%HhOuJTWQ~U>Ug3>$}u!@G-@m8PAJu$`H`b2 z;JS+zw3oC(CDIM%6V7|N;!GTe0ilOY^RpIGs!Uen(pK((P3&1RV&{Sm0P_tSel6QW-+s+n7|AY8BF? zBCA%FifUoE-Mr?s6d|Bv&ubE1VJ!g1nuk^t4kD*(P4jA>QR0^YnBwFn7#4j_3X7(b-qT+o{MORt#*;EyjXz)+rK%4gM$pmhhQ z2FKz^s#4e!xwCMnZB;E496aKXvXJZB+i|WDrz#>3LG^qXo$C0AY-ULF`P6! z=)ka>(=V>i`ui==f!o}HB8CTD3Eqco_L~tq##%;3l9`_4Jy}mxBO~s*RJP%yCrcu6 z{CLKlOA_4mOM+AFs9o)t9b?H@x^u}hJ!dOQCrO*g&A?2%w-<=U;U5VwyBH!|4({oi zVd#>n0d={+j|#;(+IQclY_=p_(tgg>6W+VXA{R;IVB5s!#y~J2YqASq(j&X8fGs8g zM7~m8?BB_sqIMs6Px3S5A}3glMfOa~%4D#us>U#xO1z-A3UXKLng??W+TyHDY~Ex0*I{98t*kAOWRO$w1Y1N22uyV|)ZuiY3ZH?IN!p&xPl32LGAO+Wiu2*mj zEZvak9vPvj6|>Q7Z-T8P^xT0YFJmUyhzZ~ou#dW7?;dkM)o2O# zBzG38_A_k3*(+&=S!kl0TbKjBJ!WRxJZqziW(4$yQYnZd@_yosKF4!` zTn{>zOQ}FPMmw#GCuyQR`*6A0wN0pOa?{vMDf6Pr^EuE3N(a_&ba{3fhj?;j_h7Vy z&4zCr54a-1bqYKPH|TLHD)7<-&rpG&J8@EUkVL)Hj)(MJ$}^l6pfZFR58B2eVX68# zlxnm=_6~Y9vlf5Rh?A@Y!#oG%2FJaA7PNo06lvRJw-|y2maj;MO3WH|DVE7F;Y@7@bXod*$J~ST0iyxC(>9w>;JLe(x zsX&t%K#M$Vs6D*OM@OwDw@8!kGr#_N&9TH~J<(tjt##(P=Tjrll+e$ESSyDQjF0Ud zpAZ`w<;Z>G>M_fiZ~bE&6rEbBB=X+xJ4en1ukU55p#=*<0;53vn@qg{F-W8D@r;<5 zjr>CZtymd1)B)7uW*Dtj28ENRS`=l?xu$=TcjCXo-~bWCxnUAssV+43FgMAD>%d|TXD~As4-Ft62e4lhyIb4{N4@rY#b?#zb zS$NW5QRR6GukPmn5nSIpVu99RED~#sMb>~SZ^wW1RY|IREhz4QP~dCv6nHBfY{$~n z`Mt4W3TEuH{k;ga9Sm+^behp=6`ek6tYoIl0N)&+Mxfoy*Eb(E0_h>6k&1j!;xksw z0Ex%GGIugDf4rdVJti{nbY_Hi?c9{;>NaAVlDiM>#MFUIz%Kny(OpI$b(_&{Dsqdj zF7>vGBZRz4AAmcHcvJPAfK`4>dcSe6dAlii0@QXzW%BP~YNNa)U$!c9Y{YPs zh=d=c_j1OHty@L@;K+Pw>cJ!PM^SKe>sGtG6{B0WR8$e%fI>Pfy4+p6nEv@mAkhRB z4pf*q0Mbiu;;frOGqP-v`yV9$_TaDZ5*lku7eTHl7s@r9^G}BH+lb#tt|^DR@AJ)c zp>RHy3qS9lY(W|fJRyy&lo-E&St`7rY|F*z&;@Uh&&<6dw*st~SPpCs4Gb0M35=O$ z4XKmux%TtdSVpV-zFd25MXn>)p*5qVYoEt6@6o9ug8X3QOm^lv&tH>U$pZuRK>+~i zVBV-YCRZV?P153a+Unfu+^P}OjWS?RYVtbdSs{5~>dO3XEn0QGb`2n0$M9SLrFNjU z8<6)}$=ivi1fEvP(<(fz&#l*1=hj2EVNC}ZH~8W@q;>&%D}61~As=x)%B@P3FV^9U z4Y>{HZ`5v3hO}@NBytIPw;t~{<~HTJavK5t2I+yW=OI~un(#Z>4QMvV(~Wpa=2F`2 z+9s|03FMgEjHg{%O4}??Taa^$Bo zw81CPipfF1vMsk=+eT@N+)d|vl(|5^?a;Pq+nhE|m*uL+k;5H+j1}-)2AW(sp92*sXsAb2f7W zSc&`Tafi&60`=6*4G0m}}S)J^6I$O*#HJnU$Yk7jN^8b#k1 zPIAp!BIqK94f7INGEs2(Zwu4n3V~vng`H+(7cNw-dsf)vm@VvNh*XPquwE9jdG16| zh~!ng@alFV4d&FD-6WkL?Vv1VSYP4BUKmV-)}csIyDdDr%M|$q#JFyU!9!YL<()k? zOdIOEhevnLWwL4WRSobC96G&yXa3~r?XEm_!|-wkU+!>T+5jIoe9tXA`UmX1AOv%O zudu*WUfkwjY}+MWYqgATiut*mCo2=223c9}yYIl*&c(&NR{ctLzFtd^Koik|osyWg z6Vnv3#-}nOhe*p>sxr=KTUCL(MWuj=g#&7w?ar;ru#eZ(^Ptsu31l?}J1rym8;38U(X|Q4Bel727@ZYKgk=3nsed%0SKmxvi`A*pb zPFJcZKqbL)feF5(X^8^Hh~WhqqSG*I!#iaKqQD7_b`Sp!kXI&bsZ7|`H?V!+rdvIw z?C9$s%ndqxHWogR>mBg2;=m$v^Diu;SC+8$^xM;9ky!9RSJ{I9k8L#V19P)lZfKFs z98QP9D1BNnTKo2S8hW`7rXv(6kuO*Cw3(-(-mTt|?A^9?`_IFUWe{gH`U+g&hA-mQ@#CtH@n}$@*($t8cW-`zu`5BJ+pu2n!M|N zYM>Gh&w{fSl(WQ~nDx#EX^Lw-*=?meIh~mGt~;d|spUo!BQ=^iF6M*&*Fc|*$kdkJ zqq~o2TZ~qvr)0mTG1_v(>izmOJbAG+dIy6o!pOQ#F|U+GTe9mEd$FF}$ORahQ4F5d zRoj-u+R^FP&gimWA+n(c(rikv^#;rhHiv|?w&h6Q5p8pc;g*yEHm{AH7<*i~NYCf? z9<{nbiRybluFSGKW!&g0PB9;9Ao0faR;fq&(8ZgRo{Dyt8%<mBbhI>xpY7u5@MvjZw|K96bf9N&DV03Zclk`IN4G5*F(U95>TkqddJQOP`%;;c z!qQIDn=$Hdk|AKMpz>6L_`UTl?3I(Rr+){b5f~aW+LfTDJ5T+2LOmm@70~uWNem%^ z6b?Sj*%BJ}d`6ZF!0;9Lw+;b}sdp}coqmFT2;sn34`{x3g2#9&pn++me-QA1twwUN z98C)`0`je>&!Qmx574)229o=x?B>w5)wk?<*Hd@iIo!~lmbWYYA5b1xmG5S;_2#rw zcH_{{H2$Hh)P!|+l?;sCxyF!tUj_Hp?r5;3s{yh*7?w=WsZpyI4i z`blKt1HBvPdN*oSoeG$h#0}T1T+pAC3y9nCxPWQix)UB}aii zlP0nWW(`i}kkiyS!KWs7%A)A{7;&~&P+S>M(7kq@pjoDIGmVg{`y#IZI$bHrQgXvC z2Q@&h7sS3>KB3EMIK}&&T51qF#8_CCrB7Kk1KdO^jP9O1i#u|DWc{;0uEfi#O0vnu zG1QlIA!#g1-GDlUNe3SmeVt_2z^%95icP(D+d!9FUbh;*Vo6ghggqi%oaDFA@`U2% zwgK}LV6Ljm3mCRgeELI(7@fk4#iCjbo;^%-2cGwtu0Dfj@AUhYf_|T|mj3u~%>ktz zFlTUqala4lKk@F}!TG5A?WZeHa{MW)2;<$HcIAjQWaSV+RpM}BaqH7~U42CrI8Ccs zK}-j=dgZb%oIPDkPtX4;)~FkmZ(`=odr5ExNYdBTk(G%*4d6U)hS@Mhjtc?U6UnZmdri`#1f_K zHsWrm*dqo}N6SC|iI=H?)*)dgOz8 z+#HblD!a5yfIQN_zh!+Mu|;Y}!G0@r^{CC`7}`&EO^msO+9WRxNr+{8t}bCAY6GBQphkZYvkC9K_BGW~w|A3ri>Hl|H8_b$ANb-m z&S6hh{db6>(+P@x54Vs*48DdhrG}O(b(2*p3@GUDLQeb~=g)5CqlrsYU%}x2Frbem@gQH0 z+k55#b%9ZC5Ux|X)qWut7s}-cX)h%lae=XS5TqPq_c3+{Vr7$IV3Usb@{PDP5m5?C zTa;Ub?6I?uYJ%ATkmA4x%^NN~!;E3Ych?WI5Xaxj1Rl#V_vMp*Ka<*_v^G1dXg%}h zs(eja$Wc$CzM6$!lQzL{-BT%dEG*i-x+*ujF2eK{jK7{CI9UaR>)xsycM}NQZOb9D zbo6(zi|%KTO_xwI13STZ7MC{U=@b9lY3r(h8JV@`2?*-3CbZ(;$6$3(ei*n2R z;ra`zEj}8wq-Th85P{0$+cZe56AdTAN>uPf2-cF1!-rA>h%%IO0*KpA1Sj+=p;W!fRd9G=SO@m;D>e-l6YJ6QH- zxBwc{={bENofeqX5OVY&J@z^?eulvxAy7t=sTinW66gpROOQJI2^^jI`KY#pDIddx zr8$r&M5l~3^?{{tbD1&DuF83$KgM+}9pD7g6$V~xwz>j!HRw#KS`M5id+=8{guq!2 zlffA*14z(tc3?h0@}^N#pBRrIPSq#In-Hh!6XQ|DVWw2#kcq*3XwjcVv6=6o6XhTt z7TpghdO#9T^j&Rx39u0;V z0R!Jt+#g@bL`={&t#~Ivy=hed0Wp=E+#XiVx4^TelKp4;`v>6ylfO69af75}%#`Wr zRF8QDU;{24=r&McwLlrSv-O4Y^RSYaFVg5FO}8yTB8?uQOgk(&FEXW?FnZ@*$x&F3 zfE031;?B&;jEda_vk;m23RMtW*Tb{Sx#}p$I*Px-B!U?qIC@y*Fu8sU9n2K{*owf5 zIW<-aoCqK_fsF-va-;x-Zx16Tt6`Fbf=(2qI+ZvTRnv|>3AusM67MIOF;hy^0}6gtRT>Fz6q=#btiC>Yv%OHV&>c} zq%kke5q0e+g2B=ICH9gfz#+>3n}C`a`V4oLoeVB8_*Ybfz2uJw!)Fnc1KDgjpjX;D zE?---+Jju=t@z9r2w@wc&Scb|@Ehwd3$Ulh0EleSGIr#UYyR`toQr`IpwZ8Vo|h3p zzXxE;9^Cl(h525`wuVc@Q7s#4%W)M6nv1RL&rzUGX1@L@26wQ}&#_~i4cytqiI=>S zt>P&9g0}r2U?9alXHox*;CB;z*)3rD0~?I91=R<=99E8a1bSRz73BN+Eg;%BMGerOU}v-M3B7z*ANm5a9dkh+cSFw|w+eO+luVo$ zB3$tJgKQ+Gi{D7u{f$or?0P+p177fL)?agC(D7l;1=zK2!0ZEHJQb@vDun=foL%r^ zmMt@n$Du-jol04OYa4K;B#*J3tQjaR(b<+z0BC3v{zA^5tnw@}c>WRZ*@M5r-y(3I zuv#D&I?*s4n8E1^Q{;krYLsscPDb!7mZ&tGyLfKGGtSv3d>YPPJV)`|L@7iKXD*(b z@r)A}&oOz9;kiX?#dE7Xx8Rvdy*@22&#ib~p|#_=U7p+Ue2vzD=Z;)Ut~J+ou3<8c zG&na!TBqb$f#;RlDm<@}=XN}=*4E&8jXYn2=j*h!cwQ^d9oqF6|8@E^XttpQbA}x! zMdq=|Gha?HUfh^sg%%EA9amiTu)9Nw|IGvntLkCSzCHMp!SVL67MuwHoeu$$24j`Y zA4r=Mr|%#&S_h1+_cqgo88mUT$lcsZU}0y!md_nhLym9zSnh zLNNYv6_(~qJqJOv=T$kZ064{|g#tOXV8I@-sz!2u-h)4{;YFHU*xbFtUuSJ0PD#ou@ z@dV?0ReV3=cdPgz#_wm`pMOC8OsSuv{0ZdK_{lG+_~VR!S;c>Y@o%a4?=t>JD*j!@ zf1u)j!ua2)_}?@Bk1GC8jK76onLcljhA(-#{Fz!^er1z>O?8Klp4Fk9md_gfm3?l# zsn_l7XMmEwgkYH_M67b&=V`^WwOUbk;*h#>?N2VT6Kj7m@$u@(Z@#GCiZ^}W?Bp+f z8aJ9~Q9t6l5iHZ9MzhEdcpCIewHkypwHJ*c;@)scG~yT#qEQP%Wg{p@8*OIbK$GW2 zycgn8Yn11R7QxQdWYic$iv}@{RSu%{re5=Vc$UB}Do>TWGV>?$J&FB!Ju?Gy3yL_r z3!1aO6db;JDSYS<+eEs)>g(d84PYSxl`5 zE(XL8&O&|&0phzEpF*(ArW9g%FM8VXt5?;I%TLE)J#!ohdTJn&HEW-S;QLi zegCt6{KWTv>-%5&{!4%S!SBDkh>7a^i&tOy(Z`=!BpvqpS6_ei4)h)@xsV?ekL7_g?t!m%sPy_g+Hy%isIlB0AUiqvu}y z(I-Ctm!JETl>4KvzwpCXo?9gI=8gCM+8givCc|?dR0;3-^*7%8fj8dsp*P;6KK<*D zed4da^2(1t{$(lX;%_~1@zv)pzWTxa+7cn{J;w%VX z8=QPwy)y9#c;Z9~G4PP-#MervrV2iL3EWZvpTcd)QXo!Cm^563Ov2Y2FF_{F3y{f( zy8bXWd138SHana|TzFI9A#(;DA{-8KIYw2YX&p=qg&ntCtkeB=bt9CSV3@suM|1}z zrBLj2e#W$1Y z>9whFA8q8@Hg#Qc!Rk{;?7(vY?uA^&u2L|OB#&gfdIf*^(Tu(Tk8YL)CfFdNzzqtS z>g($}ia2Z$)u;xFK!6>}UZR3b18v>ORJUuCrOCATk)O}bF47jac^4)^hwLaScX~#F z&=hjB(|xLF>4xEXZInh_4#MMEF)6UpkZ|UB7V^N`IMGsY(>~RW1lsRG8h?=7?e@*W zk^%8FnQ!n+F3T6gB%)Qyejb!O?SZ83wVGX~(=4;|J*w9@pz=!9nuXWrW)rW5Ct6?H z=Un0YAuLhIoX$aFLSWGU44GWJzFZxB2Wda#hR=P(=Qp-GE1sK9y=}m`(kBXG{bQW_ z?@vfhuKb^ChYp7aq0xnswVF30v`>W0xMT~8lgdMGlkzrf!7&kQawVE5@SGWHn;H{vnXTG{!3&MtRG;D>zNjiB6!Lq*)kN-@r$ zuoxKwi0Hh|xJ;!nepfM{8nN*gaV7pQ9FhdG`hqZsa|y!{;xBMJ5aRr$Hh|cua%luC zyZFt5JtG4Ed|Vf@_9KYkE>>Az2E9yyyO-GswdPGz8fy-FW;)%Bwptre{}oOV`(qC@ zgoJ;0;7_z3Dxvu`Li1hD@`_%Ipg`>1>2#|Lm@D5E)wlIHC*J`$sw1TS&l8AO7&t&k zO!kA7=w?X&IQ?Mmu{!U1V#$RG+<2PaSJ8}qQIw{&HuSKOeQ63fJcWm2-HQ3wF4 zf^z148&1c>c$Qm(^^b9TtJsML_~r4#yUtoHT{=$oQz*3*(gIIop;M>>?{1NI6!d@^ zkM)lFa^SxFHxCv8l2M`ME}+pS?%v8`t-CW43&&67fT;-r2n)3frhf$<^+<WlH4!@4|uEJumz9-$g&bv{SRfefFMO%*^Ut+&i8? z0O<$WQT`W}MY+WTo9mGUOF6DdEAw@g3!yS+fmBeqmMe){g0GR@sUp>9TAkVBM$tSa zPGoufTK_1mVUP469w5#rqm8kJIqOn`z7B_Sm}7d*FGoTUuBqW~NDFf8%{$j`c*b{> zmP_VgVfXBXmJT<;8XD^3>JJly5% zU96|?3WnKT;D7W%9>YpnZjh^&usz7XZV$5l<%%)YzruHR2K_hqc%|b=kb2+Fab$s6 zKw$qP!-#wx!4tUkl>ETELhu_d+cVsPfkMzA$_e>5q@4>8`^O><14H}nks!U=FEtgF zb$i&4=UWO1FGZ0SCa1%d)?8|qZT&OE)=JH#=*g4V=o_C7d}N&;^aw1|!|a;=WGO<8 zOwAv{bt4sNIwWOtK@frCd@Zv5<5Zd2a3eE>86!UZdn-dS8w#*@{L(lF} z=llUvb9NCbR*8khA~zxkRLag`)A_bRlT=^gz%=$So;=9rDV%A&oPEkIGXzXcR}I(# zz59^tfH1lxXFiB=m*1;-=EO10Hg^0+Zu2D1* z1>a$-0xJz+U^-|@rJmxMLWC8x2+7)V5E|y?hy`oK!J$)3bw4*3?g!yO_>ytYRUHub zF3$4=F^Ahl>$0^%H+x!yC4&Wx>i9>J6f}B=lBJlx!u#()LzKMj_}kM6%UVMHHf&ODSm$m7%;Nfs!P>0}sn)!S&-4#qeDyDK zyuQZsz@ayYy6J4t1V@Hw6$J)6-hwUYc55I(5t)-#5R3W{UR5uPyVZKoWUcOfn=0#F zuIbZOW%`qtG?>hSL)k|t9Mmi*&kbT{P{i&YU9T@0ZPS@NSVi_8qJIH>i%at_u}?pV z8p|=Y@7$Zo!+@X~4jBX@VhS(&7vwERRW|N7U=a{KE5|Fhm~y8*9G*QuhQ!I9Hg_>s zin%?P!w_nn$!Q8f5RBlf00Z`F(2De2VMhOLLglpGJ4wnNW?MX~@K(;6g8)+rY zo6MJQp~#xV&>Hu^%NbyA!)MrKC<7b{XwnQ{T~lQudCN-$$H$?5G(KVeji;Jx-X+SM z`%MncV>tO9G*bLqqV`M|VOVJkB-|m24|2-5q4oeml zxfOGw`0iJW13CINQV&KXWERC+idk^j%oxnW; z3&(r-2%@ol(so^{#4fuAmDj34O7BSwkBqvjFflg^Zea+h!j;_|f{-u^GCAN*`OV}uYJ#y%{Wk7Jnu_4ysF96XMv zg8L@$JApS};~W1(GS1UeR_=PF=v@ppF!(xxa^oYp%)@$aTF0{T={Ga4cwl>hkMBmJ z{!zwOp?v)?A75gYZ!;L-BhqmSQEN2=ezlI(?<+S!y;m;+n=X`_=!q+InI8OUV{wm+Xvgh#)g0R<-X8yLLBhJ8WW$!#uy zwDL9TR!Qzs9?9qw_r!jbmglri@AKst2ol~iLn{@!2LGFdJcXd#2U4%z4rU+H^}lDIQdvfSiGf(y`T=7Xm?fga6f;;9+g3(Da$N*{_^U*+a>`uJiVp1rW?>R-h7P=M!J(f^rc$+Z?l;CxCAwTDH&%1mt; zXsYMaxO|5CE4Cpk0rsU;f=hiS$L~d*h3*4H|Ek<%>rN@XmtSNaL7Z-9Y&U~p2BQr2 zF}R+UU&CMok8Ju3r~+6_nB?1s89c<`o6Pdx8GMU@&a~fSFvehs9Yb_RxjVR-vF!{l z@gC`lRW!i@rTV#;rV~W8C`FNT^n!#fX3NQZdK}@45`zaAOf&cdp?^1H#~3`wV1~h21{uCR!vd!G_z?!8_w_7e z&oH2{tdh8WoH0l2_H%p`;Vu#VdX|sRFc4WU5dstWFOm8pawRy~WV1p##QybU6Hyt7 zmK(&V;v}|N;!xvQ?UOKfVZ{$(Rax)oa(l8#FaycfVn-!cQvs%WXLE<&aZvv@n>Wv@ z4>J&h7>_WfF=%J=oZuQfmLo7KM0Z-duESH|PJEAx%l#ox3!y*+%w_SW=?Qs-$u}{$ z_=w}T9dt$im=m6Q*YwtQ%5lD4>yA&;Saar&-||=056`wNM}3BVw%(q zk+`+U4H#B}o4tCI}wW@!j#G@rL-d@hJX#%xC?#KN^U%Mq-gD zuI~8Xb+IwmTPqiTx5U>&>@FH_jd#bxA<$Cs@%YMkOMEZ#CnWtbUkl=^;&;XO#T(;m z;y1)Q;%)I2sB>lfYMX#AacACBJ>-+(+l@ge-)fShCT)A181yEop6`1SF&NPq0w z*o~2<$hye&kx1=Bpg}CK9&Ch%A?9Ew6;GGNg!t%{_Bs0Vh!kW=o1N_lTVMqal0&4=D55?ad^+y8SU}T;DIsjFNdJZE04vusz&>mO| WKQOHf13kg`s`hAmQ+q=UY5y0IAuUt@ literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/_distutils_hack/__init__.py b/venv/Lib/site-packages/_distutils_hack/__init__.py new file mode 100644 index 0000000..5f40996 --- /dev/null +++ b/venv/Lib/site-packages/_distutils_hack/__init__.py @@ -0,0 +1,128 @@ +import sys +import os +import re +import importlib +import warnings + + +is_pypy = '__pypy__' in sys.builtin_module_names + + +warnings.filterwarnings('ignore', + r'.+ distutils\b.+ deprecated', + DeprecationWarning) + + +def warn_distutils_present(): + if 'distutils' not in sys.modules: + return + if is_pypy and sys.version_info < (3, 7): + # PyPy for 3.6 unconditionally imports distutils, so bypass the warning + # https://foss.heptapod.net/pypy/pypy/-/blob/be829135bc0d758997b3566062999ee8b23872b4/lib-python/3/site.py#L250 + return + warnings.warn( + "Distutils was imported before Setuptools, but importing Setuptools " + "also replaces the `distutils` module in `sys.modules`. This may lead " + "to undesirable behaviors or errors. To avoid these issues, avoid " + "using distutils directly, ensure that setuptools is installed in the " + "traditional way (e.g. not an editable install), and/or make sure " + "that setuptools is always imported before distutils.") + + +def clear_distutils(): + if 'distutils' not in sys.modules: + return + warnings.warn("Setuptools is replacing distutils.") + mods = [name for name in sys.modules if re.match(r'distutils\b', name)] + for name in mods: + del sys.modules[name] + + +def enabled(): + """ + Allow selection of distutils by environment variable. + """ + which = os.environ.get('SETUPTOOLS_USE_DISTUTILS', 'stdlib') + return which == 'local' + + +def ensure_local_distutils(): + clear_distutils() + distutils = importlib.import_module('setuptools._distutils') + distutils.__name__ = 'distutils' + sys.modules['distutils'] = distutils + + # sanity check that submodules load as expected + core = importlib.import_module('distutils.core') + assert '_distutils' in core.__file__, core.__file__ + + +def do_override(): + """ + Ensure that the local copy of distutils is preferred over stdlib. + + See https://github.com/pypa/setuptools/issues/417#issuecomment-392298401 + for more motivation. + """ + if enabled(): + warn_distutils_present() + ensure_local_distutils() + + +class DistutilsMetaFinder: + def find_spec(self, fullname, path, target=None): + if path is not None: + return + + method_name = 'spec_for_{fullname}'.format(**locals()) + method = getattr(self, method_name, lambda: None) + return method() + + def spec_for_distutils(self): + import importlib.abc + import importlib.util + + class DistutilsLoader(importlib.abc.Loader): + + def create_module(self, spec): + return importlib.import_module('setuptools._distutils') + + def exec_module(self, module): + pass + + return importlib.util.spec_from_loader('distutils', DistutilsLoader()) + + def spec_for_pip(self): + """ + Ensure stdlib distutils when running under pip. + See pypa/pip#8761 for rationale. + """ + if self.pip_imported_during_build(): + return + clear_distutils() + self.spec_for_distutils = lambda: None + + @staticmethod + def pip_imported_during_build(): + """ + Detect if pip is being imported in a build script. Ref #2355. + """ + import traceback + return any( + frame.f_globals['__file__'].endswith('setup.py') + for frame, line in traceback.walk_stack(None) + ) + + +DISTUTILS_FINDER = DistutilsMetaFinder() + + +def add_shim(): + sys.meta_path.insert(0, DISTUTILS_FINDER) + + +def remove_shim(): + try: + sys.meta_path.remove(DISTUTILS_FINDER) + except ValueError: + pass diff --git a/venv/Lib/site-packages/_distutils_hack/__pycache__/__init__.cpython-39.pyc b/venv/Lib/site-packages/_distutils_hack/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6f60d8a7b6fba4d9986bdec9813e199bc57935e3 GIT binary patch literal 5100 zcmbtYO>^7E8Qujz3ZgzN%T6X~n--1IG@RI!?Kp9gQ57eWle%^2){^51M;RF8E=9;7 zLGOZ+#YmURGfgg?^dDF=z4+Q+)4{c;oO{b5>GLi~kdz#EG6iQB*u`S^+4p_kk7Y17 zS7Nw&U;gWUYo4)xQ)B!ppm7~Ve}hUe!6R17?!3lL+p1aSZr5yccWREXB-<(MvRY9( zPo{(|9O)k0oQZ-cK4!IPF(q8wOJZ7-aGwz~VixyVF(>A6pZkP~Gh*Q}6AMRHZC=hF zvDz7NRxIM(f;cD6<9=4~4OV{PTcBdqn(O=B{qDZ+r^Rr)6D!&OdF4${ggWUZVWhX} z)RJ8#8$lvPgFYI+Y&6zTbOn{*3ESbjT+Iu%!4gY(XxqYiz?5ftoF@fgA9HL6@5@d# zoz533Ti?=L-%g4q1*&ci2 z1(A+DCA(43klIUH(z~Cpci(Hrq8CXo?0EO}zOH0VeZS&uwnFW-gMBZOf$);p>ve?G zp$h64UYD(4H;k3`V&zGt@Psd7FW8Mk3~ot{DYfoN0L%J%nqcJ%;|an{qWue=?C2i$ zlC*-v)A@$*IY8+oh$8G4@CbOK0ud%*+zBGcW8Zs2R<6LE9GKpLp15sKsM=As0~}v*@EpgP#3P91uB z%G4~hXsYi%UweP+uEyxCwU!F?)`#8hdXNNLx8g<+ZEebScdHwAFZMd!pz%P8i+e38 zql@)xO>yyqLQsN8T zwoJ-AE$p?zM(gCZF^ZNDWx`JzVa_~Pq|k|NPSEZnyiM2<<18^-JqEpmb-;Z4fF;gO zL0Bu<8@SCwEi3tIx5K2>t3zh(WdzA!c_iZH%*QWZy?kxSJYozH?c!VS zUAgk!yH_t=Hj^P%A01FL1*FvEByv-dIK8FMmLJ0oJBAgWNv705!9 zDYB^|gr>-hCx}I_Y?SLL`U)z^DA7QYQ4*H0A9JJ`jwm1na70l|;a-qM;fm?U@M^Uw zBg~of-1;!^ekzmT;}Ee=RU3xRn368SK*`M-ioS~K0BKB*t68F=;5)3zLXh@|{e%CF zA36!>e1OCT2-uN*SU4ygWzoNXPIqO)hm8C$o4qI^mK~nj5mCT+MuI>qJmuAsvKk4@ zU5SErT?DIDj8^AS^_QQqp-L0`@@e^TmzRABn=b+rJZf6Z++jKevk#I)As{;NH_g-? z;!T}ykhIcb5-5yGXWBAp#ljd&TFe?_V2w_v^R;gRo;FD&{Gnql!Y}q;c`n*haf5_H zI@j-_f~RB=`#QVxuh}Q;udE603CX*{{)+8bJ2o`H@kE&zoXHV8CXb59MPfcBdzzUp z?V%d7$;^paAG_d_iHzV=0kahQA3O_uu4FMJ)=1|0_*-!Ro;Aof4zHn~JckY=rLWi% zZ0(q`SF5UCL3f#}U(wA{Z{h~sK%q~$`Xw5f)Q!OB8cHI09r&|*c&W`gy#Hyw{)p?9 zpMbYAG4m<(r>Lu5A0itlBu8fCBUncEt~LhvkN+>=v3aZIg7PqTTJi@g;SezE z`wx48F-16hGH&$_RmQ^J%Abr}&*We-?cK#|6Zc$e)>iM)U?LEVW)M*jTq;4mkr4cWYb%^^48mLk5p&eXmx@^Hjt2))>K|ah1~vUTh<{zw2;Cf zqPGnP8FH`bkpCC;ySYP)Ri5f_mG=l;8_ez3r&UC znL1I}IhBM>uWZYXeAHE|F(Du*j#d1jfBjj){ZUnpX?{2z;^Z`rr43of!3;Ls*-uM* zLG-}K(Y5ga8vi|(I-zm(A^JkXVQdqMb}je&{>7h!rOc~k*SIcqjh3L$rzoj3R8t?} zDV+s~By0>KFs(X&p=Qj z4h{}X=`L|~>o@K^3smX`0=Y>mY(K(mgHWYzahb*xJ!e=f%yM-HJ>|u0?w9ba&QN6nG9^0cc`~6S>g`kg3{_62|7UfF^Gc@ySn4%!vs}%`J#2$Slw;%_{(!nwp|po{^fGqnl)& kmSSR1Q4oV>Z4Ar~y@JYH95%W6DWy57c8oyVJ_9iW0MI`|(EtDd literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/_distutils_hack/override.py b/venv/Lib/site-packages/_distutils_hack/override.py new file mode 100644 index 0000000..2cc433a --- /dev/null +++ b/venv/Lib/site-packages/_distutils_hack/override.py @@ -0,0 +1 @@ +__import__('_distutils_hack').do_override() diff --git a/venv/Lib/site-packages/_pytest/__init__.py b/venv/Lib/site-packages/_pytest/__init__.py new file mode 100644 index 0000000..46c7827 --- /dev/null +++ b/venv/Lib/site-packages/_pytest/__init__.py @@ -0,0 +1,8 @@ +__all__ = ["__version__"] + +try: + from ._version import version as __version__ +except ImportError: + # broken installation, we don't even try + # unknown only works because we do poor mans version compare + __version__ = "unknown" diff --git a/venv/Lib/site-packages/_pytest/__pycache__/__init__.cpython-39.pyc b/venv/Lib/site-packages/_pytest/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d432e78455776398bb0feed77e7e593e8777845a GIT binary patch literal 285 zcmYjLO-lnY5S=8eyVw?b@;?;n2OdRK5J916!GpL2#!$=#2v)K?&~zU}b_0`*FhK02b;@YOcM`WuNXUODAz$m2Hc$F03UkD&t`S4=XRs{F zZSsV{Wz!n#&#g7~6>NIzr+l599rC#~k8kjE z#MOSST{G<}+Vytbv}!WwbPyw&Y|~qnoqs6<=^~=_;jFfDsdk}c9^^4_ut$S=Cjk%EQSV)C^ z6zc?0?CF$sqJ%Tu6RaO}qmU^rqoljYRLcC-FP^S#|8{3{eQ%wKowCVPFCE039S9a0 z;~0;m2z5Nf5L_a#NioHs@~Z3(`XbRgSmT$74 zqcLs<9_+1t_VA`hp%MXSX~)Rtq7w|_^Z&4$_dSm>mS(zeLo48%>@Zs|tffgO>b88c z#ZRS=ofcU_X0FWNo^Q6!%v-r=t>~!3G#Rf z0GMuB9TOK|P0`G-h%$VDfSFi#B19dN=URDz29yp48o0p3iHPGCTiEE7;cCDH%$#9* zu~6d-Ej@8A2Jwj!jr~*bB90B%uss6EDeSvD>+6r7?|3W4Rp6j25+Q+Vcx_@gQ=yD9 zZ76byeBQ#c(BU$T`s3vYdW$a?R9Y%YX{lb*!`85kjfs}Yv@3%?>Bj}2(2o*0M+Z_5 zvdN}KpFqP`2Sb)cS<8C?NQOZ|Uc@L?o^K2e_JzSM7Kgnc=@x+j+r~Cvt0)QM0T;m0 z&)Ck3z0bF|%Cz;jpLt-iLAOZzUE8i+nN2vQ!d?uy0DxzkqzBKo1B+z8Yk$YzW>06chxnuvUSFOKg=FX)s3E z?h`G4(4h?IMNJ}e-VdO_7z!o7z^Wy-Q3R<>Gs7o}XE}Qn`*vj7XXPx(qQ0^4 zQu189l;)uH8ZsNZw2U1Nlpd$i3%i-@P1(ofJY+=h9WpLv6N0mW&slOLG8M^jp8j#3 zu!x1=H-eIM=|C_HJxS#elTo*)S#S~zjjMNYu7y2HyCp_}GzWvOfw(#L7J?Ya?TI++ z3}SZD6A46;!$O#o_Mzy6(NZ-F$B9$pCJwYSxD^68?cNM$5cUXw@% zG6@~%F}bKs_Zn_RCF;l?Sw8B|UF#bg=E^$8AgH25+%cVREIEy_@7C7t@9)U;5G9M+ z-_AtxIbytjjG}3OGdkEuT_KjTAUq1XLhbv&p-_6cOmXC;yy5#%5^3Mxr2UOz_RF7q z-_X`%UDM5LnG~JqG_M_3qQ^Qn`lv8Oi(y9@;vz51kQJ($-hb4eQvFE$!0i z+sep(XBYDyS+DI^Zc^dSE4ORCs^G42cVr*i@|0s8y+^kE1LRb1jAb4V)kbciBgy&l z&!Y-L-`IL@!wN}{+`j#t{pkIFaJ_U|XfHRt+(}hl$pYQWT_v*IjYX2z$5mQh6Q^1v zT#|G1n!>RmcwUVZ=JOhuj=5%CQs^ZAbtdg`ZmSk86(W2a)%?;6^3cEg^vXYgyF_XA-rWHjwP*aeZ4hkQTxe7~Ra z0jfejpYi=K2SGfZsrx=pLkw3A(lnM=NEH_qy>!u%#68LAL6|K)NM%VN*}$!+(XcDd zoNLC~Gv&-9Kfr80d-03_C2B$OlcwGN zD2Q=Ds7d98n%5)|Ma(T_p?oG7=ya0%zp>>4gkCZxzk2BmjIn0SD7lDscv(g28x=`c zQc6ytQHcHdS+%+Qc>{_Tp7=3!T{To=tm$EfR9*^1l_Zd_^E%Q* zB-12!@kP=8uw?6bHLYZmmQHV&O2B0Eydq8`E$3fX#-%Ch+ObellY_rgzPrbFSCs-2Va!!?q*< literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/_pytest/__pycache__/_version.cpython-39.pyc b/venv/Lib/site-packages/_pytest/__pycache__/_version.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..dd756fd7cb34c6a3c2b16d905b11d26e3089d479 GIT binary patch literal 209 zcmYe~<>g`k0-n~_34K8NF^Gc<7=auIATH(s5-AK(3@MCJj44dP44TYUtY&&fdM298 zFWG={FPT6D3y|>BWV*#(mReMtnV%QM3uVNYlosTqu4E`;1*!lOzwDi@Vgictvr>~w zievl>Qu93Wb5mo=QuE4Ud@_?_iZe@6bqf-cvlG)(i(}#oDoav}OZ4NR=IIqw-r}&y S%}*)KNwovnS`4zEg%JQK%Qcz+ literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/_pytest/__pycache__/cacheprovider.cpython-39.pyc b/venv/Lib/site-packages/_pytest/__pycache__/cacheprovider.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d465a9886f9397abb7e019f771039ccd5da085f6 GIT binary patch literal 18987 zcmd6PX>eRucHY~1L8H+GK>#E{ifVBY*%Z;FHj={7iZonC8fzd%#7LvI3KPE}mloTVzM#9N$woT{XfSI$-`J5eS{mCJt; zI+5~y=e}-qgQRBSUk2;lzWaLidoC*@BN+>S*{^)}>Th4RtpChJ|6c-$7w`p7IhLiY zlBI0r)a{ZjzfQ@)uUmH;Udgjr=G7C8WGN}}1mdYuO5#bx)1|b;Q-}|hh9sUwJX6X@ zdtM6=#mBysaj{2^~ zcxhbXqxFf#WNA|3JL|g}drEsGK33n`m?}+4d>7*TO8X=}j`;r4eu+=iA88yY9gz5B z{n19gl$ZGK`eTiQrGpaRgZQD+A&Kv;A8s5e9g+A{{qe@p(ou=;L;P6jn8f$j3ytHY zfkNsmVL`E6_ImD9Y)UKTTbaDQb*L| zNIj0!DWs07V@MrC>Pe&u>Nrxzk$TEMpd9tY9Y>u|)9-nur~N5E<7fS6{Cx|~UTd$V ziYPm&PND2sf4_QCJ$2jmr_hG^PpfC%^Dz2p|Db=!FREvecUqm1yff0%*|>+Z>N)iA z9P*C%�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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/_pytest/__pycache__/capture.cpython-39.pyc b/venv/Lib/site-packages/_pytest/__pycache__/capture.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cf80ac9b2728ff453287fa016d3806428849b63d GIT binary patch literal 32017 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 X=b`Cy(_4z)V7-0%jAOf#o$LPsvRRZ= literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/_pytest/__pycache__/compat.cpython-39.pyc b/venv/Lib/site-packages/_pytest/__pycache__/compat.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e3a7ac239636bceaafa813a523c60922a2412879 GIT binary patch literal 10792 zcma)COK=>=d7kI)VzF3);6u=h8bPFhrO6eFMd`-~apnd)=X-qJqED5B~G+t#=gVKj@?PuYiw>c%t9xigH_Fimfn}X})T!@~zpL zeCxI@--d0-w`rSr>wd10xAQ8^F?_vIunV$oqCQ{`$a>B%HU{lMSjl)y zc3IX3P~T(kk@X_#!}hSO52C);-Ye@R)JNH{!q2c-4Ltef!wHE!BPv@BMhcepO)y*ujSi zJIK|QLtDE227hD4vfpGc@i8`fUg74-TkPd~=Fe4e8Ed}cA8s76-&U2NfG^E|+kTsu z*{gi0uDz`AarW9HjlIqeJv8hIUg5^$qwI|*D)#vnKgN&CUW>hn-fH)IjJ<{Lce>w) z*%5qCvKk-e<8sZnvF13Ncxc-1@)NA`NM%Raw=m`;dxITg8aw_-?}dRM$IJ?5?gc!r z5?rkM$Yk%Z$%i@nlswP7_mrP&Vn5Hb6YS(e1@N5?N;uCcQ2Dfd8d%ys)AlN-0mpmn z40e8xon`MoRQMToZcDvP^Uw0LkKgC#*tfS-b{_8!@V@wP?*%h>3;QP}ad3I{Q%C4dPLw;c8BB*tpeGF=Syrp5+OY9SN z?Yoq`4Dy+;zR5Y9vSQwo5TERiQ##ycbk2R?<;)d=eju2rLQgy z0|R%!Ggs}apfBO$4!9=$8tWN%qizND1Q=oaTH~614J%FJ>kF)V-M%h) zaRK#@?T=Ljr}>h77pM6y-@{+$uMw@lf!COWe|G_?%N7CYCrq1DDr|l1W;FwhwDiif2W&6&qD zSEFO}{;Bx5h$k9G!4;@1h13;P6`@8?iZfw6$79-D(Ab%ZkrW&!z=0hnDLPIgWG$cS zCC9nna{a8wjKh08c&dn(F-aC*e=v1!;id>zcrA_=W}7^?7B=|8Di2l{Zg`6ekr(sh zO}BOrXpI(}W;^Cl{7y=WYO^i&W1%B>A`L}BmEU47>Pc>9c7ASpzM?&Y0#=@pJ|-l9 zBrbDfxB65ArdmNQ$q9*-8XctbM|GMug(oVZh?Q8~faGndbIMb7rlKW!6boUX>!~U% zytdCIUco0$UJpgXjU87kMU!ZeAS$XLa1*6(I|9DD-6%d{VTXDpb`GM_0k^rIK!yBiTRJg{`xS}_U+PuJOd)#N0 zV^%1zMd$RGh27jZTTU)?QLgEQ)h-;>qU->3^K)3Q>0vl+0dDr{^idaK!^+gc$?ECx z$XaZ9KG2NOt?V!n*wTta%UumUCfD;C%}`*_QhEw6w3sJAJimR+da&%(mMttmf(fMf zQHU=u_FUgv=aCgJb8E~GmzFSnOs3^PsdsEAl=)P0oVw}KNAn>5qaiiRMInV zLN~8oZC<#&c+=j7%`MM-@c+M^zUWz z;FDw!+xcM)%Z0%#rbMKW6}6z2@f1`I?~hJuiq zsu0qC(S8P1Lh5$~C}zeJm6@@z1;hR0?bcjRs~Vd>Qhub?HI`?EhlLI4Fp=7>46I+s z*fD4|7P+v(x&`IjYVaVAtX2d8Z#AvOHsrl_ucv1MTFZs4t|kL;0BXw+#b`#_J=>t6 zN&fQml}op8%&!l%Ai89ss?^>mFP&rVqhpdrUBWTH9HS$yK-Z#F|5#}$(U`2MEk&U1 z7utOTN}pu2qSU;u#kA^VqBfsu_7H9AL^uuT?Aybw7r`WJ?Tt&Txr0n=ZVUGbODvl%t}ri!#r5Rc9i5( zy%Z%nI3me~i)7Ig-RA);&lwC8-=;#UnM+iYaO~Sx?B=0_DX-HL=-d`!hBR7gSY@Eq}39uI|YQjq@vmGyEe z$}6deUWABhv0m5OGpe#_s>=G|7~$E5@@tK08|JT--xv(M_)|?)?m&c@xoQ5P85Gm^ zq%x_*Cd;w>LuJEwoO_~>%MQf=6%aL{9P%K#7X4&H7r%-N8~Vz?<04dr`9zBcx3uV2 z8#z{B<`3YUZsy_=WMN=S6TjWa#Y3cEJ3SjYaCFhV0lvzZnsh56C6avIRiQ9a@JiwG zgrdF8)S0Q9TMk=3vxM}3q_Npv!S}msY|q0OH$EqI@=@!O;AD9h!xoGrnY;kDD~jEq z#>xD~a1Y5&!MgP}*L*huOpIVIBbPTIl4r0V%lTyb^E^z6_&mvH{F9ix(Tv+d>VgZHzSDFKoF`{uttl@2CBaJiz|b`4oRU`hdVP%S zC|p$-2q_Xh3a~RxkXN4!I?e;(Hk+I|PNgg(f~44`N+jN+K?4*jNh2%D^lioz(IvT* z$Hf6^D|%5!OvreH1!Kn%)6`W;Ss>GN`DHhfjvML1N`63>YwsgTXaTj$s5b~9ols?6dZfwvKnGj&H zbR`~U(HprC9d%6c$&0K@o=8N`2^3)6quZ=YEL+!Bdzn_147`(R#SP3AyRe+Ng^quw z)ydw1*?{HJ@75P<*WpV7d5fbqFt! zs71WsQzUBCZ`UzYlAkn%1fB$XLwO9+Q!paN@QG;y&{;tQ|Cc9+ogfUHrtlCrBL9NG zpyqi_x8ISi!0tWF+zIt)&$zx-{uP5$j#WjFZmC1ccZZb-DPg4)1Hnpu?-QA0nMq6u zPcpoS><0jjmNSC}oaQK;Wylzb5EFO8w#QkdGzq->^hp1PF&&VIw_l)l7iHwmks}m? z%0kBSa9h-17@#3b{X~6i06etB<^~)Zk|6g0r1N^6j5P)IO{BQ2OLJ4#uOI6;6<#dO zV_+d)0@DWrBp!GXKW6c$<~Dh1N(o*o^%==uAYa0Y`27}_u0%Z>%ix@2;JMU_FODy) z)lM#~EuO;r^uii@Z`?Y5!4fYvr{gJ-PxpkgA9#eo|Ie1$Pzk5UV4!(*shWWx#!oE- zpDx^Veg+hHQtkEkjo;g zF;6CYan%c3ku|njP3b!^o^5_Sg?qd*hHMD@veaMe%mwZ}X5F5@a{O%fUgzdy8t3qRch4)bRBEkpa1gCmKc3o6f|If%!zSb6Hxm>>#&;=zc5=$x5N=$)oxgeJdp``TjGGFaBMV3G@@-S)I2UMa3rJsi7xbWMQ z9qRgJ*_scQf$s<8eWlc@k+&%adTW$R1}`r1noGN3RCmRl%OEoqaeoy8mS)l<(XxCP zdFeR|FK=!c&yKD;&jipKZV(;RphK9H*1PN@+a+ylm-HQzp9UnWABTf?`8~8X|Lq`2Nd_ zn;NBOaXF)J>648^2^p-7l47pHPjUV6eI$9H8V6R2Sow#D;|4bYh0H6gT;&iVh%jYjw$YW* z5k52ZO*F$O@)rGeC1WR+sUL`!q=_V)3}}d!(1#*jsxQWQ#&CFh#K}_O+rH}_dhvdatZft#p3>ad&coPQGl-)@_ zYpRq4K_DZ4Da(>pVw0fSp8-{MkX7vfAjly|z2ID$5l;rXCr$EM>?SOXLijG%8IW5# z#4eoH%_GqvzvNQ=evWt-BmM_Z^fn4bH`J0*Le@Hm)IkXuD0r?KQU~<)+<0!7$VRDh zPG4qE$5iskBzuu(wSF*7Lv3j(Yrzg8)DI3CNDQ=(rMbHeC5PaD4&nZWCT_~{8d&3I zj7;E)v89Teu$y2bSj?1B^!0zbg!B-?5|X-bQRb_EFWXS0Z8f~5WklGicsC)Ql2h*9 z1#a%%mFZ(23L<5`h~mN6Ly;8C$Iw05=K$gU3GBCYX0D28N|2Tn08!8Bq3II&}DeCaeuh29ZZr-HYn<#3; zSe-wrcL6<$k2LGFfo#y0-Uat?2Cmj&E=G=)=H9}#H@thupQmYiX~#3G-AS3Wnz%zD zgWV+sDG_RzZIO%3yX~22>K@#3Yc=F1pxy(kiTe-a5JAGWMR$qPxQ+8w&mZqxexzKI zC1>WgFCI?3dn&aE*Kr|39s;b+&aJyf3wrUb6Q|yvv?j7^i_>Q(ad87-w{Ukw;6{=^ zVgHbe5J*^OH!vR|U(!7ZJew%yE|h7#=hzeEx->#L6JqA4D*+y4cHcU2X7WsSLxP*U zRF&-kJ;10tXt;w*OQh6@^~t7HyL>Q^rn$vWXbnv=`|kdJm-?iu)=QmhvyApy+e&GV zj3Q``YXKPvtWREF3g{$6N8XV!Yt?gMxH}mG_!}MCoUC4%$u4?uPZ-H`oA?odrlz3n zdZ=vR2i-$a%9WV=&*aR+gz<3w7X%N(Q>AI3Le@~mSyFglh1B{EF%mz+FyvIb@d$lm z;GUWb+AsuTA+D5Tc`qK~;@uBjhHit>Cp8T1`^(|$BAzHl(eKuAb4len`C|!<0}s#r&TM!bQt&$f{FNzW7>c~b^~R~&`%{CMMzsDre>zyKIr1KwI)+QS{l zBLz%%gO2kBVURGcz?!X(?m%9(GpxG)(vGe#oVjC`Gyf$L=!FjJBxd*kH^{r|t3LW} zgGwy)R2P&u6Ms#`uc+u}EUEjQF0G9hfB~GO^_>@#7Q zKY@z8l~V)~>vLe($M}At8VcRk5j|Fv745vTfh(spVW6jk_?&hn4;N~_|Impkb9lR* z-6N1vLqUGXs9I60^8XB+s2%R|fa64d7)QwXQ7TRBqk^tg#a~hJ=Tr<(@k=UxNX0g` zWleh0#EzfQw&aLoQ|%RGnG~e$N~-ov+0@iSx@1`~jDD!9zomuVMDc=PlW<9#9YTev z(JLQA9RXXgsN@o_AWcCoF|h>lXjj%pZwJeWuqif>2svkcwP{U!wON5G`aYIO47v_T zaj2&IZXZBe*b=S7e#>>=BF+`FBC~%GhoiRl>Q7OeQWsSrj6_k?^LsWc)!XEB8w;kIg zGr5B7hQy}5H(k}i&wzM9zZi<-4^dJo53-A2m;5P7s>ZyEHd{ z3qMAZyUfXmUIyW5)~rn%kcTeaHt8ryD=DptGzKXQI75m4 z4nG|2&)oLZJq*!WOvg~oBv79(g5W-RP=g0}`#aY`)M`7GM_s;H& z=1;eKB<;|fq$~w1Ax@`m=`d@06JQ<&ZdsC;AuDFoY2p!W34= z3`~AofyHk-u=(u-4&HVr*L4Heq`D5``5@2n9O7Q!aok0`5EM9`N4yvmIqo4|3Q8O= zAYKm294{h16U=bDgm@*WaJ<}^?al>r9G~gz>COl99Itftb{B#Lj?W^#FWATNImGt| z`#HV`@dLpDj?Z@%y9a}V9N*hH)IA&==J-PAW8FuBM>xK(bEJDTIBFVl=KH2Pfz|Er z_}xc?N4fMs=dtdw;227e%MzaBP3xc`7T>YNL2>Ag6Fe>t3QHVD`eWh|q)*5Nq>mtd zRQO0g@q!^96_4F9#ACOu;7O#8iQ`BeN2-d{n@ zUY zU%K#xD@!j>jaPc8OwIbkxY6&*h-zKB)sWoFm0q)t4EI96*KDmJwvS@5jN&WZL0`p} z!&YagsAq4z-@n%C4rtiDSOlspcZOKtuZO*GP0|eANNOISa&w#~$T19iS)2z%51y4@@4_i5v_ zk(i0qBw{+FW3PstmI!0%M;pC3yyf?s{;5*~vD)ZFexEn5_P%*$RP|et-|NSItH<TZ%++PpZ_7p;U43aFd!GlLuilWo8&|*BTD=;z zVtHy1Hm-pr(bf8ZY4`LFf7S*Y=|Uab&8w{sBZ)=UX_e?Znnz$*mN{=$Ovm&-v@Gj` zMO!_F+^UtjJaMHKk({EDg^WBNrqN|Y#uVB#6SEBwC~V<~oN(WAOzb*x^1>5^*b+ri zdMlUZmBkEdRdg+3fG)F3L4J)7MQ4;_a-^=FaaxRUPGmh+Lpiz>#i5Fs9<7NpnVmho zbg6utLrFXef-3(Da$~h0D#3?X4F_?pYNw6>pQkRJ>3%PD)@5ff^2ThC7I!ymv;Yzf zJ93)lqw9Jiq2Cx{dR;Kdu7vo7v#I%)F&}v=#A%afcDU}>uILsubkS`=0My0{IkS@G zEQ{5uqfTJI!7?%qZ-gp!VsMjs5_zNisZ%lJlT35`8da}!yHd2kez=^-u_X1TscY$7skL7c)=l;8Dt*(+s!U=HtkX zXg-En1W#G|?`N*N6F05@alM_?&r}U{iQ5`1r5?_0q-bqC(RUF*wHO`a+r)L?wQW;9 zmKbfwfY`Z>Q@?G2zo^7P$=R$VhmstX#BMvk<=r*f1;mPYN=2qh6r;v0E=D zs97%wL%8plZyHVJ;5%jTx6v+dc?qNu)Ee<@VM&qrQYR4v9u608zB*3H0+OxXjs7)x z&(!Ne?$nQ@-CjJ=Aq26}Hn*&86V$Vtt=Q~SpC9ct;d}exFPsueXG^tV#T)V<^D)0BIL0-u7esGz=3m? zYgRsVY|r#e*O||&^C)dlmC2ur#5p|Ce;|+s@S71>uYl2j+^oQnIq3pFwWxh4;}abYz=)djNoS0I6sL#jHHS!Is&Zm(8Qb7>Vd7L-OP8x=P;N!_2IC0G zD|Bd6=ND05bAnGX=7gF0WX%@xBO<9`L8+Oe13TxYHdU``a%YXnsm-9(4*UQZhbU}yt*0`*bTR{xsV04~mEZuDZDOF&62 zgc9eVvFRqcEsq4%W)c<<%`cDPF2@>@xlPklx z#?}l*GPkYGa$+atrj^q*Y6Z19z zaAJdUj;34#^{0O0o}&)3$5QY2EV5Pu2mRSiKx*nO$Z_vBdIzjS+Lf+j1o^12887@i460nxk3upr@I9FG)F;o$(yE-E; zp!p%7RqBbp&bSA_7|BjEwSnnV8@9_mD=pMD%hl_e<$UzUGD&?Iu`N82hrj^Y%Xmf? z{{QQ)jVCg-ev)&9j(Gk5fJkg$#lYdbVu&kYLL~j*uGNM>8Jf}Y7mYW}yJp+j%26I_ zfl0R9ZG%u#JHP3~-c}(Yk(5hv>N`nJSZFU$2bL5>6);q9E=!@teuMFr!+n2~`c9m! z67|-D0O2++)xcO~uJbOBk5qf6U7;ByP>ye*hJf&?&oWgqOqIH8o&IXrdEb0~bd2;9 z6n)(44W<9&m6uNKCnOOjN`FI)l~rv|5XaEQa4t7lZ6{B#vS!&tKX!ed}adP&yK4o{4t)0 z;I;u~B#Bgkd~zA5du9dFiqZOq)(1`w<&aDjbHQ}Y(dCcEwBvT4AE6p(6DJp})VN6W2z89=&djn02G z7NF4|s>xVvoyKru@+TIs2SieI65_~G6xuo@ge9!Eh!sey*`x1$X&905K$bpCpyA-j zYVAQXFG(rD1|j@j%AYvWW3i!k8h#n6XzxOJd)B3TV2{~=9sqg7ez+F4dJj@zxQecx*0gAOZodmYg&``^8=9n?)bQ?5TB+Tk0j#>t0 z1g<3%OA1CS4CLYnf5zB!uAiiGcERN`G)dYw*i+Z$>ne`ldP~T9{W4@83*iER1Grfl z*8tkWTd55vOzJi3+89l36l1?xJVxj$HDl;)u5Px}5IGZjcmv6Q!=tTTIK4b;-aJI` z>*7b)#hnROXJNM1$wdS#3tX#RMu?Z|IDKJ9moO`6rl1X0B$sh7096yvF(p~aN09Tb za~>a6VgN-eNM|z0(U6@EV26*qd$uABrvdLJit*AJs?IInho9a-d9;AQBU9h1xO0a+ zt5TtOxuR~Pfc&?a8mVDIK6#n?zDNPp(|h{{#aMK_gxC-8WX6~0EWR|#WE!+> z?crd3N-Gd|6QZG>B7gz{xK;k<$_LacfqjAc!Ne=i1;FQG!nPfjn zoOW)@MLohYZsKm`GmPUgBKir?NkO>KE!Jip=9V=yRf1YYm`!;`IV&)RN=Xi_y~Gn9 zP|&-&Toe-XZ9^1)$iAlD$;4BCpOuv0WA>VsMn%2($@=h3V?pS%Ono+u7R1=Q=J(7^ z@;RC60}2Tf(TtOVC=(`13J`QNboJ1RTkzqIaKiu`q&RwsR-`h$U%TO0nBm;4<( zP-CUAlvZ?(%sfIXYJjax-E|oXct>AHLe&wB9O`&<=ssvg?s*8$ezRGHrvNn`kA7i| zF|<-4`m4^WJJk1c2vR!4+CLQJGa0yxrBs9J2?}1N0QYQ$YEnRmNxeeBI)WXH#G;xo zQnkoTnYwU>uEU^nfXk!Qy>^q{69=MAUHmo$*AS!*wbiWaQzoC1LcN7Orjq|cUPMkN z!zL#Z8EB4KCN~n~1%V=hN=1bG>G&{YasioKcivh+tp#(mcS4$sdsU?4rkvN9VOaZI>bZ8?DfP{q{x%i=HJEjk-|47a<7f#@L)iR4uSSDKm#OqT(n)Nw6 z5wZQ)2A80|gw7N;ZY#;+b^`bnWgeQ+fRbiWH;M&Nf0|>HE6u}S)55Z$ad!nnUL%6vr|y10!5>g?5kYEGTN|Qp zg03t9G8ar8{ps6NNCaR)Tn-%}J|-SCY`P{ZXMaFn$8gZ}5yU+kU8OvvHEhd2qR|%- zOzApeQ({ZrdeX-+HvcyK6fri8+=R4Q>KPQk^|`M;=tprEG?d+4u#}c47f6G$tF~S> zj~1iZ{VQC^=E&>94HMsvJw=OSjv!a*kp~tuRk>QY@3gADG!9K({V@e)1nCUj1n>Ck z)Q_mZpAPsC5#Qq50c~lJA!>b4E;Bbja7<`|=kV~&(&RBAg8)Yb&Q{^T#f>{hc5&xK z9-7lr&!f!>Jjo@pvNIoH)m~a!((;(LfBd63Jv4SSk3f@y$nYxM8N0+VQ#Ru=fK+b| zdwhRbhi8E)18t033x@Q?!3Gt>HPD^d0a|=IpAkdXfGyDJoZcaY1yURaDfr@K%vuvQ zaT|0$A!gC3qHR(?rof*Lc)w{go*s`u7b3hpQ%Z?8fI1=Gj7Z4Ebdv^AK(}pe>)U|* zTh3ji$sss)4}xc*s^Gn`go9nrjsE)rOyM;cyt`jSjCaZJuhLC6ncGyl26tP}znpC$ z3~$->S2z5S&997JuHGdnGC_s~t`q=s)A2vGewdci^8 z)xi!`f{Y<09F$20jVGmffSE0yZRO|2)Hf{X<7X)Mm$C7#4>oI0IDxo=wIf97YFqsU zk|c}6K?JV#$$8tygxaV3$qx*VLb*SXYtg zCeQLfYYXC^@sCgpfg#?Pe?o-zh3<>#bhg>mPpX+L4CF%{ZvsF-@#b+#ZBoE^J9rnG zxFo=((vnuM3vfcLqcTefqB$&<6<8wE03)#LaGwh9 zUVrD1mZJ6k&CE~=sB?`m#!<{5aFL5iuw@by8Wj`p?P-bss08T zs!hS)Qq@vKGN|7pcR-padmqzP57F1^ZB$<-ULnz8xN~HG@I|)w^KyU_lWjPG7#cR!`L{utZQ35Ew@?<+ldYk;*%v4aKPqlr|#6rK*`1AG4+(rK|3wR(`P zy@u2uM0vnqKwmh$6_hi43Os^Vlm{up2#1H$z^E! zTPV<{i{?50jrAUH(4^iY%|_p3fK$yaTi|pthvWW4)nfP;v~QZ1ZZ9$IRTrIB^AmD1 z2YzKs(~g+}***ZiyIK2D9TnwOEK4YVPJ{ zN0vW02zFdn^nnwO?0%Zr?^5s>1vLuJQ@|RGz$fD$RzWO<)t^!xojQDJNuR8cG@-Bc zn0GXBeX6n>JW7?lv)a}7JaJUCjDS8n(0^lNVdstG`oJHcFY@fg(%j=6oCavc8DmFphuGs2Iw`pvRF||c6Vi2 z?&Fg7SfD?k=%uIjC+XT#&~q<6H2s#O96Kpc3gYbU%+B+hA>H1#4Lp?}{yg~$t3UMS z`m=`30sitQyh6h>LNhGI=CtIMOub%<*QSNhlA=X{01cMkGi=MrM+QgvFB0$4oF1aiYd7W1UjT zu%XJ1BuQh+IV4p=P8ktwnw(=-NON|fNV-r=DzIXPi;itSV=RsM8HtpZqa7@61yuohIQdW`!~x3X2f00AF7S;7>BL_k);9xqs5lLV7_3M;s@QNFvCJHRPj2 zOsP^VT=8w#2z1Vn+IfCQBA6%`6VCF~<#%+3BXLy|$9@A48C7#>>1&`L<%X{nu&@E% z?uVA2Xf%2|c>#PRHyFpsnRoZIc_1_l|; zc%bp)_=0^kLwOy46vgo5ZbszTLqxO^yb?j(;s0Z;a6u?KKx>wnR(F8q$=-xK++!E1cz8nOf-8EV?c?+Y zM04$#uuecBuTcf<&Si9^N^|!AM^zDRk^zD?!=q&J$dQ<3 z8k7HF;|BkR!Br*5<$?BZZ%--_1&SnTHXm=~CCC0sWN%8vgb56)RWTpcNGhUpjl?_~ zB%2p{Jggc9m66$Kb=uX^vK+}=aJeBEGRp&u=c`ArE&S{F%lmkJW5_$oP^SK-{HVVt z24?zN`;BgMOaL2`DwxFVoW<3WDB*Cq+3mSs)CSF}Db}(7%@F$Y^>?klf7}(xm*_Eh z+yR=NVn!aHGk$*DicXJZq}X1Xr&}KTxr?8@%ej*-mbZg33nu<5fFaaM?W|#1rl^2c zcbA*KA0~nCFUza3mwDwqC{#Kt7^+3eq|jjqt;74c(d_n`bvWMen=c2=Uc1)vyRG4o z+wOanL$}pB9JcGV-cjFs(0w&%_6L5w(`q&A1OIuaF>E!x?N@HI)$kA9mxE!i=~bS) z?P1@4<~M5nr{4BzhfAHd_uxgX*LK@Se!bS!(hdJoiH?EC*S(uf1+*cKP%CTk}NMS03PH*_Beo ftXSpEl8N6x*2XpZ)>_Bztz~8Je0}|GmGA!x7ZSl! literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/_pytest/__pycache__/doctest.cpython-39.pyc b/venv/Lib/site-packages/_pytest/__pycache__/doctest.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f03e1e7ed682cf772fd3b859860c33840c0d7db7 GIT binary patch literal 21362 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�kJ3lls|!Znm`41 zO5+?!oUCl7gVDbHbX-kh(;7q*{ADnSd`dl>-?hPx| zOJ`@Qan0B&Oj>)iOtpCq-K1c5T-0KX<6s1i4vgQu)QI-8Sav&v@Gu=i?B@}vuHg(R z{i`rix{NxfV~B1FVOX(*-;Tf6(1Wz;dd^soc;mD4aCZo1I470~BAldN#Q@xp<6VkE zuNOa^ta&KR;`{pz;83r*O?<>X-Uax*=;Npp)57z`aC)a|OB{%ZKZFKO%dw?1FQMsG z3gbsa=Ze$&L^?3U=im_*8hdm@1_oE<%K%qDi@aaJWo+UAB6hO5Gwf?5T+n3o|0qeb=*a!HL?~}0m*0I4Aux z^6fm#oT)2k$us#7dBHk_D-8fKS;AjBR5n`~Gu#V4qN2jFrIR6a;M3z2 zJJEr6gw&=~F=x|PzpT~sMjLSWKC{huR`WK(b7qH!E0U~MbN~%dMwBB0%5@JzA`HNf z^LP(*szISngU(l39&olCe_*kgEZD4-Op`O%S@|o7`N{&9e+FD)GC7_4WO6z0ptI4p zg7c~@)MoQuYALzWhl)*St{EmBtFz#xRz7&i%g*ATfD$`BTWkzsaG9JP!u~&v`Q(OH zKj#m9Vt!kF*o|cx!>b!FaR!9lTY>yYtDy5;dN9!7oMv@%YhZSat0I55$g^Q}E3Iyu z-A)eev#GmTx)bQuOS175)!xR_hn*xI;yjj1M4EM7_{ z6HUGM+SW#|co8P=Nzs|>U|=_1V$2KL`SDe#TNtd~$buX;RtOy10jrW+qI4Eq+9kEY zE<5ND9Ww6U@$Y_k{u`s*P~3DlG~8YH_|uW-)4|Qh?O@jIJ8Hp{D1aZqSEs%^dI>ly zuIdc8y0FYyt$%i=w&adm#Pm6ZxH~>{S8h!`so00TnyRc9b!IGiVk!I}$Y2H`NTHc67eQ9wK%jl>N^}RYc=CzTJ$i#8T z;$a;Har$l?>s^IO>CtDr{iPQqxz2big(H!EP%ey=>yNz%qGF~b6*g5n9~Hlj{ROm6 zPRhX#aHqL)oLLP;F_to#5Gf!RYSF0^`3^ zb9*jl1Ah)57-@<@wzV3r-$C#dz4KZnOG;Lhd|1hgN?uYTvt_J?oPV&XX4`$?&S;O~ z=!c|>8!Z?;l&Lu2Qd!n`S?yRy$u z{XKPyBUnS4OAV$_7`J7yluQlDD8X@LG4;7Dn_w&QdfL&U>*<~on9}NB_@lg7t;77* zcaexul1p8KQy-8gjEtmx*z%l^rs%S)>?xjQg|ow*IVPR(8?Umo$ayQUbfLk++)HtR3oQ0Ll4-W}N%PkOXsQ1S0R`HfwB$ z;lAgbP2D6&&We7-FUH#GgIt81A0B+i?C@BQ3c&&B1q=+7hl4ISKsg?yv4ziEN=|3S zQ0IGDQzS`R$vIB185~m=9-gV2$?X2pQ)mIi%cEyRuMnQ7gO;uf+^I(gEm2|zJk_py z;Xp$AvDY*ZbC@BOrs1}+1oG>J>#)!*90>@>gqEc}XmE>3tG&LxvEnK^tf?iG%wSFf-iCe#_g&NfyVwo$u-MpYSJ^T-;OzIo!v`*bWZ^i0i8fbLxD+iG+9_G3Dozp}&#L%s5fuJd(-BpQKOsFw#%1<`MOA0QFK^ z#7y+dV`$7dE5L|IjGSx^UsvCM9fU=AyL4Usy1KL6E(9YQv|%@SzjhQ@!DqQF51fQZ zfiBF0N6fV2KOZ26YTV$O&`t^4Z0){qqA_}`2Rk*7$;Ea?k#L&wgS@r*-ovrXSUcIk zIk^NsBHAwUYT#Mg^}@<7yNqSt7p@LgBAtc$9t>(`=am!fi=;>!c~zxr@Q4(Uf2>J$ z$F*`}&ZmcA;D<4XaK`29)fJFQ>yOA!4}|jWPG|vf?oubsCdRbUQ}gZHhPznUi4K;Hx~&d3FG* z2^!1Jw(KjEIaQBM(BJBET#3lKJJ>j>+}|s?ONkt00wS;_f*nc(^uZfS?oc9(+|D_R zAj9^ipA9s@!OcptdT2;F1eAnni2?y&Ad%k1aF8tOcA02pAZ%}#B+w=Zwy5$cl6L>$ z$a~zWlHaS_oh7#1W2*i8B(HO`{sbA2P~X^KDmj|t7%nrs3DT!L^XjM1=?zq&W#}72NbmF(C$LCn{!n#b3fFIQuL9qQa?2m>lFU&heqk5e_#p z#tMc7A`RvKo0995#N4ABg5i)q)r~(>azV+TE0I=ryw9QV&WJlt8@pn|?!cgdpZ49? z7_|#oQIJQA^J{UupYrdfX0lm4&}p&M z%-Z6=k-QUPV57G|evKBoHjQ;y8>uf%hqY8-oZgN(j1+u>TRjR3RpueLwjxu)e!V9r z!9P$e4Y^G+%bo}X7Hi+H8-@7&oGf;RG0}Z273`*B&)JmTFL=}6`fVgYg8Ct=3rJbW zuVSNwCo&Uv(bR8^zVnGybA^j|*I*9~kM}!BF-5rbbzFVDL5cNWIsl}nCD5ruQhOBtmp^yO@XTR;jVEk`0Ahb(z@E!Q8#d+MAfqeb zJ!#Xobrh#(%Jfjy{xjcHDA(WP-rp=s0+mEK2E?uVJR zX4i70?Ex#k#fEjQR$!D?5@{d=-c-8dOmd8HJgIL6M`>tEH@klXrw941>+q_FG!}fE zVTj3>bALUVqMIQ)krw-8iVkVad;KDfb{aomif|01Vh+;9ePU8$kuFx_?1>!rU@H*- zIlsphZxVTo#>yE(iv5ALy-_u7lc!D=iT82R+?CIGG-|D5oz(|&V%Ze`W!$0=t>sr z&aBB~gxzV5HBk5xDuaC#)!Dkb&7{p6Xn%NRV`F=3 zaP^ATMg`hIt{iEN(8A8v(CUuXu#R;;nI!a20)@5ikKsdgo!FEBJXHY~B<~DqVmxfgFZ*K(xQh<2rmY z`1gA1Mzf>34z;ax0f@}|@m7*b4O2t}IbpEaFwSFK! z>tS0wFiinruAN!3n--@rDXDhfBgagG+b$gCQ2Ct8_a8@dZCU5v+wY@UR9H+@umNa! zW)W4a`zs&epi!45$Qd8}qIz>eNkz#8B~2wiM#9;bDSBdckU3O2M0Jb0{DZ3bAi42P z#_qQJ8u&1y-@(7zljz_Fgy{~4c1V>!t3(Qnz?2)mqPO%L0jtOW<4d~E*n|JpZgl|R zZ}jA^EBR~Pkw!$m@@t{A(1E|Zgaz-XveY0_jgE<-#tmEtl_{>INZp>2-;+u(J4h*3 zkZQ^dRfY)SzA|s+GDFF&+-+X@E#ZBfn1KD4W5S#m`xW(F5~kzCKB!6P;BL!3rn`Sd za;v&7;TH}oogiQx8r(OFdAm17anFxJQXJ&h#u`ybKP{>_o15*EK#03hzmIJ^Wl&8y zT6!tONDlX)jsv)gCcW%y&V4IW9yFttye+JxwkzqWY~6t+tqc(uFsi3xHqk;!a_~n+4JEDhP(!^UIH5$Z59F0?FQGrC8|##IY8qke zOsUPLWU6`1rm@p`CrRPEa@G2!6wQl?BBo!d#`EhrQ%O`B)?t3@t0cmnkdhI!TS*u~ zBk)4%XXMoiA;givNMg`L#9-PLss?ujPF9J0lD|a!j!4r>8Irn4BJJ%0rBk1}{h)s0 zg{bc55}#+uoL>0M6MCN=fb-YGcbt=1Ji`^ELnKgr5PLD+KN;M_>@^2q2HnpKD|XDL zAfjnpEgg>S3vV{Frq9GH$tLz`P5k3Zv=~S+i&*6uo(Z-wdYp(bp$|>7bV3BSp=_@9 zd|1|5zDOZlrJZulAqs~D>>a)9)-E(^Soc|6+!wTbW^Ta##8+E;lF%)wH*+{mkRx0b zLh;pav!I=JknJC!%UwQ|l8T*3v_@HVw+1ZH)NNe>QE-J4UpG2kwVc73u;83Js;Bat zu@Fbg{eHbTWLwEB&4L{DdZb|iQ!J)G%7t8`j0++@7Lo)Y1z@0m#!rT$0z1ZMap9J- zQloB35cw!8KeO}6EhtgZQ}Y{183q7-lM2|$nW0y1WI4e!uK{G&Nldx!b0e}Tl!Y4D*2 zVLON}h1Lk}69XLQd?`Po6=r>!KntzZk0w9ChHVy^U!4ME;5x5`>&z{mj0`2%n0Yp3 z>ho}OUgD*cljTBw6&y!M2i9h_*dpvFh5@DUtr=nB%e)KTjcuBqB+>^)*UlbldB(>w zs_JC4@xkCa1`!C+o8mO+SMF{SLV)0@(jFv$@#0Z7jQN2FCanE8)rmC`?Jf8tIOF4r zH6B^QI1AcbXFA)t=Li@=+z_ILhS9alQK-!@Q14|8hBwN5A!^ma{e4>NY8#gg3c*E< zp?x8#Z>xwazD`5QK)A?>8X^_ZkMi*nPH@43AWDffXF(KBF1ZZ2=^xWyycwSlDQeCm zT07J0=C9mqR?yD+AsV~65k`z%$7oMRs@_@RH;QAFr2P^5uqbcXWCvvx44d zciY3eC)o<~n!*Jo+Bm2|v^NN_yp}r(YLx$^R>i0xYHO`UE@kD-s}8e(Rg4p#ei}T! zw{+M%#h%?;a?zcM1J)igS)O=Wu3{{tx_G>H6tHeH3c2ZZwi)Fjgf)_cVm9JMP#mjy z8>^XEMwhUX!Nz8%oWK+-3@bV|SS#}qmUK_CT~{(6$}A{;DSvOqdxw)HHZrDOrx7-Z znQE=F`63G0lIuoJTErzyje6HSV|;SSm*#}M!fHbm@!7x{+q5QYC5DN@y^+$S%f>Tw zmBYS-fK*jQ>@MxOVQ&OhHW_>6MTVo@c=OS?1zzQ>F9|Bn-rmhD@BUUV-%AVzEHoR4 zIEq8=d)R((k`?0ZT)Uqj@-9YXz&B8wX2uNI98J500Yh{fx)skr@PAU;CWwEBh#68I zt!Pfd@X68o{(?Z|S{}gLBt0x9G?__d-jvZn>Kj{ga&v>TehcN_KjgB(IbJlS%H2O3 zxj4vg{kKRW=F}nwZ1pms0w*hSMV|8v`vgRud_Rn8zn3Q33GAt#*QX~u$I1@+LwtTB zqq;(<`sH4lbCG^K7s-}4_=B8;SZ&G&U~Jt9$*{kX>rT{x5k5Mx$*wKxKv^H5a3?2< zJdi1G=DcMyN~AGw3s++ncVt`eG6QK|cS!52^G6?pTy#W0haH+Phj3)x7e^*7fN_9v zXyT0t`#8YUZZomFzV&8%b^=-A46m}-xQ@SG(xq4gXS?&fa_5xTo)b-oezf0Wrs6YP zL`tYdSbbx@Q&Phi11@Hqog@Yvl83q_xKk_zCAbx0``5pKRh+^jw)z(zkdlo6;o?a% zpHH)gvlsPbfxwBlwWE6APzAd{S+t^pvwK6RBNCMj#E?r=h`LO=QK8i;BO!BQDn~m( zZ1#sdTxs_k$=&SRSiMMIqF=e6Zx6VjC`g<-XK|5(i?Zg4*baB(E?K~Z!4*a`*mOsg zQZhO|@Z);EGo8D1`3Fim6Z?o-ki2ES8Mi2g@_*Cj+Dj%e zEEOHc$owOASqjZ4$I6JP-^}M!<|48>$t8BxzQ>=AWtD&I5lgJ?Nd+sKjSta>w>@8Z zL){cLPIOL`_44lXmDf{UjF{C`eSXA<#kl{=T!6h(&5ufE3XV5N_1A1*fWPxl@+Xm+ zparSrM?+e$6$n!h`U9v=+I z4=05Px`yDvb_iXI>mrnL{tfrHxiQ>yT2yCT)gzxs-z&!RFu(QxNTNkUK;0h+ye8g4 z%<VE%zW01k&us7F;o2X&oJ|Ydul_CF>2s@obSW zm6$BgZs6^N+MG33L?<%tSq0TMk>ZaSsEJE1fE?3`c_QifMu#HI^xz{Z6*+K6XfdyC z%RtO}g>_YgZ(GaukhliI1BG9?ap+4B5_~5OcL1Y3x&|=JuVZ|m>p)q^%^3z2=a=Ee zqW4@-L#9(pL(EZNoHTMDoL9MFpAHJTnlj>7M*)2WBKZ`0E@bRN+Qr;fTr{E=aW!PD zdhC`hPzfzDdtgOdALw(iBH>yryO{Dip}!Q1fK+BZSp5AVcR3O419MyW>d$lS3Pb-c zTZT1#*7$B!pF?~!+6B6uS@?G}wNB$5Q^Qg2B327HpnozW;F}HN6fwQIIWoOrfKgPc zJH&Fi6El}}AG5IhogB$LtB%`(7%BOnF2AN^P2Y9s3Zom^BC-*SZ>>S*?!0_Fb!2jU z%UVsv`?P{m2s+koSUEeqwWD3rV~kLSxhkJBY6Pag5b|{z zw*7&K5+lv^Dm6h&3^f54EJ^6V5L_>akpow-pi;~uxn}RC7Vhud!GQ*&223g^r4wOE zu`Y#mN!2H1LJ9$#FNC}Dy^WR|`!3<*BP=>&Rs0?#MlP7>&$DUemMJMHgZL{nxlq>~768wBc?!o}jrR z)SphK+zN&fV7LN@XYJD(NmfDYpgQJY0ow@$!QHUHN`fuW%dmQi6GKb=#NY38p@?Bi z<;L<}n1%fEKMD0RoELJ62UeGhpE><~MtC2Fx4%`G7Mq%i9IJRwKY zfy8*u-orJ;yBtqr)`ZdXL6t7t{|E)n*vz)QrpIk0Q)k@DT|%@G-!>1`CX^Ci`Wook z1B$e zPMDm1DTSIs;<|GWdjfX4BR9}O{G+^Z>tTNDB@%Qh;=dxxWww!gL)lc|PK8LfIL$QI z;>mWFxze}NT;shxZEvnPl?mZy=a(AVSrxwIv8cYAGGfq$@y#TI@1ZT4Jj?4HvUuAb z3YS|Ci8AR_e1t)Xx!Yv$Nwt)8Ev@O!#?bm7UFQPsU#NR7s4GTT(!rnJy+33>=#f^tNV#hl=6?C(y*Y2g*@#q$=1P zTWljUdToBUzU*emsMlXI+OV9LmY!GCfQ85`1l^MygiBSBiKz@u;+{buDTlA3cxQVJ zM+y7*Rh*Beb()EmQD~^kZ&kX99@o!_>NN8a(pt=G%}Zw2+Qug$7bBWOA);ek>A%c3 z9p{{NU6F1>6(dLS>QbY|abxI>i!Ba{Qw~*Q6<8eOOA|A%UYje0KL3Ub`!PaJo^CW2 z>-%rI30qcu`b$qw)Phqt`3pCJZ-KbjLh*EC9$$Fo%T#I>08OuE@4E>Mw?9YJnBEk= zeY>1Ia|~@Q)F;&VH5x7}5T6k5i`F1~!)g4kPzJe3lycV%?DTuWq83j9MDdS6Qs8wJ zzjnINNW2V)iJcBlc+!(jB^11iD(sxwS*<`Q1{<@!(BIr44$%QaHQyftd#ZWI^FUkt z$(u@}R&fWiKvHF9+LV3xIz;{Noa#I0FesI=K)S1VCJ zO%fT}1tU5sks-c?O+xK+J{??78DGYr%uMy6&SIgni~GTLIq_GXeaZ|f&6D_8&0Ea+F`~6D+Ena*Rodg6`eNm{)U4yd_t3{9%>xmA#>w7c zI)!wQA56E79E%dRm{_zrK3p*(F$u4EXm@DzwOgM0RdlQ_25(7$Yz#MakU+Qhy^x<= z_&846%#=db&0M2zrf#EgXD^?kmj-aoV!^h>wJN(siR~8+B(mr_q*B{JGg_rJqzdDH zVUtSb=LFkoM$t?3DMQ%qw=$SuinQy51$o~r>2jQN$8PRAJrins*aDECG;)oNZ(S>6 zJgiT|>nxG+)=qRZ~4nEcex@KOS#r{`R?c=JkM)D9XckF--Phb{&R%M+r zT24FN!26QMs52uOYHUa4EFDHDT5w*GdLx$~Nxh!!(%XHm_Z8Y6e1{T~Ar~z(qM2|1 zgb{R}vLDHb5}b5xn}Ysycna(G>$%|bdZ*2jaVcxM&>5SCg{g|`%I|4t#$ap+ewYSg z8TB2!Gx#CBvA1DsAqzk)gp4!p&QtyH#RgIq6XCy?O!6 zFG}=M)mW4GH6hH#&AW))Vp+N*u6Re=3+EILNZ!YRAI9}GuQ(Sl8$aWa_lbh+l&Ysg zCWycv`cQ`ydo)8V3d5pWZ-EE8=+HE~-)1a!cf>WyE+ov`!uJz++ef^arAg?P$%$-@lQ;RWXeNcG0o*9WG%lOF1d1_pC(n4?Ta9;F< z(KrJ65eKnu&?qj?Dnb<=DBPom5R?4od3Z`Y16cc_OlHa0%xwMGK}6bIgOiZUF+jh5Eh0uJ$T zMNpSz_52cCraOuC&msL23#Nu}9xhlI63GreJ0VSui>SyK(9Ll z3gi!A4NK4r@t9&V`pdwni&YUCxrAR`)E4R$M9nio19Cm+9Z$pz>M`xT%aSR<0rM43 zX&mxg&qja~lFqJ9M5w|B(V@SzBdQwh4dZLW$dharx2+Y`sT0D((O2lOElNmNEzDK) z@(6UI83At*qmY+i6~jaEszzNBF>>YA`;zZb|O2p`^_MKoCZ8#n>N*s6W#EFBUv>!gUN7e7x-ShD7II>*u8oU&0AxTsC z>3wTn&cxEo@LgXM-&LfA#6eWxL{cCILu!hK?MED)m@l}gghVwv=XGC=iX?aWMf1hn z+!vCMbu(t^*Rr3Gc$8^=de38K_ED;iM{@+;@33S>u_FCKY<&HW;0`)Kr#tWFSXQmHpH_IXmBHKe{Ctnwn~O8lnY`ynpl>8HRLc*-0T zFOFWiX(l$={KI3g1kj==+;ar~hFBaua}d2Y+M%=PnK*2QJyD>;aH8)$SJfAm_G4V> zkU>cs-jHr(x>noc0MxZG-ob&YI|E@dWmVWdYJT^fgFU;@OrEUO{axdwJ4-kBKBXs# zm*`#xG@J329b2xM*kX6%$2G4egA4?hv{Xd61hFBv@DjT6OqvYf-zfS@0Y z@e5k>HNzWr!>drq&Du!~(RQZEjs`B@41P@T!5DgeRO=wW^*gy6X@6v#C*^JQvTt@fb<*CIlArs)(p@STPaFdJJxj(bgz=Tt!t}h@C8RGizanPIo^iDrs+(i3PGJ!Z z_N=n)(P-aCD6^-did_{cwC{;yY()b=(!?UjTH#AVRmKo3acl^=d)t1Bh3pZSh*}kX z$$sCwKZQl;41BJW--vY6aCDc5qG0DEP8>VR#6{s%LsGASDiD$)-oxv7sJN-WPxQ|r zY-2s%rVXaa3C$oq3kYc>UzJT2uI3yc5y>}vU)|>bQSuawU`H*W*D*EGmsr9%I1X`t zn>U~4Ls%MY2M~Jo?gl5}sF{7&3xcI!qsW1RakS#cI4oEq7-!5lOqAgI4c0iojSx_{ z%;G<6!6ePka~R-J<2gySGw`H#XP=(kXW+`+E|eDD4LR_A(uq(ZZn!#E6Qjy-yWo-) zg(JhKH)bHUGMd=1wXXI<6@jKOjJe((jt~60!q02p2yU7 z*DixGj-lUPW$}vV?28{EgN)K+4H$>Kkm8k32FFNybm}6nU_nPW?Q~5L7j}3Z=P}oY8#+V8rke3n_5-rRaz!GwIAEe*{8RE@)Bb4OepdatCj4 z)c=BVzev&^s%VG#0r6Fk^A^fEcna2suxT#(T_pr%+qlFen_J?mZ?45aACEo7-1-5B z3Fd2MZ|Ehv=EHVTlvZlugjp_{a~&Vdj04X+Y+da3sd83M>rPO`Wzd$JF)7Ox^OJdS z$6QG)3f?U*VZ{-Ha%C=IPh|1L(z97+lXCfN;A^XI$Ln)~8M8Cv(}M7O+3MBIS;R^M z4Nlz_=>qsHRCw?gf6X0WcQJ+bhUcsnXWM{7Oc##fjjACIP9cSgBIpi*QFRWV_R(KO z=Z2@yd@h}wK;v|iBaPsgvtjloz7NZHG;ey(&9~mx)d0BDG z&|d5m$dGLz3wxDN7)1<`U#q0X9pR4Co^wh&5~-~c*LQ;7%F8>^ENFV zhY?j*Ev~iWj0K<3k8EFJ&bY}1AMoDgY~A#Ek@_yoBLu(l+)5K1HM>Au0z9@X`U5`k zSiC5Z&qW~ne3FwgeY+mdbwwOg#1Va98;ePvty!-?kpp*JI3Hr=YVt`}lV8Osd;}LV_Ao z@kJhJXGHe+fOK$b&7uT9$L%j^?hW%Ln>VkSX(l?{BSwPOe~3E}S~H--C2_!g-L*5w~4u~+{G z<^HV_eJw2bE+uOa-tSWOQySz}G6}pPMp7B#fECR9P_`?@w~Fc5LpTDmPR}%SleP@k zuD5~{#gMw-Fu(QBl9;LVkf6#wttv+uxjm(puZ`WFrd^0z=VThZ+T4mgiG6hrnv$R8 zbL_G);khxMD=eRI&v^u5a~^9guqk2K7F5!}HnuT1JA`%ItKiTL(Y1Y@E3u0GCV_K1 z)TlWiVO8W7LklBK?c@Z;vG1niibf`feNOr1df?XWNiE1%ba_Hm24LpEyBMQ|kwMFO zMFzECc=BFrr)qV5@O;t2c$L$~?tN5&niuD=o^1~qwuu@3=oIzm^$K%izfHN`^=oep zfW*?4{-Ooy+QMbt^d;!YYQ|aH_d`4OG8fdu&AO#5yXc71r+* zzx4@{=&T+3w$p6qWR)oEMrd6?u;Y87fA^!vI78rMc6zG0KO|#f%^c4yTICL6V=^Sp z33aWrU96c&{L4!mM*AHy zSbX9TAfq(gz@Yx;ldqVUCz334_*&{?$!YW!Uiv~&E&&0FN^oMQ~0>04;as$d8UyT;-0nv(~h`DIP$KP3Uvj)qv-mNoizvfDRy zaGfQJ#3}zTw49=L39~YYgMLJd*+Fq3^6gj%+4C0kgHh>|-p)_Z){`#8KN^$(#s>9X zGR-1$iKfFoslEg3l~~>rT6u~GWOzB8LG%eP`4uqN%$@0?`10Q&o_Fue1OpoFODg!! zNwA|62KE7pk`*6VEqjR8B5CN4N~bcGiiY1G!3q2KZjRNl@=#_&sd}|GQo| z;Bdo_sW-o-ZU~eeUNp+q(&hi7#E6ruDflKaeTwLqc1ZB+deXMW-{|r;bSV;6?$dXJ z2*2zM(lx~3w{%lmmVp~x`(IU{!3IA^uC3L*fvdzI0WVTpv-vDW-iGsBw7Ha7mS`Ij zJ~uU*=WAq%)TlqtqM# zuF|vYJ5nNe%?`p@5owZry4~&!v4zUII#WKg-K?$(&4=!De$e1t0-uZKVRQsGwD9fV zs*vUG)(9ZmN5?1jx!Z+U*TX7&Q46qc4`?=o0-6H~d!?n=A=7Vd1J*0WjnVEg!uHM=2 z-?HT%el$X3w`#Q8N#I9ER=|QUv-0PYXA{Pac6iYUg2jp2_+AgcO!_Z0R-;G`q`6;; zhCpDtx7O{~Z!?4g8r!+W;LhN6TAq|3fPHyr>~oIR`7*a?w~G#842^Bc3o3h&1P*y& zxm@TdJsr3DOS*qniQ09xLu$_Uc|>=$U&>M7FZZ3_vg+9Qep}BOlG=RPF>KJVZ71_F zZ`p)#T#^||72eEeQaN+v0g1_)irtM>q}+O%*S?eW4OZ#H+iN&RxhpOeJ7%IGD1gZU z!^I9plfh4MAE-A?LX*53O}u37$PccgJF9S_IWxTsqcUGVpiv205~=3i%lG#34@w|< zUazyA*3CUg>o2r>nBTe_xmu})e9VSginpbhpz>NkdSkE2Lrs)_kEo0CSaF+n0S$#B za9|AymicjFMx%9T;g*PJLLc^zeQ@4+EV}cJe*93pGY66A3c;dM>JA{k;kZssqL3mf zDq>fjNTm7MA?&mAvm3C_BHkJA;}aUK+$qKWz{PfUB>1IPVRcgr&YrjuI~${hEfcLm zC`S%(m(OZ&A5NWi#)5;bEcL;S;572Y9g{cS7Fta$zTsumGN0YBI2GYpPEcoB*6mec zC&Nr*WaL2k?2wZl>&(>mq|P9Tx-FUvUSLLr<*3+BPOn+*;1?(sH{k+^y=(1?_Gt1u zh~(`1eDZNc@l}E}TyjqtpD|u9j zZAjaOwtrtxnfmWsVnvJ<^n}x~MqoC6#wnXVqi6tK9uN;*sRb1@ZH}}H=F0uC`mJ2- zW{pD`R96f&snw_Cz(qvpYdL<$xh zX=RKqk92fI8TUzbiI%~y3V=UIE(-M&zuG=~(Is(qs9FcgH5IRFfC8;VI{XUU&NUbU z6=Y-TbVv4262;14o?UD0nayYNuhg*z)iHB|R6pC9Nx-Kothp(R%#l(Wqu_Uy>{0S7 zdQRlR@#-VW4J%O>9U9RT8Y3P2D^)(F%imG%dgZipo!h3C&oTQNXV(tzOu!RVc~0#3 zK{BWvcCvj_!vkZZ3IK<<<(tKk;jz(y#5YqZwmv0$zEOP7@V>FEl$`u~vv`0{eGq3H zwZ)qXMu$d5>E!r#yEs5w^yYu2MuA)GS(8c_=Co>A)>^hw5IbMEaiKDkUW) zS1Z}24b55W&b{DPJ;`Uw+>=tN&CQ%N&0J?v z?a)1Vk2-LhlH2vnMz_!vB1++_r}O&Yy(;hO-ONJpKGpY?>{s%BB?puoRdTmoYAHn> zcr@I&N^$mY3~h-Np3(fS&i1{Jt8o{d9+N^D6s_l8-6*gp%(i zDQ7Bt?auif{ICkeZw5c3 z)9@hfZ$6VxjlzAD@JRt3=_i@`ht!)XK9Qh{;csHb^>sW}MheNX>~Lm?xF;j}OadR~ z0eRgsx}lifx@*U#3~?$(h<;KWD7sv-_>JPkaBet1yd|F@1mN{UA5MHYaZf5aa`Py` z3Pv*9H;{kt@YTb4_#Ijt;LK;FlryHt;l0xe$oX%EHl|V_YGD~ Po_xKy9Zk&OaR2`Y>c>*c literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/_pytest/__pycache__/freeze_support.cpython-39.pyc b/venv/Lib/site-packages/_pytest/__pycache__/freeze_support.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..da7d26cb821b7c73a64d40fbb3306aa808cc393d GIT binary patch literal 1418 zcmZWp&5ztP6t^AcW764aDW%nNK)$sjwSgNFLMx<}3Z$hVK6)6Y3Tf<_%`_9IwzKN2 z5(#O`5s5#bhxW+7=^7nsL9c}^EGKM69-<01SnB7p8s>h)*94V;Kf)hzWXhj?eKxdg6D$pX2wD+e0eB*~2frUr3~e z8<|{<0kfvqLe!lWoIT#Lc4q_}o0GyYeImOWq#|W7s-~Jn`K`1tMzODOk7ltwQK@68i(HO3tfN$;56(l$zQ+x%h z19v?X#m1vU8?{QTs%Odptt3D$hO=}byME}OfQ+Rle#JvzNRLc#P893WK%2LFw4p=T zha4Xw;6onVKm<+(+y^hCp04pLd=EXkw+x18h=-(4)b}Rt$tHO@>1iLoB7N*^IQ@}; z-fWokxS!lW4-rOBo2U0-7mvI99;M;guX{WRCQpTe)`_vF&|`%1(0?P zhn9Nnbp=)SJs!((8vtH*N`t)KWvM@M5I8zIV%63|e{8^ij$G4k{jcY@M%DTJALVav zj1r!Dewb$Lb37VxC$eHFWXGx-(U6(+d7O zZHlH8Hqb`dB+uKzoaDLV9MFNAdQgrg$3K9=Yi?<%kdop#F?t*JD^>rX9br?)>L?e6+U&j=`gJ;C+KuR-EfRy4q z=dy%IL$hmzwh=7wiYNz5<(>lN-U9WOBOb}=Xa4E=8nnHu*FtcAmU3~}cY^=` literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/_pytest/__pycache__/helpconfig.cpython-39.pyc b/venv/Lib/site-packages/_pytest/__pycache__/helpconfig.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8d239bd6be99238d94bb3fc4db2d71db9cca680c GIT binary patch literal 7341 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 Qbh$NmsnPn()E6874`I;B{Qv*} literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/_pytest/__pycache__/hookspec.cpython-39.pyc b/venv/Lib/site-packages/_pytest/__pycache__/hookspec.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a98a2591f195f2f4612372168cf302b2b32e3ffa GIT binary patch literal 32432 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/_pytest/__pycache__/junitxml.cpython-39.pyc b/venv/Lib/site-packages/_pytest/__pycache__/junitxml.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ecfea8c0a7e4414ddbfb1d2d40b89410fab46fad GIT binary patch literal 21197 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/_pytest/__pycache__/logging.cpython-39.pyc b/venv/Lib/site-packages/_pytest/__pycache__/logging.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cee138a124de19d44eeb7ba3941b3130de81a9cc GIT binary patch literal 26814 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 N;m;e@(Zc96{})&un7sf1 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/_pytest/__pycache__/main.cpython-39.pyc b/venv/Lib/site-packages/_pytest/__pycache__/main.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2ba013c24f938762a96e47525fdb4e8c80dda183 GIT binary patch literal 22914 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/_pytest/__pycache__/monkeypatch.cpython-39.pyc b/venv/Lib/site-packages/_pytest/__pycache__/monkeypatch.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e9f8e3453beb3a33f699619a4e85b8bc74301be0 GIT binary patch literal 11185 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/_pytest/__pycache__/nodes.cpython-39.pyc b/venv/Lib/site-packages/_pytest/__pycache__/nodes.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c9f6698aae090c87bbe018591be399a52ff09e3c GIT binary patch literal 18097 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/_pytest/__pycache__/nose.cpython-39.pyc b/venv/Lib/site-packages/_pytest/__pycache__/nose.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7f21bb08df5fe2f3cbf4631653933bfe41d73af7 GIT binary patch literal 1391 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/_pytest/__pycache__/pastebin.cpython-39.pyc b/venv/Lib/site-packages/_pytest/__pycache__/pastebin.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cb8f2cacbc0957f890a9bc645f59b3799a2306ff GIT binary patch literal 3548 zcmZWrTW=f36`t7}mn({_xFD~#n`{8^X$(-=Vy3||DX{pVuA}Rh#^nHL?rYi##J+ zX)UT@+)Mp_J*pdB4RR0#M)uR0ej{oaxt`AUn^DuqL3*m+idvk>rgY`p5?f-2ESi@M zVTqY9EYT3N_nqjpto0T|Q=EFhWgR0ejLeDo`_3VcUV5TAEfz5MjP&H9oZGgRm|PSu zVdfdJ2>n^v!pgJgFNt&LF9~adwO<~+voYN2CuO)DC+SehFjrwIi!v-^Q6za5CfRnb z`f*8enTK&Wh>KEgB^hR@yGd8BJc7yCdYfy1qf|+@^UIr%Ez`OmWLSr;cO%cXlO0HB zx=O~S?37aVlPpd<`znDY1DX9I@5{}*GP}F^ezLV$zz6uq?q0kji_Om9 zfMQ^E`gtpZ1Kp~0PtTbr123RqJm3zup8P#-{ZE~P{!W`mwNf3Vh#2x(*TjID!O(Lq zv!R$i#cgyxXFUd2@jXkeL$;1=VO0(kY{=Dm=^VMjKIRzn%Gy;1$NIhcU-)P2Grnz+ zd++-bYiyCeQ#Y>l)>R8T>(~tCrB{dMRatzOaG(2XK57J8uzkh8q=Zj+UnWo`&A+Q4O(;rR@@c3M?#gFbU`nAu5aPEswai+0%Y@3b9|AA1dZn6GkzTPDesKGJVinrccuIuR1#b|I z$5|+qg3t$*BZaHwCSHlHYw&6gB7j(jc!EYwDesk$`{q8Pu5D|_K$M%xds0Q7yf;YH z!DxQBECgrQI({5aJlLUJ?A}(X~EjVw~+KL5ev>4=BAt|y)bGu4zUpv35;{DZ2 zoBNmB=SRUOlhtB3-(US?@`zGt^xE^QlI(7rCZJPOg@l(bj;xFAc0*g>-zM&zCR-~? zemn$UsjFD88z;)DMz{u*3aR-44SGVFFRI<#c#mwOy+X#SyQ{rwf3=8N?YjC2t$3Fj z+EdXkrs|0Mg|=~k_NU*aeR;3jp(;u9{x`Bj{Rr#c!&4AGFwf^eG$aGq(t7o4&u;Qp zp>g=UWoCl84UW7(kTooRQq%kk9bgvFs|W7cv(-FtjQ~gWY+&C%!ATBPIfPuK6MTui z%YOUmgpU?Lh;Gm8)dZNT2Z}^Cyn<|~A8d0VBF#s1Q>|ou#4_s&D`yVb#4a0j-q=30 zfPwsIcI*(t0S%kLQ@1=db`N>+w_mYOdD$X);$fe;vGowyIrbj1zk{kd`}q4)B1Qqu z)D>#pMDyrt{6=oLynH=u&l_M;muS$C5Gt)0Q1L@3B0tTu9q_h_W~nVz+?89XS)v*0 z0#Z~Wi>gC_27=SgcS>lthNU#_j!t*LVkf2UE+CcAP!ZuPk4^~6d|Dr9Dy|TEL(nyn z!#GG$HPEgZFKHWxYP(PJN=j%gOpFGsVJfc^5)@QOIOh8)ZA|jO7WmsuyEPl| L`GwZp*`@yjp`))D literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/_pytest/__pycache__/pathlib.cpython-39.pyc b/venv/Lib/site-packages/_pytest/__pycache__/pathlib.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4aebcf79faa8aeb587c124de1565d444d485d654 GIT binary patch literal 18178 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 M+L(qRbj@D=3#a^N(f|Me literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/_pytest/__pycache__/python.cpython-39.pyc b/venv/Lib/site-packages/_pytest/__pycache__/python.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ef03109ffe60ff83fa837f6dcf94878aea0cb88b GIT binary patch literal 49503 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ΝjsJ^#3I?cxXJ>U#f%w?qf=A7~nr9@2mz` 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/_pytest/__pycache__/python_api.cpython-39.pyc b/venv/Lib/site-packages/_pytest/__pycache__/python_api.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fa659e4929caee77336695495db5c6768cb8f433 GIT binary patch literal 26327 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/_pytest/__pycache__/recwarn.cpython-39.pyc b/venv/Lib/site-packages/_pytest/__pycache__/recwarn.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c7db3d0b442b2414fd629ee535a483516dc1fcdc GIT binary patch literal 9696 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/_pytest/__pycache__/reports.cpython-39.pyc b/venv/Lib/site-packages/_pytest/__pycache__/reports.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6ec49dd67e292b6c95937bb9e77a27a626b8aff3 GIT binary patch literal 16736 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/_pytest/__pycache__/runner.cpython-39.pyc b/venv/Lib/site-packages/_pytest/__pycache__/runner.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..afe34d5a6a87c50c33cf4223b564fccaf33a2ce3 GIT binary patch literal 13613 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/_pytest/__pycache__/setuponly.cpython-39.pyc b/venv/Lib/site-packages/_pytest/__pycache__/setuponly.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1323a5ff17cc76c15e0701a194f8ae920841be89 GIT binary patch literal 2846 zcmZuzOOqS75e6_14xgfSxhqkQZN_mqq039VbV#`zmCC7=oE#iVCCN$EWL?xO0dj|$ zmw*}XE=8VJIr<;CT-;;+Mt(#NxaOqFF}GA+-I&?6vhuJ1HX4l{;Onm&6SUh60#EBt ze}CF<5%M=2y#BZ__#B!dln_ohjYv#=N>OGYGoKmR0@?B{BioVf+c57$PVD-w(YaAA z_I%ICHIVCm-N;_lh?{=X$n~fdxBa$}8z6W54ke<=ZQlHj@fL5twEb(sUUYefU;CDd zwz%FWq9Z8p_6fiKoyE8K_DlPW`dj53tn9(c9li@Iw=4S|Xm5b+P2LCXPUY_w`11r4 zme>{b8S4}9_!g}F34hyoyut0qWcbc2#AZl!{Wl^JGRRX21MfkWhiMW-AUj_rAdXS1 zf|-yaLko95O=jU7wf9fLd_Uy^wGRWS1kN|U2v71tihE*)qsPVJBk{ZtDjzcKX2wdp zQh-S^g*aS33?z)uJ~VY3x)s?#jLzwruIPet#w|Fl!`*LfN{pt)J#@KIi zaahc!W{=C}-kyRtWoZ(fE?d=L4@bJ8;J{*%%R($aM9sih4rb-<)kg!K4w5t;wvR-VUA{scrN=+K!i@H?uiU1yOV5Mjqc9Sc-!Pe9diK4!$mcRG zGQS>ho|;GunQQ_W+A$M8}%OK>|csw3jx=|s=#Z1>^8FC3}NTwLsi^l>1+Z3953LPiEhjfH^k_BBbd5~Kx z%IO)Ck5*)1%P(>V|IeuW6!>6+oePX$&%Z=1jB0(dW{HdXHCwS2nRt191^Iktav3;b zjkN=`3u5g&zW?N_hr@<;OL}Oxgjm-NG8ipgI|@{g=TciCSK6BfQ4}0TLhiy={ig9V z2@@_(bZZ(+j|88TzIzi~KB+L6loT0SvIpzr4tD6l1h4G2V5oal5H6OPeY^uR3fB?a zg-|ps>^rQ(T$cb3!u)5E!f5~g~eJ~a}y{YG0uyWg653IC-nV#91CI1S$-a*-ft2_2P(;&mDTSa5Z z27~3z7b+LodhkL`(@emBMYHuQ1V$opAa89n(vGq5Yn6$-0h4c(XQYzbYZ7wo(1$L8}zXEeLf{s8&wWv)!hPl-R z31rm#+y1ZZd9(-PUDlwtn8$2t)}-oH_akX%^zP5NQ60y0DT$u7sl zAroeZ%f?S2i?L|jMe+lwIEi_@% literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/_pytest/__pycache__/setupplan.cpython-39.pyc b/venv/Lib/site-packages/_pytest/__pycache__/setupplan.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1f0f6b39dd7be227bd871ebf821fdb63ce32979b GIT binary patch literal 1358 zcmY*Z&5qkP5GM6!S(d%tpR`$^2LT0IsK9RTK~V(QCJ2gR6LgacVL+9|u`RFuLQ&cE z!aXH9<_&U4j(v&tJ#_7<=o=J8ks+mBxD+`2NRG+*<{LU5k3E9TfBW|@e^`Y4sV2M4 z!O27H_3vmn;WQ&TWt1vxAU2qxv6-37#5F6ka+}$D%|`4nM`I@&-G9{q(JNJv}vX(iLLh%?0Yivr;}BIm3u7U@#$Ke|rkS;;}I zK8r;S0+IJPy_SuDA7G(o7tQ=txfEqnv0==4sWTWFVu*xVTW|bE#2(K6d3g3<_Dqy(NMt>GT0!xo z%whHlidVCr()p}TC7e`oav3k7o<-G0#rN$U!P9EfPP@hE?jj%0ttV&*^{7RS?)z8l zT^r&n)W_T^eGE>v z*caxOMAnw9t&2_{dLG)X(}~kMT`Jn)9fNk5#K{VxOW3fnhCC8bHyK7J4)7{*h=%$3 zCc1Ofnd{6HH`}8=13i~WAO1P+)cU>J9_GQP4i)!t4P`jOp(D9ldI~Gjn&cMK4GWro;E|#5f{yenAJ$2G&XjsRnEV4}xRegoDyGtp(6YpUkZ<6yYEg;I{wD=NT zY8MSLT&g~Cj5s`K9l5En%7h9{C=r;`{hUquayadhEATIg2wQm^|9|H+7EAT!)G_LD zvFYvpZRv}F`mMTNR|#`hWqFzARfdYqRc-3jwPspK9oLQmnwyrv{Vp$glfm~YleNmA UPkm&Aw)@QK$iunw-(=*`Pn)<^^Z)<= literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/_pytest/__pycache__/skipping.cpython-39.pyc b/venv/Lib/site-packages/_pytest/__pycache__/skipping.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fc5c6b85b8fb15f2fa2aab9b1f5b76a88ab35c94 GIT binary patch literal 8550 zcmb_hO^h2ycJAtKHk%v{HJU&DSyo$?t)V=kWbbB!D3aG3+w%Hvta!$@wkn9CV(;6`wW@LmIvzg)~Fq z3%;Aa$#=`Q_-^|)?pBy>p-$Dx7W4`Ev}H!uj@szrgTxxY$18A7Qu*_^5xB;X{Cr z`NtTZ36Hl=_$L@X96r)M>7QhHHhi>w%0DFxby%8m?o(6F%Z0nP@2N8Si|8MbN6~*w zEvPwlS{2pf>P*8tX2@fo+VZ$OfoD&sQtL_i2>vJUnJ*dgQF-dFAx~|a+rmGKc?ESA z_#S9ICQk!@=>Y!Y@(l3LFn)#cD* zKYLH81?-wuT9TE!HrB85lXLV$x$>zI-DP>6pI+GW^rWP>s$%X1d6A!9R41@nQJtZG z!=hE?(|G=j{1$fgv@}VrI!HPi9rukc%?d)mxwT$5Q~>O?tuEc2=Q~lO zNpswrozAUhyGv-!wLr&;Cfl!L8M?oms5aecCv>NM;hy8I0Vw`S5UV#8?HfJ!`5+8m zjv5_;SFsQEs@kfUsncb`siRfW(@|Z}=0`u6hmptiJg)fLXa>d(4B);Pih*dsXrv`= znUjvp-^&R+ZJE-Q1&kEs)IA%@XMlaB6+gehZy)xH=g;e2#5+ujG3}z3=$=a5@#xk5 zWzAdmlAFrYD(;0z>~$Jm#_&YN=mxRZY!Jehx2d%9qE3=AUsmkY30NelErFZnrD_DA zk=i#^*iB1JrWPlmMh{u6v|cvufh9p(B}(ruJd&-qc~eEa-bV9%K;gXA?aGayP=W|-RX7xLLg;_^}u%kHlrqRX9+jQ z2%j1HQS`p}?(>&!T+^MFsweS{H@YhNey6Q&VDa~Fyw+U55jPWcz8lnU1sf{9QR{9I zi!P8ZkmgmpTj@+TR-3GM0%YSy&={g9Y+;W7zj0jdnHc$v_VAqaqt5E6=g@sXIdT}Z zaK&9T5Wmq9`up&7Mri(2d?Z@dPifTN$^FFmiD;OlFn8Vi0*V2(xoZqXVzr!ss5wdg zBOw88Tck!iuFOmKzBz#ld*C9$tXNYknzXf!Yv0dCkG30^&?E*Zi;s+L3#w?Y=rYD& z0{K`aL6T_Z@n>dgbwUYSVD9mlJ_W?oy&MMZbs0QM+W%j;jQ;5nJ2SrYw5nbXJ9UWs zSpXUGz8#Sf(Kc(iV%i$-U99jbV6YwNjW~5~ZPGnkn&h6XZ$+K%cW5F}F+~65!xsl8 zzg(Ih>1zKkNDG#<3gS+5Wu+6T$|ZJQY+9NAlCn&^n77$XZo-p!nOX)R?2qPFy%+BU za6>AaKBjm&>Uf*M7THL>r?ra63Wy-|4ofuS(-mGW=Jn`7H9){KXjjd~68I;Z3D1KEC=?Eb>pqL+6{vQuDtDC zx#FGOY)0RD=Ip_}l84z;UI6#fiTDi|&l^vzdaFu#H2D zD|2NsnR&RG41&f6LEt?1HdkP-!-{ykI0b_M_`6A|fqU;>rLkS2AGm%U~JO9(@6odOVr zPcVx3=255`v+Vyt{w%RdA$2l)(+gzwPf$ZnDs^l1Pz6!1i;&iZ-@aJ*GHvCs&49rAqo1X~%W@08*Vk0)> zr1;bxT8RTV-*S1xyeGEB3&wj-4sFb_lft$czm^oGwKFxa@8j7AVqoo*2o}cei^=o= z@nqZ7T^==B<<=p3(qED}q$x8$Go-p$ivzbc-~D?zHE*C_!Zp2N4-3hHEblB13ipLPBxmj+ zD!pU;z>tTB?vLGQJ-c5tK>NriVrykY{ki?rH_YVd&ave9z}*%DM7&>!-$S=JrhCzd z*88R8!~hvMzs1^UrN0@ibV<%;@3A5N?`wd>*B4_Bsx0m8!SKDJ_Lm z+A(`HtwL-T4shg^vN`maqZcm#qcb~kY9Vr^?l}Iip;rXo`jwecS#NzIF88N)Ir?mB6A$|*U(^~U55b74 z#PlchE>_3ktP&cnVCKkT1a-HMXn4$ry-zlwQH>BT&lisIFZ@blQK#?Dsy=YA*g z%;l1R-~_2WA-?seCgQu~^%uT@0ES`2EHrjHWC zw}2amXpCZsY7+eIFCE7e<*!|dQc&o3 z@N|c^O_hji+9r=6Z(wmhP~_238C@L2{}BKJG#{7UqW~QZaWDp#FWwjWH16imj7rp( zI7n%Tp*cX{rv46}6Ci|8(6_dQiHc|y=>TXfA;{+j#y|}0ZR>_P%nhwUPG8%_%W!XB zyItb7nX{O832WQDwxNH)Yud+*p%pCxHuc|9EAu>JOLzaevH+o+XY^-rPBhkP zlvOlkrQ)O|(AO+_cn~%|y+m_fr-mfO%2;uBZ80rQ=w!B(E%>K|%R-(=I-UCl1^X-D zWhtLI1I>5Ml4XT z9yLFtX4fjn(WMp&lGMqLdo-DXrnl0h>ghC{n@uupI5IA__ON%tV=aOI54d7Vbjg}Z z@Fpd}!7f_XjxC-0f^26r%10wZ8|i6g zd26`mhPjdD71;7}gIs)fgpad0%yy7sJ4k>n*cq~Mw!a+gs5;CidA2_!#;`wk=nP!_ z(LwBy6@GNPBwa|U)c@m&^{_x5ZZj;+{YJy}jiv(;tu&;X(w`M1GzhnQl-%o5Vc6m%W#DH zOhTf@$+j@aHEs)=)I}uDWrJL*zK$MhrB)%nJ}_Iw_?q&&-v6;oX%t z*4}(;n=vX z#wkjtHZ|40x?-)Ytn6bada-zb223$_z(Q$2+cTybdI_u?GteOR(i8m(a1o_j zPW%cOosVBhTrg0y3S$Po4g2^;dUqbAN5^@(g$-1S`W9}PhPDYJMn05xtcc^l+aXN$ zD--9E&K=+x&KSclffv&uv4PaZ6|?3%(jCVlSl`Czh+b+Fa-DcI{*i~8@ar!Tm89`D ztJ98gA8`|J3y+gn%srb+bMZd>>A<2A{gw%cRFGmUe2acfqLlub@pJf-niDz50&qt> zwrct4pMVMFY!!7Q`W3i3rGJ@FRxs6?PEeYoQ2%AtYt4}2#~;69d?2_~2S#h=hyf~x zaTrXrevI{JKN0bnfsL~B3{(4$f!&&emvU&f{`Hvlz`-%WuSa_*XY+wMPt>CCU?#r@ zQ?XkMxG!e7GEZ2%suPt_o@3qdyb)IxB@Uo5$^u;FJ&!)~#3)kMJ|~^{9O5@BC;TEj zXf4ar52$Xs(3@;wv;%SksZHNMd^hYw8#o%%X};4-P?D$vTAt>S^*8acNk5B~(i|mP zD)ahOljv0%bvJ{U-39NNJ%8q$IT}#j>lgR2OYUmTw(f+|IGm>cJ;#4)`^1TESU zTN1l5a`~yKX-ZR4o63)a7B()`#$Kr%;QJk)_)UXD@oSi9;9LyO33f)GN?>c~QR%TL zW|1nq4rV0H44IFSiN5`&*nNcH`jXF5##m^F7N|-^PjvR1=F8v`o>nR$W8IO$=I_5e$3yKt? z{6k}eOn9+AM5B`}LWQ7TdS)Mej<)(R#*XdhcR&aA#N)=}+;w92G4NeJ3#It)rebGdFYg dkylYEJM*W|7Uqu^sZvIwGdtyq#Ti@|{|Blrw%Gsx literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/_pytest/__pycache__/stepwise.cpython-39.pyc b/venv/Lib/site-packages/_pytest/__pycache__/stepwise.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..966249ed59e1ace761642f0e9e1508ccf827b15b GIT binary patch literal 3663 zcmai1TW=f372eq!mlR1+7t3~`xLYMi!d6m(AbqIo=2FFuodk*y87Q()v0ZUS)Kbe` zYGx^01O*he^O%>u#sQQ^|BwEPeeF}xe~^c^-xWQRs^jW|d(I#j!Fg0y~wgOAjc47xM=AFdpyMe1^ zZc^!cfv4$8Qtj7*nx?&Eu3r!8nyw~|elut?Ls;@D_Scg6ek*8UZZ235HE#3z3zIi^ z^SK=?3cI%?JjmuDYw?BW_A?eNi#jBWkSy_Kth({3!Ef-D=LTPSW(F(V-Zk1c{{>5o zHY@BeVwL0VeL2YEER7P-jmKZ_+z+=N-rxHC(e|g?kT_|^g#zJjW$8iOC2A*!p+%u(GioRJtM+GD=4F z#)f(_s$rbyQPfnPNfG99C>|8vR6P2lcpB$^mijqt@eiUn8A{lAe?!%0M))z~(3Z(N?skRw-U6FTXHXH=ii1SRxxoyfh z__lEL#7jfo0QcjETX*+%WY!a%T}tWe`{Mdu`Xy%eL6^n-8e;fs_;qaiJ(ym`%wzBWZ_BC zMM{8b?oN=hQ|@B&HV)ICDu)%Y7bcfl11fJqsz^`6sxkQ*-Y}}7${hJWR=tbWu*EoM zgm|@1bkF3*kg3(rjK8sxKeuwbYg|}qmR7c%Qomo|jKa}5S6E8q#p3Sc`#XPnw2PGf z7)c#|^60CVO#TwfuG_La3ExE7gE)=V;RY5e7lXm-tYykKKy`?{{E)pL;iqy8!Uz~Q z0#jHV(LfzS#-a|{!U6AqNA7}mg(WJ&>s4cpiYhn)k9M;u&1AJXHL=$V8 zFBqTaElB1uPuv1u1lQsWv0jp^`1H2Cg|S0LWcm<&0h@W+KcBLj)oZ!{6cnMSwP#tN)tJV=Xqic@?Oe)AjNW zmj1*S&#QapnSJJr9g0}bI5JTR z>@zgrc!TTi=+sjWc&j|ACr`-E+n+cfQr_ z9l&z8N9{E82U48G*-#~?x+e&(Yrb0d(+njTF&v=eZjRoWy-u$>$c8DuzM(qZ>yK9a zD3K!Kr~V*P3SZDZ+H5nq2}iVD`8HmX>Pt}->9CK!mW#rr-W+qZmMl987A8JNw-E{I zxZv$d8Bkrk=%Aop;t#0e;wd7bUp9R#sJ+^Ey*Ru!Ik}T1bTFD**w1);aH_TZ7zZoL zK*J(z+LjGu)=~0Z+hd;D1YKjJ_1OTKeI#8J%3La8qCrMqz(e!}kPpBhaE5#U5HR^B zXoS3H0SbF|A79Bm?zFD~WDb>F6n{=|Ib{n{5z=F!;qFAcaoN$|y zTOKJN+`cN@jAWPoL!d*c9|ZO3d_Rg)ZDlY&6_vPG+PL7xRI0B(1DTz~Tu4csu8a@e zF$0e(W!xVm`gT@YPo-;?3kvUE>Glr@xXQ;+2fpg2M`QVC+Td-gwQRFRw^jKcXgiFF literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/_pytest/__pycache__/store.cpython-39.pyc b/venv/Lib/site-packages/_pytest/__pycache__/store.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..29627cc55e26b28222aeb97e0009c19980bdf909 GIT binary patch literal 4351 zcmb_f-Et#G6`mjcS^in?##u0dRI^LLs>&+}H&nSckdRF+seqxf-UJkHjoQ;KYvR$2 z($lh#@ZAQUVjqBg2Hr%sTv5CMSA6I6jHK8ONu?N-dU{TGpZ?BopO)L(Z4b}ZU;o{I zYsd5cNgwM^9Ut%DU;GV?@|2%?BfsbS)DO@PdI9%C^uu1r{TlkUUXA;8^y|Gk_Z#Rp zdJXiOs&y9hn!2twUiiHh-fg@$o_K0gwZ8CFOV@O3-+K}E+MsQzCTPtUL2pxSKlM60 z{{j}T<1fN@vl(8sSQeJvkCT|*%_ln3CW)zgJR9qer9rp;EKBliQGaT4qZciHeW+&( z|9Ig)j)~*?jld1g(~RPR0tF`vw@-cKPpBdDTK^Ke)^e?t(X*A z2?>fg*E3;rba-O7-4=M>bzj(@mlZ`GC(>fcG_gZrhgw(w7snzW@YjV*C%XCo#~ay9 zvgC8jl9>{*%(C2yIL`(tl;YS*kcQe2qAsI&9pW)!JB26-cjiZr)H6qzlEziR&T=)J>}KbV}PFHU|x*4c0Jkv_T5*~Q6klhczT zvHIay#^>@(7bnqpW_4kIT97fi;~DF6m-Jmj)ADQ0YZh60;@>gjxTX z4eGX|f;aKGqh14Tr<{3V@f!8AQvgfw@$TCdXQxBpF)qNfhVOiF);av zS5IMdEfOmpkYh274&jK6CZ8*P=~)4DW7#a9z+KXmxSl1j-pW zht8$DUAUXl4^PuPK0iDZu<$U?+!(n$oBGggFWwdfyd!eK(&ZsH^Me5*#W|sv%FMES zJT8>9QY0C)0_?o^0SV=#C@Bc2f@L1$27{%_9!SekRPs&rg-$!)6gJ^3tcjp}z~+6i z%cmecf6(E*SUQ?^n&;`&c|@a$Jo7krYJeC3IFc3_=^Ns>FFxiRzo!L3|8sg@u%{OJ_)C z6)y5Wz*~b|>X}?)g{_iciTl3zDyAd`ai#YawmkGDzDt>%u%8TPl(E^he#Lt|Tz}jo zBsT=-=&*9n4>E*qCet$Z*0O(B{EpM8c>m9C%7Ng>Va}x!u2$!|$bMqQd6rLwJk2Ne zKo}isgdUt98Z1u~iu)=en31$r!U5I{L%ATqa+eY+^C^)c%uN~0D~@H{|5_qz;FB00+nF*eBs_y}xklG6#&w@?XyTI3AGIl0~%ZhsY!l?iaM%ETnAGT>_NO#=S>y7{D1bWZpC zR6bB#9|+VH&-?w=w$SJ`0k4?Yd2PW-r^-~7$`GH0JG@CJ*v0wh*QjV^KaO2sL>8Z9 zlmM!n{Upy<#`_zEUVG_!oDPk-JX z#j$r8Tza2*m;NKv!GI9V_nvAiWO*EurYIAaz(R8uOU!qv*{5bzFEPSRR8rcaDjG%6 zA2IGg2J_n$$mM7!a9kK5i-PI&P@n2021wPD^{fww!KIJlIUp^~drtt7jLt%`ISHgo zX%w^{$)rFm_xm5}+55)i27Z#bsuL5DayUBy3s8_kQ6B<&FA ztP=MGbDg;LmAK1MuqCA}nuusQN$t&sp|XnQ{xLf9_9e`+?|lN#s6h)e;d}JE3>w~_ zK>Jx`u%jEbaUfti=YN#UY15lkzz$AID^+m>A8u88d$n%y9{|-=i-!;~2E8EcUKsdo zf6Je5ID69=X|sB$zm&0yk83hMsAMGBA6=Jq^btqYn(=Y|l1X7*nWFYnGn9)sF8Ee3 zg%{B0YWh;xav73Gr5sAaU)C4A{!M^1f|@I)Z-Z^Q@*Kk)$(8smf4=J!u$mNK^>O-U zTN7g!f^SzAeg0UdsG(P||F16YV%fZLgH(uLrBfHbmc(MzYYx2}{c3qAaxN)iy$jB1 z^EXPHp=3wQaz3F5B*Sz$-`xlWcQxGfEFFhY^p_=;V1B2{C9C1iZL>|%P(CxasG-DR zZd3CeYUoz$Li&5u`#v?B)Zhx^nIBSfkDB{v7BwoQ%#Y}MWpwUQj^n!z>8_wJ-0FT& z3wFZ0wY}z>t-Bk0&7;oNqJi@ar-LEwxpYDq&CqSnG^n9%7r1}oa)L`I=Ng8?I4MGI e+Ii7>rm`3qZN%Ws8yoBst))QRl? literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/_pytest/__pycache__/terminal.cpython-39.pyc b/venv/Lib/site-packages/_pytest/__pycache__/terminal.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0f2d340ce39dc49b56c93493a53c189cf31653cd GIT binary patch literal 40798 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 cf2NB?_$&+Ez+R!HP6>Jd>;wuf5F7 zHrZ8jA>oLSxKgC#m_LY%B~F|W`~`3*?~V5>p(#@B_1iaoGjD$HH*en9uhq%~uIl%H zKL52s$nV&gzHDgRhe!Pdf)h?7;${ETqo!?n7W`XLu48*PMO!Y)cM4v?=r-s@uW0l< z=#J+Yy#RX2D;d2Adf6)*okkU}@{n+cmktRp9avsfIJ~?_c;zMKRbD%^y_%?O)j8fX zKUh=NSw44YA5d>*Y@a`6zrYuaefAvtMSjWH=Qv#C>dfmrv0?bUu) zz>D02xDS8Mk{?C>dW5e^4@E4buM>%e$6XyJu^)kMMb=u_bq zgff#Z2EGp3Ot+=*c^Gdpu^WgC~9L`n%>aO9S>-(nxXD^oZC~- zq3psn>6BU9NwVE68`BC9f_}ijUieW@WW!wF4SGr^9T)q;bg>%%GA+O$#fFd~4ul)@ zgGe;p)wVFc&KTTKx$8377BP75Mqv=@sPFEyp{?66_)JJ*H`MA3stowKkfcyODMg^& z5Plm*Dpv{Z>csV911eeH!VWv_FlZY@0>j-1Ve!L(;7xZK=FOP=FD3}jmkQw;UqFZy z+|67Lp*~fxAa{%zson{7d%`zsi>NDP^W4R~)^L602AzombaEBQ;lfp2edom6vY`4*c zI^;fCf=A7Q;AD@mOu~L#At$ueuu>bLrzMs|oMEFGT!e?aOd|O@&(A=tAv)byRDt+t)(rn;B1Inws5y>M$0@p=Rs@#DjI0 zSOq$`a_-_bM*{|nlO4#G&BlHNKpz0^h9#@uNMex>Fxb(B`Z##B?$Ipx6(ZprAPs5; z!uT0zWlk_$6SJHHEzKJ`8lWoUnGW8616HK&eEJu_izZu$!P+BRIZYVoBP@1W8b_Fx zGuF_btRbQcDC#KA04y;kpsq>Au7gz-KoIJ{J-GS?prbJjJ9S_xrlEePMbw~xr3Le9 zJtNdC5_*LT^)aEigb)VgQje`kGoyCD1$crd&$r^UX z{AGOc4vIw-XF`Oe!NSJaZ7?ba1S!-Z!v9O0Y*3?$Sr_{q!B|>mtdsCwgnE^+Z+m_; zv=kZRNx+z#M+AoMOQ<2CvIrureda5%JUUV&@*v+uF@b9|%o~X)F{u=~bEv&g&(G%S z#WH{{Q0pIKZ{%97mMlSs0G{!c18wwD{R9n74#7y?Y^D=xTuuQUCtkXcaWKXRCwZ^iLU*QqS@IU>Q1P1Z=Q)63 zBb09iLf!-quZ3{o41@r_AXlak;5L4XsL!A9@|g)Wd~tBd%&(^={TTGdyfpO(E0??m zBF$srQ)VwicJ{lFOcM8>#7Zae19nmXyyOi}G!g|miToDvRR0Kg3LOMCSs z{(yLL(juDDVP^6=@4caHbu~tChA*FYbBxftFaDnZieq@~4g`);#4!&lJPpzS`#gj^ zOv5&hAjc`js6@Q?iqnX%+@PfY{B^FlG$MCWD^h4foSX_$G^*{`aU9HEzoo*rwN;B$%zxzSu)l6qg3VZC;ql5fHtld9zaY31Ta!65HV$~ zoDFGhBgoqJ-fjXyPUVwp46yoH6woklL|A2Y$IEiynM-$1+`aUSys5wncZ$-HN z-;n8hybs@pC;Qp8(GTF&W>>XPXRx_!Ce$pukdw?xCpK%AKeAHTjCM-5I&JKqI!4JAhpWFZd literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/_pytest/__pycache__/tmpdir.cpython-39.pyc b/venv/Lib/site-packages/_pytest/__pycache__/tmpdir.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1842e1a4447bfc353c627f31173adf658b9f9f00 GIT binary patch literal 8406 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%0A>PKSupamYkZT*7S{>~Y?-lk5FcerKJxuw# z=%beL0YOWYY{T$H;#Adf_2R!@Vq`v-R5m&gkUV#tmlI2|r4ssn{~SF1 z%s~Gsq5}hSBMX)=A3=$PIYCY#$dherl%Vew^vyNK!a~jHeOHaZ3gjfp$9o&91U)fc zO`q11o=U}_|3$Pb^!{c`Pg{-H`>FMOwmL!%sA>D>Y*7z7%}Ni3yX~Xs zj@Rs6&gUbzl)96FVv?1r*MITajT>*>xNN`U-}Ygt|E96!$k>1 z>nP<`+ro>+hgi|MN2xoNW*6o?Pq~ChMwBC3xF%&=Mx|O?wkjb>B^5)7(sdv5CzN|J zLu57Db)VH{wC7u1Z>bIc^tz9bo9uSNX#8b}s^Mf#fdenqE}RxWB-2>b;n~782ImSm zqupj0r7H+t`64mwmuZp=CMS^?r5V#;JKc}D6?jpq%|D|<@FEfo;S@tJn52CPLVyLj z7XM#ZscCWw<5KI1VHls6QU#P0^%+p{4u07(Jd6Y8Ji<${he>nA55Z(bF?*0f(Vqo_ z7!(8})_`2KLkevn4~YwF3dA`HQrr>-BdcgLiW1A-UlVd8qIsf{;V z!Ln>qwvXg-Y`$!wIV!NCEH;bJ_7k*LauwcB30p#2u#4PE+K?T*$hu9eQr38jFrwHrB+U_}-2kx?PaZ@@E$bu| zXF+#x18eP(eP=se#}j9fboYn^FBMxquPqAop~Lpo)<9@uo3;fZ08>ux<m)h-SOvcVwNKUju05&|6?AC5^&HkM=jNW$QV&fPw+bu)j z{xIfwh}!fM3tr$Zlut(WXulC_mPj*~Z2n&Zpv`V>iOsW}e~|5b$T#A9j)~*KPF^N? z2ix9!fKoq14l++zk9652n;#y$9yF;Y!ADIxcQg7h5tmp12w*CZba zgv$O9k2xy6412Vd7Lx(hXy-<}TwV1yMFhdJOj^U&r4z9^;dHV|a2GcbuHt!D4YW0VA3$ z+Fef2lKU|(JfP8*(x=iiTvHgausHY-c)O@#I~sq4vTJk_1ppej8m=F>+tT~?m!X7( zv2Kt5j>uPO{MRUXos#R6kQFa8lu&R*HYmAGNt2Qg2^Qd1e7~*SiokUh0lr7|lA;K>!jQoRwEB57BWq8DFW67f!Ni?_(%B2frHo>w^79HA{<%7 zQZN%+?I|Vc_?mA-PytcqGV-68HHgbXW1U@sN6?qb1hZ6ZZwjr{d9?_l-AAZ|33Br! zNRAQvDLM9ZeBT_iI|q~DA-3gif^0%k#E z%$<^uas{xKyds6~nn^r9lFKcP~hr{O*MPQ2eH z7CVhLnvrKWjRL1&Hltt|aNn#i472#Tk)oh#5$q=)q9q7vj6TO81%sOt2t7t(7F8pX zil~Pui4Y4YVl||q8v@7DFz;Yn>7PD>Sp_A5B2u7&G{vPwYDfh+q8zGqQ!1*BxHRYd zw8}G8q!;8x%+Vj-4&$s=+WuT@p;S|-Odf)XzL`*(01#@S%ati2m`9*>*YFR+o20Un z(DbnM6%GTlh8VhQ^rtB{P9Ia?`q2{l0~Ly~+2gD!KVY-P8p1Ur{tr~|Shdojk66Bq z+hTkwaS}+d10YujdV>;jJW)k*GRl!Nq@oGz7tx(nl%~RPN&W&Y9_!9TY^%BOnh-v8 z6%{Z6P#@)F(Roj!y=LN^ajJCMBt7y^D1hECZ=>*zY=>=J1{`nG+K8()+(y&IK-$!v6ia+fV8}m5IpTwsXyX&6U1^o*bsjGVIR=`<_YOBl zEJ3JfvuU(okkhvo{B;IF_%eg+y*Qp5OD7!h1lzFJhwDU9?mB{1yh{0dgiqQj_Hh9` zSEs-Zz8Uc3U!vHV8vVkd&Ko5E3ROy++@-#z{0l0e4yuZ`X;XCl87e-eEhzxdpcX6~ zbpdQHsb=>uH!%ip0bP%>o68%p24|POjaPo7w}B`$3~U>5XnVzfl*#tn^v^lN^v_{L4HgV@MD{S0pyaQ9WfG(0!0oOe$0$l0W5; zl_5ED89-W86vup57Z`X8nwM1s%s65Ebqm9j<^KctGZI%EEVim%0$ znBsH0My<$fVGY5|7MY-GMQe0)TaI%`qfz#Xy19P|=c)xXy#>BiQi(3fMt-rh8YvrX w!C$OoNq12g@`>@p%D`;=qEIS*L)l1lF=%{R__`7gdMfm(w0}WMX=hgVWn(8$=`@<%*_oaB&Ftucdc8v6s(t^* ztKAwQzu{o=GGTBZ9{DE_Mi`BWm%ph;_1N$XcpGt{ZF(j}Ss^aAEzi=t34F;bX}$=& z?b({QfG>Mx&6j|$cooglxaw6O5oWXU8^X#5hF9Y@tIQKteMec1)!&$2omaOS4DYEQ zjS1;AyYR+5px)Fu@)yYPO{%R1ilqQP5R!%!ef=w#7JXEK>%V)v0Ac4JQ zR|s4b!EZxT=}4{|m1@(lW?*Hv6@@MB97h;7BB+F@&so!X2-^>lIpv-MQ3gVy=h~bE z5IGx|9C-X(LUIR4dQanTcI+oj^Kad#r#;*8vv>;2_%Q3b=i3oW*gzu8p=q zt!A@%VhmPVJlxhepQXJBH3FMV=K6&Gq&=`Txy}ic6482BaVJTgI8Cq~A#YMaZ2;OW z$a*J@5;Zb--C0m6R00pSC#xqf5?3!WVI{V0i)@83YrunQAv(f;-G&vA38(Z7Tz z^ZDqgKL%GHh)wAl0y;bq*ziLPdUzo}0s*CaGzVdYh$;+Ssue+h3P!mR3|L1kE&!Jm zH6RPXN^QB&e@|gH_2TpMFb8H7*+M6bJ+f6$#0P%pjF}Z6zN|3@4s#*u=+G>Z29i_$ zm3^N>hZDYk3#8HlLZ}UQ|HgX}9VoeGW@D>T(95&ZplM~6o?6cV)hR-=a3Mbe6cY$h zKuvY2Y&;>~K`dKDQ6D~=2=g9!RlrKip*HhC5CU-#38u_7bL21M#79Wxk(`PVN(S4T z?|%Y9X#*iv148^@+2mSo4T~-b+T8cEito2m)`jncpVfT-n{E&fBqiTxY3Tc623^pA zUqTKc6(t~9{qsQbhr`c{Kp?~wBx8RyhlL~1O{^-3=4@)tHO%Q^qf`N}Eo%I&!TTQA%FIekR-p1xk|X zyF~tQ2X@E%D|j%oZgIIkHv+_oHWS_2qoi*vI(?&3$%xy~k?L$ifeGQai8Irz41jC4x3c^oZkag`7sbYaG2E^tr#Yq1J2ZA z>mTD^t0eGXuDXU-#pApyf=-7E&sL(h5ecbIWFC!SS>dGa+L-x=fc4`6;`%HdQ*Cp? z<5AgVb9wwDN8vl(?R<&mbo(;I?SXlCjjs>yDVnhQ%pGS>?HIy;#}}}8xZxe(|3{GL zZ+Q9Ch6?^(uyi;1dScVNz`HX-cN^?e0y}tC#5O1O!Obk-p>6~oHi3Oj;9uAAX%{zf zh^JVC?b(>UsNIP*f&!xwxXYQC*q`N1<{o~APPH*psbypTtnkwk|Y zsxzd$a??IE>0|l}TA&5`lD|Y?pZZkvAM|B=?vRufJFNrD9%^(rGrafAz4r|H>guwC zPxDXzKKQEXIRB&{i=R4vJU}zMuHy(txREn)d#+1;4}Gua*?tB6O0Q!3KKg#oxBY5V z?Nu?h7S$&8Ufp#bJEATcuN={kjhB_)lB@$+63alA<-<^ zU~2`|wAdA~4s5-^RxNf_w1Bnbl3aV~W5p{Ly(Tt*ZU9}k=v9ke7n?vgi}@{!-4I*A zwhHW;#kR#wU^feF!(u-Yw}9O$uoh1vF>zgMG<&XagPT z^jzWeJmK~#!b87;=lI#`lUX87@(tH2RHKv5Pq~pSh`2Fq7;DC(h#lwm43m>ENu*$d z8JpT!ovfC3cHj6}qu*D2BK!Spx!<3}B8{lu?Dv10@~F5|@ApL<^!sldy#|}?{YrQ!OXMJ~n3&76}RW28>Lmd6?vlmvZVbQZ}-#@20;s1#1Fis5v<0((V zSeXkgC#$#KvYe1hS$?gsJXXVSRMOo(YdQsGV27mI+Q>p4 zsSOyqaC@jk-F?dwLe6FiZVZuSxrlD zs{#)klppF7IS;2&WaPsm0+lWp+ zZ?|37G)+@I*EoAPdKH&!#RlL+CgWIS6_aRvlkUxxWTB#7$Boa?3{~ES=hu9gpb0$} zQtzXZw{xra6gE>tQap@xoF<`~m)P&i2r4zwil<^}IRp7#OzGrdvvZ0-VIi(6h*OnF zjli>IqbLvvD@F>~8ZoyHnkpHIA~wtfI!9~0YFdp*Cd3Aq&0MH$*t&sBV^e#!OoGD&}Z zmf{%nBKP7UShl0t1jA8p3WlFb$+}t&y9b0H*z=5#2@fNTpv=97sUgzeCYd-bQ8?_w7b&S#F0leD59Okk8n~`8305sk|6>DP_6` zID5&c-)HyY0iCrv#qawEc1@eHwlZ4fM<59t1SVm`p-l4wK^~e^;pR_b0ap|}Bmnl2 zb~ziJs1woqQBBJH?E=+xO~|FF{-mV3$ckrUEx9V{lluP32z&c_}cB&?eB)|FUe)OB76vCRn%Vjy{4!`AsSh0 zPvzAesbA*Zo1O9vfK4P<3CSY%dOnVcl(dg&R4)ic!OOET6bApLkdEUBb*+4pu*U`^ zbB)bcBH0Wz9$^d)`V7a4V!=CQJjJLLL5_)a(Kb+M_>R%`Sel%eX2dj14l;R z031-6FFz!fHMC!#89KhcaQ=pN=pK4Q@c+X7hx=#u?>t-g`bP=A6S3wmZ>4rst1 z(F|GbcqiNE_o$OoxZ7TRr*8T@Np$9g15Nc=(}7XnqRVG=p=C8OXH8jny46){!pFFK zgqEvm#jmZqO-~cr-p*=Z8~dNHL&2CorAoavF1@wlovlL$9-e4oK3l=DNf#kz-_-0Y zkEXW=O$T#L2idH3{y3`XT$ru?%pNbx1xVPf=GY2tYlqDo>sRI)YwNpQ%NqCcceMKy kA2x-WuKn{GUbC`Z^W4^%wt?RpE#NC?OXYX7LPwnc0)7TSGXMYp literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/_pytest/__pycache__/warnings.cpython-39.pyc b/venv/Lib/site-packages/_pytest/__pycache__/warnings.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4a72e67b67d1095445bf4459ad582e9f2a1a8ad3 GIT binary patch literal 3896 zcmcgv&5s;M74Pcq>FJ%F8PBfQYdf*y#sTa>@ve~)2qq)}$HalKu^fvi%|^ZLsoI$y z_s6)Z)|)l6m)J-Mv4lS$2m6BL8-ERlzH&l{e}Eu|->dHNemFv0phwmHs@|(t@B7un zoleWb)BerhH-5WdS%0O;!NxDf7&!-F7 zVz|gG(UgzCtDPRtmck{>=Q-n@FKpiB#~!-j3E^O+Cp^(RZHWe7IBoI8FI;|{FFkZ0 zuy9$h@kw!tpTLe~eiA!Q3xE6qKZW1vJzMnZJud+NB0ppHym*MBTT`3`#Y_C0p*T}h z1ba*@*7$D#f1bZ=@MjO;5AOda|CYhOw2$ZCuJ>QS{)@bC@aH)D(CUBZZ{(%5J}bL} zBG=+moyB=P6cQ_Q?}}W=SQj+$KbYvG$m0}n=hnwJu0>a`U%UGA_txIU!ijh?Nq3^{ zSmsGSj5d>03#owct`_-bG6b|ViKP-pNU+}gP$&h)0NwYr$nfpm5;9A0)J-udBsdY- z`w0M?ZkHYe1<%rxLT|}@fPi}T5bR=H#ZW!Grq&|_8@C_ZwNtd-3ymUkfE{XbTM z)?%xRHPF8j#GD6lP|t&kEYL-uMMA7gMAf!V$P40h=7EDbc{&hf%sGmwkS=5+i5Ylv(jn>cs4f3 z9~&EB1%wFJid+QAX0R=SSc)Jo^g&J}K(QHsly>d2teIVl2cwxRU^qbv;$SV4A&Q1F z@@#C>Ay&tIB*mbRTyTgmDhh~2QkC7q!G8-`j+7?KwbF~z?RbYOh!6e6u#h{Yg_)bj znfNJD3WdW(-z|^LPRUS6sB#@gcu5NC(uWaZG6!wA1G=F?pq?R=mls?lydpk}mHzS2 z8w!oo3zzQX#daPMc|_+a8Kr5&J(&YHB-qGbmZbY zVgmh)Kp$0=vOIl5sY{L;Us*6_Xl3bGVH9UF5&NFv}ok5(Y(I`=< z-N~SIc{0$?QO~^6pRaPak&tut%@en%6dP-oY4*t0&Xhf1m{S32V0akK$BkWkY9l4t z{S~C-Jt6geGf*qF?|xB*8+C;^P)LA$xYGC0T@ zuF@lu=0)k6V(%CwLK9q3riP`1*HLqxsQJ~273m@ZQ(f zLhqa>7jH|nYav5l%bm?cDh<<7zEl#vNE=`nKWQ=^8O-K*b9>MIe3=?6RY=r0 zRJa2*?((4;S2f?TSvQU+RmYIzko}nJJI0qLAPoxL&xl67?_faHkzX^5CbG~4x>H1g zlEjv6v-VfMBfk$AiZ@kt&3Il%8wAgE{0Sz|&V$v~!rj?O_sZ&Rl-mO41G$yB%7qB zESMCM6lwBpdXe4-Y-g-!+L!-ER`9J=0.5.6 for python 3.2/3.3 (older versions fail +to find the magic string, so _ARGCOMPLETE env. var is never set, and +this does not need special code). + +Function try_argcomplete(parser) should be called directly before +the call to ArgumentParser.parse_args(). + +The filescompleter is what you normally would use on the positional +arguments specification, in order to get "dirname/" after "dirn" +instead of the default "dirname ": + + optparser.add_argument(Config._file_or_dir, nargs='*').completer=filescompleter + +Other, application specific, completers should go in the file +doing the add_argument calls as they need to be specified as .completer +attributes as well. (If argcomplete is not installed, the function the +attribute points to will not be used). + +SPEEDUP +======= + +The generic argcomplete script for bash-completion +(/etc/bash_completion.d/python-argcomplete.sh) +uses a python program to determine startup script generated by pip. +You can speed up completion somewhat by changing this script to include + # PYTHON_ARGCOMPLETE_OK +so the python-argcomplete-check-easy-install-script does not +need to be called to find the entry point of the code and see if that is +marked with PYTHON_ARGCOMPLETE_OK. + +INSTALL/DEBUGGING +================= + +To include this support in another application that has setup.py generated +scripts: + +- Add the line: + # PYTHON_ARGCOMPLETE_OK + near the top of the main python entry point. + +- Include in the file calling parse_args(): + from _argcomplete import try_argcomplete, filescompleter + Call try_argcomplete just before parse_args(), and optionally add + filescompleter to the positional arguments' add_argument(). + +If things do not work right away: + +- Switch on argcomplete debugging with (also helpful when doing custom + completers): + export _ARC_DEBUG=1 + +- Run: + python-argcomplete-check-easy-install-script $(which appname) + echo $? + will echo 0 if the magic line has been found, 1 if not. + +- Sometimes it helps to find early on errors using: + _ARGCOMPLETE=1 _ARC_DEBUG=1 appname + which should throw a KeyError: 'COMPLINE' (which is properly set by the + global argcomplete script). +""" +import argparse +import os +import sys +from glob import glob +from typing import Any +from typing import List +from typing import Optional + + +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.path.sep in prefix[1:]: + prefix_dir = len(os.path.dirname(prefix) + os.path.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.path.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: Optional[FastFilesCompleter] = 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..511d0dd --- /dev/null +++ b/venv/Lib/site-packages/_pytest/_code/__init__.py @@ -0,0 +1,22 @@ +"""Python inspection/code generation API.""" +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", + "filter_traceback", + "Frame", + "getfslineno", + "getrawcode", + "Traceback", + "TracebackEntry", + "Source", +] diff --git a/venv/Lib/site-packages/_pytest/_code/__pycache__/__init__.cpython-39.pyc b/venv/Lib/site-packages/_pytest/_code/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..782717d96d971b94dcfab8798a66e296536725cf GIT binary patch literal 590 zcmZ{g%}VSr6vxv}zh`=N;RDP{n1#+>E|+_e5e3l!U4=rd?U}jNHYrK`7;)>1xbu~| zbtS%mE6=GQBAC#BekbSTB>m@UnlK!rhu5=bhp}f0z77|IzxbJZl#+2)I;C4Xjmuq! zcz`_MgPuKPk9$4)$UgUb4v+&L^c*6GJnT6_j(F5_T*pnqlb#dgAs;$y$;#oQ!$;-l zgiX@B&H2jyS8`lQ(?Ve@C8vcdVY~zhI;VL2Z+@`-g8#uLPUr1n64!KgTR=Ke>Aa!p;S%h^)RhEDwejhfqF6$EP>50$LUbOr`uX_h$u2PcT^|jp4fV+DT8Jm6 z*Qhti+iZ6yn`?D}ewpmJ1-Y*p$gV+NXNT2!W-1F?En1K-!DOOcSul1g=n+#PDp^?} Zw%b(~{ccowRl^_h)@-17&glDe-vNTwkkSAE literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/_pytest/_code/__pycache__/code.cpython-39.pyc b/venv/Lib/site-packages/_pytest/_code/__pycache__/code.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3f02086083d6b47d06f24636faa25f5e1a1fd15c GIT binary patch literal 37708 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 O=kSp+^h8#2=Kll6bGC&5 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/_pytest/_code/__pycache__/source.cpython-39.pyc b/venv/Lib/site-packages/_pytest/_code/__pycache__/source.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3006fc08a5228095f8e921fdc24687e80bcd1dea GIT binary patch literal 6971 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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..4230693 --- /dev/null +++ b/venv/Lib/site-packages/_pytest/_code/code.py @@ -0,0 +1,1259 @@ +import inspect +import re +import sys +import traceback +from inspect import CO_VARARGS +from inspect import CO_VARKEYWORDS +from io import StringIO +from pathlib import Path +from traceback import format_exception_only +from types import CodeType +from types import FrameType +from types import TracebackType +from typing import Any +from typing import Callable +from typing import Dict +from typing import Generic +from typing import Iterable +from typing import List +from typing import Mapping +from typing import Optional +from typing import overload +from typing import Pattern +from typing import Sequence +from typing import Set +from typing import Tuple +from typing import Type +from typing import TYPE_CHECKING +from typing import TypeVar +from typing import Union +from weakref import ref + +import attr +import pluggy +import py + +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 final +from _pytest.compat import get_real_func + +if TYPE_CHECKING: + from typing_extensions import Literal + from weakref import ReferenceType + + _TracebackStyle = Literal["long", "short", "line", "no", "native", "value", "auto"] + + +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) -> Union[py.path.local, 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 = py.path.local(self.raw.co_filename) + # maybe don't try this checking + if not p.check(): + raise OSError("py.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) -> Optional["Source"]: + """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", "_excinfo", "_repr_style") + + def __init__( + self, + rawentry: TracebackType, + excinfo: Optional["ReferenceType[ExceptionInfo[BaseException]]"] = None, + ) -> None: + self._rawentry = rawentry + self._excinfo = excinfo + self._repr_style: Optional['Literal["short", "long"]'] = None + + @property + def lineno(self) -> int: + return self._rawentry.tb_lineno - 1 + + def set_repr_style(self, mode: "Literal['short', 'long']") -> None: + assert mode in ("short", "long") + self._repr_style = mode + + @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 "" % (self.frame.code.path, self.lineno + 1) + + @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) -> Union[py.path.local, 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=None) -> Optional["Source"]: + """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: + astcache[key] = astnode + return source[start:end] + + source = property(getsource) + + def ishidden(self) -> 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: Union[bool, Callable[[Optional[ExceptionInfo[BaseException]]], 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(None if self._excinfo is None else self._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 " File %r:%d in %s\n %s\n" % ( + str(self.path), + self.lineno + 1, + name, + line, + ) + + @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: Union[TracebackType, Iterable[TracebackEntry]], + excinfo: Optional["ReferenceType[ExceptionInfo[BaseException]]"] = None, + ) -> None: + """Initialize from given python traceback object and ExceptionInfo.""" + self._excinfo = excinfo + if isinstance(tb, TracebackType): + + def f(cur: TracebackType) -> Iterable[TracebackEntry]: + cur_: Optional[TracebackType] = cur + while cur_ is not None: + yield TracebackEntry(cur_, excinfo=excinfo) + cur_ = cur_.tb_next + + super().__init__(f(tb)) + else: + super().__init__(tb) + + def cut( + self, + path=None, + lineno: Optional[int] = None, + firstlineno: Optional[int] = None, + excludepath: Optional[py.path.local] = 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). + """ + for x in self: + code = x.frame.code + codepath = code.path + if ( + (path is None or codepath == path) + and ( + excludepath is None + or not isinstance(codepath, py.path.local) + or not codepath.relto(excludepath) + ) + and (lineno is None or x.lineno == lineno) + and (firstlineno is None or x.frame.code.firstlineno == firstlineno) + ): + return Traceback(x._rawentry, self._excinfo) + return self + + @overload + def __getitem__(self, key: int) -> TracebackEntry: + ... + + @overload + def __getitem__(self, key: slice) -> "Traceback": + ... + + def __getitem__(self, key: Union[int, slice]) -> Union[TracebackEntry, "Traceback"]: + if isinstance(key, slice): + return self.__class__(super().__getitem__(key)) + else: + return super().__getitem__(key) + + def filter( + self, fn: Callable[[TracebackEntry], bool] = lambda x: not x.ishidden() + ) -> "Traceback": + """Return a Traceback instance with certain items removed + + fn 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. + + By default this removes all the TracebackEntries which are hidden + (see ishidden() above). + """ + return Traceback(filter(fn, self), self._excinfo) + + def getcrashentry(self) -> TracebackEntry: + """Return last non-hidden traceback entry that lead to the exception of a traceback.""" + for i in range(-1, -len(self) - 1, -1): + entry = self[i] + if not entry.ishidden(): + return entry + return self[-1] + + def recursionindex(self) -> Optional[int]: + """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 + # print "checking for recursion at", key + values = cache.setdefault(key, []) + if values: + f = entry.frame + loc = f.f_locals + for otherloc in values: + if f.eval( + co_equal, + __recursioncache_locals_1=loc, + __recursioncache_locals_2=otherloc, + ): + return i + values.append(entry.frame.f_locals) + return None + + +co_equal = compile( + "__recursioncache_locals_1 == __recursioncache_locals_2", "?", "eval" +) + + +_E = TypeVar("_E", bound=BaseException, covariant=True) + + +@final +@attr.s(repr=False) +class ExceptionInfo(Generic[_E]): + """Wraps sys.exc_info() objects and offers help for navigating the traceback.""" + + _assert_start_repr = "AssertionError('assert " + + _excinfo = attr.ib(type=Optional[Tuple[Type["_E"], "_E", TracebackType]]) + _striptext = attr.ib(type=str, default="") + _traceback = attr.ib(type=Optional[Traceback], default=None) + + @classmethod + def from_exc_info( + cls, + exc_info: Tuple[Type[_E], _E, TracebackType], + exprinfo: Optional[str] = None, + ) -> "ExceptionInfo[_E]": + """Return an ExceptionInfo for an existing exc_info tuple. + + .. 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__()``. + """ + _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) + + @classmethod + def from_current( + cls, exprinfo: Optional[str] = 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) + + 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, excinfo=ref(self)) + 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 "<{} {} tblen={}>".format( + self.__class__.__name__, saferepr(self._excinfo[1]), 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 a + _pytest._code._AssertionError, only the actual exception part of + the exception representation is returned (so 'AssertionError: ' is + removed from the beginning). + """ + 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: Union[Type[BaseException], Tuple[Type[BaseException], ...]] + ) -> 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": + exconly = self.exconly(tryshort=True) + entry = self.traceback.getcrashentry() + path, lineno = entry.frame.code.raw.co_filename, entry.lineno + return ReprFileLocation(path, lineno + 1, exconly) + + def getrepr( + self, + showlocals: bool = False, + style: "_TracebackStyle" = "long", + abspath: bool = False, + tbfilter: bool = True, + funcargs: bool = False, + truncate_locals: bool = True, + chain: bool = True, + ) -> Union["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|no|native|value traceback style. + + :param bool abspath: + If paths should be changed to absolute or left unchanged. + + :param bool tbfilter: + Hide entries that contain a local variable ``__tracebackhide__==True``. + Ignored if ``style=="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 chain: + If chained exceptions in Python 3 should be shown. + + .. versionchanged:: 3.9 + + Added the ``chain`` parameter. + """ + if style == "native": + return ReprExceptionInfo( + ReprTracebackNative( + traceback.format_exception( + self.type, self.value, self.traceback[0]._rawentry + ) + ), + self._getreprcrash(), + ) + + fmt = FormattedExcinfo( + showlocals=showlocals, + style=style, + abspath=abspath, + tbfilter=tbfilter, + funcargs=funcargs, + truncate_locals=truncate_locals, + chain=chain, + ) + return fmt.repr_excinfo(self) + + def match(self, regexp: Union[str, 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 + msg = "Regex pattern {!r} does not match {!r}." + if regexp == str(self.value): + msg += " Did you mean to `re.escape()` the regex?" + assert re.search(regexp, str(self.value)), msg.format(regexp, str(self.value)) + # Return True to allow for "assert excinfo.match()". + return True + + +@attr.s +class FormattedExcinfo: + """Presenting information about failing Functions and Generators.""" + + # for traceback entries + flow_marker = ">" + fail_marker = "E" + + showlocals = attr.ib(type=bool, default=False) + style = attr.ib(type="_TracebackStyle", default="long") + abspath = attr.ib(type=bool, default=True) + tbfilter = attr.ib(type=bool, default=True) + funcargs = attr.ib(type=bool, default=False) + truncate_locals = attr.ib(type=bool, default=True) + chain = attr.ib(type=bool, default=True) + astcache = attr.ib(default=attr.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) -> Optional["Source"]: + source = entry.getsource(self.astcache) + if source is not None: + source = source.deindent() + return source + + def repr_args(self, entry: TracebackEntry) -> Optional["ReprFuncArgs"]: + if self.funcargs: + args = [] + for argname, argvalue in entry.frame.getargs(var=True): + args.append((argname, saferepr(argvalue))) + return ReprFuncArgs(args) + return None + + def get_source( + self, + source: Optional["Source"], + line_index: int = -1, + excinfo: Optional[ExceptionInfo[BaseException]] = None, + short: bool = False, + ) -> List[str]: + """Return formatted and marked up source lines.""" + lines = [] + if source is None or line_index >= len(source.lines): + source = Source("???") + line_index = 0 + if line_index < 0: + line_index += len(source) + space_prefix = " " + if short: + lines.append(space_prefix + source.lines[line_index].strip()) + else: + for line in source.lines[:line_index]: + lines.append(space_prefix + line) + lines.append(self.flow_marker + " " + source.lines[line_index]) + 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_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]) -> Optional["ReprLocals"]: + 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, + excinfo: Optional[ExceptionInfo[BaseException]] = None, + ) -> "ReprEntry": + lines: List[str] = [] + style = entry._repr_style if entry._repr_style is not None else self.style + if style in ("short", "long"): + source = self._getentrysource(entry) + if source is None: + source = Source("???") + line_index = 0 + else: + line_index = entry.lineno - entry.getfirstlinesource() + short = style == "short" + reprargs = self.repr_args(entry) if not short else None + s = self.get_source(source, line_index, excinfo, short=short) + lines.extend(s) + if short: + message = "in %s" % (entry.name) + else: + message = excinfo and excinfo.typename or "" + 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): + if not self.abspath: + try: + np = py.path.local().bestrelpath(path) + except OSError: + return path + if len(np) < len(str(path)): + path = np + return path + + def repr_traceback(self, excinfo: ExceptionInfo[BaseException]) -> "ReprTraceback": + traceback = excinfo.traceback + if self.tbfilter: + traceback = traceback.filter() + + if isinstance(excinfo.value, RecursionError): + traceback, extraline = self._truncate_recursive_traceback(traceback) + else: + extraline = None + + last = traceback[-1] + entries = [] + if self.style == "value": + reprentry = self.repr_traceback_entry(last, excinfo) + entries.append(reprentry) + return ReprTraceback(entries, None, style=self.style) + + for index, entry in enumerate(traceback): + einfo = (last == entry) and excinfo or None + reprentry = self.repr_traceback_entry(entry, einfo) + entries.append(reprentry) + return ReprTraceback(entries, extraline, style=self.style) + + def _truncate_recursive_traceback( + self, traceback: Traceback + ) -> Tuple[Traceback, Optional[str]]: + """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: Optional[str] = ( + "!!! 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" + " {exc_type}: {exc_msg}\n" + " Displaying first and last {max_frames} stack frames out of {total}." + ).format( + exc_type=type(e).__name__, + exc_msg=str(e), + max_frames=max_frames, + total=len(traceback), + ) + # Type ignored because adding two instaces 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, Optional[ReprFileLocation], Optional[str]] + ] = [] + e: Optional[BaseException] = excinfo.value + excinfo_: Optional[ExceptionInfo[BaseException]] = excinfo + descr = None + seen: Set[int] = set() + while e is not None and id(e) not in seen: + seen.add(id(e)) + if excinfo_: + reprtraceback = self.repr_traceback(excinfo_) + reprcrash: Optional[ReprFileLocation] = ( + excinfo_._getreprcrash() if self.style != "value" else None + ) + else: + # Fallback to native repr if the exception doesn't have a traceback: + # ExceptionInfo objects require a full traceback to work. + reprtraceback = ReprTracebackNative( + traceback.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((type(e), e, e.__traceback__)) + 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((type(e), e, e.__traceback__)) + 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) + + +@attr.s(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 "<{} instance at {:0x}>".format(self.__class__, id(self)) + + def toterminal(self, tw: TerminalWriter) -> None: + raise NotImplementedError() + + +# This class is abstract -- only subclasses are instantiated. +@attr.s(eq=False) +class ExceptionRepr(TerminalRepr): + # Provided by subclasses. + reprcrash: Optional["ReprFileLocation"] + reprtraceback: "ReprTraceback" + + def __attrs_post_init__(self) -> None: + self.sections: List[Tuple[str, str, str]] = [] + + 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) + + +@attr.s(eq=False) +class ExceptionChainRepr(ExceptionRepr): + chain = attr.ib( + type=Sequence[ + Tuple["ReprTraceback", Optional["ReprFileLocation"], Optional[str]] + ] + ) + + def __attrs_post_init__(self) -> None: + super().__attrs_post_init__() + # reprcrash and reprtraceback of the outermost (the newest) exception + # in the chain. + self.reprtraceback = self.chain[-1][0] + self.reprcrash = self.chain[-1][1] + + 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) + + +@attr.s(eq=False) +class ReprExceptionInfo(ExceptionRepr): + reprtraceback = attr.ib(type="ReprTraceback") + reprcrash = attr.ib(type="ReprFileLocation") + + def toterminal(self, tw: TerminalWriter) -> None: + self.reprtraceback.toterminal(tw) + super().toterminal(tw) + + +@attr.s(eq=False) +class ReprTraceback(TerminalRepr): + reprentries = attr.ib(type=Sequence[Union["ReprEntry", "ReprEntryNative"]]) + extraline = attr.ib(type=Optional[str]) + style = attr.ib(type="_TracebackStyle") + + entrysep = "_ " + + 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.style = "native" + self.reprentries = [ReprEntryNative(tblines)] + self.extraline = None + + +@attr.s(eq=False) +class ReprEntryNative(TerminalRepr): + lines = attr.ib(type=Sequence[str]) + style: "_TracebackStyle" = "native" + + def toterminal(self, tw: TerminalWriter) -> None: + tw.write("".join(self.lines)) + + +@attr.s(eq=False) +class ReprEntry(TerminalRepr): + lines = attr.ib(type=Sequence[str]) + reprfuncargs = attr.ib(type=Optional["ReprFuncArgs"]) + reprlocals = attr.ib(type=Optional["ReprLocals"]) + reprfileloc = attr.ib(type=Optional["ReprFileLocation"]) + style = attr.ib(type="_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 + + # 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: + if self.style == "value": + source_lines.append(line) + 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": + assert self.reprfileloc is not None + 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 + ) + + +@attr.s(eq=False) +class ReprFileLocation(TerminalRepr): + path = attr.ib(type=str, converter=str) + lineno = attr.ib(type=int) + message = attr.ib(type=str) + + 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}") + + +@attr.s(eq=False) +class ReprLocals(TerminalRepr): + lines = attr.ib(type=Sequence[str]) + + def toterminal(self, tw: TerminalWriter, indent="") -> None: + for line in self.lines: + tw.line(indent + line) + + +@attr.s(eq=False) +class ReprFuncArgs(TerminalRepr): + args = attr.ib(type=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[Union[str, py.path.local], 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 # type: ignore[attr-defined] + + 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 py.path.local(fn) or "" + lineno = -1 + if fspath: + try: + _, lineno = findsource(obj) + except OSError: + pass + return fspath, lineno + + return code.path, code.firstlineno + + +# 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 +_PY_DIR = Path(py.__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 + if _PY_DIR in parents: + return False + + return True 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..6f54057 --- /dev/null +++ b/venv/Lib/site-packages/_pytest/_code/source.py @@ -0,0 +1,212 @@ +import ast +import inspect +import textwrap +import tokenize +import types +import warnings +from bisect import bisect_right +from typing import Iterable +from typing import Iterator +from typing import List +from typing import Optional +from typing import overload +from typing import Tuple +from typing import Union + + +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] = [] + elif isinstance(obj, Source): + self.lines = obj.lines + elif isinstance(obj, (tuple, list)): + self.lines = deindent(x.rstrip("\n") for x in obj) + elif isinstance(obj, str): + self.lines = deindent(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")) + + 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: Union[int, slice]) -> Union[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] + 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.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.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) + return newsource + + def __str__(self) -> str: + return "\n".join(self.lines) + + +# +# helper functions +# + + +def findsource(obj) -> Tuple[Optional[Source], int]: + try: + sourcelines, lineno = inspect.findsource(obj) + except Exception: + return None, -1 + source = Source() + source.lines = [line.rstrip() for line in 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, Optional[int]]: + # 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)): + values.append(x.lineno - 1) + for name in ("finalbody", "orelse"): + val: Optional[List[ast.stmt]] = 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: Optional[ast.AST] = 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 = 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..db001e9 --- /dev/null +++ b/venv/Lib/site-packages/_pytest/_io/__init__.py @@ -0,0 +1,8 @@ +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-39.pyc b/venv/Lib/site-packages/_pytest/_io/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..417120eb4e1c29f779b3c9cdb5eb9f168057d124 GIT binary patch literal 285 zcmYe~<>g`k0-n~_3A2FoV-N=!FabFZKwPW>BvKes7;_kM8KW2(L2M=1_$^=RlF#{DB zfo$>9WQpQ~DKCdeg2cfpZn4M5C+6hD$FF24Vg(6;h+odmRxtrZ`B|ySCB-rR1*v%+ z`MIevWvO{(F+Q0|F~vX&bPE!bvlG)(i(}#oDoav}OZ4M2^Y!E7GxIV_;^XxSDsOSv auJ;0aXn7_BT3tlJ(g$EFlteVyOJP7 zAiKM?L>ly=awez#2R>Lw{{{J9aBVM|UV3aZox0y!kd$Q4WO^$kyj?u*V)uRTz3(le znVA~HRsZ_$fBg11V}GTG!&em#H*xE~K?o*z!rDd4+Z@kM;%vEXmy^y#-)noe@1b96 zmu$a;zTfsalVw>E{&Po^Mdg{-uF9I#RG+(|CXPXKOjcwaW9rb%h~v=Ae9FWL(Rjv0 zL-NhzyKei$^ewYu4kH>OfQDwxW7bKr@T}CH#jI1}G-jQaY;z8?-V*PMABeNhxgA@V zzO28^8rHEY1;UrJ|TAtxBfN6ux41scKH`kz03mVw2B&5@~s7rz7p&bAPR?wjz^zX3&#)B@EMOONL>SD<6A($1R_=FB}5(ss;5SXCuHoJY!+CZrI~A>q`JSVozCGE7~grvGc;q z9DvUWd{d@&&jsx6((r6Q?J22cYNFLd2H0zXXR!EW)EvHl{i`RVKt-{ZB8by~Y+h{s zK7bNo0uY191Xp&RT-zDlXjZJn77RsJsbeGv7BxcxsmO9C7EM>p(KGoauVBS6PT@TA zPazug!$Md%w7_|~FPf=I5Y%-G^>KcId%W>VorD&EbDfvBq8%L%<(rUu7(=&({CDUS zfM@)Xryd$Ec*ccu8hL+)?K&gpK$AfWjhg}B%=y}Rn+fj}fFI9yF^7@%6yiCfm@X|T zN|}9f@;-Te=(bud%CJPHxr-e(J+%P6It7vQIOm(MgHQ`)lEghSk>Y z^&3FeuEC~yURg#WJU|!>`!*e@8<-7*K@xApMx!qgCc2-PI9(5D=RtRE95_LgQW$lT zzL2^W;If`a+Jb!o{7>DiCqXCrirzzS?LoX9B?Xnkq4{4I| zjD?PsF7eGvzSQ*clJ57UBC3ef*o0wTrKmBqo?Qa2^0Z9W?88f*SHrNAL|TX8L71xZ zSm?*JB1JPp^80)^JHe7w~_6RaY3w$hd7`G ztv4VJK#>|QAhTWMG{SY9|L#-Q=094>f8%FP#x~2l&N5R0mO-k%i$+yQ5P2QSS4tuq zSOo^2xcF4k2<2xBdf!Wkm4R$OR)38bz5o%!q`BJGc)tJd1{xc@_=Kux#i(^(-1_EgFOt z+w>36DE}`&lnw#0h>aaW<2B}*Awv;U+C*tb{(>x-;4&dB;IeQ8m+A24v4SUSVYt;5 z{RAZ}uZQ8E`cYC8w7eXKqT7MmjZ4f?SMokL2LJF-Df@b z4#vGlJETykaL=7D%~us^kJNt3tEpRV;(^@q0$H^;ss7I38XUl_0}za;^9V+&@q&-| zQ=CDH)|MkWkvPK_59}EuhyoNKV+Lqo5b8{kf&u8?ru}-1@;oj#t(s+xyHT7FE5Im% zIt$R3MfDejRMbNfN9QDUA!A6!s6%n-#CcK}~}yi)o(#G7B;DscIeP>Zj1`{{sRULDca*q(=E+ z^MRrWSt2GJvAnhV(*GY(%lsEU4CJ#0WOT2}W2yQ=kYOGlQ$G4^hQd3-6=OP#V~DLjS-=uX(vLF^bBqrbJ;i^&w=3 zx`!`HF}m~(h@*vBq7dWgUZ&3g+|@N4Bv68>T$c}(>zXaZ$KZEAKlgF69S!vDSob2+ z*+4qnw`p|WW|10xbmS4nqC`h_N+8B_qz)F9T!-n2)HZdtV_`O$D6(bM!X|Sckt3t6 z+>cX%BVt~K%evb{Yk9fTMYir3Wy#PUZAnZnsw#9-@ai4}C>M-jP=#>_MHSw2MrA`p z%}152l9e7a{Xte1tk2Y4Q$pqT!6FW*+RI9rzlSd^s@yoAm9olpmX$NA>eJr^KgD*- zsPy>tS9T=6Dfa37h0lp+iGxTzApD=>hp%wkGc*X_b8upD>}Lz)KNbD1 zu0!nKhq#N6LpXX1pG2nV7Yh>z)Gr+@u8s~nWM%^+^Vx9B)l;u;LtPx5D-N%` z@*U?t>Qj0tS3jf1wFWEN=TdV){RC}Z9@9qd<9m`$gce(ivPGVZE4E~PK;L@B_l`Y3 zS(ni1Joku$3ML`Sx6F{w9LY^acHrE9{THHk)t$S(YqYfdOpd1$%A6-53Y(1sjaySO&*~V42-+X-Ar!S@+CJ z)-sz1WGXy_6sgJ^rb?>Hiyu;z%5TW8nAbe1lDE83sdB#4qm?D$R5Dw0wlAm8oIdw% zZ)Bvb;Tit=A750aHSO=z+5MR4yn{FX9RSx{jWezX%w_6dcXjn|xCZ`>z-(Kt#R$hl z+jec$w$LuP1+;Bm2#W2JTT%TO#NDn4n*we7mbjN(o84l-qp8(qyz29$g|@Xo z)E)vyili&UCea!^IvNJvi+E!PAkmhTl)h#2dSC0aRO807Ca1aC*HR5Tux6_I)(v28 z9RZ+kEqFoD@S4k8Cov#NGgUpa<5oBEgY4jenQqd zYuVVI52_E1;uCQZKx3muk(s(2NBg0knIv6VLZ?ZB8$8tB5y}EOB$N#p%_huf8LPQ5 zw|mUQ*hy~9!BhxyMU$_oUSYd;N#Pq1WpQ1Pc&crfn_9%Tsg^*sybOxfp&7Sy<4*nJ z?Dg53a~XT-R>sa=af>sv`H(wwW%lO9%XufW{n$&AwamPA`Q}Hi$-C`F)yniJ&I%Bl zFQYIsAd0Ne@(5ANDQu+5;v{yeTYUPicIDPr{du@TvQjTM$K2;z!|Y$N2Pp6TVay<)tBvP4J%q>#3d50p8Qx zQFy~*L{!8Gf98R~pXJYyU-EMpv&6`h23P{DfXeX)7C*tC$G1lL&-uv*Os%7t>I0L% zz+b#?y9fDOG>Wm)pjP8&fIWsD@pYEJ1louAc`>diD~P$5f%yu56_|&yc7(qMZeQnb z0C$2Dw+7Rh(ZA@B@=X9+w<;5dO30CI#{RRS*%c#*&<0;dVo z2%G_s7PZL9w^XZuR(0I98UdUsGo;{|wJ3!MU3*OgLA08gji4)ByVI4OK)7ae%?n*? zwFU2Tt@mh^YupsvwcgKH-BGJt^F6id#`daiH<&7*JUGR8^)$48yGj0Q#N|Turn>N z`uc4x95c1VUc1UP`rXmOqi3~L-zcPT;iV#?8c}6&;{)w0v{S8KO3Eo)r`8ZR0f#xF zU44XGfE7-0&z?~pM6oM;`4^I6M5f)U9>|P%Ek?LYc+^cK=`Y7Cb^@cnGL-g#{RA7Hj$(CQh4pms;iJh7Lb+Txw$)LBlTuiN)aY?$WLSk z9!JzXl2)1#7xflotmIrZGBXyzLhkGrh$jlOyF9*Fulu2&)a&nJJVp+Mrx%&crkT!$ z@w0UW;qf+py~(|1S{tmEq^qCAPEx2+AjO1KTS5x%GC4{u#7(vi_>duu$jg||V=$`1>+B>XKno^nvb90)!hAx65 zF$BI8FW1rD$B_IGW5325E4uoB-#L*_Xzws87!T{cbyFo@X)?7ru#-}d;{&SFli=m(A4-|{{cnMSM08dab14?TWvul zg8N0dAsqLTw$A!yYRb>KxuKH7eJwRPMT4>bAhUXf8o`7)qN7pS*x87q9$s$3up}1$aBEo!!cP8aUIi} zJIQC2K4MbJCbUlwvF@VSz}EGib?nmj4w_pFg6JGWi`OqK;#m zC<~{wzKLi-74z|w(k=_3I}=eeHD5=pt|K8YA>}Kn86>qn>roytHX!Rw$PlT%hE#8@ zeU+zrP`A0c$uy*E+bC{=#qGqNv2tE0J;KwgbVHxg>1o)s*AbcJ z#ZBL@mPquSz)$iptakh!_Jo3fO1Et6<9ylWkQV1D-p1(+oy_Kf{*|l-9z>DkvGB6t z-3fl5x+JOUJG?Q~5SwlRiP(y*k zlOJ-_S#h5GNHl||fCi;u#Zrm}Ea?<-Jfa97qQ|meQf$ zgqW&{IO@u#aLCr2P87#}BUp2GszSkQ<+8K=d?-XmT{yJ-*y%`rg$fe7i9nup8lvfS zW1%XMcW+#CfYJ<*gV$=#ZQ=gyg1X^!$$U4a9quRPWMysn+M3 z%u1nHRV6=87&(JkaWJ$mNZ^d1L{iU}{sm;Vqx(2&dXo)5(jS?Y$tuXh74pI9eLlEn zIZDn|;$}}w$AIzxis{M=0yM}m0Y$bzx@E1zX=xZ8Najg}$lc z_5|W~acyQ0w{0BL<}}1-ME2BJhhNc^!MAMPKsKhkl>*g*y>nLwha9EbMIV<$d+gp{ zAS25SOxT2dvE zyn>Cp)19@&HpPlsu5A#CYXIu@PEv$$Cw5Q*A{^s_ioPQIG+7)>%HNy8*R_>-D=`FW8l+^d+SxNp)IgCuICOjw(l-$beKSVpqwJVCP0^M p@*4tl_E#T!(q6OT`CJVbC_^fBn^X(nM+f#{I=0)D@@Pe8{{bEWj12$) literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/_pytest/_io/__pycache__/wcwidth.cpython-39.pyc b/venv/Lib/site-packages/_pytest/_io/__pycache__/wcwidth.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b6139dd33badbd06c5e881923874f525b3025e22 GIT binary patch literal 1271 zcmbW1&x;#36vy?;_N@1pNt04a52BP(hwP>ag@jN_=!Vcko0cxLU|bwUPrIIaEIE?A z#*3F8Ho4@`V^2LKw-gHff96_xF#Qioe)MT<+zq{i$h@y+-n{pu(ff?m=4O-N*m(H& z-M<7Of2NzW1AXKsT1Wx}XhAw@W(6Ix4x_{{;HT7ZBS8Ghpa%5^;*@r3m8XH84Avfq zP95kzX*cIsh1_Y=a;@;=o>sbV=76p?E2%E)-o&xDnx?4b;5aASLkqv7L^30%^n?PP zQGt&J)V~L8K>P<*P+|q}eKMmU1}u!Awu-q4^#Khdi(7`q%KIH;EAQIMTR;=5#$`RR zPa=KHX5WsA8b8dfk$pFj zLuIE@yJ9@FK`L*gH3kgG=p^J}RH&&`Qumct8q;KM@ph4~irr>Y;`p{Yq#$>$$oxRY zzBwBoq;*k_d~Tz<%j+5z44#3j57 zTPUO^Xp3g}tJ5a+@1m#OST1oewmLdj1fO^+Ic~h str: + try: + return repr(obj) + except (KeyboardInterrupt, SystemExit): + raise + except BaseException: + return '{}("{}")'.format(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 exc: + exc_info = "unpresentable exception ({})".format(_try_repr_or_str(exc)) + return "<[{} raised in repr()] {} object at 0x{:x}>".format( + exc_info, type(obj).__name__, id(obj) + ) + + +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: + super().__init__() + self.maxstring = maxsize + self.maxsize = maxsize + + def repr(self, x: object) -> str: + try: + s = super().repr(x) + except (KeyboardInterrupt, SystemExit): + raise + except BaseException as exc: + s = _format_repr_exception(exc, x) + return _ellipsize(s, self.maxsize) + + 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) + return _ellipsize(s, self.maxsize) + + +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) + + +def saferepr(obj: object, maxsize: int = 240) -> 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 + standard 2.6 lib. + """ + return SafeRepr(maxsize).repr(obj) + + +class AlwaysDispatchingPrettyPrinter(pprint.PrettyPrinter): + """PrettyPrinter that always dispatches (regardless of width).""" + + def _format( + self, + object: object, + stream: IO[str], + indent: int, + allowance: int, + context: Dict[int, Any], + level: int, + ) -> None: + # Type ignored because _dispatch is private. + p = self._dispatch.get(type(object).__repr__, None) # type: ignore[attr-defined] + + objid = id(object) + if objid in context or p is None: + # Type ignored because _format is private. + super()._format( # type: ignore[misc] + object, stream, indent, allowance, context, level, + ) + return + + context[objid] = 1 + p(self, object, stream, indent, allowance, context, level + 1) + del context[objid] + + +def _pformat_dispatch( + object: object, + indent: int = 1, + width: int = 80, + depth: Optional[int] = None, + *, + compact: bool = False, +) -> str: + return AlwaysDispatchingPrettyPrinter( + indent=indent, width=width, depth=depth, compact=compact + ).pformat(object) 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..8edf4cd --- /dev/null +++ b/venv/Lib/site-packages/_pytest/_io/terminalwriter.py @@ -0,0 +1,210 @@ +"""Helper functions for writing to terminals and files.""" +import os +import shutil +import sys +from typing import Optional +from typing import Sequence +from typing import TextIO + +from .wcwidth import wcswidth +from _pytest.compat import final + + +# 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 "NO_COLOR" in os.environ: + return False + if "FORCE_COLOR" in os.environ: + 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: Optional[TextIO] = 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: Optional[int] = 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("\x1b[%sm" % cod for cod in esc) + text + "\x1b[0m" + return text + + def sep( + self, + sepchar: str, + title: Optional[str] = None, + fullwidth: Optional[int] = None, + **markup: bool, + ) -> None: + if fullwidth is None: + fullwidth = self.fullwidth + # The goal is to have the line be as long as possible + # under the condition that len(line) <= fullwidth. + if sys.platform == "win32": + # If we print in the last column on windows we are on a + # new line but there is no way to verify/neutralize this + # (we may not know the exact line width). + # So let's be defensive to avoid empty lines in the output. + fullwidth -= 1 + if title is not None: + # we want 2 + 2*len(fill) + len(title) <= fullwidth + # i.e. 2 + 2*len(sepchar)*N + len(title) <= fullwidth + # 2*len(sepchar)*N <= fullwidth - len(title) - 2 + # N <= (fullwidth - len(title) - 2) // (2*len(sepchar)) + N = max((fullwidth - len(title) - 2) // (2 * len(sepchar)), 1) + fill = sepchar * N + line = f"{fill} {title} {fill}" + else: + # we want len(sepchar)*N <= fullwidth + # i.e. N <= fullwidth // len(sepchar) + line = sepchar * (fullwidth // len(sepchar)) + # In some situations there is room for an extra sepchar at the right, + # in particular if we consider that with a sepchar like "_ " the + # trailing space is not important at the end of the line. + if len(line) + len(sepchar.rstrip()) <= fullwidth: + line += sepchar.rstrip() + + self.line(line, **markup) + + def write(self, msg: str, *, flush: bool = False, **markup: bool) -> None: + if msg: + current_line = msg.rsplit("\n", 1)[-1] + if "\n" in msg: + self._current_line = current_line + else: + self._current_line += current_line + + msg = self.markup(msg, **markup) + + try: + self._file.write(msg) + except UnicodeEncodeError: + # Some environments don't support printing general Unicode + # strings, due to misconfiguration or otherwise; in that case, + # print the string escaped to ASCII. + # When the Unicode situation improves we should consider + # letting the error propagate instead of masking it (see #7475 + # for one brief attempt). + msg = msg.encode("unicode-escape").decode("ascii") + self._file.write(msg) + + if flush: + self.flush() + + def line(self, s: str = "", **markup: bool) -> None: + self.write(s, **markup) + self.write("\n") + + def flush(self) -> None: + self._file.flush() + + def _write_source(self, lines: Sequence[str], indents: Sequence[str] = ()) -> None: + """Write lines of source code possibly highlighted. + + Keeping this private for now because the API is clunky. We should discuss how + to evolve the terminal writer so we can have more precise color support, for example + being able to write part of a line in one color and the rest in another, and so on. + """ + if indents and len(indents) != len(lines): + raise ValueError( + "indents size ({}) should have same size as lines ({})".format( + len(indents), len(lines) + ) + ) + if not indents: + indents = [""] * len(lines) + source = "\n".join(lines) + new_lines = self._highlight(source).splitlines() + for indent, new_line in zip(indents, new_lines): + self.line(indent + new_line) + + def _highlight(self, source: str) -> str: + """Highlight the given source code if we have markup support.""" + if not self.hasmarkup or not self.code_highlight: + return source + try: + from pygments.formatters.terminal import TerminalFormatter + from pygments.lexers.python import PythonLexer + from pygments import highlight + except ImportError: + return source + else: + highlighted: str = highlight( + source, PythonLexer(), TerminalFormatter(bg="dark") + ) + 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..e5c7bf4 --- /dev/null +++ b/venv/Lib/site-packages/_pytest/_io/wcwidth.py @@ -0,0 +1,55 @@ +import unicodedata +from functools import lru_cache + + +@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/_version.py b/venv/Lib/site-packages/_pytest/_version.py new file mode 100644 index 0000000..d94fc37 --- /dev/null +++ b/venv/Lib/site-packages/_pytest/_version.py @@ -0,0 +1,5 @@ +# coding: utf-8 +# file generated by setuptools_scm +# don't change, don't track in version control +version = '6.2.4' +version_tuple = (6, 2, 4) 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..a18cf19 --- /dev/null +++ b/venv/Lib/site-packages/_pytest/assertion/__init__.py @@ -0,0 +1,179 @@ +"""Support for presenting detailed information in failing assertions.""" +import sys +from typing import Any +from typing import Generator +from typing import List +from typing import Optional +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.", + ) + + +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. + + :raises TypeError: If the given module names are not strings. + """ + 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))) + for hook in sys.meta_path: + if isinstance(hook, rewrite.AssertionRewritingHook): + importhook = hook + break + else: + # TODO(typing): Add a protocol for mark_rewrite() and use it + # for importhook and for PytestPluginManager.rewrite_hook. + importhook = DummyRewriteHook() # type: ignore + importhook.mark_rewrite(*names) + + +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: Optional[rewrite.AssertionRewritingHook] = None + + +def install_importhook(config: Config) -> rewrite.AssertionRewritingHook: + """Try to install the rewrite hook, raise SystemError if it fails.""" + config._store[assertstate_key] = AssertionState(config, "rewrite") + config._store[assertstate_key].hook = hook = rewrite.AssertionRewritingHook(config) + sys.meta_path.insert(0, hook) + config._store[assertstate_key].trace("installed rewrite import hook") + + def undo() -> None: + hook = config._store[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 master process of pytest-xdist + # (which does not collect test modules). + assertstate = session.config._store.get(assertstate_key, None) + if assertstate: + if assertstate.hook is not None: + assertstate.hook.set_session(session) + + +@hookimpl(tryfirst=True, hookwrapper=True) +def pytest_runtest_protocol(item: Item) -> Generator[None, None, None]: + """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) -> Optional[str]: + """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 + + 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 + + yield + + util._reprcompare, util._assertion_pass = saved_assert_hooks + + +def pytest_sessionfinish(session: "Session") -> None: + assertstate = session.config._store.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 +) -> Optional[List[str]]: + return util.assertrepr_compare(config=config, op=op, left=left, right=right) diff --git a/venv/Lib/site-packages/_pytest/assertion/__pycache__/__init__.cpython-39.pyc b/venv/Lib/site-packages/_pytest/assertion/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bcacd5737e75d8ff795ce4c6a9f27c15f9774fb8 GIT binary patch literal 6601 zcmbtY%X1XR8K2kAKJ;2-86*s5Y(ro%EF_MTm=J6X81O?-7D<&SvWL-j?`oFWnWcMH zSeum(5h;@csY>NaRm33&-Ez!1f5u#MO68bK%9Tp;`+8;{lI%)c*{$j6>HhllJ%3;O zGcy$pzv*B7?f&ADru~IJCVxeIyn{!s>zc+iS7SOeLfzHzHbbLhx~5KT7V4I3sd@qR zf?H5^8+F^YRlSIM(JiWaDJ*r$ZdujKs8`&Is#j2-a;H>%DxB`jxHGCg9nN;<+&NXB zLA~l$Red%*)R}kZRedgep|jvF=o&AIKVeVR@Wswy_b|TanZXV{GuS+P;j!f&;fwfQ z!1s&nFus>=YU~JGe5|p>ZNoi^uO)UAUq|uv5})Jc_A&MnsE@JZpgzuC#^@9HcGxTU zewkOUE4buQ5=1SB@x%{8&YU1ZPsdM!I6~cNqLuo6DY>9V zS^q1^ak-|a=GABuukuYEap5Piz=!o=AQQYxce+IFhp5lo`{eEo@7k>!*FLztd=m{t z!5@hr;Y62+Uexea-RdO)(a&Xz$pp>b15Rt$*W#!dv}nM396ty;T_Uva`U2ZR&AOfN z4&KETE+y8j8L8b>ovAH&(i2ewdN%qQsFm?t!y|u$Vxa9n+n(!N`ao|(6Pd{@R$w+O zvJxvlg>DVBw!tcFicPZ_Hv5z`QUgclmfb1F*Ltmn+F-wQ{(QFkv>?F|o>sEYPRw}C z5Ty2Lp&R-^r0TBq(Vgoz`hUjp5)p@EHbWqov_-5>;y9G`%4s#~wA1B+BqW_E{yK6H z=G;B)oJrO>CTjYRq1lO%?g#2W$Q!(se$ zQX69Oq^{I!c53?#l6h(|%#{`!>v7QFGA-siNQ)gP*@iDtYn_K(_b8A2wUB!w)$+Qi zdL-Y}TEn5=>$4k*L^^bk?k4SVAGt&1^^g1q+>t%O!7Lm&n@%fJ zQfPR0v*9%S24-wR<7K^OrdF~Ewk|?A;wXxm>6ThNX^FVkb;~|uu~KH&Mm2~6$g!&A zs0em7@iM-?x_#}^>Rl1Hc_Wdlce*^f6?gdR29GvYKMdAZCG`D#*Ka)VTU@Ss* z&VIr3FmvL0_3ma`9UgVK@?k6>9Tb{g(JkE={|)0G_Ux$TpMr>XjkFN?9WKQod^PZl zeg)KQJU8*k&rl4s>)NL$xAa6C=!w2!PMq&(%9W${8+BS@0TmPm0&`Jtz8?=jC zCTPv1*e-1w;^M&Ef+;+_`|x5?-qyCPfz_@IwBKkj4{|uB{|DIJ$Giovzyfb~kn!SCLBAe|;Wh4M>}f-rPCW0|k}2{Z&0nf}reK2hY_ z97GcOtL9O9lnKF6vp~$GTmeQX4>xHQnZaN?^?VOK>1@V5N3O@ckWFqNhC*RMwTCqh z^MoQozw+$_QjLdo^gy+ln-*v1oSw{EBAQq@6H4F&O|_VhxqPP^_TUTDG(D;Ov6Jgk z&Y(-e4Tnfgy zmCS2VT^VIooFLjGB3HuPfeh!mK$ft8LYpcn<61yD4I{HHTNfA6zQ=%w(8Ti&9%YeS z17^`&gXyk`D2>R07>(G0m|IXLV2js5+kfLKSbje44jn7)Q3c^g>kvls4}vdLD}#o+nOXPPN3uvg8&8F<)`uHY$>KT+%H=wfw(~ zO=m~yvqD&*e?q+!SIPWF&NVlm`Z7GDodxR5HIgKUKdRGn4UdxSz!+#A++tgA!x<7f zz&B)#(o(VCSVnLdDsZikT3`}S3yJVy4VH-GBsJk*1gTif6yL*3yhX(&Din7{EFx}a z0bm84l6>+AK81z`jD<>i#KSV?^E$66+fV2ii#^#j29TKB7TUDyLiTepcX+8 zXpfo^BS=>CmJCIrJpn-4HlAp0Juxn8iMeAv(VpuT+Notp^9qO}cDuM!V#Yv!Zs?<) zoia0@>xc+2i)Z?mT2m)r^>}&%A*dW0#}j#oTL1l zv$6>n)KQL(9AyAJ3!p0N%B=`8kpA&}4*0X&ZA_XT@aw_oNaN_lFW`}N6gZof=u3cp zTM#?bdb&6}z}ef@h9=I^d#5k}7cwv?zKbD*gFFTO1Z6-<_~H!JYA6VecH#{nU)8az zX9_VAP-^ue7T*VDt^(8gyxt$O!yCJ|`sFZ2%#l~>G@v%6BrD#gb=0y1G{speNc$!? z0JZ?hiyx!eodTfo8X@odV`mA0x z<_i^FT&F>EDBzKb2up+(Gj0P^KLtF`4$up$@VmxXG z*u86KyH+Rv3Fa7c-JOy=@$%Jo4RHg#II%KVg?%S1L>=%@4!at07$Oj2&jE(W5(>?# zA|&8sw@|-VGhE~?o6SJT#GN8f{z&*;Ajq1zyu3_a7+|k+@W6CfLSV1qkqL??dRzN7 zGz@be;_I5b_UsGf4{~Xsss)V*q8Xf!uP*;*z* z+Rj<$MrVyPhKVBn2$vPuF0IIA!|$RII!5VcrivLP^(MA=rZ%x^2oxbEr@ozG=wSe% zr3s;dhs@!_Wlx=NtosQ17@I8(8HB(9Y+`2Y0(o=-x8kTpoKvKaxMv|<-y@=-+>la4 zloaJ;Sa5Cgn-4)`qveqHCJAM~fx6roA86N7-ZC}fu6Q5nEIvT7C-9R`CXH7pgc9Nw zyz-}2^2N9zp*s@ex5h&Yx@n|(rEjc8bnB8I7;Q%VB5Vpm*za5`mqVC5nd6n;VcNgr z(faS?-UU)tUWQl{!~Kq<8|77NxGaGO88?on>Km0W1X-sF{9oy(`o^hK%e7*>%IRut}+a_Q!@{U;qnJV5Dy?^f8FlV`D zr8e3wJ4-E6@`Emo@Xg4j(Y-?E(#8%gzt}P`W48ZQ5Q$Hz_zVTa{o)?pB221_ z8{mh%l!DN~#OW95^!u)F)NRl2K{w1w-uj;o{dKd{D#FD?sU5G;9gLtro24aX)ZFRG zDCExVn?ZO)GXt5c9nt-wOih^2kO@Dh{(0$da5hXJNy1XxM@M!@bzCLqBj0cc30gUi zLMz+Gv18M^ZB&e-rft}UXrT3gC4Osk1l*)gfKl-tW)+kx?l;GUe2Y0eY9jZL>$@P# z;0|<9!5xMcWZMcD?lpi+vuK0EAJs{-6r7_|CIEApUJZ~wJylL)Sx^p&8#E=9`3#Ni zF;D8>w_EX3wEYoJrX{BOuDzNiJcO-N8y9$Rq=MX+AQcz1n@k62Pwt`o#;ra|*s@fC z{6Mysr2`c8-0dq!>CO)O^8`{QTGKT5Xk^w?$88DSJ=1<>sDEZSGD7%}g77lbF%tli zZg@9>%EF=IBo%b)pfcnP)hif(o!SU}DoqiLYQ&ZaR*kcW(&Y?@T_L!wF1Rhd1h10+ hjZrOYG2nU3>QeRSY;m?cU#(Uu`r$XLCESPn>pzpM-!%XL literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/_pytest/assertion/__pycache__/rewrite.cpython-39.pyc b/venv/Lib/site-packages/_pytest/assertion/__pycache__/rewrite.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a118d9943b229d52120e5a8571c005ee803f63ae GIT binary patch literal 32928 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

  • 0GO%bNCu*aZZLEm#fnY5;%YbuWO_%Wr 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/_pytest/assertion/__pycache__/truncate.cpython-39.pyc b/venv/Lib/site-packages/_pytest/assertion/__pycache__/truncate.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b54730f1626a27898178576fff8c1ca53c10b010 GIT binary patch literal 2862 zcmZuzTW{RP6`mP#xvSM`vzDz`N^cYQ;%pIb{7|%MVHkB}#}ObaZDh9vp@5}!M!S@_ zl;ja+SSW=mm{_G!c}#({Ji2fu(F~WFN9iA&F8M)#ORF|T(#5{ zjNVWi)BTg(i!xRrB%t5w6Rf|$aG|fBipIhH%G?k#7dX zFJO4K)8>V{9c32p+Q&l@AI3DdW%b~qhA*Pu3&zG*Mwym3A?_yWm-W+UZMRrnP^xev z>?rDk@Fa+J(zE@-GunOXU6wPLlaC)ho)}O| z+lNgqZMblxj1I7>WiL9`39OM>Nji`XbsY7g{aAO}qF4){?kHBl)F(P9HC?+LFUV{= zhLCBh)X??+%uEBXFSj;-w)YE@9_p^m_C6l!-gdOVmqF&OVc0zidpg?- zhNq;?dvmqlo1NVmo)*_;gCIHxjD9+bj8>~SB%`C5TjMoun&`E~M2{u(yPVdhg)BI? z;#(Nx?2sMuLvhA)1ke#n*R6BT8e~f`{^_s#8g?>-fh4sPi~e78y#n1d>0HnuaJ1J= zlY^*Ncxj1{qI#^&ewyi`8B9SHq)E_?W(w0~O0!KG#6L4c2j4)=Tr^D7c{$VOro0`^ z%?jO=a8hD~Zb#J66KBARa+LcVx4jRn5L()m@$JpN?j8{qp6Q!L%kCt$;?qUEhd{i{ zPBXxxQ;5wizwyD7t?m24&XY$UJow~)Y)hwSRM*ku?9&$6)xyHZ>{pDlr$Ut|-2u8p zniW-`ATnt}!_Mu(IS$Qhw=b6N^mGE8nY-VE$bX@-@!PK&uX86(yJ4K&?aaSLAUa`k z+U9-@M+A_Yy(ZXPg`~Gpr)C9mXoi9_NbaM6uym01NL9v_jBW~{IPAI?Q`NlkC#GtjJ6Q9Q z)_i7ZGJ|p5;)~A(t>^AbPYGMiy>s3ZUvQ4IfR^R)-{-tCVT~BnC({F&Sp&`l`SwFA z2jLNDd^9ZCWgf$FfhNG=5@jZ=ByAm)LH7QPjuCA?{?TnorVhK{>P+5}&-zifuMs_^ z=+Z;d=n!*RDexdNneE8OU^Sx$VN?=%=|cG|ien@S%p(zWwUYa%GL)kXgd+#weY`uR zjGcmAe2p@8I-Sl11=DyPy$?s&KgmGz{G!G}zB67aHJ1HIDV?;(&B+DkWAUzR*NQ9m zAAET4$@b&m(Y@aU+gm#ic3+*^{OH~%yM+VlFWkd4N{Z{3{JMV{kQIV%I!bJ@^knzm z!w12m-G_yzPpnQ<;lTXus`(~#HQ%D<+i3jO1i_bE{k0i$s$jtx{+v)%3HRwa&LHc( z9xcm|dlNPL0UEYk<1Vg=bHP2xdbrGDL6w$}}N&68s8fhJ0miQ1e}C2oR=?rf^6n%DjcUpQdq{p-Mh2 zK~NsLpfddz7|Lx$?GFC2MzMzbpnM07TdDB|Z-@pifX8KD3!usSPh+X&HEYNr{}$Yv JJYO|__+L)f=3xK; literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/_pytest/assertion/__pycache__/util.cpython-39.pyc b/venv/Lib/site-packages/_pytest/assertion/__pycache__/util.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..69c125d7b81573cf7591b861040781a4ceba0f56 GIT binary patch literal 12659 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 Ia^Up;0ms#o5dZ)H 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..37ff076 --- /dev/null +++ b/venv/Lib/site-packages/_pytest/assertion/rewrite.py @@ -0,0 +1,1125 @@ +"""Rewrite assertion AST to produce nice error messages.""" +import ast +import errno +import functools +import importlib.abc +import importlib.machinery +import importlib.util +import io +import itertools +import marshal +import os +import struct +import sys +import tokenize +import types +from pathlib import Path +from pathlib import PurePath +from typing import Callable +from typing import Dict +from typing import IO +from typing import Iterable +from typing import List +from typing import Optional +from typing import Sequence +from typing import Set +from typing import Tuple +from typing import TYPE_CHECKING +from typing import Union + +import py + +from _pytest._io.saferepr import saferepr +from _pytest._version import version +from _pytest.assertion import util +from _pytest.assertion.util import ( # noqa: F401 + format_explanation as _format_explanation, +) +from _pytest.config import Config +from _pytest.main import Session +from _pytest.pathlib import fnmatch_ex +from _pytest.store import StoreKey + +if TYPE_CHECKING: + from _pytest.assertion import AssertionState + + +assertstate_key = StoreKey["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 + + +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: Optional[Session] = None + self._rewritten_names: Set[str] = set() + 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: Optional[Session]) -> 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: Optional[Sequence[Union[str, bytes]]] = None, + target: Optional[types.ModuleType] = None, + ) -> Optional[importlib.machinery.ModuleSpec]: + if self._writing_pyc: + return None + state = self.config._store[assertstate_key] + if self._early_rewrite_bailout(name, state): + return None + state.trace("find_module called for: %s" % name) + + # Type ignored because mypy is confused about the `self` binding here. + spec = self._find_spec(name, path) # type: ignore + 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 + # python3.6: `namespace` + # python3.7+: `None` + or spec.origin == "namespace" + 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 + ) -> Optional[types.ModuleType]: + 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._store[assertstate_key] + + self._rewritten_names.add(module.__name__) + + # 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.path.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(os.path.sep.join(parts) + ".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(py.path.local(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( + "Module already imported so cannot be rewritten: %s" % name + ), + stacklevel=5, + ) + + def get_data(self, pathname: Union[str, bytes]) -> bytes: + """Optional PEP302 get_data API.""" + with open(pathname, "rb") as f: + return f.read() + + +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/ + if sys.version_info >= (3, 7): + 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: + try: + with atomic_write(os.fspath(pyc), mode="wb", overwrite=True) as fp: + _write_pyc_fp(fp, source_stat, co) + 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 + + +else: + + def _write_pyc( + state: "AssertionState", + co: types.CodeType, + source_stat: os.stat_result, + pyc: Path, + ) -> bool: + proc_pyc = f"{pyc}.{os.getpid()}" + try: + fp = open(proc_pyc, "wb") + except OSError as e: + state.trace(f"error writing pyc file at {proc_pyc}: errno={e.errno}") + return False + + try: + _write_pyc_fp(fp, source_stat, co) + os.rename(proc_pyc, os.fspath(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 + finally: + fp.close() + return True + + +def _rewrite_test(fn: Path, config: Config) -> Tuple[os.stat_result, types.CodeType]: + """Read and rewrite *fn* and return the code object.""" + fn_ = os.fspath(fn) + stat = os.stat(fn_) + with open(fn_, "rb") as f: + source = f.read() + tree = ast.parse(source, filename=fn_) + rewrite_asserts(tree, source, fn_, config) + co = compile(tree, fn_, "exec", dont_inherit=True) + return stat, co + + +def _read_pyc( + source: Path, pyc: Path, trace: Callable[[str], None] = lambda x: None +) -> Optional[types.CodeType]: + """Possibly read a pytest pyc containing rewritten code. + + Return rewritten code if successful or None if not. + """ + try: + fp = open(os.fspath(pyc), "rb") + except OSError: + return None + with fp: + # https://www.python.org/dev/peps/pep-0552/ + has_flags = sys.version_info >= (3, 7) + try: + stat_result = os.stat(os.fspath(source)) + mtime = int(stat_result.st_mtime) + size = stat_result.st_size + data = fp.read(16 if has_flags else 12) + 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 if has_flags else 12): + trace("_read_pyc(%s): invalid pyc (too short)" % source) + return None + if data[:4] != importlib.util.MAGIC_NUMBER: + trace("_read_pyc(%s): invalid pyc (bad magic number)" % source) + return None + if has_flags and data[4:8] != b"\x00\x00\x00\x00": + trace("_read_pyc(%s): invalid pyc (unsupported flags)" % source) + return None + mtime_data = data[8 if has_flags else 4 : 12 if has_flags else 8] + if int.from_bytes(mtime_data, "little") != mtime & 0xFFFFFFFF: + trace("_read_pyc(%s): out of date" % source) + return None + size_data = data[12 if has_flags else 8 : 16 if has_flags else 12] + if int.from_bytes(size_data, "little") != size & 0xFFFFFFFF: + trace("_read_pyc(%s): invalid pyc (incorrect size)" % source) + 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("_read_pyc(%s): not a code object" % source) + return None + return co + + +def rewrite_asserts( + mod: ast.Module, + source: bytes, + module_path: Optional[str] = None, + config: Optional[Config] = 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. + """ + return saferepr(obj).replace("\n", "\\n") + + +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) + 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): + return False + + try: + return not hasattr(obj, "__name__") + except Exception: + return True + + +def _format_boolop(explanations: Iterable[str], is_or: bool) -> str: + explanation = "(" + (is_or and " or " or " and ").join(explanations) + ")" + return explanation.replace("%", "%%") + + +def _call_reprcompare( + ops: Sequence[str], + results: Sequence[bool], + expls: Sequence[str], + each_obj: Sequence[object], +) -> str: + for i, res, expl in zip(range(len(ops)), results, expls): + try: + done = not res + except Exception: + done = True + if done: + break + if util._reprcompare is not None: + custom = util._reprcompare(ops[i], each_obj[i], each_obj[i + 1]) + if custom is not None: + return custom + return expl + + +def _call_assertion_pass(lineno: int, orig: str, expl: str) -> None: + if util._assertion_pass is not None: + util._assertion_pass(lineno, orig, expl) + + +def _check_if_assertion_pass_impl() -> bool: + """Check if any plugins implement the pytest_assertion_pass hook + in order not to generate explanation unecessarily (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 set_location(node, lineno, col_offset): + """Set node location information recursively.""" + + def _fix(node, lineno, col_offset): + if "lineno" in node._attributes: + node.lineno = lineno + if "col_offset" in node._attributes: + node.col_offset = col_offset + for child in ast.iter_child_nodes(node): + _fix(child, lineno, col_offset) + + _fix(node, lineno, col_offset) + return node + + +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: Optional[int] = 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 escapd 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. + + This state is reset on every new assert statement visited and used + by the other visitors. + """ + + def __init__( + self, module_path: Optional[str], config: Optional[Config], 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 + + @functools.lru_cache(maxsize=1) + def _assert_expr_to_lineno(self) -> Dict[int, str]: + return _get_assertion_exprs(self.source) + + 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 + lineno = 1 + for item in mod.body: + if ( + expect_docstring + and isinstance(item, ast.Expr) + and isinstance(item.value, ast.Str) + ): + doc = item.value.s + if self.is_rewrite_disabled(doc): + return + expect_docstring = False + elif ( + isinstance(item, ast.ImportFrom) + and item.level == 0 + and item.module == "__future__" + ): + pass + else: + break + pos += 1 + # Special case: for a decorated function, set the lineno to that of the + # first decorator, not the `def`. Issue #4984. + if isinstance(item, ast.FunctionDef) and item.decorator_list: + lineno = item.decorator_list[0].lineno + else: + lineno = item.lineno + # Now actually insert the special imports. + if sys.version_info >= (3, 10): + aliases = [ + ast.alias("builtins", "@py_builtins", lineno=lineno, col_offset=0), + ast.alias( + "_pytest.assertion.rewrite", + "@pytest_ar", + lineno=lineno, + col_offset=0, + ), + ] + else: + aliases = [ + ast.alias("builtins", "@py_builtins"), + ast.alias("_pytest.assertion.rewrite", "@pytest_ar"), + ] + imports = [ + ast.Import([alias], lineno=lineno, col_offset=0) for alias in aliases + ] + mod.body[pos:pos] = imports + + # Collect asserts. + nodes: List[ast.AST] = [mod] + while nodes: + node = nodes.pop() + 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.Name(name, ast.Load()) + + 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 = [ast.Str(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: + from _pytest.warning_types import PytestAssertRewriteWarning + import warnings + + # 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=os.fspath(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.Str(explanation)) + + # Failed + if assert_.msg: + assertmsg = self.helper("_format_assertmsg", assert_.msg) + gluestr = "\n>assert " + else: + assertmsg = ast.Str("") + gluestr = "assert " + err_explanation = ast.BinOp(ast.Str(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 = self._assert_expr_to_lineno()[assert_.lineno] + hook_call_pass = ast.Expr( + self.helper( + "_call_assertion_pass", + ast.Num(assert_.lineno), + ast.Str(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 = [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 = [ + ast.Name(name, ast.Store()) for name in self.format_variables + ] + clear_format = ast.Assign(variables, ast.NameConstant(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.Str("") + explanation = "assert " + explanation + template = ast.BinOp(assertmsg, ast.Add(), ast.Str(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.NameConstant(None)) + self.statements.append(clear) + # Fix line numbers. + for stmt in self.statements: + set_location(stmt, assert_.lineno, assert_.col_offset) + return self.statements + + 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.Str(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.Str(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 + self.expl_stmts = fail_inner + 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.Str(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.Num(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.UnaryOp(unary.op, operand_res)) + 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.BinOp(left_expr, binop.op, right_expr)) + 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: + res, expl = self.visit(arg) + arg_expls.append(expl) + new_args.append(res) + for keyword in call.keywords: + 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.Call(new_func, new_args, new_kwargs) + 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.Attribute(value, attr.attr, ast.Load())) + 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() + 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 = [ast.Name(v, ast.Load()) for v in res_variables] + store_names = [ast.Name(v, ast.Store()) for v in res_variables] + it = zip(range(len(comp.ops)), comp.ops, comp.comparators) + expls = [] + syms = [] + results = [left_res] + for i, op, next_operand in it: + 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.Str(sym)) + expl = f"{left_expl} {sym} {next_expl}" + expls.append(ast.Str(expl)) + res_expr = ast.Compare(left_res, [op], [next_res]) + 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(os.fspath(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 + if e.errno == errno.EROFS: + 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.version_info >= (3, 8) and 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..5ba9ddc --- /dev/null +++ b/venv/Lib/site-packages/_pytest/assertion/truncate.py @@ -0,0 +1,100 @@ +"""Utilities for truncating assertion output. + +Current default behaviour is to truncate assertion explanations at +~8 terminal lines, unless running in "-vv" mode or running on CI. +""" +import os +from typing import List +from typing import Optional + +from _pytest.nodes import Item + + +DEFAULT_MAX_LINES = 8 +DEFAULT_MAX_CHARS = 8 * 80 +USAGE_MSG = "use '-vv' to show" + + +def truncate_if_required( + explanation: List[str], item: Item, max_length: Optional[int] = None +) -> List[str]: + """Truncate this assertion explanation if the given test item is eligible.""" + if _should_truncate_item(item): + return _truncate_explanation(explanation) + return explanation + + +def _should_truncate_item(item: Item) -> bool: + """Whether or not this test item is eligible for truncation.""" + verbose = item.config.option.verbose + return verbose < 2 and not _running_on_ci() + + +def _running_on_ci() -> bool: + """Check if we're currently running on a CI system.""" + env_vars = ["CI", "BUILD_NUMBER"] + return any(var in os.environ for var in env_vars) + + +def _truncate_explanation( + input_lines: List[str], + max_lines: Optional[int] = None, + max_chars: Optional[int] = None, +) -> List[str]: + """Truncate given list of strings that makes up the assertion explanation. + + Truncates to either 8 lines, or 640 characters - whichever the input reaches + first. The remaining lines will be replaced by a usage message. + """ + + if max_lines is None: + max_lines = DEFAULT_MAX_LINES + if max_chars is None: + max_chars = DEFAULT_MAX_CHARS + + # Check if truncation required + input_char_count = len("".join(input_lines)) + if len(input_lines) <= max_lines and input_char_count <= max_chars: + return input_lines + + # Truncate first to max_lines, and then truncate to max_chars if max_chars + # is exceeded. + truncated_explanation = input_lines[:max_lines] + truncated_explanation = _truncate_by_char_count(truncated_explanation, max_chars) + + # Add ellipsis to final line + truncated_explanation[-1] = truncated_explanation[-1] + "..." + + # Append useful message to explanation + truncated_line_count = len(input_lines) - len(truncated_explanation) + truncated_line_count += 1 # Account for the part-truncated final line + msg = "...Full output truncated" + if truncated_line_count == 1: + msg += f" ({truncated_line_count} line hidden)" + else: + msg += f" ({truncated_line_count} lines hidden)" + msg += f", {USAGE_MSG}" + truncated_explanation.extend(["", str(msg)]) + return truncated_explanation + + +def _truncate_by_char_count(input_lines: List[str], max_chars: int) -> List[str]: + # Check if truncation required + if len("".join(input_lines)) <= max_chars: + return input_lines + + # 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..da1ffd1 --- /dev/null +++ b/venv/Lib/site-packages/_pytest/assertion/util.py @@ -0,0 +1,477 @@ +"""Utilities for assertion debugging.""" +import collections.abc +import pprint +from typing import AbstractSet +from typing import Any +from typing import Callable +from typing import Iterable +from typing import List +from typing import Mapping +from typing import Optional +from typing import Sequence + +import _pytest._code +from _pytest import outcomes +from _pytest._io.saferepr import _pformat_dispatch +from _pytest._io.saferepr import safeformat +from _pytest._io.saferepr import saferepr + +# 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: Optional[Callable[[str, object, object], Optional[str]]] = None + +# Works similarly as _reprcompare attribute. Is populated with the hook call +# when pytest_runtest_setup is called. +_assertion_pass: Optional[Callable[[int, str, str], None]] = None + + +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 TypeError: + return False + + +def assertrepr_compare(config, op: str, left: Any, right: Any) -> Optional[List[str]]: + """Return specialised explanations for some operators/operands.""" + verbose = config.getoption("verbose") + if verbose > 1: + left_repr = safeformat(left) + right_repr = safeformat(right) + 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) + right_repr = saferepr(right, maxsize=maxsize) + + summary = f"{left_repr} {op} {right_repr}" + + explanation = None + try: + if op == "==": + explanation = _compare_eq_any(left, right, verbose) + elif op == "not in": + if istext(left) and istext(right): + explanation = _notin_text(left, right, verbose) + except outcomes.Exit: + raise + except Exception: + explanation = [ + "(pytest_assertion plugin: representation of details failed: {}.".format( + _pytest._code.ExceptionInfo.from_current()._getreprcrash() + ), + " Probably an object has a faulty __repr__.)", + ] + + if not explanation: + return None + + return [summary] + explanation + + +def _compare_eq_any(left: Any, right: Any, verbose: int = 0) -> List[str]: + explanation = [] + if istext(left) and istext(right): + explanation = _diff_text(left, right, verbose) + else: + if type(left) == 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, verbose) + elif issequence(left) and issequence(right): + explanation = _compare_eq_sequence(left, right, verbose) + elif isset(left) and isset(right): + explanation = _compare_eq_set(left, right, verbose) + elif isdict(left) and isdict(right): + explanation = _compare_eq_dict(left, right, verbose) + elif verbose > 0: + explanation = _compare_eq_verbose(left, right) + if isiterable(left) and isiterable(right): + expl = _compare_eq_iterable(left, right, verbose) + explanation.extend(expl) + return explanation + + +def _diff_text(left: str, right: str, 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 = [ + "Skipping %s identical leading characters in diff, use -v to show" % i + ] + 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 += [ + "Skipping {} identical trailing " + "characters in diff, use -v to show".format(i) + ] + 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 += [ + line.strip("\n") + for line in ndiff(right.splitlines(keepends), left.splitlines(keepends)) + ] + return explanation + + +def _compare_eq_verbose(left: Any, right: Any) -> List[str]: + keepends = True + left_lines = repr(left).splitlines(keepends) + right_lines = repr(right).splitlines(keepends) + + explanation: List[str] = [] + explanation += ["+" + line for line in left_lines] + explanation += ["-" + line for line in right_lines] + + return explanation + + +def _surrounding_parens_on_own_lines(lines: List[str]) -> None: + """Move opening/closing parenthesis/bracket to own lines.""" + opening = lines[0][:1] + if opening in ["(", "[", "{"]: + lines[0] = " " + lines[0][1:] + lines[:] = [opening] + lines + closing = lines[-1][-1:] + if closing in [")", "]", "}"]: + lines[-1] = lines[-1][:-1] + "," + lines[:] = lines + [closing] + + +def _compare_eq_iterable( + left: Iterable[Any], right: Iterable[Any], verbose: int = 0 +) -> List[str]: + if not verbose: + return ["Use -v to get the full diff"] + # dynamic import to speedup pytest + import difflib + + left_formatting = pprint.pformat(left).splitlines() + right_formatting = pprint.pformat(right).splitlines() + + # Re-format for different output lengths. + lines_left = len(left_formatting) + lines_right = len(right_formatting) + if lines_left != lines_right: + left_formatting = _pformat_dispatch(left).splitlines() + right_formatting = _pformat_dispatch(right).splitlines() + + if lines_left > 1 or lines_right > 1: + _surrounding_parens_on_own_lines(left_formatting) + _surrounding_parens_on_own_lines(right_formatting) + + explanation = ["Full diff:"] + # "right" is the expected base against which we compare "left", + # see https://github.com/pytest-dev/pytest/issues/3333 + explanation.extend( + line.rstrip() for line in difflib.ndiff(right_formatting, left_formatting) + ) + return explanation + + +def _compare_eq_sequence( + left: Sequence[Any], right: Sequence[Any], 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 += [f"At index {i} diff: {left_value!r} != {right_value!r}"] + 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: {extra}"] + else: + explanation += [ + "%s contains %d more items, first extra item: %s" + % (dir_with_more, len_diff, extra) + ] + return explanation + + +def _compare_eq_set( + left: AbstractSet[Any], right: AbstractSet[Any], verbose: int = 0 +) -> List[str]: + explanation = [] + diff_left = left - right + diff_right = right - left + if diff_left: + explanation.append("Extra items in the left set:") + for item in diff_left: + explanation.append(saferepr(item)) + if diff_right: + explanation.append("Extra items in the right set:") + for item in diff_right: + explanation.append(saferepr(item)) + return explanation + + +def _compare_eq_dict( + left: Mapping[Any, Any], right: Mapping[Any, Any], 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 += ["Omitting %s identical items, use -vv to show" % len(same)] + elif same: + explanation += ["Common items:"] + explanation += 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 += [saferepr({k: left[k]}) + " != " + saferepr({k: right[k]})] + extra_left = set_left - set_right + len_extra_left = len(extra_left) + if len_extra_left: + explanation.append( + "Left contains %d more item%s:" + % (len_extra_left, "" if len_extra_left == 1 else "s") + ) + explanation.extend( + 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( + "Right contains %d more item%s:" + % (len_extra_right, "" if len_extra_right == 1 else "s") + ) + explanation.extend( + pprint.pformat({k: right[k] for k in extra_right}).splitlines() + ) + return explanation + + +def _compare_eq_cls(left: Any, right: Any, verbose: int) -> List[str]: + if isdatacls(left): + all_fields = left.__dataclass_fields__ + fields_to_check = [field for field, info in all_fields.items() 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("Omitting %s identical items, use -vv to show" % len(same)) + elif same: + explanation += ["Matching attributes:"] + explanation += pprint.pformat(same).splitlines() + if diff: + explanation += ["Differing attributes:"] + explanation += pprint.pformat(diff).splitlines() + for field in diff: + field_left = getattr(left, field) + field_right = getattr(right, field) + explanation += [ + "", + "Drill down into differing attribute %s:" % field, + ("%s%s: %r != %r") % (indent, field, field_left, field_right), + ] + explanation += [ + indent + line + for line in _compare_eq_any(field_left, field_right, 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, verbose) + newdiff = ["%s is contained here:" % saferepr(term, maxsize=42)] + 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..03acd03 --- /dev/null +++ b/venv/Lib/site-packages/_pytest/cacheprovider.py @@ -0,0 +1,575 @@ +"""Implementation of the cache provider.""" +# This plugin was not named "cache" to avoid conflicts with the external +# pytest-cache version. +import json +import os +from pathlib import Path +from typing import Dict +from typing import Generator +from typing import Iterable +from typing import List +from typing import Optional +from typing import Set +from typing import Union + +import attr +import py + +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.compat import final +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.python import Module +from _pytest.python import Package +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/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: +# http://www.bford.info/cachedir/spec.html +""" + + +@final +@attr.s(init=False) +class Cache: + _cachedir = attr.ib(type=Path, repr=False) + _config = attr.ib(type=Config, repr=False) + + # sub-directory under cache-dir for directories created by "makedir" + _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 makedir(self, name: str) -> py.path.local: + """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. + + :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) + res.mkdir(exist_ok=True, parents=True) + return py.path.local(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") 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: + if path.parent.is_dir(): + cache_dir_exists_already = True + else: + cache_dir_exists_already = self._cachedir.exists() + path.parent.mkdir(exist_ok=True, parents=True) + except OSError: + self.warn("could not create cache path {path}", path=path, _ispytest=True) + return + if not cache_dir_exists_already: + self._ensure_supporting_files() + data = json.dumps(value, indent=2, sort_keys=True) + try: + f = path.open("w") + except OSError: + self.warn("cache could not write path {path}", path=path, _ispytest=True) + else: + with f: + f.write(data) + + def _ensure_supporting_files(self) -> None: + """Create supporting files in the cache dir that are not really part of the cache.""" + readme_path = self._cachedir / "README.md" + readme_path.write_text(README_CONTENT) + + gitignore_path = self._cachedir.joinpath(".gitignore") + msg = "# Created by pytest automatically.\n*\n" + gitignore_path.write_text(msg, encoding="UTF-8") + + cachedir_tag_path = self._cachedir.joinpath("CACHEDIR.TAG") + cachedir_tag_path.write_bytes(CACHEDIR_TAG_CONTENT) + + +class LFPluginCollWrapper: + def __init__(self, lfplugin: "LFPlugin") -> None: + self.lfplugin = lfplugin + self._collected_at_least_one_failure = False + + @hookimpl(hookwrapper=True) + def pytest_make_collect_report(self, collector: nodes.Collector): + if isinstance(collector, Session): + out = yield + res: CollectReport = out.get_result() + + # Sort any lf-paths to the beginning. + lf_paths = self.lfplugin._last_failed_paths + res.result = sorted( + res.result, key=lambda x: 0 if Path(str(x.fspath)) in lf_paths else 1, + ) + return + + elif isinstance(collector, Module): + if Path(str(collector.fspath)) in self.lfplugin._last_failed_paths: + out = yield + res = out.get_result() + 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 + 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.fspath) + # Keep all sub-collectors. + or isinstance(x, nodes.Collector) + ] + return + yield + + +class LFPluginCollSkipfiles: + def __init__(self, lfplugin: "LFPlugin") -> None: + self.lfplugin = lfplugin + + @hookimpl + def pytest_make_collect_report( + self, collector: nodes.Collector + ) -> Optional[CollectReport]: + # Packages are Modules, but _last_failed_paths only contains + # test-bearing paths and doesn't try to include the paths of their + # packages, so don't filter them. + if isinstance(collector, Module) and not isinstance(collector, Package): + if Path(str(collector.fspath)) 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: Optional[int] = None + self._report_status: Optional[str] = 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()s of the previously failed nodeids.""" + rootpath = self.config.rootpath + result = {rootpath / nodeid.split("::")[0] for nodeid in self.lastfailed} + return {x for x in result if x.exists()} + + def pytest_report_collectionfinish(self) -> Optional[str]: + if self.active and self.config.getoption("verbose") >= 0: + return "run-last-failure: %s" % 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(hookwrapper=True, tryfirst=True) + def pytest_collection_modifyitems( + self, config: Config, items: List[nodes.Item] + ) -> Generator[None, None, None]: + yield + + if not self.active: + return + + 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 = "%d known failures not in selected tests" % ( + len(self.lastfailed), + ) + 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 = "rerun previous {count} {noun}{suffix}".format( + count=self._previously_failed_count, suffix=suffix, noun=noun + ) + + if self._skipped_files > 0: + files_noun = "file" if self._skipped_files == 1 else "files" + self._report_status += " (skipped {files} {files_noun})".format( + files=self._skipped_files, files_noun=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." + + 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(hookwrapper=True, tryfirst=True) + def pytest_collection_modifyitems( + self, items: List[nodes.Item] + ) -> Generator[None, None, None]: + 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) + + def _get_increasing_order(self, items: Iterable[nodes.Item]) -> List[nodes.Item]: + return sorted(items, key=lambda item: item.fspath.mtime(), reverse=True) # type: ignore[no-any-return] + + def pytest_sessionfinish(self) -> None: + config = self.config + if config.getoption("cacheshow") or hasattr(config, "workerinput"): + return + + if config.getoption("collectonly"): + return + + assert config.cache is not None + config.cache.set("cache/nodeids", sorted(self.cached_nodeids)) + + +def pytest_addoption(parser: Parser) -> None: + group = parser.getgroup("general") + group.addoption( + "--lf", + "--last-failed", + action="store_true", + dest="lf", + help="rerun only the tests that failed " + "at the last run (or all if none failed)", + ) + group.addoption( + "--ff", + "--failed-first", + action="store_true", + dest="failedfirst", + help="run all tests, but run the last failures first.\n" + "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="which tests to run with no previously (known) failures.", + ) + + +def pytest_cmdline_main(config: Config) -> Optional[Union[int, ExitCode]]: + if config.option.cacheshow: + from _pytest.main import wrap_session + + return wrap_session(config, cacheshow) + return None + + +@hookimpl(tryfirst=True) +def pytest_configure(config: Config) -> None: + config.cache = Cache.for_config(config, _ispytest=True) + config.pluginmanager.register(LFPlugin(config), "lfplugin") + config.pluginmanager.register(NFPlugin(config), "nfplugin") + + +@fixture +def cache(request: FixtureRequest) -> Cache: + """Return a cache object that can persist state between testing sessions. + + cache.get(key, default) + cache.set(key, value) + + Keys must be ``/`` separated strings, where the first part is usually the + name of your plugin or application to avoid clashes with other cache users. + + Values can be any object handled by the json stdlib module. + """ + assert request.config.cache is not None + return request.config.cache + + +def pytest_report_header(config: Config) -> Optional[str]: + """Display cachedir with --cache-show and if non-default.""" + if config.option.verbose > 0 or config.getini("cache_dir") != ".pytest_cache": + assert config.cache is not None + cachedir = config.cache._cachedir + # TODO: evaluate generating upward relative paths + # starting with .., ../.. if sensible + + try: + displaypath = cachedir.relative_to(config.rootpath) + except ValueError: + displaypath = cachedir + return f"cachedir: {displaypath}" + return None + + +def cacheshow(config: Config, session: Session) -> int: + from pprint import pformat + + assert config.cache is not None + + tw = TerminalWriter() + tw.line("cachedir: " + str(config.cache._cachedir)) + if not config.cache._cachedir.is_dir(): + tw.line("cache is empty") + return 0 + + glob = config.option.cacheshow[0] + if glob is None: + glob = "*" + + dummy = object() + basedir = config.cache._cachedir + vdir = basedir / Cache._CACHE_PREFIX_VALUES + tw.sep("-", "cache values for %r" % glob) + 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("%s contains unreadable content, will be ignored" % key) + else: + tw.line("%s contains:" % key) + 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("-", "cache directories for %r" % glob) + for p in contents: + # if p.check(dir=1): + # print("%s/" % p.relto(basedir)) + if p.is_file(): + key = str(p.relative_to(basedir)) + tw.line(f"{key} is a file of length {p.stat().st_size:d}") + 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..0863026 --- /dev/null +++ b/venv/Lib/site-packages/_pytest/capture.py @@ -0,0 +1,967 @@ +"""Per-test stdout/stderr capturing mechanism.""" +import contextlib +import functools +import io +import os +import sys +from io import UnsupportedOperation +from tempfile import TemporaryFile +from typing import Any +from typing import AnyStr +from typing import Generator +from typing import Generic +from typing import Iterator +from typing import Optional +from typing import TextIO +from typing import Tuple +from typing import TYPE_CHECKING +from typing import Union + +from _pytest.compat import final +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 + +if TYPE_CHECKING: + from typing_extensions import Literal + + _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( + "-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 so that it attaches to the correct stdio + handles on Windows. + + Pdb uses readline support where available--when not running from the Python + prompt, the readline module is not imported until running the pdb REPL. If + running pytest with the --pdb option this means the readline module is not + imported until after I/O capture has been started. + + This is a problem for pyreadline, which is often used to implement readline + support on Windows, as it does not attach to the correct handles for stdout + and/or stdin if they have been redirected by the FDCapture mechanism. This + workaround ensures that readline is imported before I/O capture is setup so + that it can attach to the actual stdin/out for the console. + + See https://github.com/pytest-dev/pytest/pull/1281. + """ + if sys.platform.startswith("win32"): + try: + import readline # noqa: F401 + except ImportError: + pass + + +def _py36_windowsconsoleio_workaround(stream: TextIO) -> None: + """Workaround for Windows Unicode console handling on Python>=3.6. + + 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] + return + + buffered = hasattr(stream.buffer, "raw") + raw_stdout = stream.buffer.raw if buffered else stream.buffer # type: ignore[attr-defined] + + if not isinstance(raw_stdout, io._WindowsConsoleIO): # type: ignore[attr-defined] + return + + def _reopen_stdio(f, mode): + if not buffered and mode[0] == "w": + buffering = 0 + else: + buffering = -1 + + return io.TextIOWrapper( + open(os.dup(f.fileno()), mode, buffering), # type: ignore[arg-type] + 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(hookwrapper=True) +def pytest_load_initial_conftests(early_config: Config): + ns = early_config.known_args_namespace + if ns.capture == "fd": + _py36_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() + outcome = yield + capman.suspend_global_capture() + if outcome.excinfo is not None: + out, err = capman.read_global_capture() + sys.stdout.write(out) + sys.stderr.write(err) + + +# 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. + return 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: + encoding = None + + def read(self, *args): + raise OSError( + "pytest: reading from stdin while output is captured! Consider using `-s`." + ) + + readline = read + readlines = read + __next__ = read + + def __iter__(self): + return self + + def fileno(self) -> int: + raise UnsupportedOperation("redirected stdin is pseudofile, has no fileno()") + + def isatty(self) -> bool: + return False + + def close(self) -> None: + pass + + @property + def buffer(self): + return self + + +# Capture classes. + + +patchsysdict = {0: "stdin", 1: "stdout", 2: "stderr"} + + +class NoCapture: + EMPTY_BUFFER = None + __init__ = start = done = suspend = resume = lambda *args: None + + +class SysCaptureBinary: + + EMPTY_BUFFER = b"" + + def __init__(self, fd: int, tmpfile=None, *, tee: bool = False) -> None: + name = patchsysdict[fd] + self._old = 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 snap(self): + 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 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" + + def writeorg(self, data) -> None: + self._assert_state("writeorg", ("started", "suspended")) + self._old.flush() + self._old.buffer.write(data) + self._old.buffer.flush() + + +class SysCapture(SysCaptureBinary): + EMPTY_BUFFER = "" # type: ignore[assignment] + + def snap(self): + res = self.tmpfile.getvalue() + self.tmpfile.seek(0) + self.tmpfile.truncate() + return res + + def writeorg(self, data): + self._assert_state("writeorg", ("started", "suspended")) + self._old.write(data) + self._old.flush() + + +class FDCaptureBinary: + """Capture IO to/from a given OS-level file descriptor. + + snap() produces `bytes`. + """ + + EMPTY_BUFFER = b"" + + 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 loveley 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: Optional[int] = 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) + self.syscapture = 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() + + self._state = "initialized" + + def __repr__(self) -> str: + return "<{} {} oldfd={} _state={!r} tmpfile={!r}>".format( + self.__class__.__name__, + self.targetfd, + self.targetfd_save, + 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: + """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 snap(self): + 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 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" + + def writeorg(self, data): + """Write to original file descriptor.""" + self._assert_state("writeorg", ("started", "suspended")) + os.write(self.targetfd_save, data) + + +class FDCapture(FDCaptureBinary): + """Capture IO to/from a given OS-level file descriptor. + + snap() produces text. + """ + + # Ignore type because it doesn't match the type in the superclass (bytes). + EMPTY_BUFFER = "" # type: ignore + + def snap(self): + 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): + """Write to original file descriptor.""" + super().writeorg(data.encode("utf-8")) # XXX use encoding of original stream + + +# MultiCapture + + +# This class was a namedtuple, but due to mypy limitation[0] it could not be +# made generic, so was replaced by a regular class which tries to emulate the +# pertinent parts of a namedtuple. If the mypy limitation is ever lifted, can +# make it a namedtuple again. +# [0]: https://github.com/python/mypy/issues/685 +@final +@functools.total_ordering +class CaptureResult(Generic[AnyStr]): + """The result of :method:`CaptureFixture.readouterr`.""" + + __slots__ = ("out", "err") + + def __init__(self, out: AnyStr, err: AnyStr) -> None: + self.out: AnyStr = out + self.err: AnyStr = err + + def __len__(self) -> int: + return 2 + + def __iter__(self) -> Iterator[AnyStr]: + return iter((self.out, self.err)) + + def __getitem__(self, item: int) -> AnyStr: + return tuple(self)[item] + + def _replace( + self, *, out: Optional[AnyStr] = None, err: Optional[AnyStr] = None + ) -> "CaptureResult[AnyStr]": + return CaptureResult( + out=self.out if out is None else out, err=self.err if err is None else err + ) + + def count(self, value: AnyStr) -> int: + return tuple(self).count(value) + + def index(self, value) -> int: + return tuple(self).index(value) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, (CaptureResult, tuple)): + return NotImplemented + return tuple(self) == tuple(other) + + def __hash__(self) -> int: + return hash(tuple(self)) + + def __lt__(self, other: object) -> bool: + if not isinstance(other, (CaptureResult, tuple)): + return NotImplemented + return tuple(self) < tuple(other) + + def __repr__(self) -> str: + return f"CaptureResult(out={self.out!r}, err={self.err!r})" + + +class MultiCapture(Generic[AnyStr]): + _state = None + _in_suspended = False + + def __init__(self, in_, out, err) -> None: + self.in_ = in_ + self.out = out + self.err = err + + def __repr__(self) -> str: + return "".format( + self.out, self.err, self.in_, self._state, self._in_suspended, + ) + + 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: + self.out.writeorg(out) + if err: + 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: + 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]: + if self.out: + out = self.out.snap() + else: + out = "" + if self.err: + err = self.err.snap() + else: + err = "" + return CaptureResult(out, err) + + +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 = method + self._global_capturing: Optional[MultiCapture[str]] = None + self._capture_fixture: Optional[CaptureFixture[Any]] = None + + def __repr__(self) -> str: + return "".format( + self._method, self._global_capturing, self._capture_fixture + ) + + def is_capturing(self) -> Union[str, bool]: + if self.is_globally_capturing(): + return "global" + if self._capture_fixture: + return "fixture %s" % 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( + "cannot use {} and {} at the same time".format( + requested_fixture, current_fixture + ) + ) + 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, None, 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, None, 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(hookwrapper=True) + def pytest_make_collect_report(self, collector: Collector): + if isinstance(collector, File): + self.resume_global_capture() + outcome = yield + self.suspend_global_capture() + out, err = self.read_global_capture() + rep = outcome.get_result() + if out: + rep.sections.append(("Captured stdout", out)) + if err: + rep.sections.append(("Captured stderr", err)) + else: + yield + + @hookimpl(hookwrapper=True) + def pytest_runtest_setup(self, item: Item) -> Generator[None, None, None]: + with self.item_capture("setup", item): + yield + + @hookimpl(hookwrapper=True) + def pytest_runtest_call(self, item: Item) -> Generator[None, None, None]: + with self.item_capture("call", item): + yield + + @hookimpl(hookwrapper=True) + def pytest_runtest_teardown(self, item: Item) -> Generator[None, None, None]: + with self.item_capture("teardown", item): + 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, request: SubRequest, *, _ispytest: bool = False + ) -> None: + check_ispytest(_ispytest) + self.captureclass = captureclass + self.request = request + self._capture: Optional[MultiCapture[AnyStr]] = None + self._captured_out = self.captureclass.EMPTY_BUFFER + self._captured_err = self.captureclass.EMPTY_BUFFER + + def _start(self) -> None: + if self._capture is None: + self._capture = MultiCapture( + in_=None, out=self.captureclass(1), err=self.captureclass(2), + ) + 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, None, None]: + """Temporarily disable capturing while inside the ``with`` block.""" + capmanager = self.request.config.pluginmanager.getplugin("capturemanager") + with capmanager.global_and_fixture_disabled(): + yield + + +# The fixtures. + + +@fixture +def capsys(request: SubRequest) -> Generator[CaptureFixture[str], None, None]: + """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. + """ + capman = request.config.pluginmanager.getplugin("capturemanager") + capture_fixture = CaptureFixture[str](SysCapture, request, _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], None, None]: + """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. + """ + capman = request.config.pluginmanager.getplugin("capturemanager") + capture_fixture = CaptureFixture[bytes](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], None, None]: + """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. + """ + capman = request.config.pluginmanager.getplugin("capturemanager") + capture_fixture = CaptureFixture[str](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], None, None]: + """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. + """ + capman = request.config.pluginmanager.getplugin("capturemanager") + capture_fixture = CaptureFixture[bytes](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..c7f86ea --- /dev/null +++ b/venv/Lib/site-packages/_pytest/compat.py @@ -0,0 +1,400 @@ +"""Python version compatibility code.""" +import enum +import functools +import inspect +import re +import sys +from contextlib import contextmanager +from inspect import Parameter +from inspect import signature +from pathlib import Path +from typing import Any +from typing import Callable +from typing import Generic +from typing import Optional +from typing import Tuple +from typing import TYPE_CHECKING +from typing import TypeVar +from typing import Union + +import attr + +from _pytest.outcomes import fail +from _pytest.outcomes import TEST_OUTCOME + +if TYPE_CHECKING: + from typing import NoReturn + from typing_extensions import Final + + +_T = TypeVar("_T") +_S = TypeVar("_S") + + +# 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 # noqa: E305 +# fmt: on + +if sys.version_info >= (3, 8): + from importlib import metadata as importlib_metadata +else: + import importlib_metadata # noqa: F401 + + +def _format_args(func: Callable[..., Any]) -> str: + return str(signature(func)) + + +# The type of re.compile objects is not exposed in Python. +REGEX_TYPE = type(re.compile("")) + + +def is_generator(func: object) -> bool: + genfunc = inspect.isgeneratorfunction(func) + return genfunc and not iscoroutinefunction(func) + + +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 couroutines.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 getlocation(function, curdir: Optional[str] = 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 "%s:%d" % (relfn, lineno + 1) + return "%s:%d" % (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[..., Any], + *, + name: str = "", + is_method: bool = False, + cls: Optional[type] = 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 is_method and cls arguments indicate that the function should + be treated as a bound method even though it's not unless, only in + the case of cls, 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 + except (ValueError, TypeError) as e: + fail( + f"Could not determine arguments of {function!r}: {e}", pytrace=False, + ) + + arg_names = tuple( + p.name + for p in parameters.values() + 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, remove the first + # parameter name. + if is_method or ( + cls and not isinstance(cls.__dict__.get(name, 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 + + +if sys.version_info < (3, 7): + + @contextmanager + def nullcontext(): + yield + + +else: + from contextlib import nullcontext as nullcontext # noqa: F401 + + +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 _translate_non_printable(s: str) -> str: + return s.translate(_non_printable_ascii_translate_table) + + +STRING_TYPES = bytes, str + + +def _bytes_to_ascii(val: bytes) -> str: + return val.decode("ascii", "backslashreplace") + + +def ascii_escaped(val: Union[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 unicode objects 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 = _bytes_to_ascii(val) + else: + ret = val.encode("unicode_escape").decode("ascii") + return _translate_non_printable(ret) + + +@attr.s +class _PytestWrapper: + """Dummy wrapper around a function object for internal use only. + + Used to correctly unwrap the underlying function object when we are + creating fixtures, because we wrap the function object ourselves with a + decorator to issue warnings when the fixture function is called directly. + """ + + obj = attr.ib() + + +def get_real_func(obj): + """Get the real function object of the (possibly) wrapped object by + functools.wraps or functools.partial.""" + start_obj = obj + for i in range(100): + # __pytest_wrapped__ is set by @pytest.fixture when wrapping the fixture function + # to trigger a warning if it gets called directly instead of by pytest: we don't + # want to unwrap further than this otherwise we lose useful wrappings like @mock.patch (#3774) + new_obj = getattr(obj, "__pytest_wrapped__", None) + if isinstance(new_obj, _PytestWrapper): + obj = new_obj.obj + break + new_obj = getattr(obj, "__wrapped__", None) + if new_obj is None: + break + obj = new_obj + else: + from _pytest._io.saferepr import saferepr + + raise ValueError( + ("could not find real function of {start}\nstopped at {current}").format( + start=saferepr(start_obj), current=saferepr(obj) + ) + ) + if isinstance(obj, functools.partial): + obj = obj.func + return obj + + +def get_real_method(obj, holder): + """Attempt to obtain the real function object that might be wrapping + ``obj``, while at the same time returning a bound method to ``holder`` if + the original object was a bound method.""" + try: + is_method = hasattr(obj, "__func__") + obj = get_real_func(obj) + except Exception: # pragma: no cover + return obj + if is_method and hasattr(obj, "__get__") and callable(obj.__get__): + obj = obj.__get__(holder) + 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). + """ + 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 + + +if TYPE_CHECKING: + if sys.version_info >= (3, 8): + from typing import final as final + else: + from typing_extensions import final as final +elif sys.version_info >= (3, 8): + from typing import final as final +else: + + def final(f): + return f + + +if sys.version_info >= (3, 8): + from functools import cached_property as cached_property +else: + from typing import overload + from typing import Type + + class cached_property(Generic[_S, _T]): + __slots__ = ("func", "__doc__") + + def __init__(self, func: Callable[[_S], _T]) -> None: + self.func = func + self.__doc__ = func.__doc__ + + @overload + def __get__( + self, instance: None, owner: Optional[Type[_S]] = ... + ) -> "cached_property[_S, _T]": + ... + + @overload + def __get__(self, instance: _S, owner: Optional[Type[_S]] = ...) -> _T: + ... + + def __get__(self, instance, owner=None): + if instance is None: + return self + value = instance.__dict__[self.func.__name__] = self.func(instance) + return value + + +# Perform exhaustiveness checking. +# +# Consider this example: +# +# MyUnion = Union[int, str] +# +# def handle(x: MyUnion) -> int { +# if isinstance(x, int): +# return 1 +# elif isinstance(x, str): +# return 2 +# else: +# raise Exception('unreachable') +# +# Now suppose we add a new variant: +# +# MyUnion = Union[int, str, bytes] +# +# After doing this, we must remember ourselves to go and update the handle +# function to handle the new variant. +# +# With `assert_never` we can do better: +# +# // raise Exception('unreachable') +# return assert_never(x) +# +# Now, if we forget to handle the new variant, the type-checker will emit a +# compile-time error, instead of the runtime error we would have gotten +# previously. +# +# This also work for Enums (if you use `is` to compare) and Literals. +def assert_never(value: "NoReturn") -> "NoReturn": + assert False, "Unhandled value: {} ({})".format(value, type(value).__name__) 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..bd9e288 --- /dev/null +++ b/venv/Lib/site-packages/_pytest/config/__init__.py @@ -0,0 +1,1606 @@ +"""Command line options, ini-file and conftest.py processing.""" +import argparse +import collections.abc +import contextlib +import copy +import enum +import inspect +import os +import re +import shlex +import sys +import types +import warnings +from functools import lru_cache +from pathlib import Path +from types import TracebackType +from typing import Any +from typing import Callable +from typing import Dict +from typing import Generator +from typing import IO +from typing import Iterable +from typing import Iterator +from typing import List +from typing import Optional +from typing import Sequence +from typing import Set +from typing import TextIO +from typing import Tuple +from typing import Type +from typing import TYPE_CHECKING +from typing import Union + +import attr +import py +from pluggy import HookimplMarker +from pluggy import HookspecMarker +from pluggy import PluginManager + +import _pytest._code +import _pytest.deprecated +import _pytest.hookspec +from .exceptions import PrintHelp as PrintHelp +from .exceptions import UsageError as UsageError +from .findpaths import determine_setup +from _pytest._code import ExceptionInfo +from _pytest._code import filter_traceback +from _pytest._io import TerminalWriter +from _pytest.compat import final +from _pytest.compat import importlib_metadata +from _pytest.outcomes import fail +from _pytest.outcomes import Skipped +from _pytest.pathlib import bestrelpath +from _pytest.pathlib import import_path +from _pytest.pathlib import ImportMode +from _pytest.store import Store +from _pytest.warning_types import PytestConfigWarning + +if TYPE_CHECKING: + + from _pytest._code.code import _TracebackStyle + from _pytest.terminal import TerminalReporter + from .argparsing import Argument + + +_PluggyPlugin = object +"""A type to represent plugin objects. + +Plugins can be any namespace, so we can't narrow it down much, but we use an +alias to make the intent clear. + +Ideally this type would be provided by pluggy itself. +""" + + +hookimpl = HookimplMarker("pytest") +hookspec = HookspecMarker("pytest") + + +@final +class ExitCode(enum.IntEnum): + """Encodes the valid exit codes by pytest. + + Currently users and plugins may supply other exit codes as well. + + .. versionadded:: 5.0 + """ + + #: Tests passed. + OK = 0 + #: Tests failed. + TESTS_FAILED = 1 + #: pytest was interrupted. + INTERRUPTED = 2 + #: An internal error got in the way. + INTERNAL_ERROR = 3 + #: pytest was misused. + USAGE_ERROR = 4 + #: pytest couldn't find tests. + NO_TESTS_COLLECTED = 5 + + +class ConftestImportFailure(Exception): + def __init__( + self, + path: py.path.local, + excinfo: Tuple[Type[Exception], Exception, TracebackType], + ) -> None: + super().__init__(path, excinfo) + self.path = path + self.excinfo = excinfo + + def __str__(self) -> str: + return "{}: {} (from {})".format( + self.excinfo[0].__name__, self.excinfo[1], self.path + ) + + +def filter_traceback_for_conftest_import_failure( + entry: _pytest._code.TracebackEntry, +) -> bool: + """Filter tracebacks entries which point to pytest internals or importlib. + + Make a special case for importlib because we use it to import test modules and conftest files + in _pytest.pathlib.import_path. + """ + return filter_traceback(entry) and "importlib" not in str(entry.path).split(os.sep) + + +def main( + args: Optional[Union[List[str], py.path.local]] = None, + plugins: Optional[Sequence[Union[str, _PluggyPlugin]]] = None, +) -> Union[int, ExitCode]: + """Perform an in-process test run. + + :param args: List of command line arguments. + :param plugins: List of plugin objects to be auto-registered during initialization. + + :returns: An exit code. + """ + try: + try: + config = _prepareconfig(args, plugins) + except ConftestImportFailure as e: + exc_info = ExceptionInfo(e.excinfo) + tw = TerminalWriter(sys.stderr) + tw.line(f"ImportError while loading conftest '{e.path}'.", red=True) + exc_info.traceback = exc_info.traceback.filter( + filter_traceback_for_conftest_import_failure + ) + exc_repr = ( + exc_info.getrepr(style="short", chain=False) + if exc_info.traceback + else exc_info.exconly() + ) + formatted_tb = str(exc_repr) + for line in formatted_tb.splitlines(): + tw.line(line.rstrip(), red=True) + return ExitCode.USAGE_ERROR + else: + try: + ret: Union[ExitCode, int] = config.hook.pytest_cmdline_main( + config=config + ) + try: + return ExitCode(ret) + except ValueError: + return ret + finally: + config._ensure_unconfigure() + except UsageError as e: + tw = TerminalWriter(sys.stderr) + for msg in e.args: + tw.line(f"ERROR: {msg}\n", red=True) + return ExitCode.USAGE_ERROR + + +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", + "tmpdir", + "monkeypatch", + "recwarn", + "pastebin", + "nose", + "assertion", + "junitxml", + "doctest", + "cacheprovider", + "freeze_support", + "setuponly", + "setupplan", + "stepwise", + "warnings", + "logging", + "reports", + *(["unraisableexception", "threadexception"] if sys.version_info >= (3, 8) else []), + "faulthandler", +) + +builtin_plugins = set(default_plugins) +builtin_plugins.add("pytester") +builtin_plugins.add("pytester_assertions") + + +def get_config( + args: Optional[List[str]] = None, + plugins: Optional[Sequence[Union[str, _PluggyPlugin]]] = None, +) -> "Config": + # subsequent calls to main will create a fresh instance + pluginmanager = PytestPluginManager() + config = Config( + pluginmanager, + invocation_params=Config.InvocationParams( + args=args or (), plugins=plugins, dir=Path.cwd(), + ), + ) + + if args is not None: + # Handle any "-p no:plugin" args. + pluginmanager.consider_preparse(args, exclude_only=True) + + for spec in default_plugins: + pluginmanager.import_plugin(spec) + + return config + + +def get_plugin_manager() -> "PytestPluginManager": + """Obtain a new instance of the + :py:class:`_pytest.config.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: Optional[Union[py.path.local, List[str]]] = None, + plugins: Optional[Sequence[Union[str, _PluggyPlugin]]] = None, +) -> "Config": + if args is None: + args = sys.argv[1:] + elif isinstance(args, py.path.local): + args = [str(args)] + elif not isinstance(args, list): + msg = "`args` parameter expected to be a list of strings, got: {!r} (type: {})" + raise TypeError(msg.format(args, type(args))) + + config = get_config(args, plugins) + pluginmanager = config.pluginmanager + try: + if plugins: + for plugin in plugins: + if isinstance(plugin, str): + pluginmanager.consider_pluginarg(plugin) + else: + pluginmanager.register(plugin) + config = pluginmanager.hook.pytest_cmdline_parse( + pluginmanager=pluginmanager, args=args + ) + return config + except BaseException: + config._ensure_unconfigure() + raise + + +@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: + import _pytest.assertion + + super().__init__("pytest") + # The objects are module objects, only used generically. + self._conftest_plugins: Set[types.ModuleType] = set() + + # State related to local conftest plugins. + self._dirpath2confmods: Dict[py.path.local, List[types.ModuleType]] = {} + self._conftestpath2mod: Dict[Path, types.ModuleType] = {} + self._confcutdir: Optional[py.path.local] = None + self._noconftest = False + self._duplicatepaths: Set[py.path.local] = set() + + # 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 = _pytest.assertion.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): + # pytest hooks are always prefixed with "pytest_", + # so we avoid accessing possibly non-readable attributes + # (see issue #1073). + if not name.startswith("pytest_"): + return + # Ignore names which can not be hooks. + if name == "pytest_plugins": + return + + method = getattr(plugin, name) + opts = super().parse_hookimpl_opts(plugin, name) + + # Consider only actual functions for hooks (#3775). + if not inspect.isroutine(method): + return + + # Collect unmarked hooks as long as they have the `pytest_' prefix. + if opts is None and name.startswith("pytest_"): + opts = {} + if opts is not None: + # TODO: DeprecationWarning, people should use hookimpl + # https://github.com/pytest-dev/pytest/issues/4562 + known_marks = {m.name for m in getattr(method, "pytestmark", [])} + + for name in ("tryfirst", "trylast", "optionalhook", "hookwrapper"): + opts.setdefault(name, hasattr(method, name) or name in known_marks) + return opts + + def parse_hookspec_opts(self, module_or_class, name: str): + opts = super().parse_hookspec_opts(module_or_class, name) + if opts is None: + method = getattr(module_or_class, name) + + if name.startswith("pytest_"): + # todo: deprecate hookspec hacks + # https://github.com/pytest-dev/pytest/issues/4562 + known_marks = {m.name for m in getattr(method, "pytestmark", [])} + opts = { + "firstresult": hasattr(method, "firstresult") + or "firstresult" in known_marks, + "historic": hasattr(method, "historic") + or "historic" in known_marks, + } + return opts + + def register( + self, plugin: _PluggyPlugin, name: Optional[str] = None + ) -> Optional[str]: + 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 + ret: Optional[str] = super().register(plugin, name) + if ret: + self.hook.pytest_plugin_registered.call_historic( + kwargs=dict(plugin=plugin, manager=self) + ) + + if isinstance(plugin, types.ModuleType): + self.consider_module(plugin) + return ret + + def getplugin(self, name: str): + # Support deprecated naming because plugins (xdist e.g.) use it. + plugin: Optional[_PluggyPlugin] = 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.", + ) + 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.", + ) + self._configured = True + + # + # Internal API for local conftest plugin handling. + # + def _set_initial_conftests(self, namespace: argparse.Namespace) -> 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. + """ + current = py.path.local() + self._confcutdir = ( + current.join(namespace.confcutdir, abs=True) + if namespace.confcutdir + else None + ) + self._noconftest = namespace.noconftest + self._using_pyargs = namespace.pyargs + testpaths = namespace.file_or_dir + foundanchor = False + for testpath in testpaths: + path = str(testpath) + # remove node-id syntax + i = path.find("::") + if i != -1: + path = path[:i] + anchor = current.join(path, abs=1) + if anchor.exists(): # we found some file object + self._try_load_conftest(anchor, namespace.importmode) + foundanchor = True + if not foundanchor: + self._try_load_conftest(current, namespace.importmode) + + def _try_load_conftest( + self, anchor: py.path.local, importmode: Union[str, ImportMode] + ) -> None: + self._getconftestmodules(anchor, importmode) + # let's also consider test* subdirs + if anchor.check(dir=1): + for x in anchor.listdir("test*"): + if x.check(dir=1): + self._getconftestmodules(x, importmode) + + @lru_cache(maxsize=128) + def _getconftestmodules( + self, path: py.path.local, importmode: Union[str, ImportMode], + ) -> List[types.ModuleType]: + if self._noconftest: + return [] + + if path.isfile(): + directory = path.dirpath() + else: + directory = path + + # XXX these days we may rather want to use config.rootpath + # and allow users to opt into looking into the rootdir parent + # directories instead of requiring to specify confcutdir. + clist = [] + for parent in directory.parts(): + if self._confcutdir and self._confcutdir.relto(parent): + continue + conftestpath = parent.join("conftest.py") + if conftestpath.isfile(): + mod = self._importconftest(conftestpath, importmode) + clist.append(mod) + self._dirpath2confmods[directory] = clist + return clist + + def _rget_with_confmod( + self, name: str, path: py.path.local, importmode: Union[str, ImportMode], + ) -> Tuple[types.ModuleType, Any]: + modules = self._getconftestmodules(path, importmode) + for mod in reversed(modules): + try: + return mod, getattr(mod, name) + except AttributeError: + continue + raise KeyError(name) + + def _importconftest( + self, conftestpath: py.path.local, importmode: Union[str, ImportMode], + ) -> types.ModuleType: + # Use a resolved Path object as key to avoid loading the same conftest + # twice with build systems that create build directories containing + # symlinks to actual files. + # Using Path().resolve() is better than py.path.realpath because + # it resolves to the correct path/drive in case-insensitive file systems (#5792) + key = Path(str(conftestpath)).resolve() + + with contextlib.suppress(KeyError): + return self._conftestpath2mod[key] + + pkgpath = conftestpath.pypkgpath() + if pkgpath is None: + _ensure_removed_sysmodule(conftestpath.purebasename) + + try: + mod = import_path(conftestpath, mode=importmode) + except Exception as e: + assert e.__traceback__ is not None + exc_info = (type(e), e, e.__traceback__) + raise ConftestImportFailure(conftestpath, exc_info) from e + + self._check_non_top_pytest_plugins(mod, conftestpath) + + self._conftest_plugins.add(mod) + self._conftestpath2mod[key] = mod + dirpath = conftestpath.dirpath() + if dirpath in self._dirpath2confmods: + for path, mods in self._dirpath2confmods.items(): + if path and path.relto(dirpath) or path == dirpath: + assert mod not in mods + mods.append(mod) + self.trace(f"loading conftestmodule {mod!r}") + self.consider_conftest(mod) + return mod + + def _check_non_top_pytest_plugins( + self, mod: types.ModuleType, conftestpath: py.path.local, + ) -> 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: + 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 + if exclude_only and not parg.startswith("no:"): + continue + self.consider_pluginarg(parg) + + def consider_pluginarg(self, arg: str) -> None: + if arg.startswith("no:"): + name = arg[3:] + if name in essential_plugins: + raise UsageError("plugin %s cannot be disabled" % name) + + # 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. None indicates that it has been blocked. + # There is no interface with pluggy for this. + if self._name2plugin.get(name, -1) is None: + del self._name2plugin[name] + if not name.startswith("pytest_"): + if self._name2plugin.get("pytest_" + name, -1) is None: + del self._name2plugin["pytest_" + name] + self.import_plugin(arg, consider_entry_points=True) + + def consider_conftest(self, conftestmodule: types.ModuleType) -> None: + self.register(conftestmodule, name=conftestmodule.__file__) + + def consider_env(self) -> None: + self._import_plugin_specs(os.environ.get("PYTEST_PLUGINS")) + + def consider_module(self, mod: types.ModuleType) -> None: + self._import_plugin_specs(getattr(mod, "pytest_plugins", [])) + + def _import_plugin_specs( + self, spec: Union[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), ( + "module name as text required, got %r" % modname + ) + 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( + 'Error importing plugin "{}": {}'.format(modname, str(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: Union[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( + "Plugins may be specified as a sequence or a ','-separated string of plugin names. Got: %r" + % specs + ) + + +def _ensure_removed_sysmodule(modname: str) -> None: + try: + del sys.modules[modname] + except KeyError: + pass + + +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 + if module_name != "setup": + 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) + + +def _args_converter(args: Iterable[str]) -> Tuple[str, ...]: + return tuple(args) + + +@final +class Config: + """Access to configuration values, pluginmanager and plugin hooks. + + :param PytestPluginManager pluginmanager: + + :param InvocationParams invocation_params: + Object containing parameters regarding the :func:`pytest.main` + invocation. + """ + + @final + @attr.s(frozen=True) + class InvocationParams: + """Holds parameters passed during :func:`pytest.main`. + + The object attributes are read-only. + + .. versionadded:: 5.1 + + .. note:: + + Note that the environment variable ``PYTEST_ADDOPTS`` and the ``addopts`` + ini option are handled by pytest, not being included in the ``args`` attribute. + + Plugins accessing ``InvocationParams`` must be aware of that. + """ + + args = attr.ib(type=Tuple[str, ...], converter=_args_converter) + """The command-line arguments as passed to :func:`pytest.main`. + + :type: Tuple[str, ...] + """ + plugins = attr.ib(type=Optional[Sequence[Union[str, _PluggyPlugin]]]) + """Extra plugins, might be `None`. + + :type: Optional[Sequence[Union[str, plugin]]] + """ + dir = attr.ib(type=Path) + """The directory from which :func:`pytest.main` was invoked. + + :type: pathlib.Path + """ + + def __init__( + self, + pluginmanager: PytestPluginManager, + *, + invocation_params: Optional[InvocationParams] = None, + ) -> None: + from .argparsing import Parser, FILE_OR_DIR + + if invocation_params is None: + invocation_params = self.InvocationParams( + args=(), plugins=None, dir=Path.cwd() + ) + + self.option = argparse.Namespace() + """Access to command line option as attributes. + + :type: argparse.Namespace + """ + + self.invocation_params = invocation_params + """The parameters with which pytest was invoked. + + :type: InvocationParams + """ + + _a = FILE_OR_DIR + self._parser = Parser( + usage=f"%(prog)s [options] [{_a}] [{_a}] [...]", + processopt=self._processopt, + ) + self.pluginmanager = pluginmanager + """The plugin manager handles plugin registration and hook invocation. + + :type: PytestPluginManager + """ + + self.trace = self.pluginmanager.trace.root.get("config") + self.hook = self.pluginmanager.hook + self._inicache: Dict[str, Any] = {} + self._override_ini: Sequence[str] = () + self._opt2dest: Dict[str, str] = {} + self._cleanup: List[Callable[[], None]] = [] + # A place where plugins can store information on the config for their + # own use. Currently only intended for internal plugins. + self._store = Store() + self.pluginmanager.register(self, "pytestconfig") + self._configured = False + self.hook.pytest_addoption.call_historic( + kwargs=dict(parser=self._parser, pluginmanager=self.pluginmanager) + ) + + if TYPE_CHECKING: + from _pytest.cacheprovider import Cache + + self.cache: Optional[Cache] = None + + @property + def invocation_dir(self) -> py.path.local: + """The directory from which pytest was invoked. + + Prefer to use :attr:`invocation_params.dir `, + which is a :class:`pathlib.Path`. + + :type: py.path.local + """ + return py.path.local(str(self.invocation_params.dir)) + + @property + def rootpath(self) -> Path: + """The path to the :ref:`rootdir `. + + :type: pathlib.Path + + .. versionadded:: 6.1 + """ + return self._rootpath + + @property + def rootdir(self) -> py.path.local: + """The path to the :ref:`rootdir `. + + Prefer to use :attr:`rootpath`, which is a :class:`pathlib.Path`. + + :type: py.path.local + """ + return py.path.local(str(self.rootpath)) + + @property + def inipath(self) -> Optional[Path]: + """The path to the :ref:`configfile `. + + :type: Optional[pathlib.Path] + + .. versionadded:: 6.1 + """ + return self._inipath + + @property + def inifile(self) -> Optional[py.path.local]: + """The path to the :ref:`configfile `. + + Prefer to use :attr:`inipath`, which is a :class:`pathlib.Path`. + + :type: Optional[py.path.local] + """ + return py.path.local(str(self.inipath)) if self.inipath else None + + def add_cleanup(self, func: Callable[[], None]) -> None: + """Add a function to be called when the config object gets out of + use (usually coninciding with pytest_unconfigure).""" + self._cleanup.append(func) + + def _do_configure(self) -> None: + assert not self._configured + self._configured = True + with warnings.catch_warnings(): + warnings.simplefilter("default") + self.hook.pytest_configure.call_historic(kwargs=dict(config=self)) + + def _ensure_unconfigure(self) -> None: + if self._configured: + self._configured = False + self.hook.pytest_unconfigure(config=self) + self.hook.pytest_configure._call_history = [] + while self._cleanup: + fin = self._cleanup.pop() + fin() + + def get_terminal_writer(self) -> TerminalWriter: + terminalreporter: TerminalReporter = self.pluginmanager.get_plugin( + "terminalreporter" + ) + return terminalreporter._tw + + def pytest_cmdline_parse( + self, pluginmanager: PytestPluginManager, args: List[str] + ) -> "Config": + try: + self.parse(args) + except UsageError: + + # Handle --version and --help here in a minimal fashion. + # This gets done via helpconfig normally, but its + # pytest_cmdline_main is not called in case of errors. + if getattr(self.option, "version", False) or "--version" in args: + from _pytest.helpconfig import showversion + + showversion(self) + elif ( + getattr(self.option, "help", False) or "--help" in args or "-h" in args + ): + self._parser._getparser().print_help() + sys.stdout.write( + "\nNOTE: displaying only minimal help due to UsageError.\n\n" + ) + + raise + + return self + + def notify_exception( + self, + excinfo: ExceptionInfo[BaseException], + option: Optional[argparse.Namespace] = 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("INTERNALERROR> %s\n" % line) + 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: + fullpath = self.rootpath / nodeid + nodeid = bestrelpath(self.invocation_params.dir, fullpath) + return nodeid + + @classmethod + def fromdictargs(cls, option_dict, args) -> "Config": + """Constructor usable for subprocesses.""" + config = get_config(args) + config.option.__dict__.update(option_dict) + config.parse(args, addopts=False) + for x in config.option.plugins: + config.pluginmanager.consider_pluginarg(x) + return config + + def _processopt(self, opt: "Argument") -> None: + for name in opt._short_opts + opt._long_opts: + self._opt2dest[name] = opt.dest + + if hasattr(opt, "default"): + if not hasattr(self.option, opt.dest): + setattr(self.option, opt.dest, opt.default) + + @hookimpl(trylast=True) + def pytest_load_initial_conftests(self, early_config: "Config") -> None: + self.pluginmanager._set_initial_conftests(early_config.known_args_namespace) + + def _initini(self, args: Sequence[str]) -> None: + ns, unknown_args = self._parser.parse_known_and_unknown_args( + args, namespace=copy.copy(self.option) + ) + rootpath, inipath, inicfg = determine_setup( + ns.inifilename, + ns.file_or_dir + unknown_args, + rootdir_cmd_arg=ns.rootdir or None, + config=self, + ) + self._rootpath = rootpath + self._inipath = inipath + self.inicfg = inicfg + self._parser.extra_info["rootdir"] = str(self.rootpath) + self._parser.extra_info["inifile"] = str(self.inipath) + self._parser.addini("addopts", "extra command line options", "args") + self._parser.addini("minversion", "minimally required pytest version") + self._parser.addini( + "required_plugins", + "plugins that must be present for pytest to run", + type="args", + default=[], + ) + self._override_ini = ns.override_ini or () + + def _consider_importhook(self, args: Sequence[str]) -> None: + """Install the PEP 302 import hook if using assertion rewriting. + + Needs to parse the --assert= option from the commandline + and find all the installed plugins to mark them for rewriting + by the importhook. + """ + ns, unknown_args = self._parser.parse_known_and_unknown_args(args) + mode = getattr(ns, "assertmode", "plain") + if mode == "rewrite": + import _pytest.assertion + + try: + hook = _pytest.assertion.install_importhook(self) + except SystemError: + mode = "plain" + else: + self._mark_plugins_for_rewrite(hook) + self._warn_about_missing_assertion(mode) + + def _mark_plugins_for_rewrite(self, hook) -> 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 os.environ.get("PYTEST_DISABLE_PLUGIN_AUTOLOAD"): + # We don't autoload from setuptools 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 _validate_args(self, args: List[str], via: str) -> List[str]: + """Validate known args.""" + self._parser._config_source_hint = via # type: ignore + try: + self._parser.parse_known_and_unknown_args( + args, namespace=copy.copy(self.option) + ) + finally: + del self._parser._config_source_hint # type: ignore + + return args + + def _preparse(self, args: List[str], addopts: bool = True) -> None: + if addopts: + env_addopts = os.environ.get("PYTEST_ADDOPTS", "") + if len(env_addopts): + args[:] = ( + self._validate_args(shlex.split(env_addopts), "via PYTEST_ADDOPTS") + + args + ) + self._initini(args) + if addopts: + args[:] = ( + self._validate_args(self.getini("addopts"), "via addopts config") + args + ) + + self.known_args_namespace = self._parser.parse_known_args( + args, namespace=copy.copy(self.option) + ) + self._checkversion() + self._consider_importhook(args) + self.pluginmanager.consider_preparse(args, exclude_only=False) + if not os.environ.get("PYTEST_DISABLE_PLUGIN_AUTOLOAD"): + # Don't autoload from setuptools entry point. Only explicitly specified + # plugins are going to be loaded. + self.pluginmanager.load_setuptools_entrypoints("pytest11") + self.pluginmanager.consider_env() + + self.known_args_namespace = self._parser.parse_known_args( + args, namespace=copy.copy(self.known_args_namespace) + ) + + self._validate_plugins() + self._warn_about_skipped_plugins() + + if self.known_args_namespace.strict: + self.issue_config_time_warning( + _pytest.deprecated.STRICT_OPTION, stacklevel=2 + ) + + if self.known_args_namespace.confcutdir is None and self.inipath is not None: + confcutdir = str(self.inipath.parent) + 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 is pass and print a warning at the end + self.issue_config_time_warning( + PytestConfigWarning(f"could not load initial conftests: {e.path}"), + stacklevel=2, + ) + else: + raise + + @hookimpl(hookwrapper=True) + def pytest_collection(self) -> Generator[None, None, None]: + """Validate invalid ini keys after collection is done so we take in account + options added by late-loading conftest files.""" + yield + self._validate_config_options() + + def _checkversion(self) -> None: + import pytest + + minver = self.inicfg.get("minversion", None) + if minver: + # Imported lazily to improve start-up time. + from packaging.version import Version + + if not isinstance(minver, str): + raise pytest.UsageError( + "%s: 'minversion' must be a single value" % self.inipath + ) + + if Version(minver) > Version(pytest.__version__): + raise pytest.UsageError( + "%s: 'minversion' requires pytest-%s, actual pytest-%s'" + % (self.inipath, minver, 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.version import Version + from packaging.requirements import InvalidRequirement, Requirement + + 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: + spec = Requirement(required_plugin) + except InvalidRequirement: + missing_plugins.append(required_plugin) + continue + + if spec.name not in plugin_dist_info: + missing_plugins.append(required_plugin) + elif Version(plugin_dist_info[spec.name]) not in spec.specifier: + missing_plugins.append(required_plugin) + + if missing_plugins: + raise UsageError( + "Missing required plugins: {}".format(", ".join(missing_plugins)), + ) + + def _warn_or_fail_if_strict(self, message: str) -> None: + if self.known_args_namespace.strict_config: + raise UsageError(message) + + self.issue_config_time_warning(PytestConfigWarning(message), stacklevel=3) + + def _get_unknown_ini_keys(self) -> List[str]: + parser_inicfg = self._parser._inidict + return [name for name in self.inicfg if name not in parser_inicfg] + + def parse(self, args: List[str], addopts: bool = True) -> None: + # Parse given cmdline arguments into this config object. + assert not hasattr( + self, "args" + ), "can only parse cmdline args at most once per Config object" + self.hook.pytest_addhooks.call_historic( + kwargs=dict(pluginmanager=self.pluginmanager) + ) + self._preparse(args, addopts=addopts) + # XXX deprecated hook: + self.hook.pytest_cmdline_preparse(config=self, args=args) + self._parser.after_preparse = True # type: ignore + try: + args = self._parser.parse_setoption( + args, self.option, namespace=self.option + ) + if not args: + if self.invocation_params.dir == self.rootpath: + args = self.getini("testpaths") + if not args: + args = [str(self.invocation_params.dir)] + self.args = args + except PrintHelp: + pass + + def issue_config_time_warning(self, warning: Warning, stacklevel: int) -> None: + """Issue and handle a warning during the "configure" stage. + + During ``pytest_configure`` we can't capture warnings using the ``catch_warnings_for_item`` + function because it is not possible to have hookwrappers 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_captured.call_historic( + kwargs=dict( + warning_message=records[0], + when="config", + item=None, + location=location, + ) + ) + self.hook.pytest_warning_recorded.call_historic( + kwargs=dict( + warning_message=records[0], + when="config", + nodeid="", + location=location, + ) + ) + + def addinivalue_line(self, name: str, line: str) -> None: + """Add a line to an ini-file option. The option must have been + declared but might not yet be set in which case the line becomes + the first line in its value.""" + x = self.getini(name) + assert isinstance(x, list) + x.append(line) # modifies the cached list inline + + def getini(self, name: str): + """Return configuration value from an :ref:`ini file `. + + If the specified name hasn't been registered through a prior + :py:func:`parser.addini <_pytest.config.argparsing.Parser.addini>` + call (usually from a plugin), a ValueError is raised. + """ + try: + return self._inicache[name] + except KeyError: + self._inicache[name] = val = self._getini(name) + return val + + def _getini(self, name: str): + try: + description, type, default = self._parser._inidict[name] + except KeyError as e: + raise ValueError(f"unknown configuration value: {name!r}") from e + override_value = self._get_override_ini_value(name) + if override_value is None: + try: + value = self.inicfg[name] + except KeyError: + if default is not None: + return default + if type is None: + return "" + return [] + else: + value = override_value + # Coerce the values based on types. + # + # Note: some coercions are only required if we are reading from .ini files, because + # the file format doesn't contain type information, but when reading from toml we will + # get either str or list of str values (see _parse_ini_config_from_pyproject_toml). + # For example: + # + # ini: + # a_line_list = "tests acceptance" + # in this case, we need to split the string to obtain a list of strings. + # + # toml: + # a_line_list = ["tests", "acceptance"] + # in this case, we already have a list ready to use. + # + if type == "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 [py.path.local(str(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()) + else: + assert type in [None, "string"] + return value + + def _getconftest_pathlist( + self, name: str, path: py.path.local + ) -> Optional[List[py.path.local]]: + try: + mod, relroots = self.pluginmanager._rget_with_confmod( + name, path, self.getoption("importmode") + ) + except KeyError: + return None + modpath = py.path.local(mod.__file__).dirpath() + values: List[py.path.local] = [] + for relroot in relroots: + if not isinstance(relroot, py.path.local): + relroot = relroot.replace("/", os.sep) + relroot = modpath.join(relroot, abs=True) + values.append(relroot) + return values + + def _get_override_ini_value(self, name: str) -> Optional[str]: + value = None + # override_ini is a list of "ini=value" options. + # Always use the last item if multiple values are set for same ini-name, + # e.g. -o foo=bar1 -o foo=bar2 will set foo to bar2. + for ini_config in self._override_ini: + try: + key, user_ini_value = ini_config.split("=", 1) + except ValueError as e: + raise UsageError( + "-o/--override-ini expects option=value style (got: {!r}).".format( + ini_config + ) + ) from e + else: + if key == name: + value = user_ini_value + return value + + def getoption(self, name: str, default=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: Default value if no option of that name exists. + :param skip: If True, raise pytest.skip if option does not exists + or has a None value. + """ + 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) + + 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: Optional[TextIO] = 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[str, str, Type[Warning], str, int]: + """Parse a warnings filter string. + + This is copied from warnings._setoption, but does not apply the filter, + only parses it, and makes the escaping optional. + """ + parts = arg.split(":") + if len(parts) > 5: + raise warnings._OptionError(f"too many fields (max 5): {arg!r}") + while len(parts) < 5: + parts.append("") + action_, message, category_, module, lineno_ = [s.strip() for s in parts] + action: str = warnings._getaction(action_) # type: ignore[attr-defined] + category: Type[Warning] = warnings._getcategory(category_) # type: ignore[attr-defined] + 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 + except (ValueError, OverflowError) as e: + raise warnings._OptionError(f"invalid lineno {lineno_!r}") from e + else: + lineno = 0 + return action, message, category, module, lineno + + +def apply_warning_filters( + config_filters: Iterable[str], cmdline_filters: Iterable[str] +) -> None: + """Applies pytest-configured filters to the warnings module""" + # Filters should have this precedence: cmdline options, config. + # Filters should be applied in the inverse order of precedence. + for arg in config_filters: + warnings.filterwarnings(*parse_warning_filter(arg, escape=False)) + + for arg in cmdline_filters: + warnings.filterwarnings(*parse_warning_filter(arg, escape=True)) diff --git a/venv/Lib/site-packages/_pytest/config/__pycache__/__init__.cpython-39.pyc b/venv/Lib/site-packages/_pytest/config/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..91bf00314bd0483128933289996cb27af8b2c251 GIT binary patch literal 44707 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/_pytest/config/__pycache__/exceptions.cpython-39.pyc b/venv/Lib/site-packages/_pytest/config/__pycache__/exceptions.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cac00f769b031667c7bababd4d88babc3dfa1fce GIT binary patch literal 666 zcmb7Cy>1jS5VrRxxjQZhPKJCj}8*xrMrP4X~# zrEOCo-XIm@-2+n4Fp|eJ zMIy19Oz?KvO+I?Df@9~5yMEbkOlfAX0{Fn%c*cyw(8g3ED5K{m_gOdMT#E)c?T38 zKk)t9w6$b5CNUK}TVo~*hWSf{`x=;wESY7rCk#cnRoeh&Tj9LYD<-sL8&NB{CC|l~ z{}M7-@sGG?s3K7VAlmyUv%Gk|rD0x~#){w~-fn#Sn~WZKpZu??-RSM`Jcscg_#IT! IG@<+1FDTWb=l}o! literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/_pytest/config/__pycache__/findpaths.cpython-39.pyc b/venv/Lib/site-packages/_pytest/config/__pycache__/findpaths.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..04fb0809714d27d1a3fe7dc99299ecc6a90ba97c GIT binary patch literal 5818 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 value to display in case + there's an error processing the command line arguments. + """ + + prog: Optional[str] = None + + def __init__( + self, + usage: Optional[str] = None, + processopt: Optional[Callable[["Argument"], None]] = None, + ) -> None: + self._anonymous = OptionGroup("custom options", parser=self) + self._groups: List[OptionGroup] = [] + self._processopt = processopt + self._usage = usage + self._inidict: Dict[str, Tuple[str, Optional[str], Any]] = {} + self._ininames: List[str] = [] + self.extra_info: Dict[str, Any] = {} + + def processoption(self, option: "Argument") -> None: + if self._processopt: + if option.dest: + self._processopt(option) + + def getgroup( + self, name: str, description: str = "", after: Optional[str] = None + ) -> "OptionGroup": + """Get (or create) a named option Group. + + :name: Name of the option group. + :description: Long description for --help output. + :after: Name of another group, used for ordering --help output. + + The returned group object has an ``addoption`` method with the same + signature as :py:func:`parser.addoption + <_pytest.config.argparsing.Parser.addoption>` but will be shown in the + respective group in the output of ``pytest. --help``. + """ + for group in self._groups: + if group.name == name: + return group + group = OptionGroup(name, description, parser=self) + i = 0 + for i, grp in enumerate(self._groups): + if grp.name == after: + break + self._groups.insert(i + 1, group) + return group + + def addoption(self, *opts: str, **attrs: Any) -> None: + """Register a command line option. + + :opts: Option names, can be short or long options. + :attrs: Same attributes which the ``add_argument()`` function of the + `argparse library `_ + 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[Union[str, py.path.local]], + namespace: Optional[argparse.Namespace] = None, + ) -> argparse.Namespace: + from _pytest._argcomplete import try_argcomplete + + self.optparser = self._getparser() + try_argcomplete(self.optparser) + strargs = [str(x) if isinstance(x, py.path.local) else x for x in args] + return self.optparser.parse_args(strargs, namespace=namespace) + + def _getparser(self) -> "MyOptionParser": + from _pytest._argcomplete import filescompleter + + optparser = MyOptionParser(self, self.extra_info, prog=self.prog) + groups = self._groups + [self._anonymous] + for group in groups: + if group.options: + desc = group.description or group.name + arggroup = optparser.add_argument_group(desc) + for option in group.options: + n = option.names() + a = option.attrs() + arggroup.add_argument(*n, **a) + file_or_dir_arg = optparser.add_argument(FILE_OR_DIR, nargs="*") + # bash like autocompletion for dirs (appending '/') + # Type ignored because typeshed doesn't know about argcomplete. + file_or_dir_arg.completer = filescompleter # type: ignore + return optparser + + def parse_setoption( + self, + args: Sequence[Union[str, py.path.local]], + option: argparse.Namespace, + namespace: Optional[argparse.Namespace] = None, + ) -> List[str]: + parsedoption = self.parse(args, namespace=namespace) + for name, value in parsedoption.__dict__.items(): + setattr(option, name, value) + return cast(List[str], getattr(parsedoption, FILE_OR_DIR)) + + def parse_known_args( + self, + args: Sequence[Union[str, py.path.local]], + namespace: Optional[argparse.Namespace] = None, + ) -> argparse.Namespace: + """Parse and return a namespace object with known arguments at this point.""" + return self.parse_known_and_unknown_args(args, namespace=namespace)[0] + + def parse_known_and_unknown_args( + self, + args: Sequence[Union[str, py.path.local]], + namespace: Optional[argparse.Namespace] = None, + ) -> Tuple[argparse.Namespace, List[str]]: + """Parse and return a namespace object with known arguments, and + the remaining arguments unknown at this point.""" + optparser = self._getparser() + strargs = [str(x) if isinstance(x, py.path.local) else x for x in args] + return optparser.parse_known_args(strargs, namespace=namespace) + + def addini( + self, + name: str, + help: str, + type: Optional[ + "Literal['string', 'pathlist', 'args', 'linelist', 'bool']" + ] = None, + default=None, + ) -> None: + """Register an ini-file option. + + :name: Name of the ini-variable. + :type: Type of the variable, can be ``string``, ``pathlist``, ``args``, + ``linelist`` or ``bool``. Defaults to ``string`` if ``None`` or + not passed. + :default: Default value if no ini-file option exists but is queried. + + The value of ini-variables can be retrieved via a call to + :py:func:`config.getini(name) <_pytest.config.Config.getini>`. + """ + assert type in (None, "string", "pathlist", "args", "linelist", "bool") + self._inidict[name] = (help, type, default) + self._ininames.append(name) + + +class ArgumentError(Exception): + """Raised if an Argument instance is created with invalid or + inconsistent arguments.""" + + def __init__(self, msg: str, option: Union["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 + """ + + _typ_map = {"int": int, "string": str, "float": float, "complex": complex} + + def __init__(self, *names: str, **attrs: Any) -> None: + """Store parms in private vars for use in add_argument.""" + self._attrs = attrs + self._short_opts: List[str] = [] + self._long_opts: List[str] = [] + if "%default" in (attrs.get("help") or ""): + warnings.warn( + 'pytest now uses argparse. "%default" should be' + ' changed to "%(default)s" ', + DeprecationWarning, + stacklevel=3, + ) + try: + typ = attrs["type"] + except KeyError: + pass + else: + # This might raise a keyerror as well, don't want to catch that. + if isinstance(typ, str): + if typ == "choice": + warnings.warn( + "`type` argument to addoption() is the string %r." + " For choices this is optional and can be omitted, " + " but when supplied should be a type (for example `str` or `int`)." + " (options: %s)" % (typ, names), + DeprecationWarning, + stacklevel=4, + ) + # argparse expects a type here take it from + # the type of the first element + attrs["type"] = type(attrs["choices"][0]) + else: + warnings.warn( + "`type` argument to addoption() is the string %r, " + " but when supplied should be a type (for example `str` or `int`)." + " (options: %s)" % (typ, names), + DeprecationWarning, + stacklevel=4, + ) + attrs["type"] = Argument._typ_map[typ] + # Used in test_parseopt -> test_parse_defaultgetter. + self.type = attrs["type"] + else: + self.type = typ + try: + # Attribute existence is tested in Config._processopt. + self.default = attrs["default"] + except KeyError: + pass + self._set_opt_strings(names) + dest: Optional[str] = attrs.get("dest") + if dest: + self.dest = dest + elif self._long_opts: + self.dest = self._long_opts[0][2:].replace("-", "_") + else: + try: + self.dest = self._short_opts[0][1:] + except IndexError as e: + self.dest = "???" # Needed for the error repr. + raise ArgumentError("need a long or short option", self) from e + + def names(self) -> List[str]: + return self._short_opts + self._long_opts + + def attrs(self) -> Mapping[str, Any]: + # Update any attributes set by processopt. + attrs = "default dest help".split() + attrs.append(self.dest) + for attr in attrs: + try: + self._attrs[attr] = getattr(self, attr) + except AttributeError: + pass + if self._attrs.get("help"): + a = self._attrs["help"] + a = a.replace("%default", "%(default)s") + # a = a.replace('%prog', '%(prog)s') + self._attrs["help"] = a + 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( + "invalid option string %r: " + "must be at least two characters long" % opt, + self, + ) + elif len(opt) == 2: + if not (opt[0] == "-" and opt[1] != "-"): + raise ArgumentError( + "invalid short option string %r: " + "must be of the form -x, (x any non-dash char)" % opt, + self, + ) + self._short_opts.append(opt) + else: + if not (opt[0:2] == "--" and opt[2] != "-"): + raise ArgumentError( + "invalid long option string %r: " + "must start with --, followed by non-dash" % opt, + 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: + def __init__( + self, name: str, description: str = "", parser: Optional[Parser] = None + ) -> None: + self.name = name + self.description = description + self.options: List[Argument] = [] + self.parser = parser + + def addoption(self, *optnames: 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. + """ + conflict = set(optnames).intersection( + name for opt in self.options for name in opt.names() + ) + if conflict: + raise ValueError("option names %s already added" % conflict) + option = Argument(*optnames, **attrs) + self._addoption_instance(option, shortupper=False) + + def _addoption(self, *optnames: str, **attrs: Any) -> None: + option = Argument(*optnames, **attrs) + self._addoption_instance(option, shortupper=True) + + def _addoption_instance(self, option: "Argument", shortupper: bool = False) -> None: + if not shortupper: + for opt in option._short_opts: + if opt[0] == "-" and opt[1].islower(): + raise ValueError("lowercase shortoptions reserved") + if self.parser: + self.parser.processoption(option) + self.options.append(option) + + +class MyOptionParser(argparse.ArgumentParser): + def __init__( + self, + parser: Parser, + extra_info: Optional[Dict[str, Any]] = None, + prog: Optional[str] = None, + ) -> None: + self._parser = parser + argparse.ArgumentParser.__init__( + self, + prog=prog, + usage=parser._usage, + add_help=False, + formatter_class=DropShorterLongHelpFormatter, + allow_abbrev=False, + ) + # extra_info is a dict of (param -> value) to display if there's + # an usage error to provide more contextual information to the user. + self.extra_info = extra_info if extra_info else {} + + def error(self, message: str) -> "NoReturn": + """Transform argparse error message into UsageError.""" + msg = f"{self.prog}: error: {message}" + + if hasattr(self._parser, "_config_source_hint"): + # Type ignored because the attribute is set dynamically. + msg = f"{msg} ({self._parser._config_source_hint})" # type: ignore + + raise UsageError(self.format_usage() + msg) + + # Type ignored because typeshed has a very complex type in the superclass. + def parse_args( # type: ignore + self, + args: Optional[Sequence[str]] = None, + namespace: Optional[argparse.Namespace] = None, + ) -> argparse.Namespace: + """Allow splitting of positional arguments.""" + parsed, unrecognized = self.parse_known_args(args, namespace) + if unrecognized: + for arg in unrecognized: + if arg and arg[0] == "-": + lines = ["unrecognized arguments: %s" % (" ".join(unrecognized))] + for k, v in sorted(self.extra_info.items()): + lines.append(f" {k}: {v}") + self.error("\n".join(lines)) + getattr(parsed, FILE_OR_DIR).extend(unrecognized) + return parsed + + if sys.version_info[:2] < (3, 9): # pragma: no cover + # Backport of https://github.com/python/cpython/pull/14316 so we can + # disable long --argument abbreviations without breaking short flags. + def _parse_optional( + self, arg_string: str + ) -> Optional[Tuple[Optional[argparse.Action], str, Optional[str]]]: + if not arg_string: + return None + if not arg_string[0] in self.prefix_chars: + return None + if arg_string in self._option_string_actions: + action = self._option_string_actions[arg_string] + return action, arg_string, None + if len(arg_string) == 1: + return None + if "=" in arg_string: + option_string, explicit_arg = arg_string.split("=", 1) + if option_string in self._option_string_actions: + action = self._option_string_actions[option_string] + return action, option_string, explicit_arg + if self.allow_abbrev or not arg_string.startswith("--"): + option_tuples = self._get_option_tuples(arg_string) + if len(option_tuples) > 1: + msg = gettext( + "ambiguous option: %(option)s could match %(matches)s" + ) + options = ", ".join(option for _, option, _ in option_tuples) + self.error(msg % {"option": arg_string, "matches": options}) + elif len(option_tuples) == 1: + (option_tuple,) = option_tuples + return option_tuple + if self._negative_number_matcher.match(arg_string): + if not self._has_negative_number_optionals: + return None + if " " in arg_string: + return None + return None, arg_string, None + + +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 = argparse.HelpFormatter._format_action_invocation(self, action) + if orgstr and orgstr[0] != "-": # only optional arguments + return orgstr + res: Optional[str] = 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( + 'long optional argument without "--": [%s]' % (option), option + ) + xxoption = option[2:] + shortened = xxoption.replace("-", "") + if shortened not in short_long or len(short_long[shortened]) < len( + xxoption + ): + short_long[shortened] = xxoption + # now short_long has been filled out to the longest with dashes + # **and** we keep the right option ordering from add_argument + for option in options: + if len(option) == 2 or option[2] == " ": + return_list.append(option) + if option[2:] == short_long.get(option.replace("-", "")): + return_list.append(option.replace(" ", "=", 1)) + formatted_action_invocation = ", ".join(return_list) + action._formatted_action_invocation = formatted_action_invocation # type: ignore + return formatted_action_invocation + + def _split_lines(self, text, width): + """Wrap lines after splitting on original newlines. + + This allows to have explicit line breaks in the help text. + """ + import textwrap + + lines = [] + for line in text.splitlines(): + lines.extend(textwrap.wrap(line.strip(), width)) + return lines diff --git a/venv/Lib/site-packages/_pytest/config/exceptions.py b/venv/Lib/site-packages/_pytest/config/exceptions.py new file mode 100644 index 0000000..4f1320e --- /dev/null +++ b/venv/Lib/site-packages/_pytest/config/exceptions.py @@ -0,0 +1,11 @@ +from _pytest.compat import final + + +@final +class UsageError(Exception): + """Error in pytest usage or invocation.""" + + +class PrintHelp(Exception): + """Raised when pytest should print its help to skip the rest of the + argument parsing and validation.""" diff --git a/venv/Lib/site-packages/_pytest/config/findpaths.py b/venv/Lib/site-packages/_pytest/config/findpaths.py new file mode 100644 index 0000000..2edf545 --- /dev/null +++ b/venv/Lib/site-packages/_pytest/config/findpaths.py @@ -0,0 +1,211 @@ +import os +from pathlib import Path +from typing import Dict +from typing import Iterable +from typing import List +from typing import Optional +from typing import Sequence +from typing import Tuple +from typing import TYPE_CHECKING +from typing import Union + +import iniconfig + +from .exceptions import UsageError +from _pytest.outcomes import fail +from _pytest.pathlib import absolutepath +from _pytest.pathlib import commonpath + +if TYPE_CHECKING: + from . import Config + + +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, +) -> Optional[Dict[str, Union[str, List[str]]]]: + """Load pytest configuration from the given file path, if supported. + + Return None if the file does not contain valid pytest configuration. + """ + + # Configuration from ini files are obtained from the [pytest] section, if present. + if filepath.suffix == ".ini": + iniconfig = _parse_ini_config(filepath) + + if "pytest" in iniconfig: + return dict(iniconfig["pytest"].items()) + else: + # "pytest.ini" files are always the source of configuration, even if empty. + if filepath.name == "pytest.ini": + return {} + + # '.cfg' files are considered if they contain a "[tool:pytest]" section. + elif filepath.suffix == ".cfg": + iniconfig = _parse_ini_config(filepath) + + if "tool:pytest" in iniconfig.sections: + return dict(iniconfig["tool:pytest"].items()) + elif "pytest" in iniconfig.sections: + # If a setup.cfg contains a "[pytest]" section, we raise a failure to indicate users that + # plain "[pytest]" sections in setup.cfg files is no longer supported (#3086). + fail(CFG_PYTEST_SECTION.format(filename="setup.cfg"), pytrace=False) + + # '.toml' files are considered if they contain a [tool.pytest.ini_options] table. + elif filepath.suffix == ".toml": + import toml + + config = toml.load(str(filepath)) + + result = config.get("tool", {}).get("pytest", {}).get("ini_options", None) + if result is not None: + # TOML supports richer data types than ini files (strings, arrays, floats, ints, etc), + # however we need to convert all scalar values to str for compatibility with the rest + # of the configuration system, which expects strings only. + def make_scalar(v: object) -> Union[str, List[str]]: + return v if isinstance(v, list) else str(v) + + return {k: make_scalar(v) for k, v in result.items()} + + return None + + +def locate_config( + args: Iterable[Path], +) -> Tuple[ + Optional[Path], Optional[Path], Dict[str, Union[str, List[str]]], +]: + """Search in the list of arguments for a valid ini-file for pytest, + and return a tuple of (rootdir, inifile, cfg-dict).""" + config_names = [ + "pytest.ini", + "pyproject.toml", + "tox.ini", + "setup.cfg", + ] + args = [x for x in args if not str(x).startswith("-")] + if not args: + args = [Path.cwd()] + 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(): + ini_config = load_config_dict_from_file(p) + if ini_config is not None: + return base, p, ini_config + return None, None, {} + + +def get_common_ancestor(paths: Iterable[Path]) -> Path: + common_ancestor: Optional[Path] = 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 = Path.cwd() + 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 + + def safe_exists(path: Path) -> bool: + # This can throw on paths that contain characters unrepresentable at the OS level, + # or with invalid syntax on Windows (https://bugs.python.org/issue35306) + try: + return path.exists() + except OSError: + return False + + # These look like paths but may not exist + possible_paths = ( + absolutepath(get_file_part_from_node_id(arg)) + for arg in args + if not is_option(arg) + ) + + return [get_dir_from_path(path) for path in possible_paths if safe_exists(path)] + + +CFG_PYTEST_SECTION = "[pytest] section in {filename} files is no longer supported, change to [tool:pytest] instead." + + +def determine_setup( + inifile: Optional[str], + args: Sequence[str], + rootdir_cmd_arg: Optional[str] = None, + config: Optional["Config"] = None, +) -> Tuple[Path, Optional[Path], Dict[str, Union[str, List[str]]]]: + rootdir = None + dirs = get_dirs_from_args(args) + if inifile: + inipath_ = absolutepath(inifile) + inipath: Optional[Path] = inipath_ + inicfg = load_config_dict_from_file(inipath_) or {} + if rootdir_cmd_arg is None: + rootdir = get_common_ancestor(dirs) + else: + ancestor = get_common_ancestor(dirs) + rootdir, inipath, inicfg = locate_config([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(dirs) + if rootdir is None: + if config is not None: + cwd = config.invocation_params.dir + else: + cwd = Path.cwd() + rootdir = get_common_ancestor([cwd, ancestor]) + is_fs_root = os.path.splitdrive(str(rootdir))[1] == "/" + if is_fs_root: + rootdir = ancestor + if rootdir_cmd_arg: + rootdir = absolutepath(os.path.expandvars(rootdir_cmd_arg)) + if not rootdir.is_dir(): + raise UsageError( + "Directory '{}' not found. Check your '--rootdir' option.".format( + rootdir + ) + ) + assert rootdir is not None + return rootdir, inipath, inicfg or {} diff --git a/venv/Lib/site-packages/_pytest/debugging.py b/venv/Lib/site-packages/_pytest/debugging.py new file mode 100644 index 0000000..d3a5c61 --- /dev/null +++ b/venv/Lib/site-packages/_pytest/debugging.py @@ -0,0 +1,388 @@ +"""Interactive debugging with PDB, the Python Debugger.""" +import argparse +import functools +import sys +import types +from typing import Any +from typing import Callable +from typing import Generator +from typing import List +from typing import Optional +from typing import Tuple +from typing import Type +from typing import TYPE_CHECKING +from typing import Union + +from _pytest import outcomes +from _pytest._code import ExceptionInfo +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 + +if TYPE_CHECKING: + from _pytest.capture import CaptureManager + 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="start a custom interactive Python debugger on errors. " + "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._cleanup.append(fin) + + +class pytestPDB: + """Pseudo PDB that defers to the real pdb.""" + + _pluginmanager: Optional[PytestPluginManager] = None + _config: Optional[Config] = None + _saved: List[ + Tuple[Callable[..., None], Optional[PytestPluginManager], Optional[Config]] + ] = [] + _recursive_debug = 0 + _wrapped_pdb_cls: Optional[Tuple[Type[Any], Type[Any]]] = None + + @classmethod + def _is_capturing(cls, capman: Optional["CaptureManager"]) -> Union[str, bool]: + if capman: + return capman.is_capturing() + return False + + @classmethod + def _import_pdb_cls(cls, capman: Optional["CaptureManager"]): + 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: Optional["CaptureManager"]): + import _pytest.config + + # Type ignored because mypy doesn't support "dynamic" + # inheritance like this. + class PytestPdbWrapper(pdb_cls): # type: ignore[valid-type,misc] + _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 + + 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( + ">", + "PDB continue (IO-capturing resumed for %s)" + % 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 + + 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 + + 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: Optional[CaptureManager] = 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( + ">", + "PDB %s (IO-capturing turned off for %s)" + % (method, 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 + _enter_pdb(node, call.excinfo, report) + + def pytest_internalerror(self, excinfo: ExceptionInfo[BaseException]) -> None: + tb = _postmortem_traceback(excinfo) + post_mortem(tb) + + +class PdbTrace: + @hookimpl(hookwrapper=True) + def pytest_pyfunc_call(self, pyfuncitem) -> Generator[None, None, None]: + wrap_pytest_function_for_tracing(pyfuncitem) + yield + + +def wrap_pytest_function_for_tracing(pyfuncitem): + """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): + func = functools.partial(testfunction, *args, **kwargs) + _pdb.runcall(func) + + pyfuncitem.obj = wrapper + + +def maybe_wrap_pytest_function_for_tracing(pyfuncitem): + """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 re-use 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 = _postmortem_traceback(excinfo) + rep._pdbshown = True # type: ignore[attr-defined] + post_mortem(tb) + return rep + + +def _postmortem_traceback(excinfo: ExceptionInfo[BaseException]) -> types.TracebackType: + from doctest import UnexpectedException + + if isinstance(excinfo.value, UnexpectedException): + # A doctest.UnexpectedException is not useful for post_mortem. + # Use the underlying exception instead: + return excinfo.value.exc_info[2] + elif isinstance(excinfo.value, ConftestImportFailure): + # A config.ConftestImportFailure is not useful for post_mortem. + # Use the underlying exception instead: + return excinfo.value.excinfo[2] + else: + assert excinfo._excinfo is not None + return excinfo._excinfo[2] + + +def post_mortem(t: types.TracebackType) -> None: + p = pytestPDB._init_pdb("post_mortem") + p.reset() + p.interaction(None, t) + 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..19b31d6 --- /dev/null +++ b/venv/Lib/site-packages/_pytest/deprecated.py @@ -0,0 +1,87 @@ +"""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 warnings import warn + +from _pytest.warning_types import PytestDeprecationWarning +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", +} + + +FILLFUNCARGS = UnformattedWarning( + PytestDeprecationWarning, + "{name} is deprecated, use " + "function._request._fillfixtures() instead if you cannot avoid reaching into internals.", +) + +PYTEST_COLLECT_MODULE = UnformattedWarning( + PytestDeprecationWarning, + "pytest.collect.{name} was moved to pytest.{name}\n" + "Please update to the new name.", +) + +YIELD_FIXTURE = PytestDeprecationWarning( + "@pytest.yield_fixture is deprecated.\n" + "Use @pytest.fixture instead; they are the same." +) + +MINUS_K_DASH = PytestDeprecationWarning( + "The `-k '-expr'` syntax to -k is deprecated.\nUse `-k 'not expr'` instead." +) + +MINUS_K_COLON = PytestDeprecationWarning( + "The `-k 'expr:'` syntax to -k is deprecated.\n" + "Please open an issue if you use this and want a replacement." +) + +WARNING_CAPTURED_HOOK = PytestDeprecationWarning( + "The pytest_warning_captured is deprecated and will be removed in a future release.\n" + "Please use pytest_warning_recorded instead." +) + +FSCOLLECTOR_GETHOOKPROXY_ISINITPATH = PytestDeprecationWarning( + "The gethookproxy() and isinitpath() methods of FSCollector and Package are deprecated; " + "use self.session.gethookproxy() and self.session.isinitpath() instead. " +) + +STRICT_OPTION = PytestDeprecationWarning( + "The --strict option is deprecated, use --strict-markers instead." +) + +PRIVATE = PytestDeprecationWarning("A private pytest class or function was used.") + + +# 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..64e8f0e --- /dev/null +++ b/venv/Lib/site-packages/_pytest/doctest.py @@ -0,0 +1,724 @@ +"""Discover and run doctests in modules and test files.""" +import bdb +import inspect +import platform +import sys +import traceback +import types +import warnings +from contextlib import contextmanager +from typing import Any +from typing import Callable +from typing import Dict +from typing import Generator +from typing import Iterable +from typing import List +from typing import Optional +from typing import Pattern +from typing import Sequence +from typing import Tuple +from typing import Type +from typing import TYPE_CHECKING +from typing import Union + +import py.path + +import pytest +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 FixtureRequest +from _pytest.nodes import Collector +from _pytest.outcomes import OutcomeException +from _pytest.pathlib import import_path +from _pytest.python_api import approx +from _pytest.warning_types import PytestWarning + +if TYPE_CHECKING: + import doctest + +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: Optional[Type["doctest.OutputChecker"]] = 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 ImportErrors", + 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( + path: py.path.local, parent: Collector, +) -> Optional[Union["DoctestModule", "DoctestTextfile"]]: + config = parent.config + if path.ext == ".py": + if config.option.doctestmodules and not _is_setup_py(path): + mod: DoctestModule = DoctestModule.from_parent(parent, fspath=path) + return mod + elif _is_doctest(config, path, parent): + txt: DoctestTextfile = DoctestTextfile.from_parent(parent, fspath=path) + return txt + return None + + +def _is_setup_py(path: py.path.local) -> bool: + if path.basename != "setup.py": + return False + contents = path.read_binary() + return b"setuptools" in contents or b"distutils" in contents + + +def _is_doctest(config: Config, path: py.path.local, parent) -> bool: + if path.ext in (".txt", ".rst") and parent.session.isinitpath(path): + return True + globs = config.getoption("doctestglob") or ["test*.txt"] + for glob in globs: + if path.check(fnmatch=glob): + return True + return False + + +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: Optional["doctest.OutputChecker"] = None, + verbose: Optional[bool] = None, + optionflags: int = 0, + continue_on_failure: bool = True, + ) -> None: + doctest.DebugRunner.__init__( + self, 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: Optional["doctest.OutputChecker"] = None, + verbose: Optional[bool] = 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(pytest.Item): + def __init__( + self, + name: str, + parent: "Union[DoctestTextfile, DoctestModule]", + runner: Optional["doctest.DocTestRunner"] = None, + dtest: Optional["doctest.DocTest"] = None, + ) -> None: + super().__init__(name, parent) + self.runner = runner + self.dtest = dtest + self.obj = None + self.fixture_request: Optional[FixtureRequest] = None + + @classmethod + def from_parent( # type: ignore + cls, + parent: "Union[DoctestTextfile, DoctestModule]", + *, + name: str, + runner: "doctest.DocTestRunner", + dtest: "doctest.DocTest", + ): + # incompatible signature due to to imposed limits on sublcass + """The public named constructor.""" + return super().from_parent(name=name, parent=parent, runner=runner, dtest=dtest) + + def setup(self) -> None: + if self.dtest is not None: + self.fixture_request = _setup_fixtures(self) + globs = dict(getfixture=self.fixture_request.getfixturevalue) + for name, value in self.fixture_request.getfixturevalue( + "doctest_namespace" + ).items(): + globs[name] = value + self.dtest.globs.update(globs) + + def runtest(self) -> None: + assert self.dtest is not None + assert self.runner is not 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], + ) -> Union[str, TerminalRepr]: + import doctest + + failures: Optional[ + Sequence[Union[doctest.DocTestFailure, doctest.UnexpectedException]] + ] = (None) + if isinstance( + excinfo.value, (doctest.DocTestFailure, doctest.UnexpectedException) + ): + failures = [excinfo.value] + elif isinstance(excinfo.value, MultipleDoctestFailures): + failures = excinfo.value.failures + + if failures is not None: + 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 = [ + "%03d %s" % (i + test.lineno + 1, 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(failure.exc_info) + lines += ["UNEXPECTED EXCEPTION: %s" % repr(inner_excinfo.value)] + lines += [ + x.strip("\n") + for x in traceback.format_exception(*failure.exc_info) + ] + reprlocation_lines.append((reprlocation, lines)) + return ReprFailDoctest(reprlocation_lines) + else: + return super().repr_failure(excinfo) + + def reportinfo(self): + assert self.dtest is not None + return self.fspath, self.dtest.lineno, "[doctest] %s" % 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(parent): + optionflags_str = parent.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): + continue_on_failure = 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(pytest.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.fspath.read_text(encoding) + filename = str(self.fspath) + name = self.fspath.basename + globs = {"__name__": "__main__"} + + optionflags = get_optionflags(self) + + 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: + pytest.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, None, 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: Optional[Callable[[Any], Any]] = 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( + "Got %r when unwrapping %r. This is usually caused " + "by a violation of Python's object protocol; see e.g. " + "https://github.com/pytest-dev/pytest/issues/5080" % (e, func), + PytestWarning, + ) + raise + + inspect.unwrap = _mock_aware_unwrap + try: + yield + finally: + inspect.unwrap = real_unwrap + + +class DoctestModule(pytest.Module): + def collect(self) -> Iterable[DoctestItem]: + import doctest + + class MockAwareDocTestFinder(doctest.DocTestFinder): + """A hackish doctest finder that overrides stdlib internals to fix a stdlib bug. + + https://github.com/pytest-dev/pytest/issues/3456 + https://bugs.python.org/issue25532 + """ + + def _find_lineno(self, obj, source_lines): + """Doctest code does not take into account `@property`, this + is a hackish way to fix it. + + https://bugs.python.org/issue17446 + """ + if isinstance(obj, property): + obj = getattr(obj, "fget", obj) + # Type ignored because this is a private function. + return doctest.DocTestFinder._find_lineno( # type: ignore + self, obj, source_lines, + ) + + def _find( + self, tests, obj, name, module, source_lines, globs, seen + ) -> None: + if _is_mocked(obj): + return + with _patch_unwrap_mock_aware(): + + # Type ignored because this is a private function. + doctest.DocTestFinder._find( # type: ignore + self, tests, obj, name, module, source_lines, globs, seen + ) + + if self.fspath.basename == "conftest.py": + module = self.config.pluginmanager._importconftest( + self.fspath, self.config.getoption("importmode") + ) + else: + try: + module = import_path(self.fspath) + except ImportError: + if self.config.getvalue("doctest_ignore_import_errors"): + pytest.skip("unable to import module %r" % self.fspath) + else: + raise + # Uses internal doctest module parsing mechanism. + finder = MockAwareDocTestFinder() + optionflags = get_optionflags(self) + 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 _setup_fixtures(doctest_item: DoctestItem) -> FixtureRequest: + """Used by DoctestTextfile and DoctestItem to setup fixture information.""" + + def func() -> None: + pass + + doctest_item.funcargs = {} # type: ignore[attr-defined] + fm = doctest_item.session._fixturemanager + doctest_item._fixtureinfo = fm.getfixtureinfo( # type: ignore[attr-defined] + node=doctest_item, func=func, cls=None, funcargs=False + ) + fixture_request = FixtureRequest(doctest_item, _ispytest=True) + fixture_request._fillfixtures() + return fixture_request + + +def _init_checker_class() -> Type["doctest.OutputChecker"]: + import doctest + import re + + 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 doctest.OutputChecker.check_output(self, 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: 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 doctest.OutputChecker.check_output(self, want, got, optionflags) + + def _remove_unwanted_precision(self, want: str, got: str) -> str: + wants = list(self._number_re.finditer(want)) + gots = list(self._number_re.finditer(got)) + if len(wants) != len(gots): + return got + offset = 0 + for w, g in zip(wants, gots): + fraction: Optional[str] = w.group("fraction") + exponent: Optional[str] = w.group("exponent1") + if exponent is None: + exponent = w.group("exponent2") + if fraction is None: + precision = 0 + else: + precision = 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] + + +@pytest.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.""" + 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..ff673b5 --- /dev/null +++ b/venv/Lib/site-packages/_pytest/faulthandler.py @@ -0,0 +1,116 @@ +import io +import os +import sys +from typing import Generator +from typing import TextIO + +import pytest +from _pytest.config import Config +from _pytest.config.argparsing import Parser +from _pytest.nodes import Item +from _pytest.store import StoreKey + + +fault_handler_stderr_key = StoreKey[TextIO]() + + +def pytest_addoption(parser: Parser) -> None: + help = ( + "Dump the traceback of all threads if a test takes " + "more than TIMEOUT seconds to finish." + ) + parser.addini("faulthandler_timeout", help, default=0.0) + + +def pytest_configure(config: Config) -> None: + import faulthandler + + if not faulthandler.is_enabled(): + # faulthhandler is not enabled, so install plugin that does the actual work + # of enabling faulthandler before each test executes. + config.pluginmanager.register(FaultHandlerHooks(), "faulthandler-hooks") + else: + # Do not handle dumping to stderr if faulthandler is already enabled, so warn + # users that the option is being ignored. + timeout = FaultHandlerHooks.get_timeout_config_value(config) + if timeout > 0: + config.issue_config_time_warning( + pytest.PytestConfigWarning( + "faulthandler module enabled before pytest configuration step, " + "'faulthandler_timeout' option ignored" + ), + stacklevel=2, + ) + + +class FaultHandlerHooks: + """Implements hooks that will actually install fault handler before tests execute, + as well as correctly handle pdb and internal errors.""" + + def pytest_configure(self, config: Config) -> None: + import faulthandler + + stderr_fd_copy = os.dup(self._get_stderr_fileno()) + config._store[fault_handler_stderr_key] = open(stderr_fd_copy, "w") + faulthandler.enable(file=config._store[fault_handler_stderr_key]) + + def pytest_unconfigure(self, config: Config) -> None: + import faulthandler + + faulthandler.disable() + # close our dup file installed during pytest_configure + # re-enable the faulthandler, attaching it to the default sys.stderr + # so we can see crashes after pytest has finished, usually during + # garbage collection during interpreter shutdown + config._store[fault_handler_stderr_key].close() + del config._store[fault_handler_stderr_key] + faulthandler.enable(file=self._get_stderr_fileno()) + + @staticmethod + def _get_stderr_fileno(): + 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, io.UnsupportedOperation): + # 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. + return sys.__stderr__.fileno() + + @staticmethod + def get_timeout_config_value(config): + return float(config.getini("faulthandler_timeout") or 0.0) + + @pytest.hookimpl(hookwrapper=True, trylast=True) + def pytest_runtest_protocol(self, item: Item) -> Generator[None, None, None]: + timeout = self.get_timeout_config_value(item.config) + stderr = item.config._store[fault_handler_stderr_key] + if timeout > 0 and stderr is not None: + import faulthandler + + faulthandler.dump_traceback_later(timeout, file=stderr) + try: + yield + finally: + faulthandler.cancel_dump_traceback_later() + else: + yield + + @pytest.hookimpl(tryfirst=True) + def pytest_enter_pdb(self) -> 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(self) -> 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..273bcaf --- /dev/null +++ b/venv/Lib/site-packages/_pytest/fixtures.py @@ -0,0 +1,1680 @@ +import functools +import inspect +import os +import sys +import warnings +from collections import defaultdict +from collections import deque +from types import TracebackType +from typing import Any +from typing import Callable +from typing import cast +from typing import Dict +from typing import Generator +from typing import Generic +from typing import Iterable +from typing import Iterator +from typing import List +from typing import Optional +from typing import overload +from typing import Sequence +from typing import Set +from typing import Tuple +from typing import Type +from typing import TYPE_CHECKING +from typing import TypeVar +from typing import Union + +import attr +import py + +import _pytest +from _pytest import nodes +from _pytest._code import getfslineno +from _pytest._code.code import FormattedExcinfo +from _pytest._code.code import TerminalRepr +from _pytest._io import TerminalWriter +from _pytest.compat import _format_args +from _pytest.compat import _PytestWrapper +from _pytest.compat import assert_never +from _pytest.compat import final +from _pytest.compat import get_real_func +from _pytest.compat import get_real_method +from _pytest.compat import getfuncargnames +from _pytest.compat import getimfunc +from _pytest.compat import getlocation +from _pytest.compat import is_generator +from _pytest.compat import NOTSET +from _pytest.compat import safe_getattr +from _pytest.config import _PluggyPlugin +from _pytest.config import Config +from _pytest.config.argparsing import Parser +from _pytest.deprecated import check_ispytest +from _pytest.deprecated import FILLFUNCARGS +from _pytest.deprecated import YIELD_FIXTURE +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 TEST_OUTCOME +from _pytest.pathlib import absolutepath +from _pytest.store import StoreKey + +if TYPE_CHECKING: + from typing import Deque + from typing import NoReturn + from typing_extensions import Literal + + from _pytest.main import Session + from _pytest.python import CallSpec2 + from _pytest.python import Function + from _pytest.python import Metafunc + + _Scope = Literal["session", "package", "module", "class", "function"] + + +# The value of the fixture -- return/yield of the fixture function (type variable). +_FixtureValue = TypeVar("_FixtureValue") +# The type of the fixture function (type variable). +_FixtureFunction = TypeVar("_FixtureFunction", bound=Callable[..., object]) +# The type of a fixture function (type alias generic in fixture value). +_FixtureFunc = Union[ + Callable[..., _FixtureValue], Callable[..., Generator[_FixtureValue, None, None]] +] +# The type of FixtureDef.cached_result (type alias generic in fixture value). +_FixtureCachedResult = Union[ + Tuple[ + # The result. + _FixtureValue, + # Cache key. + object, + None, + ], + Tuple[ + None, + # Cache key. + object, + # Exc info if raised. + Tuple[Type[BaseException], BaseException, TracebackType], + ], +] + + +@attr.s(frozen=True) +class PseudoFixtureDef(Generic[_FixtureValue]): + cached_result = attr.ib(type="_FixtureCachedResult[_FixtureValue]") + scope = attr.ib(type="_Scope") + + +def pytest_sessionstart(session: "Session") -> None: + session._fixturemanager = FixtureManager(session) + + +def get_scope_package(node, fixturedef: "FixtureDef[object]"): + import pytest + + cls = pytest.Package + current = node + fixture_package_name = "{}/{}".format(fixturedef.baseid, "__init__.py") + while current and ( + type(current) is not cls or fixture_package_name != current.nodeid + ): + current = current.parent + if current is None: + return node.session + return current + + +def get_scope_node( + node: nodes.Node, scope: "_Scope" +) -> Optional[Union[nodes.Item, nodes.Collector]]: + import _pytest.python + + if scope == "function": + return node.getparent(nodes.Item) + elif scope == "class": + return node.getparent(_pytest.python.Class) + elif scope == "module": + return node.getparent(_pytest.python.Module) + elif scope == "package": + return node.getparent(_pytest.python.Package) + elif scope == "session": + return node.getparent(_pytest.main.Session) + else: + assert_never(scope) + + +# Used for storing artificial fixturedefs for direct parametrization. +name2pseudofixturedef_key = StoreKey[Dict[str, "FixtureDef[Any]"]]() + + +def add_funcarg_pseudo_fixture_def( + collector: nodes.Collector, metafunc: "Metafunc", fixturemanager: "FixtureManager" +) -> None: + # This function will transform all collected calls to functions + # if they use direct funcargs (i.e. direct parametrization) + # because we want later test execution to be able to rely on + # an existing FixtureDef structure for all arguments. + # XXX we can probably avoid this algorithm if we modify CallSpec2 + # to directly care for creating the fixturedefs within its methods. + if not metafunc._calls[0].funcargs: + # This function call does not have direct parametrization. + return + # Collect funcargs of all callspecs into a list of values. + arg2params: Dict[str, List[object]] = {} + arg2scope: Dict[str, _Scope] = {} + for callspec in metafunc._calls: + for argname, argvalue in callspec.funcargs.items(): + assert argname not in callspec.params + callspec.params[argname] = argvalue + arg2params_list = arg2params.setdefault(argname, []) + callspec.indices[argname] = len(arg2params_list) + arg2params_list.append(argvalue) + if argname not in arg2scope: + scopenum = callspec._arg2scopenum.get(argname, scopenum_function) + arg2scope[argname] = scopes[scopenum] + callspec.funcargs.clear() + + # Register artificial FixtureDef's so that later at test execution + # time we can rely on a proper FixtureDef to exist for fixture setup. + arg2fixturedefs = metafunc._arg2fixturedefs + for argname, valuelist in arg2params.items(): + # If we have a scope that is higher than function, we need + # to make sure we only ever create an according fixturedef on + # a per-scope basis. We thus store and cache the fixturedef on the + # node related to the scope. + scope = arg2scope[argname] + node = None + if scope != "function": + node = get_scope_node(collector, scope) + if node is None: + assert scope == "class" and isinstance(collector, _pytest.python.Module) + # Use module-level collector for class-scope (for now). + node = collector + if node is None: + name2pseudofixturedef = None + else: + default: Dict[str, FixtureDef[Any]] = {} + name2pseudofixturedef = node._store.setdefault( + name2pseudofixturedef_key, default + ) + if name2pseudofixturedef is not None and argname in name2pseudofixturedef: + arg2fixturedefs[argname] = [name2pseudofixturedef[argname]] + else: + fixturedef = FixtureDef( + fixturemanager=fixturemanager, + baseid="", + argname=argname, + func=get_direct_param_fixture_func, + scope=arg2scope[argname], + params=valuelist, + unittest=False, + ids=None, + ) + arg2fixturedefs[argname] = [fixturedef] + if name2pseudofixturedef is not None: + name2pseudofixturedef[argname] = fixturedef + + +def getfixturemarker(obj: object) -> Optional["FixtureFunctionMarker"]: + """Return fixturemarker or None if it doesn't exist or raised + exceptions.""" + try: + fixturemarker: Optional[FixtureFunctionMarker] = getattr( + obj, "_pytestfixturefunction", None + ) + except TEST_OUTCOME: + # some objects raise errors like request (from flask import request) + # we don't expect them to be fixture functions + return None + return fixturemarker + + +# Parametrized fixture key, helper alias for code below. +_Key = Tuple[object, ...] + + +def get_parametrized_fixture_keys(item: nodes.Item, scopenum: int) -> Iterator[_Key]: + """Return list of keys for all parametrized arguments which match + the specified scope. """ + assert scopenum < scopenum_function # function + try: + callspec = item.callspec # type: ignore[attr-defined] + except AttributeError: + pass + else: + cs: CallSpec2 = callspec + # cs.indices.items() is random order of argnames. Need to + # sort this so that different calls to + # get_parametrized_fixture_keys will be deterministic. + for argname, param_index in sorted(cs.indices.items()): + if cs._arg2scopenum[argname] != scopenum: + continue + if scopenum == 0: # session + key: _Key = (argname, param_index) + elif scopenum == 1: # package + key = (argname, param_index, item.fspath.dirpath()) + elif scopenum == 2: # module + key = (argname, param_index, item.fspath) + elif scopenum == 3: # class + item_cls = item.cls # type: ignore[attr-defined] + key = (argname, param_index, item.fspath, item_cls) + yield key + + +# Algorithm for sorting on a per-parametrized resource setup basis. +# It is called for scopenum==0 (session) first and performs sorting +# down to the lower scopes such as to minimize number of "high scope" +# setups and teardowns. + + +def reorder_items(items: Sequence[nodes.Item]) -> List[nodes.Item]: + argkeys_cache: Dict[int, Dict[nodes.Item, Dict[_Key, None]]] = {} + items_by_argkey: Dict[int, Dict[_Key, Deque[nodes.Item]]] = {} + for scopenum in range(0, scopenum_function): + d: Dict[nodes.Item, Dict[_Key, None]] = {} + argkeys_cache[scopenum] = d + item_d: Dict[_Key, Deque[nodes.Item]] = defaultdict(deque) + items_by_argkey[scopenum] = item_d + for item in items: + keys = dict.fromkeys(get_parametrized_fixture_keys(item, scopenum), None) + if keys: + d[item] = keys + for key in keys: + item_d[key].append(item) + items_dict = dict.fromkeys(items, None) + return list(reorder_items_atscope(items_dict, argkeys_cache, items_by_argkey, 0)) + + +def fix_cache_order( + item: nodes.Item, + argkeys_cache: Dict[int, Dict[nodes.Item, Dict[_Key, None]]], + items_by_argkey: Dict[int, Dict[_Key, "Deque[nodes.Item]"]], +) -> None: + for scopenum in range(0, scopenum_function): + for key in argkeys_cache[scopenum].get(item, []): + items_by_argkey[scopenum][key].appendleft(item) + + +def reorder_items_atscope( + items: Dict[nodes.Item, None], + argkeys_cache: Dict[int, Dict[nodes.Item, Dict[_Key, None]]], + items_by_argkey: Dict[int, Dict[_Key, "Deque[nodes.Item]"]], + scopenum: int, +) -> Dict[nodes.Item, None]: + if scopenum >= scopenum_function or len(items) < 3: + return items + ignore: Set[Optional[_Key]] = set() + items_deque = deque(items) + items_done: Dict[nodes.Item, None] = {} + scoped_items_by_argkey = items_by_argkey[scopenum] + scoped_argkeys_cache = argkeys_cache[scopenum] + while items_deque: + no_argkey_group: Dict[nodes.Item, None] = {} + slicing_argkey = None + while items_deque: + item = items_deque.popleft() + if item in items_done or item in no_argkey_group: + continue + argkeys = dict.fromkeys( + (k for k in scoped_argkeys_cache.get(item, []) if k not in ignore), None + ) + if not argkeys: + no_argkey_group[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): + fix_cache_order(i, argkeys_cache, items_by_argkey) + items_deque.appendleft(i) + break + if no_argkey_group: + no_argkey_group = reorder_items_atscope( + no_argkey_group, argkeys_cache, items_by_argkey, scopenum + 1 + ) + for item in no_argkey_group: + items_done[item] = None + ignore.add(slicing_argkey) + return items_done + + +def _fillfuncargs(function: "Function") -> None: + """Fill missing fixtures for a test function, old public API (deprecated).""" + warnings.warn(FILLFUNCARGS.format(name="pytest._fillfuncargs()"), stacklevel=2) + _fill_fixtures_impl(function) + + +def fillfixtures(function: "Function") -> None: + """Fill missing fixtures for a test function (deprecated).""" + warnings.warn( + FILLFUNCARGS.format(name="_pytest.fixtures.fillfixtures()"), stacklevel=2 + ) + _fill_fixtures_impl(function) + + +def _fill_fixtures_impl(function: "Function") -> None: + """Internal implementation to fill fixtures on the given function object.""" + try: + request = function._request + except AttributeError: + # XXX this special code path is only expected to execute + # with the oejskit plugin. It uses classes with funcargs + # and we thus have to work a bit to allow this. + fm = function.session._fixturemanager + assert function.parent is not None + fi = fm.getfixtureinfo(function.parent, function.obj, None) + function._fixtureinfo = fi + request = function._request = FixtureRequest(function, _ispytest=True) + request._fillfixtures() + # Prune out funcargs for jstests. + newfuncargs = {} + for name in fi.argnames: + newfuncargs[name] = function.funcargs[name] + function.funcargs = newfuncargs + else: + request._fillfixtures() + + +def get_direct_param_fixture_func(request): + return request.param + + +@attr.s(slots=True) +class FuncFixtureInfo: + # Original function argument names. + argnames = attr.ib(type=Tuple[str, ...]) + # Argnames that function immediately requires. These include argnames + + # fixture names specified via usefixtures and via autouse=True in fixture + # definitions. + initialnames = attr.ib(type=Tuple[str, ...]) + names_closure = attr.ib(type=List[str]) + name2fixturedefs = attr.ib(type=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 smth 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: + """A request for a fixture from a test or fixture function. + + A request object gives access to the requesting test context and has + an optional ``param`` attribute in case the fixture is parametrized + indirectly. + """ + + def __init__(self, pyfuncitem, *, _ispytest: bool = False) -> None: + check_ispytest(_ispytest) + self._pyfuncitem = pyfuncitem + #: Fixture for which this request is being performed. + self.fixturename: Optional[str] = None + #: Scope string, one of "function", "class", "module", "session". + self.scope: _Scope = "function" + self._fixture_defs: Dict[str, FixtureDef[Any]] = {} + fixtureinfo: FuncFixtureInfo = pyfuncitem._fixtureinfo + self._arg2fixturedefs = fixtureinfo.name2fixturedefs.copy() + self._arg2index: Dict[str, int] = {} + self._fixturemanager: FixtureManager = (pyfuncitem.session._fixturemanager) + + @property + def fixturenames(self) -> List[str]: + """Names of all active fixtures in this request.""" + result = list(self._pyfuncitem._fixtureinfo.names_closure) + result.extend(set(self._fixture_defs).difference(result)) + return result + + @property + def node(self): + """Underlying collection node (depends on current request scope).""" + return self._getscopeitem(self.scope) + + def _getnextfixturedef(self, argname: str) -> "FixtureDef[Any]": + fixturedefs = self._arg2fixturedefs.get(argname, None) + if fixturedefs is None: + # We arrive here because of a dynamic call to + # getfixturevalue(argname) usage which was naturally + # not known at parsing/collection time. + assert self._pyfuncitem.parent is not None + parentid = self._pyfuncitem.parent.nodeid + fixturedefs = self._fixturemanager.getfixturedefs(argname, parentid) + # TODO: Fix this type ignore. Either add assert or adjust types. + # Can this be None here? + self._arg2fixturedefs[argname] = fixturedefs # type: ignore[assignment] + # fixturedefs list is immutable so we maintain a decreasing index. + index = self._arg2index.get(argname, 0) - 1 + if fixturedefs is None or (-index > len(fixturedefs)): + raise FixtureLookupError(argname, self) + self._arg2index[argname] = index + return fixturedefs[index] + + @property + def config(self) -> Config: + """The pytest config object associated with this request.""" + return self._pyfuncitem.config # type: ignore[no-any-return] + + @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.""" + # unittest support hack, see _pytest.unittest.TestCaseFunction. + try: + return self._pyfuncitem._testcase + except AttributeError: + function = getattr(self, "function", None) + return getattr(function, "__self__", 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") + return self._pyfuncitem.getparent(_pytest.python.Module).obj + + @property + def fspath(self) -> py.path.local: + """The file system path of the test module which collected this test.""" + if self.scope not in ("function", "class", "module", "package"): + raise AttributeError(f"module not available in {self.scope}-scoped context") + # TODO: Remove ignore once _pyfuncitem is properly typed. + return self._pyfuncitem.fspath # type: ignore + + @property + def keywords(self): + """Keywords/markers dictionary for the underlying node.""" + return self.node.keywords + + @property + def session(self) -> "Session": + """Pytest session object.""" + return self._pyfuncitem.session # type: ignore[no-any-return] + + def addfinalizer(self, finalizer: Callable[[], object]) -> None: + """Add finalizer/teardown function to be called after the last test + within the requesting test context finished execution.""" + # XXX usually this method is shadowed by fixturedef specific ones. + self._addfinalizer(finalizer, scope=self.scope) + + def _addfinalizer(self, finalizer: Callable[[], object], scope) -> None: + colitem = self._getscopeitem(scope) + self._pyfuncitem.session._setupstate.addfinalizer( + finalizer=finalizer, colitem=colitem + ) + + def applymarker(self, marker: Union[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: + A :py:class:`_pytest.mark.MarkDecorator` object created by a call + to ``pytest.mark.NAME(...)``. + """ + self.node.add_marker(marker) + + def raiseerror(self, msg: Optional[str]) -> "NoReturn": + """Raise a FixtureLookupError with the given message.""" + raise self._fixturemanager.FixtureLookupError(None, self, msg) + + def _fillfixtures(self) -> None: + item = self._pyfuncitem + fixturenames = getattr(item, "fixturenames", self.fixturenames) + for argname in fixturenames: + if argname not in item.funcargs: + item.funcargs[argname] = self.getfixturevalue(argname) + + 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. + + :raises pytest.FixtureLookupError: + If the given fixture could not be found. + """ + fixturedef = self._get_active_fixturedef(argname) + assert fixturedef.cached_result is not None + return fixturedef.cached_result[0] + + def _get_active_fixturedef( + self, argname: str + ) -> Union["FixtureDef[object]", PseudoFixtureDef[object]]: + try: + return self._fixture_defs[argname] + except KeyError: + try: + fixturedef = self._getnextfixturedef(argname) + except FixtureLookupError: + if argname == "request": + cached_result = (self, [0], None) + scope: _Scope = "function" + return PseudoFixtureDef(cached_result, scope) + raise + # Remove indent to prevent the python3 exception + # from leaking into the call. + self._compute_fixture_value(fixturedef) + self._fixture_defs[argname] = fixturedef + return fixturedef + + def _get_fixturestack(self) -> List["FixtureDef[Any]"]: + current = self + values: List[FixtureDef[Any]] = [] + while 1: + fixturedef = getattr(current, "_fixturedef", None) + if fixturedef is None: + values.reverse() + return values + values.append(fixturedef) + assert isinstance(current, SubRequest) + current = current._parent_request + + def _compute_fixture_value(self, fixturedef: "FixtureDef[object]") -> None: + """Create a SubRequest based on "self" and call the execute method + of the given FixtureDef object. + + This will force the FixtureDef object to throw away any previous + results and compute a new fixture value, which will be stored into + the FixtureDef object itself. + """ + # prepare a subrequest object before calling fixture function + # (latter managed by fixturedef) + argname = fixturedef.argname + funcitem = self._pyfuncitem + scope = fixturedef.scope + try: + param = funcitem.callspec.getparam(argname) + except (AttributeError, ValueError): + param = NOTSET + param_index = 0 + has_params = fixturedef.params is not None + fixtures_not_supported = getattr(funcitem, "nofuncargs", False) + if has_params and fixtures_not_supported: + msg = ( + "{name} does not support fixtures, maybe unittest.TestCase subclass?\n" + "Node id: {nodeid}\n" + "Function type: {typename}" + ).format( + name=funcitem.name, + nodeid=funcitem.nodeid, + typename=type(funcitem).__name__, + ) + fail(msg, pytrace=False) + if has_params: + frame = inspect.stack()[3] + frameinfo = inspect.getframeinfo(frame[0]) + source_path = py.path.local(frameinfo.filename) + source_lineno = frameinfo.lineno + rel_source_path = source_path.relto(funcitem.config.rootdir) + if rel_source_path: + source_path_str = rel_source_path + else: + source_path_str = str(source_path) + msg = ( + "The requested fixture has no parameter defined for test:\n" + " {}\n\n" + "Requested fixture '{}' defined in:\n{}" + "\n\nRequested here:\n{}:{}".format( + funcitem.nodeid, + fixturedef.argname, + getlocation(fixturedef.func, funcitem.config.rootdir), + source_path_str, + source_lineno, + ) + ) + fail(msg, pytrace=False) + else: + param_index = funcitem.callspec.indices[argname] + # If a parametrize invocation set a scope it will override + # the static scope defined with the fixture function. + paramscopenum = funcitem.callspec._arg2scopenum.get(argname) + if paramscopenum is not None: + scope = scopes[paramscopenum] + + subrequest = SubRequest( + self, scope, param, param_index, fixturedef, _ispytest=True + ) + + # Check if a higher-level scoped fixture accesses a lower level one. + subrequest._check_scope(argname, self.scope, scope) + try: + # Call the fixture function. + fixturedef.execute(request=subrequest) + finally: + self._schedule_finalizers(fixturedef, subrequest) + + def _schedule_finalizers( + self, fixturedef: "FixtureDef[object]", subrequest: "SubRequest" + ) -> None: + # If fixture function failed it might have registered finalizers. + self.session._setupstate.addfinalizer( + functools.partial(fixturedef.finish, request=subrequest), subrequest.node + ) + + def _check_scope( + self, argname: str, invoking_scope: "_Scope", requested_scope: "_Scope", + ) -> None: + if argname == "request": + return + if scopemismatch(invoking_scope, requested_scope): + # Try to report something helpful. + lines = self._factorytraceback() + fail( + "ScopeMismatch: You tried to access the %r scoped " + "fixture %r with a %r scoped request object, " + "involved factories\n%s" + % ((requested_scope, argname, invoking_scope, "\n".join(lines))), + pytrace=False, + ) + + def _factorytraceback(self) -> List[str]: + lines = [] + for fixturedef in self._get_fixturestack(): + factory = fixturedef.func + fs, lineno = getfslineno(factory) + p = self._pyfuncitem.session.fspath.bestrelpath(fs) + args = _format_args(factory) + lines.append("%s:%d: def %s%s" % (p, lineno + 1, factory.__name__, args)) + return lines + + def _getscopeitem(self, scope: "_Scope") -> Union[nodes.Item, nodes.Collector]: + if scope == "function": + # This might also be a non-function Item despite its attribute name. + node: Optional[Union[nodes.Item, nodes.Collector]] = self._pyfuncitem + elif scope == "package": + # FIXME: _fixturedef is not defined on FixtureRequest (this class), + # but on FixtureRequest (a subclass). + node = get_scope_package(self._pyfuncitem, self._fixturedef) # type: ignore[attr-defined] + else: + node = get_scope_node(self._pyfuncitem, scope) + if node is None and scope == "class": + # Fallback to function item itself. + node = self._pyfuncitem + assert node, 'Could not obtain a node for scope "{}" for function {!r}'.format( + scope, self._pyfuncitem + ) + return node + + def __repr__(self) -> str: + return "" % (self.node) + + +@final +class SubRequest(FixtureRequest): + """A sub request for handling getting a fixture from a test function/fixture.""" + + def __init__( + self, + request: "FixtureRequest", + scope: "_Scope", + param, + param_index: int, + fixturedef: "FixtureDef[object]", + *, + _ispytest: bool = False, + ) -> None: + check_ispytest(_ispytest) + self._parent_request = request + self.fixturename = fixturedef.argname + if param is not NOTSET: + self.param = param + self.param_index = param_index + self.scope = scope + self._fixturedef = fixturedef + self._pyfuncitem = request._pyfuncitem + self._fixture_defs = request._fixture_defs + self._arg2fixturedefs = request._arg2fixturedefs + self._arg2index = request._arg2index + self._fixturemanager = request._fixturemanager + + def __repr__(self) -> str: + return f"" + + def addfinalizer(self, finalizer: Callable[[], object]) -> None: + """Add finalizer/teardown function to be called after the last test + within the requesting test context finished execution.""" + self._fixturedef.addfinalizer(finalizer) + + def _schedule_finalizers( + self, fixturedef: "FixtureDef[object]", subrequest: "SubRequest" + ) -> None: + # If the executing fixturedef was not explicitly requested in the argument list (via + # getfixturevalue inside the fixture call) then ensure this fixture def will be finished + # first. + if fixturedef.argname not in self.fixturenames: + fixturedef.addfinalizer( + functools.partial(self._fixturedef.finish, request=self) + ) + super()._schedule_finalizers(fixturedef, subrequest) + + +scopes: List["_Scope"] = ["session", "package", "module", "class", "function"] +scopenum_function = scopes.index("function") + + +def scopemismatch(currentscope: "_Scope", newscope: "_Scope") -> bool: + return scopes.index(newscope) > scopes.index(currentscope) + + +def scope2index(scope: str, descr: str, where: Optional[str] = None) -> int: + """Look up the index of ``scope`` and raise a descriptive value error + if not defined.""" + strscopes: Sequence[str] = scopes + try: + return strscopes.index(scope) + except ValueError: + fail( + "{} {}got an unexpected scope value '{}'".format( + descr, f"from {where} " if where else "", scope + ), + pytrace=False, + ) + + +@final +class FixtureLookupError(LookupError): + """Could not return a requested fixture (missing or invalid).""" + + def __init__( + self, argname: Optional[str], request: FixtureRequest, msg: Optional[str] = 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 + 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("file {}, line {}".format(fspath, 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() + parentid = self.request._pyfuncitem.parent.nodeid + for name, fixturedefs in fm._arg2fixturedefs.items(): + faclist = list(fm._matchfactories(fixturedefs, parentid)) + if faclist: + available.add(name) + if self.argname in available: + msg = " recursive dependency involving fixture '{}' detected".format( + self.argname + ) + 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: Union[str, py.path.local], + firstlineno: int, + tblines: Sequence[str], + errorstring: str, + argname: Optional[str], + ) -> 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( + "{} {}".format(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("%s:%d" % (self.filename, self.firstlineno + 1)) + + +def fail_fixturefunc(fixturefunc, msg: str) -> "NoReturn": + fs, lineno = getfslineno(fixturefunc) + location = "{}:{}".format(fs, lineno + 1) + source = _pytest._code.Source(fixturefunc) + fail(msg + ":\n\n" + str(source.indent()) + "\n" + location, pytrace=False) + + +def call_fixture_func( + fixturefunc: "_FixtureFunc[_FixtureValue]", request: FixtureRequest, kwargs +) -> _FixtureValue: + if is_generator(fixturefunc): + fixturefunc = cast( + Callable[..., Generator[_FixtureValue, None, None]], 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: + fail_fixturefunc(fixturefunc, "fixture function has more than one 'yield'") + + +def _eval_scope_callable( + scope_callable: "Callable[[str, Config], _Scope]", + fixture_name: str, + config: Config, +) -> "_Scope": + 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( + "Error evaluating {} while defining fixture '{}'.\n" + "Expected a function with the signature (*, fixture_name, config)".format( + scope_callable, fixture_name + ) + ) from e + if not isinstance(result, str): + fail( + "Expected {} to return a 'str' while defining fixture '{}', but it returned:\n" + "{!r}".format(scope_callable, fixture_name, result), + pytrace=False, + ) + return result + + +@final +class FixtureDef(Generic[_FixtureValue]): + """A container for a factory definition.""" + + def __init__( + self, + fixturemanager: "FixtureManager", + baseid: Optional[str], + argname: str, + func: "_FixtureFunc[_FixtureValue]", + scope: "Union[_Scope, Callable[[str, Config], _Scope]]", + params: Optional[Sequence[object]], + unittest: bool = False, + ids: Optional[ + Union[ + Tuple[Union[None, str, float, int, bool], ...], + Callable[[Any], Optional[object]], + ] + ] = None, + ) -> None: + self._fixturemanager = fixturemanager + self.baseid = baseid or "" + self.has_location = baseid is not None + self.func = func + self.argname = argname + if callable(scope): + scope_ = _eval_scope_callable(scope, argname, fixturemanager.config) + else: + scope_ = scope + self.scopenum = scope2index( + # TODO: Check if the `or` here is really necessary. + scope_ or "function", # type: ignore[unreachable] + descr=f"Fixture '{func.__name__}'", + where=baseid, + ) + self.scope = scope_ + self.params: Optional[Sequence[object]] = params + self.argnames: Tuple[str, ...] = getfuncargnames( + func, name=argname, is_method=unittest + ) + self.unittest = unittest + self.ids = ids + self.cached_result: Optional[_FixtureCachedResult[_FixtureValue]] = None + self._finalizers: List[Callable[[], object]] = [] + + def addfinalizer(self, finalizer: Callable[[], object]) -> None: + self._finalizers.append(finalizer) + + def finish(self, request: SubRequest) -> None: + exc = None + try: + while self._finalizers: + try: + func = self._finalizers.pop() + func() + except BaseException as e: + # XXX Only first exception will be seen by user, + # ideally all should be reported. + if exc is None: + exc = e + if exc: + raise exc + finally: + hook = self._fixturemanager.session.gethookproxy(request.node.fspath) + hook.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 = [] + + def execute(self, request: SubRequest) -> _FixtureValue: + # Get required arguments and register our own finish() + # with their finalization. + for argname in self.argnames: + fixturedef = request._get_active_fixturedef(argname) + if argname != "request": + # PseudoFixtureDef is only for "request". + assert isinstance(fixturedef, FixtureDef) + fixturedef.addfinalizer(functools.partial(self.finish, request=request)) + + my_cache_key = self.cache_key(request) + if self.cached_result is not None: + # note: comparison with `==` can fail (or be expensive) for e.g. + # numpy arrays (#6497). + cache_key = self.cached_result[1] + if my_cache_key is cache_key: + if self.cached_result[2] is not None: + _, val, tb = self.cached_result[2] + raise val.with_traceback(tb) + else: + result = self.cached_result[0] + return result + # We have a previous but differently parametrized fixture instance + # so we need to tear it down before creating a new one. + self.finish(request) + assert self.cached_result is None + + hook = self._fixturemanager.session.gethookproxy(request.node.fspath) + result = hook.pytest_fixture_setup(fixturedef=self, request=request) + return result + + def cache_key(self, request: SubRequest) -> object: + return request.param_index if not hasattr(request, "param") else request.param + + def __repr__(self) -> str: + return "".format( + self.argname, self.scope, self.baseid + ) + + +def resolve_fixture_function( + fixturedef: FixtureDef[_FixtureValue], request: FixtureRequest +) -> "_FixtureFunc[_FixtureValue]": + """Get the actual callable that can be called to obtain the fixture + value, dealing with unittest-specific instances and bound methods.""" + fixturefunc = fixturedef.func + if fixturedef.unittest: + if request.instance is not None: + # Bind the unbound method to the TestCase instance. + fixturefunc = fixturedef.func.__get__(request.instance) # type: ignore[union-attr] + else: + # The fixture function needs to be bound to the actual + # request.instance so that code working with "fixturedef" behaves + # as expected. + if request.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( + request.instance, fixturefunc.__self__.__class__ # type: ignore[union-attr] + ): + return fixturefunc + fixturefunc = getimfunc(fixturedef.func) + if fixturefunc != fixturedef.func: + fixturefunc = fixturefunc.__get__(request.instance) # type: ignore[union-attr] + return fixturefunc + + +def pytest_fixture_setup( + fixturedef: FixtureDef[_FixtureValue], request: SubRequest +) -> _FixtureValue: + """Execution of fixture setup.""" + kwargs = {} + for argname in fixturedef.argnames: + fixdef = request._get_active_fixturedef(argname) + assert fixdef.cached_result is not None + result, arg_cache_key, exc = fixdef.cached_result + request._check_scope(argname, request.scope, fixdef.scope) + kwargs[argname] = result + + fixturefunc = resolve_fixture_function(fixturedef, request) + my_cache_key = fixturedef.cache_key(request) + try: + result = call_fixture_func(fixturefunc, request, kwargs) + except TEST_OUTCOME: + exc_info = sys.exc_info() + assert exc_info[0] is not None + fixturedef.cached_result = (None, my_cache_key, exc_info) + raise + fixturedef.cached_result = (result, my_cache_key, None) + return result + + +def _ensure_immutable_ids( + ids: Optional[ + Union[ + Iterable[Union[None, str, float, int, bool]], + Callable[[Any], Optional[object]], + ] + ], +) -> Optional[ + Union[ + Tuple[Union[None, str, float, int, bool], ...], + Callable[[Any], Optional[object]], + ] +]: + if ids is None: + return None + if callable(ids): + return ids + return tuple(ids) + + +def _params_converter( + params: Optional[Iterable[object]], +) -> Optional[Tuple[object, ...]]: + return tuple(params) if params is not None else None + + +def wrap_function_to_error_out_if_called_directly( + function: _FixtureFunction, fixture_marker: "FixtureFunctionMarker", +) -> _FixtureFunction: + """Wrap the given fixture function so we can raise an error about it being called directly, + instead of used as an argument in a test function.""" + message = ( + 'Fixture "{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/fixture.html for more information about fixtures, and\n" + "https://docs.pytest.org/en/stable/deprecations.html#calling-fixtures-directly about how to update your code." + ).format(name=fixture_marker.name or function.__name__) + + @functools.wraps(function) + def result(*args, **kwargs): + fail(message, pytrace=False) + + # Keep reference to the original function in our own custom attribute so we don't unwrap + # further than this point and lose useful wrappings like @mock.patch (#3774). + result.__pytest_wrapped__ = _PytestWrapper(function) # type: ignore[attr-defined] + + return cast(_FixtureFunction, result) + + +@final +@attr.s(frozen=True) +class FixtureFunctionMarker: + scope = attr.ib(type="Union[_Scope, Callable[[str, Config], _Scope]]") + params = attr.ib(type=Optional[Tuple[object, ...]], converter=_params_converter) + autouse = attr.ib(type=bool, default=False) + ids = attr.ib( + type=Union[ + Tuple[Union[None, str, float, int, bool], ...], + Callable[[Any], Optional[object]], + ], + default=None, + converter=_ensure_immutable_ids, + ) + name = attr.ib(type=Optional[str], default=None) + + def __call__(self, function: _FixtureFunction) -> _FixtureFunction: + if inspect.isclass(function): + raise ValueError("class fixtures not supported (maybe in the future)") + + if getattr(function, "_pytestfixturefunction", False): + raise ValueError( + "fixture is being applied more than once to the same function" + ) + + function = wrap_function_to_error_out_if_called_directly(function, self) + + name = self.name or function.__name__ + if name == "request": + location = getlocation(function) + fail( + "'request' is a reserved word for fixtures, use another name:\n {}".format( + location + ), + pytrace=False, + ) + + # Type ignored because https://github.com/python/mypy/issues/2087. + function._pytestfixturefunction = self # type: ignore[attr-defined] + return function + + +@overload +def fixture( + fixture_function: _FixtureFunction, + *, + scope: "Union[_Scope, Callable[[str, Config], _Scope]]" = ..., + params: Optional[Iterable[object]] = ..., + autouse: bool = ..., + ids: Optional[ + Union[ + Iterable[Union[None, str, float, int, bool]], + Callable[[Any], Optional[object]], + ] + ] = ..., + name: Optional[str] = ..., +) -> _FixtureFunction: + ... + + +@overload +def fixture( + fixture_function: None = ..., + *, + scope: "Union[_Scope, Callable[[str, Config], _Scope]]" = ..., + params: Optional[Iterable[object]] = ..., + autouse: bool = ..., + ids: Optional[ + Union[ + Iterable[Union[None, str, float, int, bool]], + Callable[[Any], Optional[object]], + ] + ] = ..., + name: Optional[str] = None, +) -> FixtureFunctionMarker: + ... + + +def fixture( + fixture_function: Optional[_FixtureFunction] = None, + *, + scope: "Union[_Scope, Callable[[str, Config], _Scope]]" = "function", + params: Optional[Iterable[object]] = None, + autouse: bool = False, + ids: Optional[ + Union[ + Iterable[Union[None, str, float, int, bool]], + Callable[[Any], Optional[object]], + ] + ] = None, + name: Optional[str] = None, +) -> Union[FixtureFunctionMarker, _FixtureFunction]: + """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: + List of string 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=params, autouse=autouse, ids=ids, name=name, + ) + + # 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 :class:`_pytest.config.Config` object. + + Example:: + + def test_foo(pytestconfig): + if pytestconfig.getoption("verbose") > 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", + ) + + +class FixtureManager: + """pytest fixture definitions and information is stored and managed + from this class. + + During collection fm.parsefactories() is called multiple times to parse + fixture function definitions into FixtureDef objects and internal + data structures. + + During collection of test functions, metafunc-mechanics instantiate + a FuncFixtureInfo object which is cached per node/func-name. + This FuncFixtureInfo object is later retrieved by Function nodes + which themselves offer a fixturenames attribute. + + The FuncFixtureInfo object holds information about fixtures and FixtureDefs + relevant for a particular function. An initial list of fixtures is + assembled like this: + + - ini-defined usefixtures + - autouse-marked fixtures along the collection chain up from the function + - usefixtures markers at module/class/function level + - test function funcargs + + Subsequently the funcfixtureinfo.fixturenames attribute is computed + as the closure of the fixtures needed to setup the initial fixtures, + i.e. fixtures needed by fixture functions themselves are appended + to the fixturenames list. + + Upon the test-setup phases all fixturenames are instantiated, retrieved + by a lookup of their FuncFixtureInfo. + """ + + FixtureLookupError = FixtureLookupError + FixtureLookupErrorRepr = FixtureLookupErrorRepr + + def __init__(self, session: "Session") -> None: + self.session = session + self.config: Config = session.config + self._arg2fixturedefs: Dict[str, List[FixtureDef[Any]]] = {} + self._holderobjseen: Set[object] = set() + # A mapping from a nodeid to a list of autouse fixtures it defines. + self._nodeid_autousenames: Dict[str, List[str]] = { + "": self.config.getini("usefixtures"), + } + session.config.pluginmanager.register(self, "funcmanage") + + def _get_direct_parametrize_args(self, node: nodes.Node) -> List[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: List[str] = [] + 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.extend(p_argnames) + + return parametrize_argnames + + def getfixtureinfo( + self, node: nodes.Node, func, cls, funcargs: bool = True + ) -> FuncFixtureInfo: + if funcargs and not getattr(node, "nofuncargs", False): + argnames = getfuncargnames(func, name=node.name, cls=cls) + else: + argnames = () + + usefixtures = tuple( + arg for mark in node.iter_markers(name="usefixtures") for arg in mark.args + ) + initialnames = usefixtures + argnames + fm = node.session._fixturemanager + initialnames, names_closure, arg2fixturedefs = fm.getfixtureclosure( + initialnames, node, ignore_args=self._get_direct_parametrize_args(node) + ) + return FuncFixtureInfo(argnames, initialnames, names_closure, arg2fixturedefs) + + def pytest_plugin_registered(self, plugin: _PluggyPlugin) -> None: + nodeid = None + try: + p = absolutepath(plugin.__file__) # type: ignore[attr-defined] + except AttributeError: + pass + else: + # Construct the base nodeid which is later used to check + # what fixtures are visible for particular tests (as denoted + # by their test id). + if p.name.startswith("conftest.py"): + try: + nodeid = str(p.parent.relative_to(self.config.rootpath)) + except ValueError: + nodeid = "" + if nodeid == ".": + nodeid = "" + if os.sep != nodes.SEP: + nodeid = nodeid.replace(os.sep, nodes.SEP) + + self.parsefactories(plugin, nodeid) + + def _getautousenames(self, nodeid: str) -> Iterator[str]: + """Return the names of autouse fixtures applicable to nodeid.""" + for parentnodeid in nodes.iterparentnodeids(nodeid): + basenames = self._nodeid_autousenames.get(parentnodeid) + if basenames: + yield from basenames + + def getfixtureclosure( + self, + fixturenames: Tuple[str, ...], + parentnode: nodes.Node, + ignore_args: Sequence[str] = (), + ) -> Tuple[Tuple[str, ...], 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). + + parentid = parentnode.nodeid + fixturenames_closure = list(self._getautousenames(parentid)) + + def merge(otherlist: Iterable[str]) -> None: + for arg in otherlist: + if arg not in fixturenames_closure: + fixturenames_closure.append(arg) + + merge(fixturenames) + + # At this point, fixturenames_closure contains what we call "initialnames", + # which is a set of fixturenames the function immediately requests. We + # need to return it as well, so save this. + initialnames = tuple(fixturenames_closure) + + arg2fixturedefs: Dict[str, Sequence[FixtureDef[Any]]] = {} + lastlen = -1 + while lastlen != len(fixturenames_closure): + lastlen = len(fixturenames_closure) + for argname in fixturenames_closure: + if argname in ignore_args: + continue + if argname in arg2fixturedefs: + continue + fixturedefs = self.getfixturedefs(argname, parentid) + if fixturedefs: + arg2fixturedefs[argname] = fixturedefs + merge(fixturedefs[-1].argnames) + + def sort_by_scope(arg_name: str) -> int: + try: + fixturedefs = arg2fixturedefs[arg_name] + except KeyError: + return scopes.index("function") + else: + return fixturedefs[-1].scopenum + + fixturenames_closure.sort(key=sort_by_scope) + return initialnames, 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 parsefactories( + self, node_or_obj, nodeid=NOTSET, unittest: bool = False + ) -> None: + if nodeid is not NOTSET: + holderobj = node_or_obj + else: + holderobj = node_or_obj.obj + nodeid = node_or_obj.nodeid + if holderobj in self._holderobjseen: + return + + self._holderobjseen.add(holderobj) + autousenames = [] + for name in dir(holderobj): + # The attribute can be an arbitrary descriptor, so the attribute + # access below can raise. safe_getatt() ignores such exceptions. + obj = safe_getattr(holderobj, name, None) + marker = getfixturemarker(obj) + if not isinstance(marker, FixtureFunctionMarker): + # Magic globals with __getattr__ might have got us a wrong + # fixture attribute. + continue + + if marker.name: + name = marker.name + + # During fixture definition we wrap the original fixture function + # to issue a warning if called directly, so here we unwrap it in + # order to not emit the warning when pytest itself calls the + # fixture function. + obj = get_real_method(obj, holderobj) + + fixture_def = FixtureDef( + fixturemanager=self, + baseid=nodeid, + argname=name, + func=obj, + scope=marker.scope, + params=marker.params, + unittest=unittest, + ids=marker.ids, + ) + + 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 marker.autouse: + autousenames.append(name) + + if autousenames: + self._nodeid_autousenames.setdefault(nodeid or "", []).extend(autousenames) + + def getfixturedefs( + self, argname: str, nodeid: str + ) -> Optional[Sequence[FixtureDef[Any]]]: + """Get a list of fixtures which are applicable to the given node id. + + :param str argname: Name of the fixture to search for. + :param str nodeid: Full node id of the requesting test. + :rtype: Sequence[FixtureDef] + """ + try: + fixturedefs = self._arg2fixturedefs[argname] + except KeyError: + return None + return tuple(self._matchfactories(fixturedefs, nodeid)) + + def _matchfactories( + self, fixturedefs: Iterable[FixtureDef[Any]], nodeid: str + ) -> Iterator[FixtureDef[Any]]: + parentnodeids = set(nodes.iterparentnodeids(nodeid)) + for fixturedef in fixturedefs: + if fixturedef.baseid in parentnodeids: + yield fixturedef 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..8b93ed5 --- /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.""" +import types +from typing import Iterator +from typing import List +from typing import Union + + +def freeze_includes() -> List[str]: + """Return a list of module names used by pytest that should be + included by cx_freeze.""" + import py + import _pytest + + result = list(_iter_all_modules(py)) + result += list(_iter_all_modules(_pytest)) + return result + + +def _iter_all_modules( + package: Union[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__ # type: ignore[attr-defined] + 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..4384d07 --- /dev/null +++ b/venv/Lib/site-packages/_pytest/helpconfig.py @@ -0,0 +1,261 @@ +"""Version info, help messages, tracing configuration.""" +import os +import sys +from argparse import Action +from typing import List +from typing import Optional +from typing import Union + +import py + +import pytest +from _pytest.config import Config +from _pytest.config import ExitCode +from _pytest.config import PrintHelp +from _pytest.config.argparsing import Parser + + +class HelpAction(Action): + """An argparse Action that will raise an exception in order to skip the + rest of the argument parsing when --help is passed. + + This prevents argparse from quitting due to missing required arguments + when any are defined, for example by ``pytest_addoption``. + This is similar to the way that the builtin argparse --help option is + implemented by raising SystemExit. + """ + + def __init__(self, option_strings, dest=None, default=False, help=None): + super().__init__( + option_strings=option_strings, + dest=dest, + const=True, + default=default, + nargs=0, + help=help, + ) + + def __call__(self, parser, namespace, values, option_string=None): + setattr(namespace, self.dest, self.const) + + # We should only skip the rest of the parsing after preparse is done. + if getattr(parser._parser, "after_preparse", False): + raise PrintHelp + + +def pytest_addoption(parser: Parser) -> None: + group = parser.getgroup("debugconfig") + group.addoption( + "--version", + "-V", + action="count", + default=0, + dest="version", + help="display pytest version and information about plugins." + "When given twice, also display information about plugins.", + ) + group._addoption( + "-h", + "--help", + action=HelpAction, + dest="help", + help="show help message and configuration info", + ) + group._addoption( + "-p", + action="append", + dest="plugins", + default=[], + metavar="name", + help="early-load given plugin module name or entry point (multi-allowed).\n" + "To avoid loading of plugins, use the `no:` prefix, e.g. " + "`no:doctest`.", + ) + group.addoption( + "--traceconfig", + "--trace-config", + action="store_true", + default=False, + help="trace considerations of conftest.py files.", + ) + group.addoption( + "--debug", + action="store_true", + dest="debug", + default=False, + help="store internal tracing debug information in 'pytestdebug.log'.", + ) + group._addoption( + "-o", + "--override-ini", + dest="override_ini", + action="append", + help='override ini option with "option=value" style, e.g. `-o xfail_strict=True -o cache_dir=cache`.', + ) + + +@pytest.hookimpl(hookwrapper=True) +def pytest_cmdline_parse(): + outcome = yield + config: Config = outcome.get_result() + if config.option.debug: + path = os.path.abspath("pytestdebug.log") + debugfile = open(path, "w") + debugfile.write( + "versions pytest-%s, py-%s, " + "python-%s\ncwd=%s\nargs=%s\n\n" + % ( + pytest.__version__, + py.__version__, + ".".join(map(str, sys.version_info)), + os.getcwd(), + config.invocation_params.args, + ) + ) + config.trace.root.setwriter(debugfile.write) + undo_tracing = config.pluginmanager.enable_tracing() + sys.stderr.write("writing pytestdebug information to %s\n" % path) + + def unset_tracing() -> None: + debugfile.close() + sys.stderr.write("wrote pytestdebug information to %s\n" % debugfile.name) + config.trace.root.setwriter(None) + undo_tracing() + + config.add_cleanup(unset_tracing) + + +def showversion(config: Config) -> None: + if config.option.version > 1: + sys.stderr.write( + "This is pytest version {}, imported from {}\n".format( + pytest.__version__, pytest.__file__ + ) + ) + plugininfo = getpluginversioninfo(config) + if plugininfo: + for line in plugininfo: + sys.stderr.write(line + "\n") + else: + sys.stderr.write(f"pytest {pytest.__version__}\n") + + +def pytest_cmdline_main(config: Config) -> Optional[Union[int, ExitCode]]: + if config.option.version > 0: + showversion(config) + return 0 + elif config.option.help: + config._do_configure() + showhelp(config) + config._ensure_unconfigure() + return 0 + return None + + +def showhelp(config: Config) -> None: + import textwrap + + reporter = config.pluginmanager.get_plugin("terminalreporter") + tw = reporter._tw + tw.write(config._parser.optparser.format_help()) + tw.line() + tw.line( + "[pytest] ini-options in the first pytest.ini|tox.ini|setup.cfg file found:" + ) + tw.line() + + columns = tw.fullwidth # costly call + indent_len = 24 # based on argparse's max_help_position=24 + indent = " " * indent_len + for name in config._parser._ininames: + help, type, default = config._parser._inidict[name] + if type is None: + type = "string" + if help is None: + raise TypeError(f"help argument cannot be None for {name}") + spec = f"{name} ({type}):" + tw.write(" %s" % 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 = [ + ("PYTEST_ADDOPTS", "extra command line options"), + ("PYTEST_PLUGINS", "comma-separated plugins to load during startup"), + ("PYTEST_DISABLE_PLUGIN_AUTOLOAD", "set to disable plugin auto-loading"), + ("PYTEST_DEBUG", "set to enable debug tracing of pytest's internals"), + ] + for name, help in vars: + tw.line(f" {name:<24} {help}") + tw.line() + tw.line() + + tw.line("to see available markers type: pytest --markers") + tw.line("to see available fixtures type: pytest --fixtures") + tw.line( + "(shown according to specified file_or_dir or current dir " + "if not specified; fixtures with leading '_' are only shown " + "with the '-v' option" + ) + + for warningreport in reporter.stats.get("warnings", []): + tw.line("warning : " + warningreport.message, red=True) + return + + +conftest_options = [("pytest_plugins", "list of plugin names to load")] + + +def getpluginversioninfo(config: Config) -> List[str]: + lines = [] + plugininfo = config.pluginmanager.list_plugin_distinfo() + if plugininfo: + lines.append("setuptools registered 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__} pylib-{py.__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..e499b74 --- /dev/null +++ b/venv/Lib/site-packages/_pytest/hookspec.py @@ -0,0 +1,891 @@ +"""Hook specifications for pytest plugins which are invoked by pytest itself +and by builtin plugins.""" +from typing import Any +from typing import Dict +from typing import List +from typing import Mapping +from typing import Optional +from typing import Sequence +from typing import Tuple +from typing import TYPE_CHECKING +from typing import Union + +import py.path +from pluggy import HookspecMarker + +from _pytest.deprecated import WARNING_CAPTURED_HOOK + +if TYPE_CHECKING: + import pdb + import warnings + from typing_extensions import Literal + + from _pytest._code.code import ExceptionRepr + from _pytest.code import ExceptionInfo + from _pytest.config import Config + from _pytest.config import ExitCode + from _pytest.config import PytestPluginManager + from _pytest.config import _PluggyPlugin + 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 Function + from _pytest.python import Metafunc + from _pytest.python import Module + from _pytest.python import PyCollector + from _pytest.reports import CollectReport + from _pytest.reports import TestReport + from _pytest.runner import CallInfo + from _pytest.terminal import TerminalReporter + + +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 + ``pluginmanager.add_hookspecs(module_or_class, prefix)``. + + :param _pytest.config.PytestPluginManager pluginmanager: pytest plugin manager. + + .. note:: + This hook is incompatible with ``hookwrapper=True``. + """ + + +@hookspec(historic=True) +def pytest_plugin_registered( + plugin: "_PluggyPlugin", manager: "PytestPluginManager" +) -> None: + """A new pytest plugin got registered. + + :param plugin: The plugin module or instance. + :param _pytest.config.PytestPluginManager manager: pytest plugin manager. + + .. note:: + This hook is incompatible with ``hookwrapper=True``. + """ + + +@hookspec(historic=True) +def pytest_addoption(parser: "Parser", pluginmanager: "PytestPluginManager") -> None: + """Register argparse-style options and ini-style config values, + called once at the beginning of a test run. + + .. note:: + + This function should be implemented only in plugins or ``conftest.py`` + files situated at the tests root directory due to how pytest + :ref:`discovers plugins during startup `. + + :param _pytest.config.argparsing.Parser parser: + To add command line options, call + :py:func:`parser.addoption(...) <_pytest.config.argparsing.Parser.addoption>`. + To add ini-file values call :py:func:`parser.addini(...) + <_pytest.config.argparsing.Parser.addini>`. + + :param _pytest.config.PytestPluginManager pluginmanager: + pytest plugin manager, which can be used to install :py:func:`hookspec`'s + or :py:func:`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 <_pytest.config.Config>` object, respectively: + + - :py:func:`config.getoption(name) <_pytest.config.Config.getoption>` to + retrieve the value of a command line option. + + - :py:func:`config.getini(name) <_pytest.config.Config.getini>` to retrieve + a value read from an ini-style file. + + The config object is passed around on many internal objects via the ``.config`` + attribute or can be retrieved as the ``pytestconfig`` fixture. + + .. note:: + This hook is incompatible with ``hookwrapper=True``. + """ + + +@hookspec(historic=True) +def pytest_configure(config: "Config") -> None: + """Allow plugins and conftest files to perform initial configuration. + + This hook is called for every plugin and initial conftest file + after command line options have been parsed. + + After that, the hook is called for other conftest files as they are + imported. + + .. note:: + This hook is incompatible with ``hookwrapper=True``. + + :param _pytest.config.Config config: The pytest config object. + """ + + +# ------------------------------------------------------------------------- +# 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] +) -> Optional["Config"]: + """Return an initialized config object, parsing the specified args. + + Stops at first non-None result, see :ref:`firstresult`. + + .. note:: + This hook will only be called for plugin classes passed to the + ``plugins`` arg when using `pytest.main`_ to perform an in-process + test run. + + :param _pytest.config.PytestPluginManager pluginmanager: Pytest plugin manager. + :param List[str] args: List of arguments passed on the command line. + """ + + +def pytest_cmdline_preparse(config: "Config", args: List[str]) -> None: + """(**Deprecated**) modify command line arguments before option parsing. + + This hook is considered deprecated and will be removed in a future pytest version. Consider + using :func:`pytest_load_initial_conftests` instead. + + .. note:: + This hook will not be called for ``conftest.py`` files, only for setuptools plugins. + + :param _pytest.config.Config config: The pytest config object. + :param List[str] args: Arguments passed on the command line. + """ + + +@hookspec(firstresult=True) +def pytest_cmdline_main(config: "Config") -> Optional[Union["ExitCode", int]]: + """Called for performing the main command line action. The default + implementation will invoke the configure hooks and runtest_mainloop. + + .. note:: + This hook will not be called for ``conftest.py`` files, only for setuptools plugins. + + Stops at first non-None result, see :ref:`firstresult`. + + :param _pytest.config.Config config: The pytest config object. + """ + + +def pytest_load_initial_conftests( + early_config: "Config", parser: "Parser", args: List[str] +) -> None: + """Called to implement the loading of initial conftest files ahead + of command line option parsing. + + .. note:: + This hook will not be called for ``conftest.py`` files, only for setuptools plugins. + + :param _pytest.config.Config early_config: The pytest config object. + :param List[str] args: Arguments passed on the command line. + :param _pytest.config.argparsing.Parser parser: To add command line options. + """ + + +# ------------------------------------------------------------------------- +# collection hooks +# ------------------------------------------------------------------------- + + +@hookspec(firstresult=True) +def pytest_collection(session: "Session") -> Optional[object]: + """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 pytest.Session session: The pytest session object. + """ + + +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. + + :param pytest.Session session: The pytest session object. + :param _pytest.config.Config config: The pytest config object. + :param List[pytest.Item] items: List of item objects. + """ + + +def pytest_collection_finish(session: "Session") -> None: + """Called after collection has been performed and modified. + + :param pytest.Session session: The pytest session object. + """ + + +@hookspec(firstresult=True) +def pytest_ignore_collect(path: py.path.local, config: "Config") -> Optional[bool]: + """Return True to prevent considering this path for collection. + + 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 py.path.local path: The path to analyze. + :param _pytest.config.Config config: The pytest config object. + """ + + +def pytest_collect_file( + path: py.path.local, parent: "Collector" +) -> "Optional[Collector]": + """Create a Collector for the given path, or None if not relevant. + + The new node needs to have the specified ``parent`` as a parent. + + :param py.path.local path: The path to collect. + """ + + +# logging hooks for collection + + +def pytest_collectstart(collector: "Collector") -> None: + """Collector starts collecting.""" + + +def pytest_itemcollected(item: "Item") -> None: + """We just collected a test item.""" + + +def pytest_collectreport(report: "CollectReport") -> None: + """Collector finished collecting.""" + + +def pytest_deselected(items: Sequence["Item"]) -> None: + """Called for deselected test items, e.g. by keyword. + + May be called multiple times. + """ + + +@hookspec(firstresult=True) +def pytest_make_collect_report(collector: "Collector") -> "Optional[CollectReport]": + """Perform ``collector.collect()`` and return a CollectReport. + + Stops at first non-None result, see :ref:`firstresult`. + """ + + +# ------------------------------------------------------------------------- +# Python test function related hooks +# ------------------------------------------------------------------------- + + +@hookspec(firstresult=True) +def pytest_pycollect_makemodule(path: py.path.local, parent) -> Optional["Module"]: + """Return a Module collector or None for the given path. + + This hook will be called for each matching test module path. + The 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 py.path.local path: The path of module to collect. + """ + + +@hookspec(firstresult=True) +def pytest_pycollect_makeitem( + collector: "PyCollector", name: str, obj: object +) -> Union[None, "Item", "Collector", List[Union["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`. + """ + + +@hookspec(firstresult=True) +def pytest_pyfunc_call(pyfuncitem: "Function") -> Optional[object]: + """Call underlying test function. + + Stops at first non-None result, see :ref:`firstresult`. + """ + + +def pytest_generate_tests(metafunc: "Metafunc") -> None: + """Generate (multiple) parametrized calls to a test function.""" + + +@hookspec(firstresult=True) +def pytest_make_parametrize_id( + config: "Config", val: object, argname: str +) -> Optional[str]: + """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 _pytest.config.Config config: The pytest config object. + :param val: The parametrized value. + :param str argname: The automatic parameter name produced by pytest. + """ + + +# ------------------------------------------------------------------------- +# runtest related hooks +# ------------------------------------------------------------------------- + + +@hookspec(firstresult=True) +def pytest_runtestloop(session: "Session") -> Optional[object]: + """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 pytest.Session 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. + """ + + +@hookspec(firstresult=True) +def pytest_runtest_protocol( + item: "Item", nextitem: "Optional[Item]" +) -> Optional[object]: + """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 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. + """ + + +def pytest_runtest_logstart( + nodeid: str, location: Tuple[str, Optional[int], str] +) -> None: + """Called at the start of running the runtest protocol for a single item. + + See :func:`pytest_runtest_protocol` for a description of the runtest protocol. + + :param str nodeid: Full node ID of the item. + :param location: A tuple of ``(filename, lineno, testname)``. + """ + + +def pytest_runtest_logfinish( + nodeid: str, location: Tuple[str, Optional[int], str] +) -> None: + """Called at the end of running the runtest protocol for a single item. + + See :func:`pytest_runtest_protocol` for a description of the runtest protocol. + + :param str nodeid: Full node ID of the item. + :param location: A tuple of ``(filename, lineno, testname)``. + """ + + +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). + """ + + +def pytest_runtest_call(item: "Item") -> None: + """Called to run the test for test item (the call phase). + + The default implementation calls ``item.runtest()``. + """ + + +def pytest_runtest_teardown(item: "Item", nextitem: Optional["Item"]) -> 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 nextitem: + The scheduled-to-be-next test item (None if no further test item is + scheduled). This argument can be used to perform exact teardowns, + i.e. calling just enough finalizers so that nextitem only needs to + call setup-functions. + """ + + +@hookspec(firstresult=True) +def pytest_runtest_makereport( + item: "Item", call: "CallInfo[None]" +) -> Optional["TestReport"]: + """Called to create a :py:class:`_pytest.reports.TestReport` for each of + the setup, call and teardown runtest phases of a test item. + + See :func:`pytest_runtest_protocol` for a description of the runtest protocol. + + :param CallInfo[None] call: The ``CallInfo`` for the phase. + + Stops at first non-None result, see :ref:`firstresult`. + """ + + +def pytest_runtest_logreport(report: "TestReport") -> None: + """Process the :py:class:`_pytest.reports.TestReport` produced for each + of the setup, call and teardown runtest phases of an item. + + See :func:`pytest_runtest_protocol` for a description of the runtest protocol. + """ + + +@hookspec(firstresult=True) +def pytest_report_to_serializable( + config: "Config", report: Union["CollectReport", "TestReport"], +) -> Optional[Dict[str, Any]]: + """Serialize the given report object into a data structure suitable for + sending over the wire, e.g. converted to JSON.""" + + +@hookspec(firstresult=True) +def pytest_report_from_serializable( + config: "Config", data: Dict[str, Any], +) -> Optional[Union["CollectReport", "TestReport"]]: + """Restore a report object previously serialized with pytest_report_to_serializable().""" + + +# ------------------------------------------------------------------------- +# Fixture related hooks +# ------------------------------------------------------------------------- + + +@hookspec(firstresult=True) +def pytest_fixture_setup( + fixturedef: "FixtureDef[Any]", request: "SubRequest" +) -> Optional[object]: + """Perform fixture setup execution. + + :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. + """ + + +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``).""" + + +# ------------------------------------------------------------------------- +# 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 pytest.Session session: The pytest session object. + """ + + +def pytest_sessionfinish( + session: "Session", exitstatus: Union[int, "ExitCode"], +) -> None: + """Called after whole test run finished, right before returning the exit status to the system. + + :param pytest.Session session: The pytest session object. + :param int exitstatus: The status which pytest will return to the system. + """ + + +def pytest_unconfigure(config: "Config") -> None: + """Called before test process is exited. + + :param _pytest.config.Config config: The pytest config object. + """ + + +# ------------------------------------------------------------------------- +# hooks for customizing the assert methods +# ------------------------------------------------------------------------- + + +def pytest_assertrepr_compare( + config: "Config", op: str, left: object, right: object +) -> Optional[List[str]]: + """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 _pytest.config.Config config: The pytest config object. + """ + + +def pytest_assertion_pass(item: "Item", lineno: int, orig: str, expl: str) -> None: + """**(Experimental)** Called whenever an assertion passes. + + .. versionadded:: 5.0 + + Use this hook to do some processing after a passing assertion. + The original assertion information is available in the `orig` string + and the pytest introspected assertion information is available in the + `expl` string. + + This hook must be explicitly enabled by the ``enable_assertion_pass_hook`` + ini-file option: + + .. code-block:: ini + + [pytest] + enable_assertion_pass_hook=true + + You need to **clean the .pyc** files in your project directory and interpreter libraries + when enabling this option, as assertions will require to be re-written. + + :param pytest.Item item: pytest item object of current test. + :param int lineno: Line number of the assert statement. + :param str orig: String with the original assertion. + :param str expl: String with the assert explanation. + + .. note:: + + This hook is **experimental**, so its parameters or even the hook itself might + be changed/removed without warning in any future pytest release. + + If you find this hook useful, please share your feedback in an issue. + """ + + +# ------------------------------------------------------------------------- +# Hooks for influencing reporting (invoked from _pytest_terminal). +# ------------------------------------------------------------------------- + + +def pytest_report_header( + config: "Config", startdir: py.path.local +) -> Union[str, List[str]]: + """Return a string or list of strings to be displayed as header info for terminal reporting. + + :param _pytest.config.Config config: The pytest config object. + :param py.path.local startdir: The starting dir. + + .. 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 `. + + .. note:: + + This function should be implemented only in plugins or ``conftest.py`` + files situated at the tests root directory due to how pytest + :ref:`discovers plugins during startup `. + """ + + +def pytest_report_collectionfinish( + config: "Config", startdir: py.path.local, items: Sequence["Item"], +) -> Union[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 _pytest.config.Config config: The pytest config object. + :param py.path.local startdir: The starting dir. + :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 `. + """ + + +@hookspec(firstresult=True) +def pytest_report_teststatus( + report: Union["CollectReport", "TestReport"], config: "Config" +) -> Tuple[ + str, str, Union[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 _pytest.config.Config config: The pytest config object. + + Stops at first non-None result, see :ref:`firstresult`. + """ + + +def pytest_terminal_summary( + terminalreporter: "TerminalReporter", exitstatus: "ExitCode", config: "Config", +) -> None: + """Add a section to terminal summary reporting. + + :param _pytest.terminal.TerminalReporter terminalreporter: The internal terminal reporter object. + :param int exitstatus: The exit status that will be reported back to the OS. + :param _pytest.config.Config config: The pytest config object. + + .. versionadded:: 4.2 + The ``config`` parameter. + """ + + +@hookspec(historic=True, warn_on_impl=WARNING_CAPTURED_HOOK) +def pytest_warning_captured( + warning_message: "warnings.WarningMessage", + when: "Literal['config', 'collect', 'runtest']", + item: Optional["Item"], + location: Optional[Tuple[str, int, str]], +) -> None: + """(**Deprecated**) Process a warning captured by the internal pytest warnings plugin. + + .. deprecated:: 6.0 + + This hook is considered deprecated and will be removed in a future pytest version. + Use :func:`pytest_warning_recorded` instead. + + :param warnings.WarningMessage warning_message: + The captured warning. This is the same object produced by :py:func:`warnings.catch_warnings`, and contains + the same attributes as the parameters of :py:func:`warnings.showwarning`. + + :param str when: + Indicates when the warning was captured. Possible values: + + * ``"config"``: during pytest configuration/initialization stage. + * ``"collect"``: during test collection. + * ``"runtest"``: during test execution. + + :param pytest.Item|None item: + The item being executed if ``when`` is ``"runtest"``, otherwise ``None``. + + :param tuple 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. + """ + + +@hookspec(historic=True) +def pytest_warning_recorded( + warning_message: "warnings.WarningMessage", + when: "Literal['config', 'collect', 'runtest']", + nodeid: str, + location: Optional[Tuple[str, int, str]], +) -> None: + """Process a warning captured by the internal pytest warnings plugin. + + :param warnings.WarningMessage warning_message: + The captured warning. This is the same object produced by :py:func:`warnings.catch_warnings`, and contains + the same attributes as the parameters of :py:func:`warnings.showwarning`. + + :param str when: + Indicates when the warning was captured. Possible values: + + * ``"config"``: during pytest configuration/initialization stage. + * ``"collect"``: during test collection. + * ``"runtest"``: during test execution. + + :param str nodeid: + Full id of the item. + + :param tuple|None 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 + """ + + +# ------------------------------------------------------------------------- +# Hooks for influencing skipping +# ------------------------------------------------------------------------- + + +def pytest_markeval_namespace(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 _pytest.config.Config config: The pytest config object. + :returns: A dictionary of additional globals to add. + """ + + +# ------------------------------------------------------------------------- +# error handling and internal debugging hooks +# ------------------------------------------------------------------------- + + +def pytest_internalerror( + excrepr: "ExceptionRepr", excinfo: "ExceptionInfo[BaseException]", +) -> Optional[bool]: + """Called for internal errors. + + Return True to suppress the fallback handling of printing an + INTERNALERROR message directly to sys.stderr. + """ + + +def pytest_keyboard_interrupt( + excinfo: "ExceptionInfo[Union[KeyboardInterrupt, Exit]]", +) -> None: + """Called for keyboard interrupt.""" + + +def pytest_exception_interact( + node: Union["Item", "Collector"], + call: "CallInfo[Any]", + report: Union["CollectReport", "TestReport"], +) -> None: + """Called when an exception was raised which can potentially be + interactively handled. + + May be called during collection (see :py:func:`pytest_make_collect_report`), + in which case ``report`` is a :py:class:`_pytest.reports.CollectReport`. + + May be called during runtest of an item (see :py:func:`pytest_runtest_protocol`), + in which case ``report`` is a :py:class:`_pytest.reports.TestReport`. + + This hook is not called if the exception that was raised is an internal + exception like ``skip.Exception``. + """ + + +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 _pytest.config.Config config: The pytest config object. + :param pdb.Pdb pdb: The Pdb instance. + """ + + +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 _pytest.config.Config config: The pytest config object. + :param pdb.Pdb pdb: The Pdb instance. + """ diff --git a/venv/Lib/site-packages/_pytest/junitxml.py b/venv/Lib/site-packages/_pytest/junitxml.py new file mode 100644 index 0000000..c4761cd --- /dev/null +++ b/venv/Lib/site-packages/_pytest/junitxml.py @@ -0,0 +1,700 @@ +"""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 +""" +import functools +import os +import platform +import re +import xml.etree.ElementTree as ET +from datetime import datetime +from typing import Callable +from typing import Dict +from typing import List +from typing import Match +from typing import Optional +from typing import Tuple +from typing import Union + +import pytest +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.store import StoreKey +from _pytest.terminal import TerminalReporter + + +xml_key = StoreKey["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: Match[str]) -> str: + i = ord(matchobj.group()) + if i <= 0xFF: + return "#x%02X" % i + else: + return "#x%04X" % i + + # 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 = {} +families["_base"] = {"testcase": ["classname", "name"]} +families["_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: Union[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 + 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) -> Optional[ET.Element]: + """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.keys(): + 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="%.3f" % self.duration) + 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: Optional[str] = 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: Optional[ReprFileLocation] = 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: Optional[ReprFileLocation] = 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", 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=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 becuase mypy doesn't like overriding a method. + # Also the return value doesn't match... + self.to_xml = lambda: data # type: ignore[assignment] + + +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._store.get(xml_key, None) + if xml is not None and xml.family not in ("xunit1", "legacy"): + request.node.warn( + PytestWarning( + "{fixture_name} is incompatible with junit_family '{family}' (use 'legacy' or 'xunit1')".format( + fixture_name=fixture_name, family=xml.family + ) + ) + ) + + +@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._store.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") + + ``name`` must be a string, ``value`` will be converted to a string and properly xml-escaped. + + .. 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 --junitxml was not passed in the command-line.""" + __tracebackhide__ = True + _check_record_param_type("name", name) + + xml = request.config._store.get(xml_key, None) + if xml is not None: + record_func = xml.add_global_property # noqa + 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._store[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._store[xml_key]) + + +def pytest_unconfigure(config: Config) -> None: + xml = config._store.get(xml_key, None) + if xml: + del config._store[xml_key] + config.pluginmanager.unregister(xml) + + +def mangle_test_address(address: str) -> List[str]: + path, possible_open_bracket, params = address.partition("[") + names = path.split("::") + try: + names.remove("()") + except ValueError: + pass + # 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: Optional[str], + 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[Union[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)) + if reporter is not None: + reporter.finalize() + + def node_reporter(self, report: Union[TestReport, str]) -> _NodeReporter: + nodeid: Union[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) + + for propname, propvalue in report.user_properties: + reporter.add_property(propname, str(propvalue)) + + 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 == "total" or report.when == self.report_duration: + 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_time = timing.time() + + def pytest_sessionfinish(self) -> None: + dirname = os.path.dirname(os.path.abspath(self.logfile)) + if not os.path.isdir(dirname): + os.makedirs(dirname) + logfile = open(self.logfile, "w", encoding="utf-8") + suite_stop_time = timing.time() + suite_time_delta = suite_stop_time - self.suite_start_time + + 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="%.3f" % suite_time_delta, + timestamp=datetime.fromtimestamp(self.suite_start_time).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.append(suite_node) + logfile.write(ET.tostring(testsuites, encoding="unicode")) + logfile.close() + + def pytest_terminal_summary(self, terminalreporter: TerminalReporter) -> None: + terminalreporter.write_sep("-", f"generated xml file: {self.logfile}") + + def add_global_property(self, name: str, value: object) -> None: + __tracebackhide__ = True + _check_record_param_type("name", name) + self.global_properties.append((name, bin_xml_escape(value))) + + def _get_global_properties_node(self) -> Optional[ET.Element]: + """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/logging.py b/venv/Lib/site-packages/_pytest/logging.py new file mode 100644 index 0000000..2e48473 --- /dev/null +++ b/venv/Lib/site-packages/_pytest/logging.py @@ -0,0 +1,821 @@ +"""Access and control log capturing.""" +import logging +import os +import re +import sys +from contextlib import contextmanager +from io import StringIO +from pathlib import Path +from typing import AbstractSet +from typing import Dict +from typing import Generator +from typing import List +from typing import Mapping +from typing import Optional +from typing import Tuple +from typing import TypeVar +from typing import Union + +from _pytest import nodes +from _pytest._io import TerminalWriter +from _pytest.capture import CaptureManager +from _pytest.compat import final +from _pytest.compat import nullcontext +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.store import StoreKey +from _pytest.terminal import TerminalReporter + + +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 = StoreKey["LogCaptureHandler"]() +caplog_records_key = StoreKey[Dict[str, List[logging.LogRecord]]]() + + +def _remove_ansi_escape_sequences(text: str) -> str: + return _ANSI_ESCAPE_SEQ.sub("", text) + + +class ColoredLevelFormatter(logging.Formatter): + """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*s)") + + def __init__(self, terminalwriter: TerminalWriter, *args, **kwargs) -> None: + super().__init__(*args, **kwargs) + self._original_fmt = self._style._fmt + self._level_to_fmt_mapping: Dict[int, str] = {} + + 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() + + for level, color_opts in self.LOGLEVEL_COLOROPTS.items(): + 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 = 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: Union[int, str, bool, None]) -> None: + super().__init__(fmt) + self._auto_indent = self._get_auto_indent(auto_indent) + + @staticmethod + def _update_message( + record_dict: Dict[str, object], message: str + ) -> Dict[str, object]: + tmp = record_dict.copy() + tmp["message"] = message + return tmp + + @staticmethod + def _get_auto_indent(auto_indent_option: Union[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) # type: ignore[attr-defined] + else: + auto_indent = self._auto_indent + + if auto_indent: + lines = record.message.splitlines() + formatted = self._fmt % self._update_message(record.__dict__, 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.\n" + "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 as used by the logging module.", + ) + add_option_ini( + "--log-date-format", + dest="log_date_format", + default=DEFAULT_LOG_DATE_FORMAT, + help="log date format as 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 as used by the logging module.", + ) + add_option_ini( + "--log-cli-date-format", + dest="log_cli_date_format", + default=None, + help="log date format as 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-level", + dest="log_file_level", + default=None, + help="log file logging level.", + ) + add_option_ini( + "--log-file-format", + dest="log_file_format", + default=DEFAULT_LOG_FORMAT, + help="log format as used by the logging module.", + ) + add_option_ini( + "--log-file-date-format", + dest="log_file_date_format", + default=DEFAULT_LOG_DATE_FORMAT, + help="log date format as 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.", + ) + + +_HandlerType = TypeVar("_HandlerType", bound=logging.Handler) + + +# Not using @contextmanager for performance reasons. +class catching_logs: + """Context manager that prepares the whole logging machinery properly.""" + + __slots__ = ("handler", "level", "orig_level") + + def __init__(self, handler: _HandlerType, level: Optional[int] = None) -> None: + self.handler = handler + self.level = level + + def __enter__(self): + 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, type, value, traceback): + 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.""" + + stream: StringIO + + 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 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 + + +@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: Optional[int] = None + # Dict of log name -> log level. + self._initial_logger_levels: Dict[Optional[str], int] = {} + + def _finalize(self) -> None: + """Finalize the fixture. + + This restores the log 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) + + @property + def handler(self) -> LogCaptureHandler: + """Get the logging handler used by the fixture. + + :rtype: LogCaptureHandler + """ + return self._item._store[caplog_handler_key] + + def get_records(self, when: str) -> List[logging.LogRecord]: + """Get the logging records for one of the possible test phases. + + :param str 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. + :rtype: List[logging.LogRecord] + + .. versionadded:: 3.4 + """ + return self._item._store[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.reset() + + def set_level(self, level: Union[int, str], logger: Optional[str] = None) -> None: + """Set the level of a logger for the duration of a test. + + .. 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. + + :param int level: The level. + :param str 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) + + @contextmanager + def at_level( + self, level: int, logger: Optional[str] = None + ) -> Generator[None, None, 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. + + :param int level: The level. + :param str 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) + try: + yield + finally: + logger_obj.setLevel(orig_level) + self.handler.setLevel(handler_orig_level) + + +@fixture +def caplog(request: FixtureRequest) -> Generator[LogCaptureFixture, None, None]: + """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) -> Optional[int]: + 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( + "'{}' is not recognized as a logging level name for " + "'{}'. Please consider passing the " + "logging level num instead.".format(log_level, setting_name) + ) 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_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_handler = _FileHandler(log_file, mode="w", 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 = logging.Formatter( + 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") + capture_manager = config.pluginmanager.get_plugin("capturemanager") + # if capturemanager plugin is disabled, live logging still works. + self.log_cli_handler: Union[ + _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) + + 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 = logging.Formatter(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) + + stream = fpath.open(mode="w", encoding="UTF-8") + if sys.version_info >= (3, 7): + old_stream = self.log_file_handler.setStream(stream) + else: + old_stream = self.log_file_handler.stream + self.log_file_handler.acquire() + try: + self.log_file_handler.flush() + self.log_file_handler.stream = stream + finally: + self.log_file_handler.release() + if old_stream: + old_stream.close() + + def _log_cli_enabled(self): + """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(hookwrapper=True, tryfirst=True) + def pytest_sessionstart(self) -> Generator[None, None, 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): + yield + + @hookimpl(hookwrapper=True, tryfirst=True) + def pytest_collection(self) -> Generator[None, None, 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): + yield + + @hookimpl(hookwrapper=True) + def pytest_runtestloop(self, session: Session) -> Generator[None, None, None]: + if session.config.option.collectonly: + yield + return + + if self._log_cli_enabled() and self._config.getoption("verbose") < 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): + 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") + + def _runtest_for(self, item: nodes.Item, when: str) -> Generator[None, None, 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._store[caplog_records_key][when] = caplog_handler.records + item._store[caplog_handler_key] = caplog_handler + + yield + + log = report_handler.stream.getvalue().strip() + item.add_report_section(when, "log", log) + + @hookimpl(hookwrapper=True) + def pytest_runtest_setup(self, item: nodes.Item) -> Generator[None, None, None]: + self.log_cli_handler.set_when("setup") + + empty: Dict[str, List[logging.LogRecord]] = {} + item._store[caplog_records_key] = empty + yield from self._runtest_for(item, "setup") + + @hookimpl(hookwrapper=True) + def pytest_runtest_call(self, item: nodes.Item) -> Generator[None, None, None]: + self.log_cli_handler.set_when("call") + + yield from self._runtest_for(item, "call") + + @hookimpl(hookwrapper=True) + def pytest_runtest_teardown(self, item: nodes.Item) -> Generator[None, None, None]: + self.log_cli_handler.set_when("teardown") + + yield from self._runtest_for(item, "teardown") + del item._store[caplog_records_key] + del item._store[caplog_handler_key] + + @hookimpl + def pytest_runtest_logfinish(self) -> None: + self.log_cli_handler.set_when("finish") + + @hookimpl(hookwrapper=True, tryfirst=True) + def pytest_sessionfinish(self) -> Generator[None, None, 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): + 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: Optional[CaptureManager], + ) -> None: + logging.StreamHandler.__init__(self, 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: Optional[str]) -> 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..41a33d4 --- /dev/null +++ b/venv/Lib/site-packages/_pytest/main.py @@ -0,0 +1,876 @@ +"""Core implementation of the testing process: init, session, runtest loop.""" +import argparse +import fnmatch +import functools +import importlib +import os +import sys +from pathlib import Path +from typing import Callable +from typing import Dict +from typing import FrozenSet +from typing import Iterator +from typing import List +from typing import Optional +from typing import overload +from typing import Sequence +from typing import Set +from typing import Tuple +from typing import Type +from typing import TYPE_CHECKING +from typing import Union + +import attr +import py + +import _pytest._code +from _pytest import nodes +from _pytest.compat import final +from _pytest.config import Config +from _pytest.config import directory_arg +from _pytest.config import ExitCode +from _pytest.config import hookimpl +from _pytest.config import PytestPluginManager +from _pytest.config import UsageError +from _pytest.config.argparsing import Parser +from _pytest.fixtures import FixtureManager +from _pytest.outcomes import exit +from _pytest.pathlib import absolutepath +from _pytest.pathlib import bestrelpath +from _pytest.pathlib import visit +from _pytest.reports import CollectReport +from _pytest.reports import TestReport +from _pytest.runner import collect_one_node +from _pytest.runner import SetupState + + +if TYPE_CHECKING: + from typing_extensions import Literal + + +def pytest_addoption(parser: Parser) -> None: + 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 in the " + "command line.", + type="args", + default=[], + ) + group = parser.getgroup("general", "running and selection options") + group._addoption( + "-x", + "--exitfirst", + action="store_const", + dest="maxfail", + const=1, + help="exit instantly on first error or failed test.", + ) + 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._addoption( + "--maxfail", + metavar="num", + action="store", + type=int, + dest="maxfail", + default=0, + help="exit after first num failures or errors.", + ) + group._addoption( + "--strict-config", + action="store_true", + help="any warnings encountered while parsing the `pytest` section of the configuration file raise errors.", + ) + group._addoption( + "--strict-markers", + action="store_true", + help="markers not registered in the `markers` section of the configuration file raise errors.", + ) + group._addoption( + "--strict", action="store_true", help="(deprecated) alias to --strict-markers.", + ) + group._addoption( + "-c", + metavar="file", + type=str, + dest="inifilename", + help="load configuration from `file` instead of trying to locate one of the implicit " + "configuration files.", + ) + 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( + "--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 = 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( + "--import-mode", + default="prepend", + choices=["prepend", "append", "importlib"], + dest="importmode", + help="prepend/append to sys.path when importing test modules and conftest files, " + "default is to prepend.", + ) + + group = parser.getgroup("debugconfig", "test session debugging and configuration") + 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 + for parent in base.parents: + if parent == query: + return True + return False + + # 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"], Optional[Union[int, ExitCode]]] +) -> Union[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: Union[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("{}: {}\n".format(type(exc).__name__, exc)) + else: + if isinstance(excinfo.value, SystemExit): + sys.stderr.write("mainloop: caught unexpected SystemExit!\n") + + finally: + # Explicitly break reference cycle. + excinfo = None # type: ignore + session.startdir.chdir() + 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("{}: {}\n".format(type(exc).__name__, exc)) + config._ensure_unconfigure() + return session.exitstatus + + +def pytest_cmdline_main(config: Config) -> Union[int, ExitCode]: + return wrap_session(config, _main) + + +def _main(config: Config, session: "Session") -> Optional[Union[int, ExitCode]]: + """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( + "%d error%s during collection" + % (session.testsfailed, "s" if session.testsfailed != 1 else "") + ) + + 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: py.path.local) -> bool: + """Attempt to detect if ``path`` is the root of a Virtual Environment by + checking for the existence of the appropriate activate script.""" + bindir = path.join("Scripts" if sys.platform.startswith("win") else "bin") + if not bindir.isdir(): + return False + activates = ( + "activate", + "activate.csh", + "activate.fish", + "Activate", + "Activate.bat", + "Activate.ps1", + ) + return any([fname.basename in activates for fname in bindir.listdir()]) + + +def pytest_ignore_collect(path: py.path.local, config: Config) -> Optional[bool]: + ignore_paths = config._getconftest_pathlist("collect_ignore", path=path.dirpath()) + ignore_paths = ignore_paths or [] + excludeopt = config.getoption("ignore") + if excludeopt: + ignore_paths.extend([py.path.local(x) for x in excludeopt]) + + if py.path.local(path) in ignore_paths: + return True + + ignore_globs = config._getconftest_pathlist( + "collect_ignore_glob", path=path.dirpath() + ) + ignore_globs = ignore_globs or [] + excludeglobopt = config.getoption("ignore_glob") + if excludeglobopt: + ignore_globs.extend([py.path.local(x) for x in excludeglobopt]) + + if any(fnmatch.fnmatch(str(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(path): + return True + return None + + +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) -> None: + self.pm = pm + self.remove_mods = remove_mods + + def __getattr__(self, name: str): + 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.""" + + +@attr.s +class _bestrelpath_cache(Dict[Path, str]): + path = attr.ib(type=Path) + + def __missing__(self, path: Path) -> str: + r = bestrelpath(self.path, path) + self[path] = r + return r + + +@final +class Session(nodes.FSCollector): + 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: Union[int, ExitCode] + + def __init__(self, config: Config) -> None: + super().__init__( + config.rootdir, parent=None, config=config, session=self, nodeid="" + ) + self.testsfailed = 0 + self.testscollected = 0 + self.shouldstop: Union[bool, str] = False + self.shouldfail: Union[bool, str] = False + self.trace = config.trace.root.get("collection") + self.startdir = config.invocation_dir + self._initialpaths: FrozenSet[py.path.local] = frozenset() + + 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) + return session + + def __repr__(self) -> str: + return "<%s %s exitstatus=%r testsfailed=%d testscollected=%d>" % ( + self.__class__.__name__, + self.name, + getattr(self, "exitstatus", ""), + self.testsfailed, + self.testscollected, + ) + + 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: Union[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 = "stopping after %d failures" % (self.testsfailed) + + pytest_collectreport = pytest_runtest_logreport + + def isinitpath(self, path: py.path.local) -> bool: + return path in self._initialpaths + + def gethookproxy(self, fspath: py.path.local): + # Check if we have the common case of running + # hooks with all conftest.py files. + pm = self.config.pluginmanager + my_conftestmodules = pm._getconftestmodules( + fspath, self.config.getoption("importmode") + ) + remove_mods = pm._conftest_plugins.difference(my_conftestmodules) + if remove_mods: + # One or more conftests are not in use at this fspath. + proxy = FSHookProxy(pm, remove_mods) + else: + # All plugins are active for this fspath. + proxy = self.config.hook + return proxy + + def _recurse(self, direntry: "os.DirEntry[str]") -> bool: + if direntry.name == "__pycache__": + return False + path = py.path.local(direntry.path) + ihook = self.gethookproxy(path.dirpath()) + if ihook.pytest_ignore_collect(path=path, config=self.config): + return False + norecursepatterns = self.config.getini("norecursedirs") + if any(path.check(fnmatch=pat) for pat in norecursepatterns): + return False + return True + + def _collectfile( + self, path: py.path.local, handle_dupes: bool = True + ) -> Sequence[nodes.Collector]: + assert ( + path.isfile() + ), "{!r} is not a file (isdir={!r}, exists={!r}, islink={!r})".format( + path, path.isdir(), path.exists(), path.islink() + ) + ihook = self.gethookproxy(path) + if not self.isinitpath(path): + if ihook.pytest_ignore_collect(path=path, config=self.config): + return () + + if handle_dupes: + keepduplicates = self.config.getoption("keepduplicates") + if not keepduplicates: + duplicate_paths = self.config.pluginmanager._duplicatepaths + if path in duplicate_paths: + return () + else: + duplicate_paths.add(path) + + return ihook.pytest_collect_file(path=path, parent=self) # type: ignore[no-any-return] + + @overload + def perform_collect( + self, args: Optional[Sequence[str]] = ..., genitems: "Literal[True]" = ... + ) -> Sequence[nodes.Item]: + ... + + @overload + def perform_collect( + self, args: Optional[Sequence[str]] = ..., genitems: bool = ... + ) -> Sequence[Union[nodes.Item, nodes.Collector]]: + ... + + def perform_collect( + self, args: Optional[Sequence[str]] = None, genitems: bool = True + ) -> Sequence[Union[nodes.Item, nodes.Collector]]: + """Perform the collection phase for this session. + + This is called by the default + :func:`pytest_collection <_pytest.hookspec.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 + + self._notfound: List[Tuple[str, Sequence[nodes.Collector]]] = [] + self._initial_parts: List[Tuple[py.path.local, List[str]]] = [] + self.items: List[nodes.Item] = [] + + hook = self.config.hook + + items: Sequence[Union[nodes.Item, nodes.Collector]] = self.items + try: + initialpaths: List[py.path.local] = [] + for arg in args: + fspath, parts = resolve_collection_argument( + self.config.invocation_params.dir, + arg, + as_pypath=self.config.option.pyargs, + ) + self._initial_parts.append((fspath, parts)) + initialpaths.append(fspath) + self._initialpaths = frozenset(initialpaths) + rep = collect_one_node(self) + self.ihook.pytest_collectreport(report=rep) + self.trace.root.indent -= 1 + if self._notfound: + errors = [] + for arg, cols in self._notfound: + line = f"(no name {arg!r} in any of {cols!r})" + errors.append(f"not found: {arg}\n{line}") + 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: + hook.pytest_collection_finish(session=self) + + self.testscollected = len(items) + return items + + def collect(self) -> Iterator[Union[nodes.Item, nodes.Collector]]: + from _pytest.python import Package + + # Keep track of any collected nodes in here, so we don't duplicate fixtures. + node_cache1: Dict[py.path.local, Sequence[nodes.Collector]] = {} + node_cache2: Dict[ + Tuple[Type[nodes.Collector], py.path.local], nodes.Collector + ] = ({}) + + # Keep track of any collected collectors in matchnodes paths, so they + # are not collected more than once. + matchnodes_cache: Dict[Tuple[Type[nodes.Collector], str], CollectReport] = ({}) + + # Dirnames of pkgs with dunder-init files. + pkg_roots: Dict[str, Package] = {} + + for argpath, names in self._initial_parts: + self.trace("processing argument", (argpath, names)) + self.trace.root.indent += 1 + + # Start with a Session root, and delve to argpath item (dir or file) + # and stack all Packages found on the way. + # No point in finding packages when collecting doctests. + if not self.config.getoption("doctestmodules", False): + pm = self.config.pluginmanager + for parent in reversed(argpath.parts()): + if pm._confcutdir and pm._confcutdir.relto(parent): + break + + if parent.isdir(): + pkginit = parent.join("__init__.py") + if pkginit.isfile() and pkginit not in node_cache1: + col = self._collectfile(pkginit, handle_dupes=False) + if col: + if isinstance(col[0], Package): + pkg_roots[str(parent)] = col[0] + node_cache1[col[0].fspath] = [col[0]] + + # If it's a directory argument, recurse and look for any Subpackages. + # Let the Package collector deal with subnodes, don't collect here. + if argpath.check(dir=1): + assert not names, "invalid arg {!r}".format((argpath, names)) + + seen_dirs: Set[py.path.local] = set() + for direntry in visit(str(argpath), self._recurse): + if not direntry.is_file(): + continue + + path = py.path.local(direntry.path) + dirpath = path.dirpath() + + if dirpath not in seen_dirs: + # Collect packages first. + seen_dirs.add(dirpath) + pkginit = dirpath.join("__init__.py") + if pkginit.exists(): + for x in self._collectfile(pkginit): + yield x + if isinstance(x, Package): + pkg_roots[str(dirpath)] = x + if str(dirpath) in pkg_roots: + # Do not collect packages here. + continue + + for x in self._collectfile(path): + key = (type(x), x.fspath) + if key in node_cache2: + yield node_cache2[key] + else: + node_cache2[key] = x + yield x + else: + assert argpath.check(file=1) + + if argpath in node_cache1: + col = node_cache1[argpath] + else: + collect_root = pkg_roots.get(argpath.dirname, self) + col = collect_root._collectfile(argpath, handle_dupes=False) + if col: + node_cache1[argpath] = col + + matching = [] + work: List[ + Tuple[Sequence[Union[nodes.Item, nodes.Collector]], Sequence[str]] + ] = [(col, names)] + while work: + self.trace("matchnodes", col, names) + self.trace.root.indent += 1 + + matchnodes, matchnames = work.pop() + for node in matchnodes: + if not matchnames: + matching.append(node) + continue + if not isinstance(node, nodes.Collector): + continue + key = (type(node), node.nodeid) + if key in matchnodes_cache: + rep = matchnodes_cache[key] + else: + rep = collect_one_node(node) + matchnodes_cache[key] = rep + if rep.passed: + submatchnodes = [] + for r in rep.result: + # TODO: Remove parametrized workaround once collection structure contains + # parametrization. + if ( + r.name == matchnames[0] + or r.name.split("[")[0] == matchnames[0] + ): + submatchnodes.append(r) + if submatchnodes: + work.append((submatchnodes, matchnames[1:])) + # XXX Accept IDs that don't have "()" for class instances. + elif len(rep.result) == 1 and rep.result[0].name == "()": + work.append((rep.result, matchnames)) + else: + # Report collection failures here to avoid failing to run some test + # specified in the command line because the module could not be + # imported (#134). + node.ihook.pytest_collectreport(report=rep) + + self.trace("matchnodes finished -> ", len(matching), "nodes") + self.trace.root.indent -= 1 + + if not matching: + report_arg = "::".join((str(argpath), *names)) + self._notfound.append((report_arg, col)) + continue + + # If __init__.py was the only file requested, then the matched + # node will be the corresponding Package (by default), and the + # first yielded item will be the __init__ Module itself, so + # just use that. If this special case isn't taken, then all the + # files in the package will be yielded. + if argpath.basename == "__init__.py" and isinstance( + matching[0], Package + ): + try: + yield next(iter(matching[0].collect())) + except StopIteration: + # The package collects nothing with only an __init__.py + # file in it, which gets ignored by the default + # "python_files" option. + pass + continue + + yield from matching + + self.trace.root.indent -= 1 + + def genitems( + self, node: Union[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) + rep = collect_one_node(node) + if rep.passed: + for subnode in rep.result: + yield from self.genitems(subnode) + node.ihook.pytest_collectreport(report=rep) + + +def search_pypath(module_name: str) -> str: + """Search sys.path for the given a dotted module name, and return its file system path.""" + 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 module_name + if spec is None or spec.origin is None or spec.origin == "namespace": + return module_name + elif spec.submodule_search_locations: + return os.path.dirname(spec.origin) + else: + return spec.origin + + +def resolve_collection_argument( + invocation_path: Path, arg: str, *, as_pypath: bool = False +) -> Tuple[py.path.local, List[str]]: + """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 tuple: + + (py.path.path("/full/path/to/pkg/tests/test_foo.py"), ["TestClass", "test_foo"]) + + When as_pypath is True, expects that the command-line argument actually contains + module paths instead of file-system paths: + + "pkg.tests.test_foo::TestClass::test_foo" + + In which case we search sys.path for a matching module, and then return the *path* to the + found module. + + If the path doesn't exist, raise UsageError. + If the path is a directory and selection parts are present, raise UsageError. + """ + strpath, *parts = str(arg).split("::") + if as_pypath: + strpath = search_pypath(strpath) + fspath = invocation_path / strpath + fspath = absolutepath(fspath) + if not fspath.exists(): + 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 py.path.local(str(fspath)), parts 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..329a11c --- /dev/null +++ b/venv/Lib/site-packages/_pytest/mark/__init__.py @@ -0,0 +1,282 @@ +"""Generic mechanism for marking and selecting python functions.""" +import warnings +from typing import AbstractSet +from typing import Collection +from typing import List +from typing import Optional +from typing import TYPE_CHECKING +from typing import Union + +import attr + +from .expression import Expression +from .expression import ParseError +from .structures import EMPTY_PARAMETERSET_OPTION +from .structures import get_empty_parameterset_mark +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 Parser +from _pytest.deprecated import MINUS_K_COLON +from _pytest.deprecated import MINUS_K_DASH +from _pytest.store import StoreKey + +if TYPE_CHECKING: + from _pytest.nodes import Item + + +__all__ = [ + "MARK_GEN", + "Mark", + "MarkDecorator", + "MarkGenerator", + "ParameterSet", + "get_empty_parameterset_mark", +] + + +old_mark_config_key = StoreKey[Optional[Config]]() + + +def param( + *values: object, + marks: Union[MarkDecorator, Collection[Union[MarkDecorator, Mark]]] = (), + id: Optional[str] = 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. + :keyword marks: A single mark or a list of marks to be applied to this parameter set. + :keyword str id: The id to attribute to this parameter set. + """ + return ParameterSet.param(*values, marks=marks, id=id) + + +def pytest_addoption(parser: Parser) -> None: + group = parser.getgroup("general") + group._addoption( + "-k", + action="store", + dest="keyword", + default="", + metavar="EXPRESSION", + help="only run tests which match the given substring expression. " + "An expression is a python evaluatable 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( + "-m", + action="store", + dest="markexpr", + default="", + metavar="MARKEXPR", + help="only run tests matching given mark expression.\n" + "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", "markers for test functions", "linelist") + parser.addini(EMPTY_PARAMETERSET_OPTION, "default marker for empty parametersets") + + +@hookimpl(tryfirst=True) +def pytest_cmdline_main(config: Config) -> Optional[Union[int, ExitCode]]: + 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("@pytest.mark.%s:" % name, bold=True) + tw.line(rest) + tw.line() + config._ensure_unconfigure() + return 0 + + return None + + +@attr.s(slots=True) +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. + """ + + _names = attr.ib(type=AbstractSet[str]) + + @classmethod + def from_item(cls, item: "Item") -> "KeywordMatcher": + mapped_names = set() + + # Add the names of the current item and any parent items. + import pytest + + for node in item.listchain(): + if not isinstance(node, (pytest.Instance, pytest.Session)): + 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) -> bool: + subname = subname.lower() + names = (name.lower() for name in self._names) + + for name in names: + if subname in name: + return True + return False + + +def deselect_by_keyword(items: "List[Item]", config: Config) -> None: + keywordexpr = config.option.keyword.lstrip() + if not keywordexpr: + return + + if keywordexpr.startswith("-"): + # To be removed in pytest 7.0.0. + warnings.warn(MINUS_K_DASH, stacklevel=2) + keywordexpr = "not " + keywordexpr[1:] + selectuntil = False + if keywordexpr[-1:] == ":": + # To be removed in pytest 7.0.0. + warnings.warn(MINUS_K_COLON, stacklevel=2) + selectuntil = True + keywordexpr = keywordexpr[:-1] + + try: + expression = Expression.compile(keywordexpr) + except ParseError as e: + raise UsageError( + f"Wrong expression passed to '-k': {keywordexpr}: {e}" + ) from None + + remaining = [] + deselected = [] + for colitem in items: + if keywordexpr and not expression.evaluate(KeywordMatcher.from_item(colitem)): + deselected.append(colitem) + else: + if selectuntil: + keywordexpr = None + remaining.append(colitem) + + if deselected: + config.hook.pytest_deselected(items=deselected) + items[:] = remaining + + +@attr.s(slots=True) +class MarkMatcher: + """A matcher for markers which are present. + + Tries to match on any marker names, attached to the given colitem. + """ + + own_mark_names = attr.ib() + + @classmethod + def from_item(cls, item) -> "MarkMatcher": + mark_names = {mark.name for mark in item.iter_markers()} + return cls(mark_names) + + def __call__(self, name: str) -> bool: + return name in self.own_mark_names + + +def deselect_by_mark(items: "List[Item]", config: Config) -> None: + matchexpr = config.option.markexpr + if not matchexpr: + return + + try: + expression = Expression.compile(matchexpr) + except ParseError as e: + raise UsageError(f"Wrong expression passed to '-m': {matchexpr}: {e}") from None + + remaining = [] + deselected = [] + for item in items: + if expression.evaluate(MarkMatcher.from_item(item)): + remaining.append(item) + else: + deselected.append(item) + + if deselected: + config.hook.pytest_deselected(items=deselected) + items[:] = remaining + + +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._store[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( + "{!s} must be one of skip, xfail or fail_at_collect" + " but it is {!r}".format(EMPTY_PARAMETERSET_OPTION, empty_parameterset) + ) + + +def pytest_unconfigure(config: Config) -> None: + MARK_GEN._config = config._store.get(old_mark_config_key, None) diff --git a/venv/Lib/site-packages/_pytest/mark/__pycache__/__init__.cpython-39.pyc b/venv/Lib/site-packages/_pytest/mark/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ad47615a4aaf0703acc94e0c751663fcd20deb6c GIT binary patch literal 8670 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/_pytest/mark/__pycache__/expression.cpython-39.pyc b/venv/Lib/site-packages/_pytest/mark/__pycache__/expression.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..524bd6ad9c6acd86306917190f6f0d1bd7f0c084 GIT binary patch literal 7316 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 BTrU6s literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/_pytest/mark/__pycache__/structures.cpython-39.pyc b/venv/Lib/site-packages/_pytest/mark/__pycache__/structures.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ad3d1e2c8d965424ae4b553ef74ba2bddfdbda79 GIT binary patch literal 17316 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 None: + self.column = column + self.message = message + + def __str__(self) -> str: + return f"at column {self.column}: {self.message}" + + +class Scanner: + __slots__ = ("tokens", "current") + + def __init__(self, input: str) -> None: + self.tokens = self.lex(input) + self.current = next(self.tokens) + + def lex(self, input: str) -> Iterator[Token]: + pos = 0 + while pos < len(input): + if input[pos] in (" ", "\t"): + pos += 1 + elif input[pos] == "(": + yield Token(TokenType.LPAREN, "(", pos) + pos += 1 + elif input[pos] == ")": + yield Token(TokenType.RPAREN, ")", pos) + pos += 1 + else: + match = re.match(r"(:?\w|:|\+|-|\.|\[|\])+", input[pos:]) + if match: + value = match.group(0) + if value == "or": + yield Token(TokenType.OR, value, pos) + elif value == "and": + yield Token(TokenType.AND, value, pos) + elif value == "not": + yield Token(TokenType.NOT, value, pos) + else: + yield Token(TokenType.IDENT, value, pos) + pos += len(value) + else: + raise ParseError( + pos + 1, 'unexpected character "{}"'.format(input[pos]), + ) + yield Token(TokenType.EOF, "", pos) + + def accept(self, type: TokenType, *, reject: bool = False) -> Optional[Token]: + if self.current.type is type: + token = self.current + if token.type is not TokenType.EOF: + self.current = next(self.tokens) + return token + if reject: + self.reject((type,)) + return None + + def reject(self, expected: Sequence[TokenType]) -> "NoReturn": + raise ParseError( + self.current.pos + 1, + "expected {}; got {}".format( + " OR ".join(type.value for type in expected), self.current.type.value, + ), + ) + + +# True, False and None are legal match expression identifiers, +# but illegal as Python identifiers. To fix this, this prefix +# is added to identifiers in the conversion to Python AST. +IDENT_PREFIX = "$" + + +def expression(s: Scanner) -> ast.Expression: + if s.accept(TokenType.EOF): + ret: ast.expr = ast.NameConstant(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: + return ast.Name(IDENT_PREFIX + ident.value, ast.Load()) + s.reject((TokenType.NOT, TokenType.LPAREN, TokenType.IDENT)) + + +class MatcherAdapter(Mapping[str, bool]): + """Adapts a matcher function to a locals mapping as required by eval().""" + + def __init__(self, matcher: Callable[[str], bool]) -> None: + self.matcher = matcher + + def __getitem__(self, key: str) -> bool: + return self.matcher(key[len(IDENT_PREFIX) :]) + + def __iter__(self) -> Iterator[str]: + raise NotImplementedError() + + def __len__(self) -> int: + raise NotImplementedError() + + +class Expression: + """A compiled match expression as used by -k and -m. + + The expression can be evaulated against different matchers. + """ + + __slots__ = ("code",) + + def __init__(self, code: types.CodeType) -> None: + self.code = code + + @classmethod + def compile(self, input: str) -> "Expression": + """Compile a match expression. + + :param input: The input expression - one line. + """ + astexpr = expression(Scanner(input)) + code: types.CodeType = compile( + astexpr, filename="", mode="eval", + ) + return Expression(code) + + def evaluate(self, matcher: Callable[[str], bool]) -> bool: + """Evaluate the match expression. + + :param matcher: + Given an identifier, should return whether it matches or not. + Should be prepared to handle arbitrary strings as input. + + :returns: Whether the expression matches or not. + """ + ret: bool = eval(self.code, {"__builtins__": {}}, MatcherAdapter(matcher)) + return ret 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..6c126cf --- /dev/null +++ b/venv/Lib/site-packages/_pytest/mark/structures.py @@ -0,0 +1,559 @@ +import collections.abc +import inspect +import warnings +from typing import Any +from typing import Callable +from typing import Collection +from typing import Iterable +from typing import Iterator +from typing import List +from typing import Mapping +from typing import MutableMapping +from typing import NamedTuple +from typing import Optional +from typing import overload +from typing import Sequence +from typing import Set +from typing import Tuple +from typing import Type +from typing import TYPE_CHECKING +from typing import TypeVar +from typing import Union + +import attr + +from .._code import getfslineno +from ..compat import ascii_escaped +from ..compat import final +from ..compat import NOTSET +from ..compat import NotSetType +from _pytest.config import Config +from _pytest.outcomes import fail +from _pytest.warning_types import PytestUnknownMarkWarning + +if TYPE_CHECKING: + from ..nodes import Node + + +EMPTY_PARAMETERSET_OPTION = "empty_parameter_set_mark" + + +def istestfunc(func) -> bool: + return ( + hasattr(func, "__call__") + and getattr(func, "__name__", "") != "" + ) + + +def get_empty_parameterset_mark( + config: Config, argnames: Sequence[str], func +) -> "MarkDecorator": + from ..nodes import Collector + + fs, lineno = getfslineno(func) + reason = "got empty parameter set %r, function %s at %s:%d" % ( + argnames, + func.__name__, + fs, + lineno, + ) + + 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": + f_name = func.__name__ + _, lineno = getfslineno(func) + raise Collector.CollectError( + "Empty parameter set in '%s' at line %d" % (f_name, lineno + 1) + ) + else: + raise LookupError(requested_mark) + return mark + + +class ParameterSet( + NamedTuple( + "ParameterSet", + [ + ("values", Sequence[Union[object, NotSetType]]), + ("marks", Collection[Union["MarkDecorator", "Mark"]]), + ("id", Optional[str]), + ], + ) +): + @classmethod + def param( + cls, + *values: object, + marks: Union["MarkDecorator", Collection[Union["MarkDecorator", "Mark"]]] = (), + id: Optional[str] = None, + ) -> "ParameterSet": + if isinstance(marks, MarkDecorator): + marks = (marks,) + else: + assert isinstance(marks, collections.abc.Collection) + + if id is not None: + if not isinstance(id, str): + raise TypeError( + "Expected id to be a string, got {}: {!r}".format(type(id), id) + ) + id = ascii_escaped(id) + return cls(values, marks, id) + + @classmethod + def extract_from( + cls, + parameterset: Union["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: Union[str, List[str], Tuple[str, ...]], + argvalues: Iterable[Union["ParameterSet", Sequence[object], object]], + *args, + **kwargs, + ) -> Tuple[Union[List[str], Tuple[str, ...]], bool]: + if not isinstance(argnames, (tuple, list)): + 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[Union["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: Union[str, List[str], Tuple[str, ...]], + argvalues: Iterable[Union["ParameterSet", Sequence[object], object]], + func, + config: Config, + nodeid: str, + ) -> Tuple[Union[List[str], Tuple[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=None) + ) + return argnames, parameters + + +@final +@attr.s(frozen=True) +class Mark: + #: Name of the mark. + name = attr.ib(type=str) + #: Positional arguments of the mark decorator. + args = attr.ib(type=Tuple[Any, ...]) + #: Keyword arguments of the mark decorator. + kwargs = attr.ib(type=Mapping[str, Any]) + + #: Source Mark for ids with parametrize Marks. + _param_ids_from = attr.ib(type=Optional["Mark"], default=None, repr=False) + #: Resolved/generated ids with parametrize Marks. + _param_ids_generated = attr.ib( + type=Optional[Sequence[str]], default=None, repr=False + ) + + 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: Optional[Mark] = 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, + ) + + +# A generic parameter designating an object to which a Mark may +# be applied -- a test function (callable) or class. +# Note: a lambda is not allowed, but this can't be represented. +_Markable = TypeVar("_Markable", bound=Union[Callable[..., object], type]) + + +@attr.s +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 MarkDecorators from storing only a single + function or class reference as their positional argument with no + additional keyword or positional arguments. You can work around this by + using `with_args()`. + """ + + mark = attr.ib(type=Mark, validator=attr.validators.instance_of(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: + return self.name # for backward-compat (2.4.1 had this attr) + + def __repr__(self) -> str: + return f"" + + 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. + + :rtype: MarkDecorator + """ + mark = Mark(self.name, args, kwargs) + return self.__class__(self.mark.combined_with(mark)) + + # 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[misc] + 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) + if len(args) == 1 and (istestfunc(func) or is_class): + store_mark(func, self.mark) + return func + return self.with_args(*args, **kwargs) + + +def get_unpacked_marks(obj) -> List[Mark]: + """Obtain the unpacked marks that are stored on an object.""" + mark_list = getattr(obj, "pytestmark", []) + if not isinstance(mark_list, list): + mark_list = [mark_list] + return normalize_mark_list(mark_list) + + +def normalize_mark_list(mark_list: Iterable[Union[Mark, MarkDecorator]]) -> List[Mark]: + """Normalize marker decorating helpers to mark objects. + + :type List[Union[Mark, Markdecorator]] mark_list: + :rtype: List[Mark] + """ + extracted = [ + getattr(mark, "mark", mark) for mark in mark_list + ] # unpack MarkDecorator + for mark in extracted: + if not isinstance(mark, Mark): + raise TypeError(f"got {mark!r} instead of Mark") + return [x for x in extracted if isinstance(x, Mark)] + + +def store_mark(obj, mark: Mark) -> None: + """Store a Mark on an object. + + This is used to implement the Mark declarations/decorators correctly. + """ + assert isinstance(mark, Mark), mark + # Always reassign name to avoid updating pytestmark in a reference that + # was only borrowed. + obj.pytestmark = get_unpacked_marks(obj) + [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: + from _pytest.fixtures import _Scope + + class _SkipMarkDecorator(MarkDecorator): + @overload # type: ignore[override,misc] + def __call__(self, arg: _Markable) -> _Markable: + ... + + @overload + def __call__(self, reason: str = ...) -> "MarkDecorator": + ... + + class _SkipifMarkDecorator(MarkDecorator): + def __call__( # type: ignore[override] + self, + condition: Union[str, bool] = ..., + *conditions: Union[str, bool], + reason: str = ..., + ) -> MarkDecorator: + ... + + class _XfailMarkDecorator(MarkDecorator): + @overload # type: ignore[override,misc] + def __call__(self, arg: _Markable) -> _Markable: + ... + + @overload + def __call__( + self, + condition: Union[str, bool] = ..., + *conditions: Union[str, bool], + reason: str = ..., + run: bool = ..., + raises: Union[Type[BaseException], Tuple[Type[BaseException], ...]] = ..., + strict: bool = ..., + ) -> MarkDecorator: + ... + + class _ParametrizeMarkDecorator(MarkDecorator): + def __call__( # type: ignore[override] + self, + argnames: Union[str, List[str], Tuple[str, ...]], + argvalues: Iterable[Union[ParameterSet, Sequence[object], object]], + *, + indirect: Union[bool, Sequence[str]] = ..., + ids: Optional[ + Union[ + Iterable[Union[None, str, float, int, bool]], + Callable[[Any], Optional[object]], + ] + ] = ..., + scope: Optional[_Scope] = ..., + ) -> MarkDecorator: + ... + + class _UsefixturesMarkDecorator(MarkDecorator): + def __call__( # type: ignore[override] + self, *fixtures: str + ) -> MarkDecorator: + ... + + class _FilterwarningsMarkDecorator(MarkDecorator): + def __call__( # type: ignore[override] + self, *filters: str + ) -> MarkDecorator: + ... + + +@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``. + """ + + _config: Optional[Config] = None + _markers: Set[str] = set() + + # See TYPE_CHECKING above. + if TYPE_CHECKING: + skip: _SkipMarkDecorator + skipif: _SkipifMarkDecorator + xfail: _XfailMarkDecorator + parametrize: _ParametrizeMarkDecorator + usefixtures: _UsefixturesMarkDecorator + filterwarnings: _FilterwarningsMarkDecorator + + def __getattr__(self, name: str) -> MarkDecorator: + if name[0] == "_": + raise AttributeError("Marker name must NOT start with underscore") + + if self._config is not None: + # We store a set of markers as a performance optimisation - if a mark + # name is in the set we definitely know it, but a mark may be known and + # not in the set. We therefore start by updating the set! + if name not in self._markers: + for line in self._config.getini("markers"): + # example lines: "skipif(condition): skip the given test if..." + # or "hypothesis: tests which use Hypothesis", so to get the + # marker name we split on both `:` and `(`. + marker = line.split(":")[0].split("(")[0].strip() + self._markers.add(marker) + + # If the name is not in the set of known marks after updating, + # then it really is time to issue a warning or an error. + if name not in self._markers: + if self._config.option.strict_markers or self._config.option.strict: + fail( + f"{name!r} not found in `markers` configuration option", + pytrace=False, + ) + + # Raise a specific error for common misspellings of "parametrize". + if name in ["parameterize", "parametrise", "parameterise"]: + __tracebackhide__ = True + fail(f"Unknown '{name}' mark, did you mean 'parametrize'?") + + warnings.warn( + "Unknown pytest.mark.%s - is this a typo? You can register " + "custom marks to avoid this warning - for details, see " + "https://docs.pytest.org/en/stable/mark.html" % name, + PytestUnknownMarkWarning, + 2, + ) + + return MarkDecorator(Mark(name, (), {})) + + +MARK_GEN = MarkGenerator() + + +@final +class NodeKeywords(MutableMapping[str, Any]): + 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 + + def __delitem__(self, key: str) -> None: + raise ValueError("cannot delete key in keywords dict") + + def __iter__(self) -> Iterator[str]: + seen = self._seen() + return iter(seen) + + def _seen(self) -> Set[str]: + seen = set(self._markers) + if self.parent is not None: + seen.update(self.parent.keywords) + return seen + + def __len__(self) -> int: + return len(self._seen()) + + 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..a052f69 --- /dev/null +++ b/venv/Lib/site-packages/_pytest/monkeypatch.py @@ -0,0 +1,379 @@ +"""Monkeypatching and mocking functionality.""" +import os +import re +import sys +import warnings +from contextlib import contextmanager +from pathlib import Path +from typing import Any +from typing import Generator +from typing import List +from typing import MutableMapping +from typing import Optional +from typing import overload +from typing import Tuple +from typing import TypeVar +from typing import Union + +from _pytest.compat import final +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", None, None]: + """A convenient fixture for monkey-patching. + + The fixture provides these methods to modify objects, dictionaries or + os.environ:: + + monkeypatch.setattr(obj, name, value, raising=True) + monkeypatch.delattr(obj, name, raising=True) + monkeypatch.setitem(mapping, name, value) + monkeypatch.delitem(obj, name, raising=True) + monkeypatch.setenv(name, value, prepend=False) + monkeypatch.delenv(name, raising=True) + monkeypatch.syspath_prepend(path) + monkeypatch.chdir(path) + + All modifications will be undone after the requesting test function or + fixture has finished. The ``raising`` parameter determines if a KeyError + or AttributeError will be raised if the set/deletion operation has no target. + """ + mpatch = MonkeyPatch() + yield mpatch + mpatch.undo() + + +def resolve(name: str) -> object: + # Simplified from zope.dottedname. + parts = name.split(".") + + used = parts.pop(0) + found = __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( + "{!r} object at {} has no attribute {!r}".format( + type(obj).__name__, ann, name + ) + ) 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: # type: ignore[unreachable] + 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[MutableMapping[Any, Any], object, object]] = ([]) + self._cwd: Optional[str] = None + self._savesyspath: Optional[List[str]] = None + + @classmethod + @contextmanager + def context(cls) -> Generator["MonkeyPatch", None, None]: + """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 `#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: Union[str, object], + name: Union[object, str], + value: object = notset, + raising: bool = True, + ) -> None: + """Set attribute value on target, memorizing the old value. + + 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. For example, + ``monkeypatch.setattr("os.getcwd", lambda: "/")`` + would set the ``getcwd`` function of the ``os`` module. + + Raises AttributeError if the attribute does not exist, unless + ``raising`` is set to False. + """ + __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: Union[object, str], + name: Union[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: MutableMapping[K, V], name: K, value: V) -> None: + """Set dictionary entry ``name`` to value.""" + self._setitem.append((dic, name, dic.get(name, notset))) + dic[name] = value + + def delitem(self, dic: MutableMapping[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))) + del dic[name] + + def setenv(self, name: str, value: str, prepend: Optional[str] = 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( + "Value of environment variable {name} type should be str, but got " + "{value!r} (type: {type}); converted to str implicitly".format( + name=name, value=value, type=type(value).__name__ + ) + ), + 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.""" + from pkg_resources import fixup_namespace_packages + + 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 + 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) -> None: + """Change the current working directory to the specified path. + + Path can be a string or a py.path.local object. + """ + if self._cwd is None: + self._cwd = os.getcwd() + if hasattr(path, "chdir"): + path.chdir() + elif isinstance(path, Path): + # Modern python uses the fspath protocol here LEGACY + os.chdir(str(path)) + else: + 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 that 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. + """ + 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: + del dictionary[key] + except KeyError: + pass # Was already deleted, so we have the desired state. + else: + dictionary[key] = value + 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..27434fb --- /dev/null +++ b/venv/Lib/site-packages/_pytest/nodes.py @@ -0,0 +1,591 @@ +import os +import warnings +from pathlib import Path +from typing import Callable +from typing import Iterable +from typing import Iterator +from typing import List +from typing import Optional +from typing import overload +from typing import Set +from typing import Tuple +from typing import Type +from typing import TYPE_CHECKING +from typing import TypeVar +from typing import Union + +import py + +import _pytest._code +from _pytest._code import getfslineno +from _pytest._code.code import ExceptionInfo +from _pytest._code.code import TerminalRepr +from _pytest.compat import cached_property +from _pytest.config import Config +from _pytest.config import ConftestImportFailure +from _pytest.deprecated import FSCOLLECTOR_GETHOOKPROXY_ISINITPATH +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.store import Store + +if TYPE_CHECKING: + # Imported here due to circular import. + from _pytest.main import Session + from _pytest._code.code import _TracebackStyle + + +SEP = "/" + +tracebackcutdir = py.path.local(_pytest.__file__).dirpath() + + +def iterparentnodeids(nodeid: str) -> Iterator[str]: + """Return the parent node IDs of a given node ID, inclusive. + + For the node ID + + "testing/code/test_excinfo.py::TestFormattedExcinfo::test_repr_source" + + the result would be + + "" + "testing" + "testing/code" + "testing/code/test_excinfo.py" + "testing/code/test_excinfo.py::TestFormattedExcinfo" + "testing/code/test_excinfo.py::TestFormattedExcinfo::test_repr_source" + + Note that :: parts are only considered at the last / component. + """ + pos = 0 + sep = SEP + yield "" + while True: + at = nodeid.find(sep, pos) + if at == -1 and sep == SEP: + sep = "::" + elif at == -1: + if nodeid: + yield nodeid + break + else: + if at: + yield nodeid[:at] + pos = at + len(sep) + + +_NodeType = TypeVar("_NodeType", bound="Node") + + +class NodeMeta(type): + def __call__(self, *k, **kw): + 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=self.__name__) + fail(msg, pytrace=False) + + def _create(self, *k, **kw): + return super().__call__(*k, **kw) + + +class Node(metaclass=NodeMeta): + """Base class for Collector and Item, the components of the test + collection tree. + + Collector subclasses have children; Items are leaf nodes. + """ + + # Use __slots__ to make attribute access faster. + # Note that __dict__ is still available. + __slots__ = ( + "name", + "parent", + "config", + "session", + "fspath", + "_nodeid", + "_store", + "__dict__", + ) + + def __init__( + self, + name: str, + parent: "Optional[Node]" = None, + config: Optional[Config] = None, + session: "Optional[Session]" = None, + fspath: Optional[py.path.local] = None, + nodeid: Optional[str] = None, + ) -> None: + #: A unique name within the scope of the parent node. + self.name = name + + #: The parent collector node. + self.parent = parent + + #: The pytest config object. + if config: + self.config: Config = config + else: + if not parent: + raise TypeError("config or parent must be provided") + self.config = parent.config + + #: The pytest session this node is part of. + if session: + self.session = session + else: + if not parent: + raise TypeError("session or parent must be provided") + self.session = parent.session + + #: Filesystem path where this node was collected from (can be None). + self.fspath = fspath or getattr(parent, "fspath", None) + + #: Keywords/markers collected from all scopes. + self.keywords = 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 + if self.name != "()": + self._nodeid += "::" + self.name + + # A place where plugins can store information on the node for their + # own use. Currently only intended for internal plugins. + self._store = Store() + + @classmethod + def from_parent(cls, parent: "Node", **kw): + """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): + """fspath-sensitive hook proxy used to call pytest hooks.""" + return self.session.gethookproxy(self.fspath) + + 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( + "warning must be an instance of Warning or subclass, got {!r}".format( + warning + ) + ) + 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 listchain(self) -> List["Node"]: + """Return list of all parent collectors up to self, starting from + the root of collection tree.""" + chain = [] + item: Optional[Node] = self + while item is not None: + chain.append(item) + item = item.parent + chain.reverse() + return chain + + def add_marker( + self, marker: Union[str, MarkDecorator], append: bool = True + ) -> None: + """Dynamically add a marker object to the node. + + :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: Optional[str] = None) -> Iterator[Mark]: + """Iterate over all markers of the node. + + :param name: If given, filter the results by the name attribute. + """ + return (x[1] for x in self.iter_markers_with_node(name=name)) + + def iter_markers_with_node( + self, name: Optional[str] = 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 reversed(self.listchain()): + 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) -> Optional[Mark]: + ... + + @overload + def get_closest_marker(self, name: str, default: Mark) -> Mark: + ... + + def get_closest_marker( + self, name: str, default: Optional[Mark] = None + ) -> Optional[Mark]: + """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 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]) -> Optional[_NodeType]: + """Get the next parent node (including self) which is an instance of + the given class.""" + current: Optional[Node] = self + while current and not isinstance(current, cls): + current = current.parent + assert current is None or isinstance(current, cls) + return current + + def _prunetraceback(self, excinfo: ExceptionInfo[BaseException]) -> None: + pass + + def _repr_failure_py( + self, + excinfo: ExceptionInfo[BaseException], + style: "Optional[_TracebackStyle]" = None, + ) -> TerminalRepr: + from _pytest.fixtures import FixtureLookupError + + if isinstance(excinfo.value, ConftestImportFailure): + excinfo = ExceptionInfo(excinfo.value.excinfo) + if isinstance(excinfo.value, fail.Exception): + if not excinfo.value.pytrace: + style = "value" + if isinstance(excinfo.value, FixtureLookupError): + return excinfo.value.formatrepr() + if self.config.getoption("fulltrace", False): + style = "long" + else: + tb = _pytest._code.Traceback([excinfo.traceback[-1]]) + self._prunetraceback(excinfo) + if len(excinfo.traceback) == 0: + excinfo.traceback = tb + 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.getoption("verbose", 0) > 1: + truncate_locals = False + else: + truncate_locals = 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=False, # pruned already, or in --fulltrace mode. + truncate_locals=truncate_locals, + ) + + def repr_failure( + self, + excinfo: ExceptionInfo[BaseException], + style: "Optional[_TracebackStyle]" = None, + ) -> Union[str, TerminalRepr]: + """Return a representation of a collection or test failure. + + :param excinfo: Exception information for the failure. + """ + return self._repr_failure_py(excinfo, style) + + +def get_fslocation_from_item( + node: "Node", +) -> Tuple[Union[str, py.path.local], Optional[int]]: + """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. + * "fspath": just a path + + :rtype: A tuple of (str|py.path.local, int) with filename and line number. + """ + # See Item.location. + location: Optional[Tuple[str, Optional[int], str]] = 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, "fspath", "unknown location"), -1 + + +class Collector(Node): + """Collector instances create children through collect() and thus + iteratively build a tree.""" + + class CollectError(Exception): + """An error during collection, contains a custom message.""" + + def collect(self) -> Iterable[Union["Item", "Collector"]]: + """Return a list of children (items and collectors) for this + collection node.""" + raise NotImplementedError("abstract") + + # TODO: This omits the style= parameter which breaks Liskov Substitution. + def repr_failure( # type: ignore[override] + self, excinfo: ExceptionInfo[BaseException] + ) -> Union[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 _prunetraceback(self, excinfo: ExceptionInfo[BaseException]) -> None: + if hasattr(self, "fspath"): + traceback = excinfo.traceback + ntraceback = traceback.cut(path=self.fspath) + if ntraceback == traceback: + ntraceback = ntraceback.cut(excludepath=tracebackcutdir) + excinfo.traceback = ntraceback.filter() + + +def _check_initialpaths_for_relpath(session, fspath): + for initial_path in session._initialpaths: + if fspath.common(initial_path) == initial_path: + return fspath.relto(initial_path) + + +class FSCollector(Collector): + def __init__( + self, + fspath: py.path.local, + parent=None, + config: Optional[Config] = None, + session: Optional["Session"] = None, + nodeid: Optional[str] = None, + ) -> None: + name = fspath.basename + if parent is not None: + rel = fspath.relto(parent.fspath) + if rel: + name = rel + name = name.replace(os.sep, SEP) + self.fspath = fspath + + session = session or parent.session + + if nodeid is None: + nodeid = self.fspath.relto(session.config.rootdir) + + if not nodeid: + nodeid = _check_initialpaths_for_relpath(session, fspath) + if nodeid and os.sep != SEP: + nodeid = nodeid.replace(os.sep, SEP) + + super().__init__(name, parent, config, session, nodeid=nodeid, fspath=fspath) + + @classmethod + def from_parent(cls, parent, *, fspath, **kw): + """The public constructor.""" + return super().from_parent(parent=parent, fspath=fspath, **kw) + + def gethookproxy(self, fspath: py.path.local): + warnings.warn(FSCOLLECTOR_GETHOOKPROXY_ISINITPATH, stacklevel=2) + return self.session.gethookproxy(fspath) + + def isinitpath(self, path: py.path.local) -> bool: + warnings.warn(FSCOLLECTOR_GETHOOKPROXY_ISINITPATH, stacklevel=2) + return self.session.isinitpath(path) + + +class File(FSCollector): + """Base class for collecting tests from a file. + + :ref:`non-python tests`. + """ + + +class Item(Node): + """A basic test invocation item. + + Note that for a single function there might be multiple test invocation items. + """ + + nextitem = None + + def __init__( + self, + name, + parent=None, + config: Optional[Config] = None, + session: Optional["Session"] = None, + nodeid: Optional[str] = None, + ) -> None: + super().__init__(name, parent, config, session, nodeid=nodeid) + 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]] = [] + + def runtest(self) -> None: + 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[Union[py.path.local, str], Optional[int], str]: + return self.fspath, None, "" + + @cached_property + def location(self) -> Tuple[str, Optional[int], str]: + location = self.reportinfo() + fspath = absolutepath(str(location[0])) + relfspath = self.session._node_location_to_relpath(fspath) + assert type(location[2]) is str + return (relfspath, location[1], location[2]) diff --git a/venv/Lib/site-packages/_pytest/nose.py b/venv/Lib/site-packages/_pytest/nose.py new file mode 100644 index 0000000..bb8f997 --- /dev/null +++ b/venv/Lib/site-packages/_pytest/nose.py @@ -0,0 +1,39 @@ +"""Run testsuites written for nose.""" +from _pytest import python +from _pytest import unittest +from _pytest.config import hookimpl +from _pytest.nodes import Item + + +@hookimpl(trylast=True) +def pytest_runtest_setup(item): + if is_potential_nosetest(item): + if not call_optional(item.obj, "setup"): + # Call module level setup if there is no object level one. + call_optional(item.parent.obj, "setup") + # XXX This implies we only call teardown when setup worked. + item.session._setupstate.addfinalizer((lambda: teardown_nose(item)), item) + + +def teardown_nose(item): + if is_potential_nosetest(item): + if not call_optional(item.obj, "teardown"): + call_optional(item.parent.obj, "teardown") + + +def is_potential_nosetest(item: Item) -> bool: + # Extra check needed since we do not do nose style setup/teardown + # on direct unittest style classes. + return isinstance(item, python.Function) and not isinstance( + item, unittest.TestCaseFunction + ) + + +def call_optional(obj, name): + method = getattr(obj, name, None) + isfixture = hasattr(method, "_pytestfixturefunction") + if method is not None and not isfixture and callable(method): + # If there's any problems allow the exception to raise rather than + # silently ignoring them. + method() + return True diff --git a/venv/Lib/site-packages/_pytest/outcomes.py b/venv/Lib/site-packages/_pytest/outcomes.py new file mode 100644 index 0000000..8f6203f --- /dev/null +++ b/venv/Lib/site-packages/_pytest/outcomes.py @@ -0,0 +1,227 @@ +"""Exception classes and constants handling test outcomes as well as +functions creating them.""" +import sys +from typing import Any +from typing import Callable +from typing import cast +from typing import Optional +from typing import Type +from typing import TypeVar + +TYPE_CHECKING = False # Avoid circular import through compat. + +if TYPE_CHECKING: + from typing import NoReturn + from typing_extensions import Protocol +else: + # typing.Protocol is only available starting from Python 3.8. It is also + # available from typing_extensions, but we don't want a runtime dependency + # on that. So use a dummy runtime implementation. + from typing import Generic + + Protocol = Generic + + +class OutcomeException(BaseException): + """OutcomeException and its subclass instances indicate and contain info + about test and collection outcomes.""" + + def __init__(self, msg: Optional[str] = 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__)) + BaseException.__init__(self, 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: Optional[str] = None, + pytrace: bool = True, + allow_module_level: bool = False, + ) -> None: + OutcomeException.__init__(self, msg=msg, pytrace=pytrace) + self.allow_module_level = allow_module_level + + +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: Optional[int] = None + ) -> None: + self.msg = msg + self.returncode = returncode + super().__init__(msg) + + +# Elaborate hack to work around https://github.com/python/mypy/issues/2087. +# Ideally would just be `exit.Exception = Exit` etc. + +_F = TypeVar("_F", bound=Callable[..., object]) +_ET = TypeVar("_ET", bound=Type[BaseException]) + + +class _WithException(Protocol[_F, _ET]): + Exception: _ET + __call__: _F + + +def _with_exception(exception_type: _ET) -> Callable[[_F], _WithException[_F, _ET]]: + def decorate(func: _F) -> _WithException[_F, _ET]: + func_with_exception = cast(_WithException[_F, _ET], func) + func_with_exception.Exception = exception_type + return func_with_exception + + return decorate + + +# Exposed helper methods. + + +@_with_exception(Exit) +def exit(msg: str, returncode: Optional[int] = None) -> "NoReturn": + """Exit testing process. + + :param str msg: Message to display upon exit. + :param int returncode: Return code to be used when exiting pytest. + """ + __tracebackhide__ = True + raise Exit(msg, returncode) + + +@_with_exception(Skipped) +def skip(msg: str = "", *, allow_module_level: bool = False) -> "NoReturn": + """Skip an executing test with the given message. + + This function should be called only during testing (setup, call or teardown) or + during collection by using the ``allow_module_level`` flag. This function can + be called in doctests as well. + + :param bool allow_module_level: + Allows this function to be called at module level, skipping the rest + of the module. Defaults to False. + + .. 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 `doctest.SKIP + `_) + to skip a doctest statically. + """ + __tracebackhide__ = True + raise Skipped(msg=msg, allow_module_level=allow_module_level) + + +@_with_exception(Failed) +def fail(msg: str = "", pytrace: bool = True) -> "NoReturn": + """Explicitly fail an executing test with the given message. + + :param str msg: + The message to show the user as reason for the failure. + :param bool pytrace: + If False, msg represents the full failure information and no + python traceback will be reported. + """ + __tracebackhide__ = True + raise Failed(msg=msg, pytrace=pytrace) + + +class XFailed(Failed): + """Raised from an explicit call to pytest.xfail().""" + + +@_with_exception(XFailed) +def xfail(reason: str = "") -> "NoReturn": + """Imperatively xfail an executing test or setup function with the given reason. + + This function should be called only during testing (setup, call or teardown). + + .. 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. + """ + __tracebackhide__ = True + raise XFailed(reason) + + +def importorskip( + modname: str, minversion: Optional[str] = None, reason: Optional[str] = None +) -> Any: + """Import and return the requested module ``modname``, or skip the + current test if the module cannot be imported. + + :param str modname: + The name of the module to import. + :param str minversion: + If given, the imported module's ``__version__`` attribute must be at + least this minimal version, otherwise the test is still skipped. + :param str reason: + If given, this reason is shown as the message when the module cannot + be imported. + + :returns: + The imported module. This should be assigned to its canonical name. + + Example:: + + docutils = pytest.importorskip("docutils") + """ + import warnings + + __tracebackhide__ = True + compile(modname, "", "eval") # to catch syntaxerrors + + 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 ImportError as exc: + if reason is None: + reason = f"could not import {modname!r}: {exc}" + raise Skipped(reason, allow_module_level=True) from None + 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( + "module %r has __version__ %r, required is: %r" + % (modname, verattr, minversion), + 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..131873c --- /dev/null +++ b/venv/Lib/site-packages/_pytest/pastebin.py @@ -0,0 +1,110 @@ +"""Submit failure or test session information to a pastebin service.""" +import tempfile +from io import StringIO +from typing import IO +from typing import Union + +import pytest +from _pytest.config import Config +from _pytest.config import create_terminal_writer +from _pytest.config.argparsing import Parser +from _pytest.store import StoreKey +from _pytest.terminal import TerminalReporter + + +pastebinfile_key = StoreKey[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._store[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._store[pastebinfile_key].write(s) + + tr._tw.write = tee_write + + +def pytest_unconfigure(config: Config) -> None: + if pastebinfile_key in config._store: + pastebinfile = config._store[pastebinfile_key] + # Get terminal contents and delete file. + pastebinfile.seek(0) + sessionlog = pastebinfile.read() + pastebinfile.close() + del config._store[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("pastebin session-log: %s\n" % pastebinurl) + + +def create_new_paste(contents: Union[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.request import urlopen + from urllib.parse import urlencode + + params = {"code": contents, "lexer": "text", "expiry": "1week"} + url = "https://bpaste.net" + try: + response: str = ( + urlopen(url, data=urlencode(params).encode("ascii")).read().decode("utf-8") + ) + except OSError as exc_info: # urllib errors + return "bad response: %s" % exc_info + m = re.search(r'href="/raw/(\w+)"', response) + if m: + return "{}/show/{}".format(url, 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..7d9269a --- /dev/null +++ b/venv/Lib/site-packages/_pytest/pathlib.py @@ -0,0 +1,654 @@ +import atexit +import contextlib +import fnmatch +import importlib.util +import itertools +import os +import shutil +import sys +import uuid +import warnings +from enum import Enum +from errno import EBADF +from errno import ELOOP +from errno import ENOENT +from errno import ENOTDIR +from functools import partial +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 +from types import ModuleType +from typing import Callable +from typing import Iterable +from typing import Iterator +from typing import Optional +from typing import Set +from typing import TypeVar +from typing import Union + +import py + +from _pytest.compat import assert_never +from _pytest.outcomes import skip +from _pytest.warning_types import PytestWarning + +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): + 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, path: str, exc, *, start_path: Path) -> bool: + """Handle known read-only errors during rmtree. + + The returned value is used only by our own tests. + """ + exctype, excvalue = exc[:2] + + # 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(excvalue, FileNotFoundError): + return False + + if not isinstance(excvalue, PermissionError): + warnings.warn( + PytestWarning(f"(rm_rf) error removing {path}\n{exctype}: {excvalue}") + ) + return False + + if func not in (os.rmdir, os.remove, os.unlink): + if func not in (os.open,): + warnings.warn( + PytestWarning( + "(rm_rf) unknown function {} when removing {}:\n{}: {}".format( + func, path, exctype, excvalue + ) + ) + ) + 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) + shutil.rmtree(str(path), onerror=onerror) + + +def find_prefixed(root: Path, prefix: str) -> Iterator[Path]: + """Find all elements in root that begin with the prefix, case insensitive.""" + l_prefix = prefix.lower() + for x in root.iterdir(): + if x.name.lower().startswith(l_prefix): + yield x + + +def extract_suffixes(iter: Iterable[PurePath], 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 p in iter: + yield p.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) -> int: + """Parse number path suffixes, returns -1 on error.""" + try: + return int(maybe_num) + except ValueError: + return -1 + + +def _force_symlink( + root: Path, target: Union[str, PurePath], link_to: Union[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 " + "{prefix} in {root} after 10 tries".format(prefix=prefix, root=root) + ) + + +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=atexit.register): + """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 + paths = find_prefixed(root, prefix) + paths, paths2 = itertools.tee(paths) + numbers = map(parse_num, extract_suffixes(paths2, prefix)) + for path, number in zip(paths, numbers): + if number <= max_delete: + yield path + + +def cleanup_numbered_dir( + root: Path, prefix: str, keep: int, consider_lock_dead_if_created_before: float +) -> None: + """Cleanup for lock driven numbered directories.""" + 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) + + +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) + 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) -> 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, dst, **kwargs): + """Make a symlink, or skip the test in case symlinks are not supported.""" + try: + os.symlink(str(src), str(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( + p: Union[str, py.path.local, Path], + *, + mode: Union[str, ImportMode] = ImportMode.prepend, +) -> ModuleType: + """Import and return a module from the given path, which can be a file (a module) or + a directory (a package). + + The import mechanism used is controlled by the `mode` parameter: + + * `mode == 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 `__import__. + + * `mode == ImportMode.append`: same as `prepend`, but the directory will be appended + to the end of `sys.path`, if not already in `sys.path`. + + * `mode == ImportMode.importlib`: uses more fine control mechanisms provided by `importlib` + to import the module, which avoids having to use `__import__` and muck with `sys.path` + at all. It effectively allows having same-named test modules in different places. + + :raises ImportPathMismatchError: + If after importing the given `path` and the module `__file__` + are different. Only raised in `prepend` and `append` modes. + """ + mode = ImportMode(mode) + + path = Path(str(p)) + + if not path.exists(): + raise ImportError(path) + + if mode is ImportMode.importlib: + module_name = path.stem + + for meta_importer in sys.meta_path: + spec = meta_importer.find_spec(module_name, [str(path.parent)]) + if spec is not None: + break + else: + spec = importlib.util.spec_from_file_location(module_name, str(path)) + + if spec is None: + raise ImportError( + "Can't find module {} at location {}".format(module_name, str(path)) + ) + mod = importlib.util.module_from_spec(spec) + spec.loader.exec_module(mod) # type: ignore[union-attr] + return mod + + pkg_path = resolve_package_path(path) + if pkg_path is not None: + pkg_root = pkg_path.parent + names = list(path.with_suffix("").relative_to(pkg_root).parts) + if names[-1] == "__init__": + names.pop() + module_name = ".".join(names) + else: + pkg_root = path.parent + module_name = 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.endswith((".pyc", ".pyo")): + module_file = module_file[:-1] + if module_file.endswith(os.path.sep + "__init__.py"): + module_file = module_file[: -(len(os.path.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 + + +# 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 resolve_package_path(path: Path) -> Optional[Path]: + """Return the Python package path by looking for the last + directory upwards which still contains an __init__.py. + + Returns None if it can not be determined. + """ + result = None + for parent in itertools.chain((path,), path.parents): + if parent.is_dir(): + if not parent.joinpath("__init__.py").is_file(): + break + if not parent.name.isidentifier(): + break + result = parent + return result + + +def visit( + path: str, recurse: Callable[["os.DirEntry[str]"], bool] +) -> Iterator["os.DirEntry[str]"]: + """Walk a directory recursively, in breadth-first order. + + Entries at each directory level are sorted. + """ + + # Skip entries with symlink loops and other brokenness, so the caller doesn't + # have to deal with it. + entries = [] + for entry in os.scandir(path): + try: + entry.is_file() + except OSError as err: + if _ignore_error(err): + continue + raise + entries.append(entry) + + entries.sort(key=lambda entry: entry.name) + + yield from entries + + for entry in entries: + if entry.is_dir() and recurse(entry): + yield from visit(entry.path, recurse) + + +def absolutepath(path: Union[Path, 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(str(path))) + + +def commonpath(path1: Path, path2: Path) -> Optional[Path]: + """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. + """ + 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, + ) 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..31259d1 --- /dev/null +++ b/venv/Lib/site-packages/_pytest/pytester.py @@ -0,0 +1,1922 @@ +"""(Disabled by default) support for testing pytest and pytest plugins. + +PYTEST_DONT_REWRITE +""" +import collections.abc +import contextlib +import gc +import importlib +import os +import platform +import re +import shutil +import subprocess +import sys +import traceback +from fnmatch import fnmatch +from io import StringIO +from pathlib import Path +from typing import Any +from typing import Callable +from typing import Dict +from typing import Generator +from typing import Iterable +from typing import List +from typing import Optional +from typing import overload +from typing import Sequence +from typing import TextIO +from typing import Tuple +from typing import Type +from typing import TYPE_CHECKING +from typing import Union +from weakref import WeakKeyDictionary + +import attr +import py +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 final +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 make_numbered_dir +from _pytest.reports import CollectReport +from _pytest.reports import TestReport +from _pytest.tmpdir import TempPathFactory +from _pytest.warning_types import PytestWarning + +if TYPE_CHECKING: + from typing_extensions import Literal + + 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]]: + out = subprocess.run( + ("lsof", "-Ffn0", "-p", str(os.getpid())), + stdout=subprocess.PIPE, + stderr=subprocess.DEVNULL, + check=True, + universal_newlines=True, + ).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(hookwrapper=True, tryfirst=True) + def pytest_runtest_protocol(self, item: Item) -> Generator[None, None, None]: + lines1 = self.get_open_files() + yield + 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 = [ + "***** %s FD leakage detected" % len(leaked_files), + *(str(f) for f in leaked_files), + "*** Before:", + *(str(f) for f in lines1), + "*** After:", + *(str(f) for f in lines2), + "***** %s FD leakage detected" % len(leaked_files), + "*** function %s:%s: %s " % item.location, + "See issue #2366", + ] + item.warn(PytestWarning("\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] != "_"] + + +class ParsedCall: + 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): + ... + + +class HookRecorder: + """Record all hooks called in a plugin manager. + + This wraps all the hook calls in the plugin manager, recording each call + before propagating the normal calls. + """ + + def __init__(self, pluginmanager: PytestPluginManager) -> None: + self._pluginmanager = pluginmanager + self.calls: List[ParsedCall] = [] + self.ret: Optional[Union[int, ExitCode]] = None + + def before(hook_name: str, hook_impls, kwargs) -> None: + self.calls.append(ParsedCall(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: Union[str, Iterable[str]]) -> List[ParsedCall]: + 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) + backlocals = 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) -> ParsedCall: + __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([" %s" % x for x in self.calls]) + fail("\n".join(lines)) + + def getcall(self, name: str) -> ParsedCall: + 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: Union[str, Iterable[str]] = ( + "pytest_collectreport", + "pytest_runtest_logreport", + ), + ) -> Sequence[Union[CollectReport, TestReport]]: + ... + + def getreports( + self, + names: Union[str, Iterable[str]] = ( + "pytest_collectreport", + "pytest_runtest_logreport", + ), + ) -> Sequence[Union[CollectReport, TestReport]]: + return [x.report for x in self.getcalls(names)] + + def matchreport( + self, + inamepart: str = "", + names: Union[str, Iterable[str]] = ( + "pytest_runtest_logreport", + "pytest_collectreport", + ), + when: Optional[str] = None, + ) -> Union[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( + "could not find test report matching %r: " + "no test reports at all!" % (inamepart,) + ) + if len(values) > 1: + raise ValueError( + "found 2 or more testreports matching {!r}: {}".format( + inamepart, 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: Union[str, Iterable[str]] = ( + "pytest_collectreport", + "pytest_runtest_logreport", + ), + ) -> Sequence[Union[CollectReport, TestReport]]: + ... + + def getfailures( + self, + names: Union[str, Iterable[str]] = ( + "pytest_collectreport", + "pytest_runtest_logreport", + ), + ) -> Sequence[Union[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[Union[CollectReport, TestReport]], + Sequence[Union[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) -> "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, _ispytest=True) + + +@fixture +def testdir(pytester: "Pytester") -> "Testdir": + """ + Identical to :fixture:`pytester`, and provides an instance whose methods return + legacy ``py.path.local`` objects instead when applicable. + + New code should avoid using :fixture:`testdir` in favor of :fixture:`pytester`. + """ + return Testdir(pytester, _ispytest=True) + + +@fixture +def _sys_snapshot() -> Generator[None, None, None]: + snappaths = SysPathsSnapshot() + snapmods = SysModulesSnapshot() + yield + snapmods.restore() + snappaths.restore() + + +@fixture +def _config_for_test() -> Generator[Config, None, None]: + 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+)") + + +class RunResult: + """The result of running a command.""" + + def __init__( + self, + ret: Union[int, ExitCode], + outlines: List[str], + errlines: List[str], + duration: float, + ) -> None: + try: + self.ret: Union[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:`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:`LineMatcher` of stderr.""" + self.duration = duration + """Duration in seconds.""" + + def __repr__(self) -> str: + return ( + "" + % (self.ret, len(self.stdout.lines), len(self.stderr.lines), self.duration) + ) + + 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, + ) -> 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 + 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, + ) + + +class CwdSnapshot: + def __init__(self) -> None: + self.__saved = os.getcwd() + + def restore(self) -> None: + os.chdir(self.__saved) + + +class SysModulesSnapshot: + def __init__(self, preserve: Optional[Callable[[str], bool]] = 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 ``path`` and environment variables during initialization. + + Attributes: + + :ivar Path path: temporary directory path used to create files/run tests from, etc. + + :ivar plugins: + A list of plugins to use with :py:meth:`parseconfig` and + :py:meth:`runpytest`. Initially this is an empty list but plugins can + be added to the list. The type of items to add to the list depends on + the method using them so refer to them for details. + """ + + __test__ = False + + CLOSE_STDIN = object + + class TimeoutExpired(Exception): + pass + + def __init__( + self, + request: FixtureRequest, + tmp_path_factory: TempPathFactory, + *, + _ispytest: bool = False, + ) -> None: + check_ispytest(_ispytest) + self._request = request + self._mod_collections: WeakKeyDictionary[ + Collector, List[Union[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) + self.plugins: List[Union[str, _PluggyPlugin]] = [] + self._cwd_snapshot = CwdSnapshot() + self._sys_path_snapshot = SysPathsSnapshot() + self._sys_modules_snapshot = self.__take_sys_modules_snapshot() + self.chdir() + 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() + 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 where files are created and pytest is executed.""" + 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() + self._cwd_snapshot.restore() + self._monkeypatch.undo() + + 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 :py:class:`HookRecorder` for a PluginManager.""" + pluginmanager.reprec = reprec = HookRecorder(pluginmanager) + self._request.addfinalizer(reprec.finish_recording) + return reprec + + def chdir(self) -> None: + """Cd into the temporary directory. + + This is done automatically upon instantiation. + """ + os.chdir(self.path) + + def _makefile( + self, + ext: str, + lines: Sequence[Union[Any, bytes]], + files: Dict[str, str], + encoding: str = "utf-8", + ) -> Path: + items = list(files.items()) + + def to_text(s: Union[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 file(s) in the test directory. + + :param str 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. + + Examples: + + .. code-block:: python + + pytester.makefile(".txt", "line1", "line2") + + pytester.makefile(".ini", pytest="[pytest]\naddopts=-rs\n") + + """ + return self._makefile(ext, args, kwargs) + + def makeconftest(self, source: str) -> Path: + """Write a contest.py file with 'source' as contents.""" + return self.makepyfile(conftest=source) + + def makeini(self, source: str) -> Path: + """Write a tox.ini file with 'source' as contents.""" + return self.makefile(".ini", tox=source) + + def getinicfg(self, source: str) -> SectionWrapper: + """Return the pytest section from the tox.ini config file.""" + p = self.makeini(source) + return IniConfig(str(p))["pytest"] + + def makepyprojecttoml(self, source: str) -> Path: + """Write a pyproject.toml file with 'source' as contents. + + .. 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: Optional[Union[str, "os.PathLike[str]"]] = None + ) -> None: + """Prepend a directory to sys.path, defaults to :py:attr:`tmpdir`. + + This is undone automatically when this object dies at the end of each + test. + """ + if path is None: + path = self.path + + self._monkeypatch.syspath_prepend(str(path)) + + def mkdir(self, name: str) -> Path: + """Create a new (sub)directory.""" + p = self.path / name + p.mkdir() + return p + + def mkpydir(self, name: 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: Optional[str] = None) -> Path: + """Copy file from project's directory into the testdir. + + :param str name: The name of the file to copy. + :return: path to the copied directory (inside ``self.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(str(self._request.config.rootdir)) / 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(): + # TODO: py.path.local.copy can copy files to existing directories, + # while with shutil.copytree the destination directory cannot exist, + # we will need to roll our own in order to drop py.path.local completely + py.path.local(example_path).copy(py.path.local(self.path)) + 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' + ) + + Session = Session + + def getnode( + self, config: Config, arg: Union[str, "os.PathLike[str]"] + ) -> Optional[Union[Collector, Item]]: + """Return the collection node of a file. + + :param _pytest.config.Config config: + A pytest config. + See :py:meth:`parseconfig` and :py:meth:`parseconfigure` for creating it. + :param py.path.local arg: + Path to the file. + """ + session = Session.from_config(config) + assert "::" not in str(arg) + p = py.path.local(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: Union[str, "os.PathLike[str]"]): + """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 py.path.local path: Path to the file. + """ + path = py.path.local(path) + config = self.parseconfigure(path) + session = Session.from_config(config) + x = session.fspath.bestrelpath(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[Union[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. + """ + 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. + :py:func:`_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. + + :returns: :py:class:`HookRecorder` instance of the result. + """ + 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(['--collectonly'])`` 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: Union[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. + + :returns: A :py:class:`HookRecorder` instance. + """ + # (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 Collect: + def pytest_configure(x, config: Config) -> None: + rec.append(self.make_hook_recorder(config.pluginmanager)) + + plugins.append(Collect()) + 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 # type: ignore + + # 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: Union[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() + now = timing.time() + 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(), timing.time() - now + ) + res.reprec = reprec # type: ignore + return res + + def runpytest( + self, *args: Union[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:`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[Union[str, "os.PathLike[str]"]] + ) -> List[Union[str, "os.PathLike[str]"]]: + new_args = list(args) + for x in new_args: + if str(x).startswith("--basetemp"): + break + else: + new_args.append("--basetemp=%s" % self.path.parent.joinpath("basetemp")) + return new_args + + def parseconfig(self, *args: Union[str, "os.PathLike[str]"]) -> Config: + """Return a new pytest Config instance from given commandline args. + + This invokes the pytest bootstrapping code in _pytest.config to create + a new :py:class:`_pytest.core.PluginManager` and call the + pytest_cmdline_parse hook to create a new + :py:class:`_pytest.config.Config` instance. + + If :py:attr:`plugins` has been populated they should be plugin modules + to be registered with the PluginManager. + """ + import _pytest.config + + new_args = self._ensure_basetemp(args) + new_args = [str(x) for x in new_args] + + config = _pytest.config._prepareconfig(new_args, self.plugins) # type: ignore[arg-type] + # we don't know what the test will do with this half-setup config + # object and thus we make sure it gets unconfigured properly in any + # case (otherwise capturing could still be active, for example) + self._request.addfinalizer(config._ensure_unconfigure) + return config + + def parseconfigure(self, *args: Union[str, "os.PathLike[str]"]) -> Config: + """Return a new pytest configured Config instance. + + Returns a new :py:class:`_pytest.config.Config` instance like + :py:meth:`parseconfig`, but also calls the pytest_configure hook. + """ + config = self.parseconfig(*args) + config._do_configure() + return config + + def getitem(self, source: 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. + """ + items = self.getitems(source) + for item in items: + if item.name == funcname: + return item + assert 0, "{!r} item not found in module:\n{}\nitems: {}".format( + funcname, source, items + ) + + def getitems(self, source: 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: Union[str, Path], 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, Path): + 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 + ) -> Optional[Union[Item, Collector]]: + """Return the collection node for name from the module collection. + + Searchs 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, + stdout: Union[int, TextIO] = subprocess.PIPE, + stderr: Union[int, TextIO] = subprocess.PIPE, + stdin=CLOSE_STDIN, + **kw, + ): + """Invoke subprocess.Popen. + + Calls subprocess.Popen making sure the current working directory is + in the 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: Union[str, "os.PathLike[str]"], + timeout: Optional[float] = None, + stdin=CLOSE_STDIN, + ) -> RunResult: + """Run a command with arguments. + + Run a process using subprocess.Popen saving the stdout and stderr. + + :param cmdargs: + The sequence of arguments to pass to `subprocess.Popen()`, with path-like objects + being converted to ``str`` automatically. + :param timeout: + The period in seconds after which to timeout and raise + :py:class:`Pytester.TimeoutExpired`. + :param stdin: + Optional standard input. Bytes are being send, closing + the pipe, otherwise it is passed through to ``popen``. + Defaults to ``CLOSE_STDIN``, which translates to using a pipe + (``subprocess.PIPE``) that gets closed. + + :rtype: RunResult + """ + __tracebackhide__ = True + + # TODO: Remove type ignore in next mypy release. + # https://github.com/python/typeshed/pull/4582 + cmdargs = tuple( + os.fspath(arg) if isinstance(arg, os.PathLike) else arg for arg in cmdargs # type: ignore[misc] + ) + 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: + now = timing.time() + popen = self.popen( + cmdargs, + stdin=stdin, + stdout=f1, + stderr=f2, + close_fds=(sys.platform != "win32"), + ) + if popen.stdin is not None: + popen.stdin.close() + + def handle_timeout() -> None: + __tracebackhide__ = True + + timeout_message = ( + "{seconds} second timeout expired running:" + " {command}".format(seconds=timeout, command=cmdargs) + ) + + popen.kill() + popen.wait() + raise self.TimeoutExpired(timeout_message) + + if timeout is None: + ret = popen.wait() + else: + try: + ret = popen.wait(timeout) + except subprocess.TimeoutExpired: + handle_timeout() + + with p1.open(encoding="utf8") as f1, p2.open(encoding="utf8") as f2: + out = f1.read().splitlines() + err = f2.read().splitlines() + + self._dump_lines(out, sys.stdout) + self._dump_lines(err, sys.stderr) + + with contextlib.suppress(ValueError): + ret = ExitCode(ret) + return RunResult(ret, out, err, timing.time() - now) + + 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) -> RunResult: + """Run a python script using sys.executable as interpreter. + + :rtype: RunResult + """ + return self.run(sys.executable, script) + + def runpython_c(self, command): + """Run python -c "command". + + :rtype: RunResult + """ + return self.run(sys.executable, "-c", command) + + def runpytest_subprocess(self, *args, timeout: Optional[float] = 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`. + + :rtype: RunResult + """ + __tracebackhide__ = True + p = make_numbered_dir(root=self.path, prefix="runpytest-", mode=0o700) + args = ("--basetemp=%s" % p,) + args + plugins = [x for x in self.plugins if isinstance(x, str)] + if plugins: + args = ("-p", plugins[0]) + args + args = self._getpytestargs() + args + return self.run(*args, timeout=timeout) + + def spawn_pytest( + self, string: str, expect_timeout: float = 10.0 + ) -> "pexpect.spawn": + """Run pytest using pexpect. + + This makes sure to use the right pytest and sets up the temporary + directory locations. + + The pexpect child is returned. + """ + basetemp = self.path / "temp-pexpect" + basetemp.mkdir(mode=0o700) + invoke = " ".join(map(str, self._getpytestargs())) + cmd = f"{invoke} --basetemp={basetemp} {string}" + return self.spawn(cmd, expect_timeout=expect_timeout) + + def spawn(self, cmd: str, expect_timeout: float = 10.0) -> "pexpect.spawn": + """Run a command using pexpect. + + The pexpect child is returned. + """ + pexpect = importorskip("pexpect", "3.0") + if hasattr(sys, "pypy_version_info") and "64" in platform.machine(): + skip("pypy-64 bit not supported") + if not hasattr(pexpect, "spawn"): + skip("pexpect.spawn not available") + logfile = self.path.joinpath("spawn.out").open("wb") + + child = pexpect.spawn(cmd, logfile=logfile, timeout=expect_timeout) + self._request.addfinalizer(logfile.close) + return child + + +class LineComp: + def __init__(self) -> None: + self.stringio = StringIO() + """:class:`python:io.StringIO()` instance used for input.""" + + def assert_contains_lines(self, lines2: Sequence[str]) -> None: + """Assert that ``lines2`` are contained (linearly) in :attr:`stringio`'s value. + + Lines are matched using :func:`LineMatcher.fnmatch_lines`. + """ + __tracebackhide__ = True + val = self.stringio.getvalue() + self.stringio.truncate(0) + self.stringio.seek(0) + lines1 = val.split("\n") + LineMatcher(lines1).fnmatch_lines(lines2) + + +@final +@attr.s(repr=False, str=False, init=False) +class Testdir: + """ + Similar to :class:`Pytester`, but this class works with legacy py.path.local objects instead. + + All methods just forward to an internal :class:`Pytester` instance, converting results + to `py.path.local` objects as necessary. + """ + + __test__ = False + + CLOSE_STDIN = Pytester.CLOSE_STDIN + TimeoutExpired = Pytester.TimeoutExpired + Session = Pytester.Session + + def __init__(self, pytester: Pytester, *, _ispytest: bool = False) -> None: + check_ispytest(_ispytest) + self._pytester = pytester + + @property + def tmpdir(self) -> py.path.local: + """Temporary directory where tests are executed.""" + return py.path.local(self._pytester.path) + + @property + def test_tmproot(self) -> py.path.local: + return py.path.local(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: + """See :meth:`Pytester._finalize`.""" + return self._pytester._finalize() + + def makefile(self, ext, *args, **kwargs) -> py.path.local: + """See :meth:`Pytester.makefile`.""" + return py.path.local(str(self._pytester.makefile(ext, *args, **kwargs))) + + def makeconftest(self, source) -> py.path.local: + """See :meth:`Pytester.makeconftest`.""" + return py.path.local(str(self._pytester.makeconftest(source))) + + def makeini(self, source) -> py.path.local: + """See :meth:`Pytester.makeini`.""" + return py.path.local(str(self._pytester.makeini(source))) + + def getinicfg(self, source: str) -> SectionWrapper: + """See :meth:`Pytester.getinicfg`.""" + return self._pytester.getinicfg(source) + + def makepyprojecttoml(self, source) -> py.path.local: + """See :meth:`Pytester.makepyprojecttoml`.""" + return py.path.local(str(self._pytester.makepyprojecttoml(source))) + + def makepyfile(self, *args, **kwargs) -> py.path.local: + """See :meth:`Pytester.makepyfile`.""" + return py.path.local(str(self._pytester.makepyfile(*args, **kwargs))) + + def maketxtfile(self, *args, **kwargs) -> py.path.local: + """See :meth:`Pytester.maketxtfile`.""" + return py.path.local(str(self._pytester.maketxtfile(*args, **kwargs))) + + def syspathinsert(self, path=None) -> None: + """See :meth:`Pytester.syspathinsert`.""" + return self._pytester.syspathinsert(path) + + def mkdir(self, name) -> py.path.local: + """See :meth:`Pytester.mkdir`.""" + return py.path.local(str(self._pytester.mkdir(name))) + + def mkpydir(self, name) -> py.path.local: + """See :meth:`Pytester.mkpydir`.""" + return py.path.local(str(self._pytester.mkpydir(name))) + + def copy_example(self, name=None) -> py.path.local: + """See :meth:`Pytester.copy_example`.""" + return py.path.local(str(self._pytester.copy_example(name))) + + def getnode(self, config: Config, arg) -> Optional[Union[Item, Collector]]: + """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[Union[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 + ) -> Optional[Union[Item, Collector]]: + """See :meth:`Pytester.collect_by_name`.""" + return self._pytester.collect_by_name(modcol, name) + + def popen( + self, + cmdargs, + stdout: Union[int, TextIO] = subprocess.PIPE, + stderr: Union[int, TextIO] = 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 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: Union[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 = "line %r not found in output" % line + 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("line %r not found in output" % fnline) + + 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("invalid type for lines2: {}".format(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("%s:" % 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..630c1d3 --- /dev/null +++ b/venv/Lib/site-packages/_pytest/pytester_assertions.py @@ -0,0 +1,66 @@ +"""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 typing import Dict +from typing import Sequence +from typing import Tuple +from typing import Union + +from _pytest.reports import CollectReport +from _pytest.reports import TestReport + + +def assertoutcome( + outcomes: Tuple[ + Sequence[TestReport], + Sequence[Union[CollectReport, TestReport]], + Sequence[Union[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, +) -> 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, + } + 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..e48e753 --- /dev/null +++ b/venv/Lib/site-packages/_pytest/python.py @@ -0,0 +1,1689 @@ +"""Python test discovery, setup and run of test functions.""" +import enum +import fnmatch +import inspect +import itertools +import os +import sys +import types +import warnings +from collections import Counter +from collections import defaultdict +from functools import partial +from typing import Any +from typing import Callable +from typing import Dict +from typing import Generator +from typing import Iterable +from typing import Iterator +from typing import List +from typing import Mapping +from typing import Optional +from typing import Sequence +from typing import Set +from typing import Tuple +from typing import Type +from typing import TYPE_CHECKING +from typing import Union + +import py + +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._io import TerminalWriter +from _pytest._io.saferepr import saferepr +from _pytest.compat import ascii_escaped +from _pytest.compat import final +from _pytest.compat import get_default_arg_names +from _pytest.compat import get_real_func +from _pytest.compat import getimfunc +from _pytest.compat import getlocation +from _pytest.compat import is_async_function +from _pytest.compat import is_generator +from _pytest.compat import NOTSET +from _pytest.compat import REGEX_TYPE +from _pytest.compat import safe_getattr +from _pytest.compat import safe_isclass +from _pytest.compat import STRING_TYPES +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 FSCOLLECTOR_GETHOOKPROXY_ISINITPATH +from _pytest.fixtures import FuncFixtureInfo +from _pytest.main import Session +from _pytest.mark import MARK_GEN +from _pytest.mark import ParameterSet +from _pytest.mark.structures import get_unpacked_marks +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 import_path +from _pytest.pathlib import ImportPathMismatchError +from _pytest.pathlib import parts +from _pytest.pathlib import visit +from _pytest.warning_types import PytestCollectionWarning +from _pytest.warning_types import PytestUnhandledCoroutineWarning + +if TYPE_CHECKING: + from typing_extensions import Literal + from _pytest.fixtures import _Scope + + +def pytest_addoption(parser: Parser) -> None: + 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", + ) + parser.addini( + "python_files", + type="args", + # NOTE: default is also used in AssertionRewritingHook. + default=["test_*.py", "*_test.py"], + help="glob-style file patterns for Python test module discovery", + ) + parser.addini( + "python_classes", + type="args", + default=["Test"], + help="prefixes or glob names for Python test class discovery", + ) + parser.addini( + "python_functions", + type="args", + default=["test"], + help="prefixes or glob names for Python test function and method discovery", + ) + parser.addini( + "disable_test_id_escaping_and_forfeit_all_rights_to_community_support", + type="bool", + default=False, + help="disable string escape non-ascii characters, might cause unwanted " + "side effects(use at your own risk)", + ) + + +def pytest_cmdline_main(config: Config) -> Optional[Union[int, ExitCode]]: + 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 pytest_generate_tests(metafunc: "Metafunc") -> None: + for marker in metafunc.definition.iter_markers(name="parametrize"): + # TODO: Fix this type-ignore (overlapping kwargs). + metafunc.parametrize(*marker.args, **marker.kwargs, _param_mark=marker) # type: ignore[misc] + + +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/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/fixture.html#usefixtures ", + ) + + +def async_warn_and_skip(nodeid: str) -> None: + msg = "async def functions are not natively supported and have been skipped.\n" + msg += ( + "You need to install a suitable plugin for your async framework, for example:\n" + ) + msg += " - anyio\n" + msg += " - pytest-asyncio\n" + msg += " - pytest-tornasync\n" + msg += " - pytest-trio\n" + msg += " - pytest-twisted" + warnings.warn(PytestUnhandledCoroutineWarning(msg.format(nodeid))) + skip(msg="async def function and no async plugin installed (see warnings)") + + +@hookimpl(trylast=True) +def pytest_pyfunc_call(pyfuncitem: "Function") -> Optional[object]: + testfunction = pyfuncitem.obj + if is_async_function(testfunction): + async_warn_and_skip(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_warn_and_skip(pyfuncitem.nodeid) + return True + + +def pytest_collect_file( + path: py.path.local, parent: nodes.Collector +) -> Optional["Module"]: + ext = path.ext + if ext == ".py": + if not parent.session.isinitpath(path): + if not path_matches_patterns( + path, parent.config.getini("python_files") + ["__init__.py"] + ): + return None + ihook = parent.session.gethookproxy(path) + module: Module = ihook.pytest_pycollect_makemodule(path=path, parent=parent) + return module + return None + + +def path_matches_patterns(path: py.path.local, patterns: Iterable[str]) -> bool: + """Return whether path matches any of the patterns in the list of globs given.""" + return any(path.fnmatch(pattern) for pattern in patterns) + + +def pytest_pycollect_makemodule(path: py.path.local, parent) -> "Module": + if path.basename == "__init__.py": + pkg: Package = Package.from_parent(parent, fspath=path) + return pkg + mod: Module = Module.from_parent(parent, fspath=path) + return mod + + +@hookimpl(trylast=True) +def pytest_pycollect_makeitem(collector: "PyCollector", name: str, obj: object): + # 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( + "cannot collect %r because it is not a function." % name + ), + category=None, + filename=str(filename), + lineno=lineno + 1, + ) + elif getattr(obj, "__test__", True): + if is_generator(obj): + res = Function.from_parent(collector, name=name) + reason = "yield tests were removed in pytest 4.0 - {name} will be ignored".format( + name=name + ) + res.add_marker(MARK_GEN.xfail(run=False, reason=reason)) + res.warn(PytestCollectionWarning(reason)) + else: + res = list(collector._genfunctions(name, obj)) + return res + + +class PyobjMixin: + _ALLOW_MARKERS = True + + # Function and attributes that the mixin needs (for type-checking only). + if TYPE_CHECKING: + name: str = "" + parent: Optional[nodes.Node] = None + own_markers: List[Mark] = [] + + def getparent(self, cls: Type[nodes._NodeType]) -> Optional[nodes._NodeType]: + ... + + def listchain(self) -> List[nodes.Node]: + ... + + @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 this node was collected from (can be None).""" + node = self.getparent(Instance) + return node.obj if node is not None else 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 Instance collector marker duplication + if self._ALLOW_MARKERS: + self.own_markers.extend(get_unpacked_marks(self.obj)) + 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.""" + chain = self.listchain() + chain.reverse() + parts = [] + for node in chain: + if isinstance(node, Instance): + continue + 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[Union[py.path.local, str], int, str]: + # XXX caching? + obj = self.obj + compat_co_firstlineno = getattr(obj, "compat_co_firstlineno", None) + if isinstance(compat_co_firstlineno, int): + # nose compatibility + file_path = sys.modules[obj.__module__].__file__ + if file_path.endswith(".pyc"): + file_path = file_path[:-1] + fspath: Union[py.path.local, str] = file_path + lineno = compat_co_firstlineno + else: + fspath, lineno = getfslineno(obj) + modpath = self.getmodpath() + assert isinstance(lineno, int) + return fspath, 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( # noqa: E305 + 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): + 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): + # staticmethods need to be unwrapped. + obj = safe_getattr(obj, "__func__", False) + return ( + safe_getattr(obj, "__call__", False) + and fixtures.getfixturemarker(obj) is None + ) + else: + return False + + def istestclass(self, obj: object, name: str) -> bool: + return self.classnamefilter(name) or self.isnosetest(obj) + + def _matches_prefix_or_glob_option(self, option_name: str, name: str) -> bool: + """Check if the given name matches the prefix or glob-pattern defined + in ini configuration.""" + for option in self.config.getini(option_name): + if name.startswith(option): + return True + # Check that name looks like a glob-string before calling fnmatch + # because this is called for every name in each collected module, + # and fnmatch is somewhat expensive to call. + elif ("*" in option or "?" in option or "[" in option) and fnmatch.fnmatch( + name, option + ): + return True + return False + + def collect(self) -> Iterable[Union[nodes.Item, nodes.Collector]]: + if not getattr(self.obj, "__test__", True): + return [] + + # NB. we avoid random getattrs and peek in the __dict__ instead + # (XXX originally introduced from a PyPy need, still true?) + dicts = [getattr(self.obj, "__dict__", {})] + for basecls in self.obj.__class__.__mro__: + dicts.append(basecls.__dict__) + seen: Set[str] = set() + values: List[Union[nodes.Item, nodes.Collector]] = [] + ihook = self.ihook + for dic in dicts: + # 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) + 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) + + def sort_key(item): + fspath, lineno, _ = item.reportinfo() + return (str(fspath), lineno) + + values.sort(key=sort_key) + return values + + 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 + fm = self.session._fixturemanager + + definition = FunctionDefinition.from_parent(self, name=name, callobj=funcobj) + fixtureinfo = definition._fixtureinfo + + metafunc = Metafunc( + definition, fixtureinfo, self.config, cls=cls, module=module + ) + 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: + # Add funcargs() as fixturedefs to fixtureinfo.arg2fixturedefs. + fixtures.add_funcarg_pseudo_fixture_def(self, metafunc, fm) + + # Add_funcarg_pseudo_fixture_def may have shadowed some fixtures + # with direct parametrization, so make sure we update what the + # function really needs. + fixtureinfo.prune_dependency_tree() + + for callspec in metafunc._calls: + subname = f"{name}[{callspec.id}]" + yield Function.from_parent( + self, + name=subname, + callspec=callspec, + callobj=funcobj, + fixtureinfo=fixtureinfo, + keywords={callspec.id: True}, + originalname=name, + ) + + +class Module(nodes.File, PyCollector): + """Collector for test classes and functions.""" + + def _getobj(self): + return self._importtestmodule() + + def collect(self) -> Iterable[Union[nodes.Item, nodes.Collector]]: + self._inject_setup_module_fixture() + self._inject_setup_function_fixture() + self.session._fixturemanager.parsefactories(self) + return super().collect() + + def _inject_setup_module_fixture(self) -> None: + """Inject a hidden autouse, module scoped fixture into 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 + + @fixtures.fixture( + autouse=True, + scope="module", + # Use a unique name to speed up lookup. + name=f"xunit_setup_module_fixture_{self.obj.__name__}", + ) + def xunit_setup_module_fixture(request) -> Generator[None, None, None]: + if setup_module is not None: + _call_with_optional_argument(setup_module, request.module) + yield + if teardown_module is not None: + _call_with_optional_argument(teardown_module, request.module) + + self.obj.__pytest_setup_module = xunit_setup_module_fixture + + def _inject_setup_function_fixture(self) -> None: + """Inject a hidden autouse, function scoped fixture into 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 + + @fixtures.fixture( + autouse=True, + scope="function", + # Use a unique name to speed up lookup. + name=f"xunit_setup_function_fixture_{self.obj.__name__}", + ) + def xunit_setup_function_fixture(request) -> Generator[None, None, 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 + if setup_function is not None: + _call_with_optional_argument(setup_function, request.function) + yield + if teardown_function is not None: + _call_with_optional_argument(teardown_function, request.function) + + self.obj.__pytest_setup_function = xunit_setup_function_fixture + + def _importtestmodule(self): + # We assume we are only called once per module. + importmode = self.config.getoption("--import-mode") + try: + mod = import_path(self.fspath, mode=importmode) + except SyntaxError as e: + raise self.CollectError( + ExceptionInfo.from_current().getrepr(style="short") + ) from e + except ImportPathMismatchError as e: + raise self.CollectError( + "import file mismatch:\n" + "imported module %r has this __file__ attribute:\n" + " %s\n" + "which is not the same as the test file we want to collect:\n" + " %s\n" + "HINT: remove __pycache__ / .pyc files and/or use a " + "unique basename for your test file modules" % e.args + ) from e + except ImportError as e: + exc_info = ExceptionInfo.from_current() + if self.config.getoption("verbose") < 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 self.CollectError( + "ImportError while importing test module '{fspath}'.\n" + "Hint: make sure your test modules/packages have valid Python names.\n" + "Traceback:\n" + "{traceback}".format(fspath=self.fspath, traceback=formatted_tb) + ) from e + except skip.Exception as e: + if e.allow_module_level: + raise + raise self.CollectError( + "Using pytest.skip outside of a test is not allowed. " + "To decorate a test function, use the @pytest.mark.skip " + "or @pytest.mark.skipif decorators instead, and to skip a " + "module use `pytestmark = pytest.mark.{skip,skipif}." + ) from e + self.config.pluginmanager.consider_module(mod) + return mod + + +class Package(Module): + def __init__( + self, + fspath: py.path.local, + parent: nodes.Collector, + # NOTE: following args are unused: + config=None, + session=None, + nodeid=None, + ) -> None: + # NOTE: Could be just the following, but kept as-is for compat. + # nodes.FSCollector.__init__(self, fspath, parent=parent) + session = parent.session + nodes.FSCollector.__init__( + self, fspath, parent=parent, config=config, session=session, nodeid=nodeid + ) + self.name = os.path.basename(str(fspath.dirname)) + + def setup(self) -> None: + # 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( + self.obj, ("setUpModule", "setup_module") + ) + if setup_module is not None: + _call_with_optional_argument(setup_module, self.obj) + + teardown_module = _get_first_non_fixture_func( + self.obj, ("tearDownModule", "teardown_module") + ) + if teardown_module is not None: + func = partial(_call_with_optional_argument, teardown_module, self.obj) + self.addfinalizer(func) + + def gethookproxy(self, fspath: py.path.local): + warnings.warn(FSCOLLECTOR_GETHOOKPROXY_ISINITPATH, stacklevel=2) + return self.session.gethookproxy(fspath) + + def isinitpath(self, path: py.path.local) -> bool: + warnings.warn(FSCOLLECTOR_GETHOOKPROXY_ISINITPATH, stacklevel=2) + return self.session.isinitpath(path) + + def _recurse(self, direntry: "os.DirEntry[str]") -> bool: + if direntry.name == "__pycache__": + return False + path = py.path.local(direntry.path) + ihook = self.session.gethookproxy(path.dirpath()) + if ihook.pytest_ignore_collect(path=path, config=self.config): + return False + norecursepatterns = self.config.getini("norecursedirs") + if any(path.check(fnmatch=pat) for pat in norecursepatterns): + return False + return True + + def _collectfile( + self, path: py.path.local, handle_dupes: bool = True + ) -> Sequence[nodes.Collector]: + assert ( + path.isfile() + ), "{!r} is not a file (isdir={!r}, exists={!r}, islink={!r})".format( + path, path.isdir(), path.exists(), path.islink() + ) + ihook = self.session.gethookproxy(path) + if not self.session.isinitpath(path): + if ihook.pytest_ignore_collect(path=path, config=self.config): + return () + + if handle_dupes: + keepduplicates = self.config.getoption("keepduplicates") + if not keepduplicates: + duplicate_paths = self.config.pluginmanager._duplicatepaths + if path in duplicate_paths: + return () + else: + duplicate_paths.add(path) + + return ihook.pytest_collect_file(path=path, parent=self) # type: ignore[no-any-return] + + def collect(self) -> Iterable[Union[nodes.Item, nodes.Collector]]: + this_path = self.fspath.dirpath() + init_module = this_path.join("__init__.py") + if init_module.check(file=1) and path_matches_patterns( + init_module, self.config.getini("python_files") + ): + yield Module.from_parent(self, fspath=init_module) + pkg_prefixes: Set[py.path.local] = set() + for direntry in visit(str(this_path), recurse=self._recurse): + path = py.path.local(direntry.path) + + # We will visit our own __init__.py file, in which case we skip it. + if direntry.is_file(): + if direntry.name == "__init__.py" and path.dirpath() == this_path: + continue + + parts_ = parts(direntry.path) + if any( + str(pkg_prefix) in parts_ and pkg_prefix.join("__init__.py") != path + for pkg_prefix in pkg_prefixes + ): + continue + + if direntry.is_file(): + yield from self._collectfile(path) + elif not direntry.is_dir(): + # Broken symlink or invalid/missing file. + continue + elif path.join("__init__.py").check(file=1): + pkg_prefixes.add(path) + + +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]): + """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 = getattr(obj, name, None) + if meth is not None and fixtures.getfixturemarker(meth) is None: + return meth + + +class Class(PyCollector): + """Collector for test methods.""" + + @classmethod + def from_parent(cls, parent, *, name, obj=None): + """The public constructor.""" + return super().from_parent(name=name, parent=parent) + + def collect(self) -> Iterable[Union[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( + "cannot collect test class %r because it has a " + "__init__ constructor (from: %s)" + % (self.obj.__name__, self.parent.nodeid) + ) + ) + return [] + elif hasnew(self.obj): + assert self.parent is not None + self.warn( + PytestCollectionWarning( + "cannot collect test class %r because it has a " + "__new__ constructor (from: %s)" + % (self.obj.__name__, self.parent.nodeid) + ) + ) + return [] + + self._inject_setup_class_fixture() + self._inject_setup_method_fixture() + + return [Instance.from_parent(self, name="()")] + + def _inject_setup_class_fixture(self) -> None: + """Inject a hidden 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 = getattr(self.obj, "teardown_class", None) + if setup_class is None and teardown_class is None: + return + + @fixtures.fixture( + autouse=True, + scope="class", + # Use a unique name to speed up lookup. + name=f"xunit_setup_class_fixture_{self.obj.__qualname__}", + ) + def xunit_setup_class_fixture(cls) -> Generator[None, None, None]: + if setup_class is not None: + func = getimfunc(setup_class) + _call_with_optional_argument(func, self.obj) + yield + if teardown_class is not None: + func = getimfunc(teardown_class) + _call_with_optional_argument(func, self.obj) + + self.obj.__pytest_setup_class = xunit_setup_class_fixture + + def _inject_setup_method_fixture(self) -> None: + """Inject a hidden 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 this methods ensures we play nicely and unsurprisingly with + other fixtures (#517). + """ + setup_method = _get_first_non_fixture_func(self.obj, ("setup_method",)) + teardown_method = getattr(self.obj, "teardown_method", None) + if setup_method is None and teardown_method is None: + return + + @fixtures.fixture( + autouse=True, + scope="function", + # Use a unique name to speed up lookup. + name=f"xunit_setup_method_fixture_{self.obj.__qualname__}", + ) + def xunit_setup_method_fixture(self, request) -> Generator[None, None, None]: + method = request.function + if setup_method is not None: + func = getattr(self, "setup_method") + _call_with_optional_argument(func, method) + yield + if teardown_method is not None: + func = getattr(self, "teardown_method") + _call_with_optional_argument(func, method) + + self.obj.__pytest_setup_method = xunit_setup_method_fixture + + +class Instance(PyCollector): + _ALLOW_MARKERS = False # hack, destroy later + # Instances share the object with their parents in a way + # that duplicates markers instances if not taken out + # can be removed at node structure reorganization time. + + def _getobj(self): + # 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 obj() + + def collect(self) -> Iterable[Union[nodes.Item, nodes.Collector]]: + self.session._fixturemanager.parsefactories(self) + return super().collect() + + def newinstance(self): + self.obj = self._getobj() + return self.obj + + +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 +class CallSpec2: + def __init__(self, metafunc: "Metafunc") -> None: + self.metafunc = metafunc + self.funcargs: Dict[str, object] = {} + self._idlist: List[str] = [] + self.params: Dict[str, object] = {} + # Used for sorting parametrized resources. + self._arg2scopenum: Dict[str, int] = {} + self.marks: List[Mark] = [] + self.indices: Dict[str, int] = {} + + def copy(self) -> "CallSpec2": + cs = CallSpec2(self.metafunc) + cs.funcargs.update(self.funcargs) + cs.params.update(self.params) + cs.marks.extend(self.marks) + cs.indices.update(self.indices) + cs._arg2scopenum.update(self._arg2scopenum) + cs._idlist = list(self._idlist) + return cs + + def _checkargnotcontained(self, arg: str) -> None: + if arg in self.params or arg in self.funcargs: + raise ValueError(f"duplicate {arg!r}") + + 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(map(str, self._idlist)) + + def setmulti2( + self, + valtypes: Mapping[str, "Literal['params', 'funcargs']"], + argnames: Sequence[str], + valset: Iterable[object], + id: str, + marks: Iterable[Union[Mark, MarkDecorator]], + scopenum: int, + param_index: int, + ) -> None: + for arg, val in zip(argnames, valset): + self._checkargnotcontained(arg) + valtype_for_arg = valtypes[arg] + if valtype_for_arg == "params": + self.params[arg] = val + elif valtype_for_arg == "funcargs": + self.funcargs[arg] = val + else: # pragma: no cover + assert False, f"Unhandled valtype for arg: {valtype_for_arg}" + self.indices[arg] = param_index + self._arg2scopenum[arg] = scopenum + self._idlist.append(id) + self.marks.extend(normalize_mark_list(marks)) + + +@final +class Metafunc: + """Objects passed to the :func:`pytest_generate_tests <_pytest.hookspec.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, + ) -> None: + #: Access to the underlying :class:`_pytest.python.FunctionDefinition`. + self.definition = definition + + #: Access to the :class:`_pytest.config.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._calls: List[CallSpec2] = [] + self._arg2fixturedefs = fixtureinfo.name2fixturedefs + + def parametrize( + self, + argnames: Union[str, List[str], Tuple[str, ...]], + argvalues: Iterable[Union[ParameterSet, Sequence[object], object]], + indirect: Union[bool, Sequence[str]] = False, + ids: Optional[ + Union[ + Iterable[Union[None, str, float, int, bool]], + Callable[[Any], Optional[object]], + ] + ] = None, + scope: "Optional[_Scope]" = None, + *, + _param_mark: Optional[Mark] = 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 rather at test setup time. + + :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. + + 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. + """ + from _pytest.fixtures import scope2index + + argnames, parameters = ParameterSet._for_parametrize( + argnames, + argvalues, + self.function, + self.config, + nodeid=self.definition.nodeid, + ) + del argvalues + + if "request" in argnames: + fail( + "'request' is a reserved name and cannot be used in @pytest.mark.parametrize", + pytrace=False, + ) + + if scope is None: + scope = _find_parametrized_scope(argnames, self._arg2fixturedefs, indirect) + + self._validate_if_using_arg_names(argnames, indirect) + + arg_values_types = self._resolve_arg_value_types(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_arg_ids( + argnames, ids, parameters, 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) + + scopenum = scope2index( + scope, descr=f"parametrize() call in {self.function.__name__}" + ) + + # 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(self)]: + for param_index, (param_id, param_set) in enumerate(zip(ids, parameters)): + newcallspec = callspec.copy() + newcallspec.setmulti2( + arg_values_types, + argnames, + param_set.values, + param_id, + param_set.marks, + scopenum, + param_index, + ) + newcalls.append(newcallspec) + self._calls = newcalls + + def _resolve_arg_ids( + self, + argnames: Sequence[str], + ids: Optional[ + Union[ + Iterable[Union[None, str, float, int, bool]], + Callable[[Any], Optional[object]], + ] + ], + parameters: Sequence[ParameterSet], + nodeid: str, + ) -> List[str]: + """Resolve the actual ids for the given argnames, based on the ``ids`` parameter given + to ``parametrize``. + + :param List[str] argnames: List of argument names passed to ``parametrize()``. + :param ids: The ids parameter of the parametrized call (see docs). + :param List[ParameterSet] parameters: The list of parameter values, same size as ``argnames``. + :param str str: The nodeid of the item that generated this parametrized call. + :rtype: List[str] + :returns: The list of ids for each argname given. + """ + if ids is None: + idfn = None + ids_ = None + elif callable(ids): + idfn = ids + ids_ = None + else: + idfn = None + ids_ = self._validate_ids(ids, parameters, self.function.__name__) + return idmaker(argnames, parameters, idfn, ids_, self.config, nodeid=nodeid) + + def _validate_ids( + self, + ids: Iterable[Union[None, str, float, int, bool]], + parameters: Sequence[ParameterSet], + func_name: str, + ) -> List[Union[None, str]]: + 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(parameters) + + # num_ids == 0 is a special case: https://github.com/pytest-dev/pytest/issues/1849 + if num_ids != len(parameters) and num_ids != 0: + msg = "In {}: {} parameter sets specified, with different number of ids: {}" + fail(msg.format(func_name, len(parameters), num_ids), pytrace=False) + + new_ids = [] + for idx, id_value in enumerate(itertools.islice(ids, num_ids)): + if id_value is None or isinstance(id_value, str): + new_ids.append(id_value) + elif isinstance(id_value, (float, int, bool)): + new_ids.append(str(id_value)) + else: + msg = ( # type: ignore[unreachable] + "In {}: ids must be list of string/float/int/bool, " + "found: {} (type: {!r}) at index {}" + ) + fail( + msg.format(func_name, saferepr(id_value), type(id_value), idx), + pytrace=False, + ) + return new_ids + + def _resolve_arg_value_types( + self, argnames: Sequence[str], indirect: Union[bool, Sequence[str]], + ) -> Dict[str, "Literal['params', 'funcargs']"]: + """Resolve if each parametrized argument must be considered a + parameter to a fixture or a "funcarg" to the function, based on the + ``indirect`` parameter of the parametrized() call. + + :param List[str] argnames: List of argument names passed to ``parametrize()``. + :param indirect: Same as the ``indirect`` parameter of ``parametrize()``. + :rtype: Dict[str, str] + A dict mapping each arg name to either: + * "params" if the argname should be the parameter of a fixture of the same name. + * "funcargs" if the argname should be a parameter to the parametrized test function. + """ + if isinstance(indirect, bool): + valtypes: Dict[str, Literal["params", "funcargs"]] = dict.fromkeys( + argnames, "params" if indirect else "funcargs" + ) + elif isinstance(indirect, Sequence): + valtypes = dict.fromkeys(argnames, "funcargs") + for arg in indirect: + if arg not in argnames: + fail( + "In {}: indirect fixture '{}' doesn't exist".format( + self.function.__name__, arg + ), + pytrace=False, + ) + valtypes[arg] = "params" + else: + fail( + "In {func}: expected Sequence or boolean for indirect, got {type}".format( + type=type(indirect).__name__, func=self.function.__name__ + ), + pytrace=False, + ) + return valtypes + + def _validate_if_using_arg_names( + self, argnames: Sequence[str], indirect: Union[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( + "In {}: function already takes an argument '{}' with a default value".format( + func_name, arg + ), + 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 _find_parametrized_scope( + argnames: Sequence[str], + arg2fixturedefs: Mapping[str, Sequence[fixtures.FixtureDef[object]]], + indirect: Union[bool, Sequence[str]], +) -> "fixtures._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[0].scope + for name, fixturedef in fixturedefs.items() + if name in argnames + ] + if used_scopes: + # Takes the most narrow scope from used fixtures. + for scope in reversed(fixtures.scopes): + if scope in used_scopes: + return scope + + return "function" + + +def _ascii_escaped_by_config(val: Union[str, bytes], config: Optional[Config]) -> 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 + + +def _idval( + val: object, + argname: str, + idx: int, + idfn: Optional[Callable[[Any], Optional[object]]], + nodeid: Optional[str], + config: Optional[Config], +) -> str: + if idfn: + try: + generated_id = idfn(val) + if generated_id is not None: + val = generated_id + except Exception as e: + prefix = f"{nodeid}: " if 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 + elif config: + hook_id: Optional[str] = config.hook.pytest_make_parametrize_id( + config=config, val=val, argname=argname + ) + if hook_id: + return hook_id + + if isinstance(val, STRING_TYPES): + return _ascii_escaped_by_config(val, config) + elif val is None or isinstance(val, (float, int, bool)): + return str(val) + elif isinstance(val, REGEX_TYPE): + 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 str(argname) + str(idx) + + +def _idvalset( + idx: int, + parameterset: ParameterSet, + argnames: Iterable[str], + idfn: Optional[Callable[[Any], Optional[object]]], + ids: Optional[List[Union[None, str]]], + nodeid: Optional[str], + config: Optional[Config], +) -> str: + if parameterset.id is not None: + return parameterset.id + id = None if ids is None or idx >= len(ids) else ids[idx] + if id is None: + this_id = [ + _idval(val, argname, idx, idfn, nodeid=nodeid, config=config) + for val, argname in zip(parameterset.values, argnames) + ] + return "-".join(this_id) + else: + return _ascii_escaped_by_config(id, config) + + +def idmaker( + argnames: Iterable[str], + parametersets: Iterable[ParameterSet], + idfn: Optional[Callable[[Any], Optional[object]]] = None, + ids: Optional[List[Union[None, str]]] = None, + config: Optional[Config] = None, + nodeid: Optional[str] = None, +) -> List[str]: + resolved_ids = [ + _idvalset( + valindex, parameterset, argnames, idfn, ids, config=config, nodeid=nodeid + ) + for valindex, parameterset in enumerate(parametersets) + ] + + # All IDs must be unique! + unique_ids = set(resolved_ids) + if len(unique_ids) != len(resolved_ids): + + # Record the number of occurrences of each test ID. + test_id_counts = Counter(resolved_ids) + + # Map the test ID to its next suffix. + test_id_suffixes: Dict[str, int] = defaultdict(int) + + # Suffix non-unique IDs to make them unique. + for index, test_id in enumerate(resolved_ids): + if test_id_counts[test_id] > 1: + resolved_ids[index] = "{}{}".format(test_id, test_id_suffixes[test_id]) + test_id_suffixes[test_id] += 1 + + return resolved_ids + + +def show_fixtures_per_test(config): + from _pytest.main import wrap_session + + return wrap_session(config, _show_fixtures_per_test) + + +def _show_fixtures_per_test(config: Config, session: Session) -> None: + import _pytest.config + + session.perform_collect() + curdir = py.path.local() + tw = _pytest.config.create_terminal_writer(config) + verbose = config.getvalue("verbose") + + def get_best_relpath(func): + loc = getlocation(func, str(curdir)) + return curdir.bestrelpath(py.path.local(loc)) + + def write_fixture(fixture_def: fixtures.FixtureDef[object]) -> None: + argname = fixture_def.argname + if verbose <= 0 and argname.startswith("_"): + return + if verbose > 0: + bestrel = get_best_relpath(fixture_def.func) + funcargspec = f"{argname} -- {bestrel}" + else: + funcargspec = argname + tw.line(funcargspec, green=True) + fixture_doc = inspect.getdoc(fixture_def.func) + if fixture_doc: + write_docstring(tw, fixture_doc) + else: + tw.line(" no docstring available", red=True) + + def write_item(item: nodes.Item) -> None: + # Not all items have _fixtureinfo attribute. + info: Optional[FuncFixtureInfo] = 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("-", "({})".format(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) -> Union[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() + curdir = py.path.local() + tw = _pytest.config.create_terminal_writer(config) + verbose = config.getvalue("verbose") + + 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, str(curdir)) + if (fixturedef.argname, loc) in seen: + continue + seen.add((fixturedef.argname, loc)) + available.append( + ( + len(fixturedef.baseid), + fixturedef.func.__module__, + curdir.bestrelpath(py.path.local(loc)), + fixturedef.argname, + fixturedef, + ) + ) + + available.sort() + currentmodule = None + for baseid, module, bestrel, 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[0] == "_": + continue + tw.write(argname, green=True) + if fixturedef.scope != "function": + tw.write(" [%s scope]" % fixturedef.scope, cyan=True) + if verbose > 0: + tw.write(" -- %s" % bestrel, yellow=True) + tw.write("\n") + loc = getlocation(fixturedef.func, str(curdir)) + doc = inspect.getdoc(fixturedef.func) + if doc: + write_docstring(tw, doc) + else: + tw.line(f" {loc}: 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) + + +class Function(PyobjMixin, nodes.Item): + """An 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 is 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: Optional[Config] = None, + callspec: Optional[CallSpec2] = None, + callobj=NOTSET, + keywords=None, + session: Optional[Session] = None, + fixtureinfo: Optional[FuncFixtureInfo] = None, + originalname: Optional[str] = None, + ) -> None: + super().__init__(name, parent, config=config, session=session) + + if callobj is not NOTSET: + self.obj = callobj + + #: 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.keywords.update(self.obj.__dict__) + self.own_markers.extend(get_unpacked_marks(self.obj)) + if callspec: + self.callspec = callspec + # this is total hostile and a mess + # keywords are broken by design by now + # this will be redeemed later + for mark in callspec.marks: + # feel free to cry, this was broken for years before + # and keywords cant fix it per design + self.keywords[mark.name] = mark + self.own_markers.extend(normalize_mark_list(callspec.marks)) + if keywords: + self.keywords.update(keywords) + + # todo: this is a hell of a hack + # https://github.com/pytest-dev/pytest/issues/4569 + + self.keywords.update( + { + mark.name: True + for mark in self.iter_markers() + if mark.name not in self.keywords + } + ) + + if fixtureinfo is None: + fixtureinfo = self.session._fixturemanager.getfixtureinfo( + self, self.obj, self.cls, funcargs=True + ) + self._fixtureinfo: FuncFixtureInfo = fixtureinfo + self.fixturenames = fixtureinfo.names_closure + self._initrequest() + + @classmethod + def from_parent(cls, parent, **kw): # todo: determine sound type limitations + """The public constructor.""" + return super().from_parent(parent=parent, **kw) + + def _initrequest(self) -> None: + self.funcargs: Dict[str, object] = {} + self._request = fixtures.FixtureRequest(self, _ispytest=True) + + @property + def function(self): + """Underlying python 'function' object.""" + return getimfunc(self.obj) + + def _getobj(self): + assert self.parent is not None + return getattr(self.parent.obj, self.originalname) # type: ignore[attr-defined] + + @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: + if isinstance(self.parent, Instance): + self.parent.newinstance() + self.obj = self._getobj() + self._request._fillfixtures() + + def _prunetraceback(self, excinfo: ExceptionInfo[BaseException]) -> None: + 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 + + excinfo.traceback = ntraceback.filter() + # 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(excinfo.traceback) > 2: + for entry in excinfo.traceback[1:-1]: + entry.set_repr_style("short") + + # TODO: Type ignored -- breaks Liskov Substitution. + def repr_failure( # type: ignore[override] + self, excinfo: ExceptionInfo[BaseException], + ) -> Union[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 step 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..81ce4f8 --- /dev/null +++ b/venv/Lib/site-packages/_pytest/python_api.py @@ -0,0 +1,786 @@ +import math +import pprint +from collections.abc import Iterable +from collections.abc import Mapping +from collections.abc import Sized +from decimal import Decimal +from numbers import Complex +from types import TracebackType +from typing import Any +from typing import Callable +from typing import cast +from typing import Generic +from typing import Optional +from typing import overload +from typing import Pattern +from typing import Tuple +from typing import Type +from typing import TYPE_CHECKING +from typing import TypeVar +from typing import Union + +if TYPE_CHECKING: + from numpy import ndarray + + +import _pytest._code +from _pytest.compat import final +from _pytest.compat import STRING_TYPES +from _pytest.outcomes import fail + + +def _non_numeric_type_error(value, at: Optional[str]) -> TypeError: + at_str = f" at {at}" if at else "" + return TypeError( + "cannot make approximate comparisons to non-numeric values: {!r} {}".format( + value, at_str + ) + ) + + +# 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 __eq__(self, actual) -> bool: + return all( + a == self._approx_scalar(x) for a, x in self._yield_comparisons(actual) + ) + + # 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": + 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. + pass + + +def _recursive_list_map(f, x): + if isinstance(x, list): + return list(_recursive_list_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_list_map(self._approx_scalar, self.expected.tolist()) + return f"approx({list_scalars!r})" + + 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 ApproxBase.__eq__(self, 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 "approx({!r})".format( + {k: self._approx_scalar(v) for k, v in self.expected.items()} + ) + + def __eq__(self, actual) -> bool: + try: + if set(actual.keys()) != set(self.expected.keys()): + return False + except AttributeError: + return False + + return ApproxBase.__eq__(self, 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, set): + seq_type = list + return "approx({!r})".format( + seq_type(self._approx_scalar(x) for x in self.expected) + ) + + def __eq__(self, actual) -> bool: + try: + if len(actual) != len(self.expected): + return False + except TypeError: + return False + return ApproxBase.__eq__(self, actual) + + def _yield_comparisons(self, actual): + return zip(actual, self.expected) + + def _check_type(self) -> None: + __tracebackhide__ = True + for index, x in enumerate(self.expected): + if isinstance(x, type(self.expected)): + msg = "pytest.approx() does not support nested data structures: {!r} at index {}\n full sequence: {}" + raise TypeError(msg.format(x, index, pprint.pformat(self.expected))) + + +class ApproxScalar(ApproxBase): + """Perform approximate comparisons where the expected value is a single number.""" + + # Using Real should be better than this Union, but not possible yet: + # https://github.com/python/typeshed/pull/3108 + DEFAULT_ABSOLUTE_TOLERANCE: Union[float, Decimal] = 1e-12 + DEFAULT_RELATIVE_TOLERANCE: Union[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 (not isinstance(self.expected, (Complex, Decimal))) or math.isinf( + abs(self.expected) # type: ignore[arg-type] + ): + return str(self.expected) + + # If a sensible tolerance can't be calculated, self.tolerance will + # raise a ValueError. In this case, display '???'. + try: + 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.""" + 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. + if 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. + if 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)): # type: ignore[arg-type] + return self.nan_ok and math.isnan(abs(actual)) # type: ignore[arg-type] + + # 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)): # type: ignore[arg-type] + return False + + # Return true if the two numbers are within the tolerance. + result: bool = abs(self.expected - actual) <= self.tolerance + return result + + # Ignore type because of https://github.com/python/mypy/issues/4266. + __hash__ = None # type: ignore + + @property + def tolerance(self): + """Return the tolerance for the comparison. + + This could be either an absolute tolerance or a relative tolerance, + depending on what the user specified or which would be larger. + """ + + def set_default(x, default): + return x if x is not None else default + + # Figure out what the absolute tolerance should be. ``self.abs`` is + # either None or a value specified by the user. + absolute_tolerance = set_default(self.abs, self.DEFAULT_ABSOLUTE_TOLERANCE) + + if absolute_tolerance < 0: + raise ValueError( + f"absolute tolerance can't be negative: {absolute_tolerance}" + ) + if math.isnan(absolute_tolerance): + raise ValueError("absolute tolerance can't be NaN.") + + # If the user specified an absolute tolerance but not a relative one, + # just return the absolute tolerance. + if self.rel is None: + if self.abs is not None: + return absolute_tolerance + + # Figure out what the relative tolerance should be. ``self.rel`` is + # either None or a value specified by the user. This is done after + # we've made sure the user didn't ask for an absolute tolerance only, + # because we don't want to raise errors about the relative tolerance if + # we aren't even going to use it. + relative_tolerance = set_default( + self.rel, self.DEFAULT_RELATIVE_TOLERANCE + ) * abs(self.expected) + + if relative_tolerance < 0: + raise ValueError( + f"relative tolerance can't be negative: {absolute_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 approx(expected, rel=None, abs=None, nan_ok: bool = False) -> ApproxBase: + """Assert that two numbers (or two sets of numbers) are equal to each other + within some tolerance. + + Due to the `intricacies of floating-point arithmetic`__, numbers that we + would intuitively expect to be equal are not always so:: + + >>> 0.1 + 0.2 == 0.3 + False + + __ https://docs.python.org/3/tutorial/floatingpoint.html + + 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 sequences of numbers:: + + >>> (0.1 + 0.2, 0.2 + 0.4) == approx((0.3, 0.6)) + True + + Dictionary *values*:: + + >>> {'a': 0.1 + 0.2, 'b': 0.2 + 0.4} == approx({'a': 0.3, 'b': 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 + + 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 + + You can also use ``approx`` to compare nonnumeric types, or dicts and + sequences containing nonnumeric 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...`__ + + __ https://docs.python.org/3/library/math.html#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 ``numpy.allclose``. `More information...`__ + + __ https://numpy.org/doc/stable/reference/generated/numpy.isclose.html + + - ``unittest.TestCase.assertAlmostEqual(a, b)``: True if ``a`` and ``b`` + are within an absolute tolerance of ``1e-7``. No relative tolerance is + considered and the absolute tolerance cannot be changed, 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...`__ + + __ https://docs.python.org/3/library/unittest.html#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. + + .. warning:: + + .. versionchanged:: 3.2 + + In order to avoid inconsistent behavior, ``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...`__ + + __ https://docs.python.org/3/reference/datamodel.html#object.__ge__ + + .. versionchanged:: 3.7.1 + ``approx`` raises ``TypeError`` when it encounters a dict value or + sequence element of nonnumeric type. + + .. versionchanged:: 6.1.0 + ``approx`` falls back to strict equality for nonnumeric 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 ( + isinstance(expected, Iterable) + and isinstance(expected, Sized) + # Type ignored because the error is wrong -- not unreachable. + and not isinstance(expected, STRING_TYPES) # type: ignore[unreachable] + ): + cls = ApproxSequencelike + else: + cls = ApproxScalar + + return cls(expected, rel, abs, nan_ok) + + +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) -> Optional["ndarray"]: + """ + Return an ndarray if the given object is implicitly convertible to ndarray, + and numpy is already imported, otherwise None. + """ + import sys + + 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 + + +# builtin pytest.raises helper + +_E = TypeVar("_E", bound=BaseException) + + +@overload +def raises( + expected_exception: Union[Type[_E], Tuple[Type[_E], ...]], + *, + match: Optional[Union[str, Pattern[str]]] = ..., +) -> "RaisesContext[_E]": + ... + + +@overload +def raises( + expected_exception: Union[Type[_E], Tuple[Type[_E], ...]], + func: Callable[..., Any], + *args: Any, + **kwargs: Any, +) -> _pytest._code.ExceptionInfo[_E]: + ... + + +def raises( + expected_exception: Union[Type[_E], Tuple[Type[_E], ...]], *args: Any, **kwargs: Any +) -> Union["RaisesContext[_E]", _pytest._code.ExceptionInfo[_E]]: + r"""Assert that a code block/function call raises ``expected_exception`` + or raise a failure exception otherwise. + + :kwparam match: + If specified, a string containing a regular expression, + or a regular expression object, that is tested against the string + representation of the exception using ``re.search``. To match a literal + string that may contain `special characters`__, the pattern can + first be escaped with ``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")``.) + + __ https://docs.python.org/3/library/re.html#regular-expression-syntax + + .. currentmodule:: _pytest._code + + Use ``pytest.raises`` as a context manager, which will capture the exception of the given + type:: + + >>> import pytest + >>> with pytest.raises(ZeroDivisionError): + ... 1/0 + + If the code block does not raise the expected exception (``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 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" + + .. 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 + + **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. + + **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 isinstance(expected_exception, type): + excepted_exceptions: Tuple[Type[_E], ...] = (expected_exception,) + else: + excepted_exceptions = expected_exception + for exc in excepted_exceptions: + if not isinstance(exc, type) or not issubclass(exc, BaseException): # type: ignore[unreachable] + msg = "expected exception must be a BaseException type, not {}" # type: ignore[unreachable] + not_a = exc.__name__ if isinstance(exc, type) else type(exc).__name__ + raise TypeError(msg.format(not_a)) + + message = f"DID NOT RAISE {expected_exception}" + + if not args: + match: Optional[Union[str, Pattern[str]]] = kwargs.pop("match", None) + if kwargs: + msg = "Unexpected keyword arguments passed to pytest.raises: " + msg += ", ".join(sorted(kwargs)) + msg += "\nUse context-manager form instead?" + raise TypeError(msg) + return RaisesContext(expected_exception, message, match) + else: + func = args[0] + if not callable(func): + raise TypeError( + "{!r} object (type: {}) must be callable".format(func, type(func)) + ) + try: + func(*args[1:], **kwargs) + except expected_exception as e: + # We just caught the exception - there is a traceback. + assert e.__traceback__ is not None + return _pytest._code.ExceptionInfo.from_exc_info( + (type(e), e, e.__traceback__) + ) + fail(message) + + +# This doesn't work with mypy for now. Use fail.Exception instead. +raises.Exception = fail.Exception # type: ignore + + +@final +class RaisesContext(Generic[_E]): + def __init__( + self, + expected_exception: Union[Type[_E], Tuple[Type[_E], ...]], + message: str, + match_expr: Optional[Union[str, Pattern[str]]] = None, + ) -> None: + self.expected_exception = expected_exception + self.message = message + self.match_expr = match_expr + self.excinfo: Optional[_pytest._code.ExceptionInfo[_E]] = None + + def __enter__(self) -> _pytest._code.ExceptionInfo[_E]: + self.excinfo = _pytest._code.ExceptionInfo.for_later() + return self.excinfo + + def __exit__( + self, + exc_type: Optional[Type[BaseException]], + exc_val: Optional[BaseException], + exc_tb: Optional[TracebackType], + ) -> bool: + __tracebackhide__ = True + if exc_type is None: + fail(self.message) + assert self.excinfo is not None + if not issubclass(exc_type, self.expected_exception): + return False + # Cast to narrow the exception type now that it's verified. + exc_info = cast(Tuple[Type[_E], _E, TracebackType], (exc_type, exc_val, exc_tb)) + self.excinfo.fill_unfilled(exc_info) + if self.match_expr is not None: + self.excinfo.match(self.match_expr) + return True diff --git a/venv/Lib/site-packages/_pytest/recwarn.py b/venv/Lib/site-packages/_pytest/recwarn.py new file mode 100644 index 0000000..d872d9d --- /dev/null +++ b/venv/Lib/site-packages/_pytest/recwarn.py @@ -0,0 +1,296 @@ +"""Record warnings during test function execution.""" +import re +import warnings +from types import TracebackType +from typing import Any +from typing import Callable +from typing import Generator +from typing import Iterator +from typing import List +from typing import Optional +from typing import overload +from typing import Pattern +from typing import Tuple +from typing import Type +from typing import TypeVar +from typing import Union + +from _pytest.compat import final +from _pytest.deprecated import check_ispytest +from _pytest.fixtures import fixture +from _pytest.outcomes import fail + + +T = TypeVar("T") + + +@fixture +def recwarn() -> Generator["WarningsRecorder", None, None]: + """Return a :class:`WarningsRecorder` instance that records all warnings emitted by test functions. + + See http://docs.python.org/library/warnings.html for information + on warning categories. + """ + wrec = WarningsRecorder(_ispytest=True) + with wrec: + warnings.simplefilter("default") + yield wrec + + +@overload +def deprecated_call( + *, match: Optional[Union[str, Pattern[str]]] = ... +) -> "WarningsRecorder": + ... + + +@overload +def deprecated_call(func: Callable[..., T], *args: Any, **kwargs: Any) -> T: + ... + + +def deprecated_call( + func: Optional[Callable[..., Any]] = None, *args: Any, **kwargs: Any +) -> Union["WarningsRecorder", Any]: + """Assert that code produces a ``DeprecationWarning`` or ``PendingDeprecationWarning``. + + 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), *args, **kwargs) + + +@overload +def warns( + expected_warning: Optional[Union[Type[Warning], Tuple[Type[Warning], ...]]], + *, + match: Optional[Union[str, Pattern[str]]] = ..., +) -> "WarningsChecker": + ... + + +@overload +def warns( + expected_warning: Optional[Union[Type[Warning], Tuple[Type[Warning], ...]]], + func: Callable[..., T], + *args: Any, + **kwargs: Any, +) -> T: + ... + + +def warns( + expected_warning: Optional[Union[Type[Warning], Tuple[Type[Warning], ...]]], + *args: Any, + match: Optional[Union[str, Pattern[str]]] = None, + **kwargs: Any, +) -> Union["WarningsChecker", Any]: + r"""Assert that code raises a particular class of warning. + + Specifically, the parameter ``expected_warning`` can be a warning class or + sequence of warning classes, and the inside the ``with`` block must issue a warning of that class or + classes. + + This helper produces a list of :class:`warnings.WarningMessage` objects, + one for each warning raised. + + This function can be used as a context manager, or any of the other ways + :func:`pytest.raises` can be used:: + + >>> 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, 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... was emitted... + + """ + __tracebackhide__ = True + if not args: + if kwargs: + msg = "Unexpected keyword arguments passed to pytest.warns: " + msg += ", ".join(sorted(kwargs)) + msg += "\nUse context-manager form instead?" + raise TypeError(msg) + return WarningsChecker(expected_warning, match_expr=match, _ispytest=True) + else: + func = args[0] + if not callable(func): + raise TypeError( + "{!r} object (type: {}) must be callable".format(func, type(func)) + ) + with WarningsChecker(expected_warning, _ispytest=True): + return func(*args[1:], **kwargs) + + +class WarningsRecorder(warnings.catch_warnings): + """A context manager to record raised warnings. + + Adapted from `warnings.catch_warnings`. + """ + + def __init__(self, *, _ispytest: bool = False) -> None: + check_ispytest(_ispytest) + # Type ignored due to the way typeshed handles warnings.catch_warnings. + super().__init__(record=True) # type: ignore[call-arg] + 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, raise exception if not exists.""" + for i, w in enumerate(self._list): + if issubclass(w.category, cls): + return self._list.pop(i) + __tracebackhide__ = True + raise AssertionError("%r not found in warning list" % cls) + + def clear(self) -> None: + """Clear the list of recorded warnings.""" + self._list[:] = [] + + # Type ignored because it doesn't exactly warnings.catch_warnings.__enter__ + # -- it returns a List but we only emulate one. + def __enter__(self) -> "WarningsRecorder": # type: ignore + if self._entered: + __tracebackhide__ = True + raise RuntimeError("Cannot enter %r twice" % self) + _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: Optional[Type[BaseException]], + exc_val: Optional[BaseException], + exc_tb: Optional[TracebackType], + ) -> None: + if not self._entered: + __tracebackhide__ = True + raise RuntimeError("Cannot exit %r without entering first" % self) + + 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: Optional[ + Union[Type[Warning], Tuple[Type[Warning], ...]] + ] = None, + match_expr: Optional[Union[str, Pattern[str]]] = None, + *, + _ispytest: bool = False, + ) -> None: + check_ispytest(_ispytest) + super().__init__(_ispytest=True) + + msg = "exceptions must be derived from Warning, not %s" + if expected_warning is None: + expected_warning_tup = None + elif 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 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 __exit__( + self, + exc_type: Optional[Type[BaseException]], + exc_val: Optional[BaseException], + exc_tb: Optional[TracebackType], + ) -> None: + super().__exit__(exc_type, exc_val, exc_tb) + + __tracebackhide__ = True + + # only check if we're not currently handling an exception + if exc_type is None and exc_val is None and exc_tb is None: + if self.expected_warning is not None: + if not any(issubclass(r.category, self.expected_warning) for r in self): + __tracebackhide__ = True + fail( + "DID NOT WARN. No warnings of type {} was emitted. " + "The list of emitted warnings is: {}.".format( + self.expected_warning, [each.message for each in self] + ) + ) + elif self.match_expr is not None: + for r in self: + if issubclass(r.category, self.expected_warning): + if re.compile(self.match_expr).search(str(r.message)): + break + else: + fail( + "DID NOT WARN. No warnings of type {} matching" + " ('{}') was emitted. The list of emitted warnings" + " is: {}.".format( + self.expected_warning, + self.match_expr, + [each.message for each in self], + ) + ) diff --git a/venv/Lib/site-packages/_pytest/reports.py b/venv/Lib/site-packages/_pytest/reports.py new file mode 100644 index 0000000..58f1251 --- /dev/null +++ b/venv/Lib/site-packages/_pytest/reports.py @@ -0,0 +1,572 @@ +from io import StringIO +from pathlib import Path +from pprint import pprint +from typing import Any +from typing import cast +from typing import Dict +from typing import Iterable +from typing import Iterator +from typing import List +from typing import Optional +from typing import Tuple +from typing import Type +from typing import TYPE_CHECKING +from typing import TypeVar +from typing import Union + +import attr +import py + +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.compat import final +from _pytest.config import Config +from _pytest.nodes import Collector +from _pytest.nodes import Item +from _pytest.outcomes import skip + +if TYPE_CHECKING: + from typing import NoReturn + from typing_extensions import Literal + + from _pytest.runner import CallInfo + + +def getworkerinfoline(node): + try: + return node._workerinfocache + except AttributeError: + d = node.workerinfo + ver = "%s.%s.%s" % d["version_info"][:3] + node._workerinfocache = s = "[{}] {} -- Python {} {}".format( + d["id"], d["sysplatform"], ver, d["executable"] + ) + return s + + +_R = TypeVar("_R", bound="BaseReport") + + +class BaseReport: + when: Optional[str] + location: Optional[Tuple[str, Optional[int], str]] + longrepr: Union[ + None, ExceptionInfo[BaseException], Tuple[str, int, str], str, TerminalRepr + ] + sections: List[Tuple[str, str]] + nodeid: str + + 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"): + out.line(getworkerinfoline(self.node)) + + 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") + ) + + passed = property(lambda x: x.outcome == "passed") + failed = property(lambda x: x.outcome == "failed") + skipped = property(lambda x: x.outcome == "skipped") + + @property + def fspath(self) -> str: + 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) -> Optional[str]: + """**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(self, config: Config): + _category, _short, verbose = config.hook.pytest_report_teststatus( + report=self, config=config + ) + return verbose + + 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: Type[_R], reportdict: Dict[str, object]) -> _R: + """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("INTERNALERROR: Unknown entry type returned: %s" % type_name, stream=stream) + pprint("report_name: %s" % report_class, stream=stream) + pprint(reportdict, stream=stream) + pprint("Please report this bug at %s" % url, stream=stream) + pprint("-" * 100, stream=stream) + raise RuntimeError(stream.getvalue()) + + +@final +class TestReport(BaseReport): + """Basic test report object (also used for setup and teardown calls if + they fail).""" + + __test__ = False + + def __init__( + self, + nodeid: str, + location: Tuple[str, Optional[int], str], + keywords, + outcome: "Literal['passed', 'failed', 'skipped']", + longrepr: Union[ + None, ExceptionInfo[BaseException], Tuple[str, int, str], str, TerminalRepr + ], + when: "Literal['setup', 'call', 'teardown']", + sections: Iterable[Tuple[str, str]] = (), + duration: float = 0, + user_properties: Optional[Iterable[Tuple[str, object]]] = 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. + self.location: Tuple[str, Optional[int], str] = location + + #: A name -> value dictionary containing all keywords and + #: markers associated with a test invocation. + self.keywords = 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 = 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 []) + + #: List of pairs ``(str, str)`` of extra information which needs to + #: marshallable. Used by pytest to add captured text + #: from ``stdout`` and ``stderr``, but 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 = duration + + self.__dict__.update(extra) + + def __repr__(self) -> str: + return "<{} {!r} when={!r} outcome={!r}>".format( + self.__class__.__name__, self.nodeid, self.when, self.outcome + ) + + @classmethod + def from_item_and_call(cls, item: Item, call: "CallInfo[None]") -> "TestReport": + """Create and fill a TestReport with standard item and call info.""" + when = call.when + # Remove "collect" from the Literal type -- only for collection calls. + assert when != "collect" + duration = call.duration + keywords = {x: 1 for x in item.keywords} + excinfo = call.excinfo + sections = [] + if not call.excinfo: + outcome: Literal["passed", "failed", "skipped"] = "passed" + longrepr: Union[ + 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() + longrepr = (str(r.path), r.lineno, r.message) + else: + outcome = "failed" + if call.when == "call": + longrepr = item.repr_failure(excinfo) + else: # exception in setup or teardown + longrepr = item._repr_failure_py( + excinfo, style=item.config.getoption("tbstyle", "auto") + ) + for rwhen, key, content in item._report_sections: + sections.append((f"Captured {key} {rwhen}", content)) + return cls( + item.nodeid, + item.location, + keywords, + outcome, + longrepr, + when, + sections, + duration, + user_properties=item.user_properties, + ) + + +@final +class CollectReport(BaseReport): + """Collection report object.""" + + when = "collect" + + def __init__( + self, + nodeid: str, + outcome: "Literal['passed', 'skipped', 'failed']", + longrepr, + result: Optional[List[Union[Item, Collector]]], + 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 [] + + #: List of pairs ``(str, str)`` of extra information which needs to + #: marshallable. + # Used by pytest to add captured text : from ``stdout`` and ``stderr``, + # but may be used by other plugins : to add arbitrary information to + # reports. + self.sections = list(sections) + + self.__dict__.update(extra) + + @property + def location(self): + return (self.fspath, None, self.fspath) + + def __repr__(self) -> str: + return "".format( + self.nodeid, len(self.result), self.outcome + ) + + +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: Union[CollectReport, TestReport] +) -> Optional[Dict[str, Any]]: + 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], +) -> Optional[Union[CollectReport, TestReport]]: + 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: Union[ReprEntry, ReprEntryNative] + ) -> Dict[str, Any]: + data = attr.asdict(entry) + for key, value in data.items(): + if hasattr(value, "__dict__"): + data[key] = attr.asdict(value) + entry_data = {"type": type(entry).__name__, "data": data} + return entry_data + + def serialize_repr_traceback(reprtraceback: ReprTraceback) -> Dict[str, Any]: + result = attr.asdict(reprtraceback) + result["reprentries"] = [ + serialize_repr_entry(x) for x in reprtraceback.reprentries + ] + return result + + def serialize_repr_crash( + reprcrash: Optional[ReprFileLocation], + ) -> Optional[Dict[str, Any]]: + if reprcrash is not None: + return attr.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], (py.path.local, Path)): + d[name] = str(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: Union[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: Optional[Dict[str, Any]]): + 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: Union[ + ExceptionChainRepr, ReprExceptionInfo + ] = ExceptionChainRepr(chain) + else: + exception_info = ReprExceptionInfo(reprtraceback, 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..794690d --- /dev/null +++ b/venv/Lib/site-packages/_pytest/runner.py @@ -0,0 +1,462 @@ +"""Basic collect and runtest protocol implementations.""" +import bdb +import os +import sys +from typing import Callable +from typing import cast +from typing import Dict +from typing import Generic +from typing import List +from typing import Optional +from typing import Tuple +from typing import Type +from typing import TYPE_CHECKING +from typing import TypeVar +from typing import Union + +import attr + +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.compat import final +from _pytest.config.argparsing import Parser +from _pytest.nodes import Collector +from _pytest.nodes import Item +from _pytest.nodes import Node +from _pytest.outcomes import Exit +from _pytest.outcomes import Skipped +from _pytest.outcomes import TEST_OUTCOME + +if TYPE_CHECKING: + from typing_extensions import Literal + + 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=0.005, + metavar="N", + help="Minimal duration in seconds for inclusion in slowest list. Default 0.005", + ) + + +def pytest_terminal_summary(terminalreporter: "TerminalReporter") -> None: + durations = terminalreporter.config.option.durations + durations_min = terminalreporter.config.option.durations_min + verbose = terminalreporter.config.getvalue("verbose") + if durations is None: + return + 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) # type: ignore[no-any-return] + if not durations: + tr.write_sep("=", "slowest durations") + else: + tr.write_sep("=", "slowest %s durations" % durations) + dlist = dlist[:durations] + + for i, rep in enumerate(dlist): + if verbose < 2 and rep.duration < durations_min: + tr.write_line("") + tr.write_line( + "(%s durations < %gs hidden. Use -vv to show these durations.)" + % (len(dlist) - i, durations_min) + ) + 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_all() + + +def pytest_runtest_protocol(item: Item, nextitem: Optional[Item]) -> 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: Optional[Item] = None +) -> List[TestReport]: + hasrequest = hasattr(item, "_request") + if hasrequest and not item._request: # type: ignore[attr-defined] + 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)) + 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.prepare(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 + 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 + assert e.__traceback__ is not None + # Skip *this* frame + sys.last_traceback = e.__traceback__.tb_next + raise e + + +def pytest_runtest_teardown(item: Item, nextitem: Optional[Item]) -> None: + _update_current_test_var(item, "teardown") + item.session._setupstate.teardown_exact(item, nextitem) + _update_current_test_var(item, None) + + +def _update_current_test_var( + item: Item, when: Optional["Literal['setup', 'call', 'teardown']"] +) -> 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) -> Optional[Tuple[str, str, str]]: + 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: + call = call_runtest_hook(item, when, **kwds) + hook = item.ihook + report: TestReport = hook.pytest_runtest_makereport(item=item, call=call) + if log: + hook.pytest_runtest_logreport(report=report) + if check_interactive_exception(call, report): + hook.pytest_exception_interact(node=item, call=call, report=report) + return report + + +def check_interactive_exception(call: "CallInfo[object]", report: BaseReport) -> bool: + """Check whether the call raised an exception that should be reported as + interactive.""" + if call.excinfo is None: + # Didn't raise. + return False + if hasattr(report, "wasxfail"): + # Exception was expected. + return False + if isinstance(call.excinfo.value, (Skipped, bdb.BdbQuit)): + # Special control flow exception. + return False + return True + + +def call_runtest_hook( + item: Item, when: "Literal['setup', 'call', 'teardown']", **kwds +) -> "CallInfo[None]": + if when == "setup": + ihook: Callable[..., None] = item.ihook.pytest_runtest_setup + elif when == "call": + ihook = item.ihook.pytest_runtest_call + elif when == "teardown": + ihook = item.ihook.pytest_runtest_teardown + else: + assert False, f"Unhandled runtest hook case: {when}" + reraise: Tuple[Type[BaseException], ...] = (Exit,) + if not item.config.getoption("usepdb", False): + reraise += (KeyboardInterrupt,) + return CallInfo.from_call( + lambda: ihook(item=item, **kwds), when=when, reraise=reraise + ) + + +TResult = TypeVar("TResult", covariant=True) + + +@final +@attr.s(repr=False) +class CallInfo(Generic[TResult]): + """Result/Exception info a function invocation. + + :param T result: + The return value of the call, if it didn't raise. Can only be + accessed if excinfo is None. + :param Optional[ExceptionInfo] excinfo: + The captured exception of the call, if it raised. + :param float start: + The system time when the call started, in seconds since the epoch. + :param float stop: + The system time when the call ended, in seconds since the epoch. + :param float duration: + The call duration, in seconds. + :param str when: + The context of invocation: "setup", "call", "teardown", ... + """ + + _result = attr.ib(type="Optional[TResult]") + excinfo = attr.ib(type=Optional[ExceptionInfo[BaseException]]) + start = attr.ib(type=float) + stop = attr.ib(type=float) + duration = attr.ib(type=float) + when = attr.ib(type="Literal['collect', 'setup', 'call', 'teardown']") + + @property + def result(self) -> TResult: + 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: Optional[ + Union[Type[BaseException], Tuple[Type[BaseException], ...]] + ] = None, + ) -> "CallInfo[TResult]": + excinfo = None + start = timing.time() + precise_start = timing.perf_counter() + try: + result: Optional[TResult] = func() + except BaseException: + excinfo = ExceptionInfo.from_current() + if reraise is not None and isinstance(excinfo.value, reraise): + raise + result = None + # use the perf counter + precise_stop = timing.perf_counter() + duration = precise_stop - precise_start + stop = timing.time() + return cls( + start=start, + stop=stop, + duration=duration, + when=when, + result=result, + excinfo=excinfo, + ) + + 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: + call = CallInfo.from_call(lambda: list(collector.collect()), "collect") + longrepr: Union[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: + # Type ignored because unittest is loaded dynamically. + skip_exceptions.append(unittest.SkipTest) # type: ignore + 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.""" + + def __init__(self): + self.stack: List[Node] = [] + self._finalizers: Dict[Node, List[Callable[[], object]]] = {} + + def addfinalizer(self, finalizer: Callable[[], object], colitem) -> None: + """Attach a finalizer to the given colitem.""" + assert colitem and not isinstance(colitem, tuple) + assert callable(finalizer) + # assert colitem in self.stack # some unit tests don't setup stack :/ + self._finalizers.setdefault(colitem, []).append(finalizer) + + def _pop_and_teardown(self): + colitem = self.stack.pop() + self._teardown_with_finalization(colitem) + + def _callfinalizers(self, colitem) -> None: + finalizers = self._finalizers.pop(colitem, None) + exc = None + while finalizers: + fin = finalizers.pop() + try: + fin() + except TEST_OUTCOME as e: + # XXX Only first exception will be seen by user, + # ideally all should be reported. + if exc is None: + exc = e + if exc: + raise exc + + def _teardown_with_finalization(self, colitem) -> None: + self._callfinalizers(colitem) + colitem.teardown() + for colitem in self._finalizers: + assert colitem in self.stack + + def teardown_all(self) -> None: + while self.stack: + self._pop_and_teardown() + for key in list(self._finalizers): + self._teardown_with_finalization(key) + assert not self._finalizers + + def teardown_exact(self, item, nextitem) -> None: + needed_collectors = nextitem and nextitem.listchain() or [] + self._teardown_towards(needed_collectors) + + def _teardown_towards(self, needed_collectors) -> None: + exc = None + while self.stack: + if self.stack == needed_collectors[: len(self.stack)]: + break + try: + self._pop_and_teardown() + except TEST_OUTCOME as e: + # XXX Only first exception will be seen by user, + # ideally all should be reported. + if exc is None: + exc = e + if exc: + raise exc + + def prepare(self, colitem) -> None: + """Setup objects along the collector chain to the test-method.""" + + # Check if the last collection node has raised an error. + for col in self.stack: + if hasattr(col, "_prepare_exc"): + exc = col._prepare_exc # type: ignore[attr-defined] + raise exc + + needed_collectors = colitem.listchain() + for col in needed_collectors[len(self.stack) :]: + self.stack.append(col) + try: + col.setup() + except TEST_OUTCOME as e: + col._prepare_exc = e # type: ignore[attr-defined] + raise e + + +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/setuponly.py b/venv/Lib/site-packages/_pytest/setuponly.py new file mode 100644 index 0000000..44a1094 --- /dev/null +++ b/venv/Lib/site-packages/_pytest/setuponly.py @@ -0,0 +1,94 @@ +from typing import Generator +from typing import Optional +from typing import Union + +import pytest +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 + + +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(hookwrapper=True) +def pytest_fixture_setup( + fixturedef: FixtureDef[object], request: SubRequest +) -> Generator[None, None, None]: + yield + 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, "SETUP") + + +def pytest_fixture_post_finalizer(fixturedef: FixtureDef[object]) -> None: + if fixturedef.cached_result is not None: + config = fixturedef._fixturemanager.config + if config.option.setupshow: + _show_fixture_action(fixturedef, "TEARDOWN") + if hasattr(fixturedef, "cached_param"): + del fixturedef.cached_param # type: ignore[attr-defined] + + +def _show_fixture_action(fixturedef: FixtureDef[object], msg: str) -> None: + config = fixturedef._fixturemanager.config + capman = config.pluginmanager.getplugin("capturemanager") + if capman: + capman.suspend_global_capture() + + tw = config.get_terminal_writer() + tw.line() + tw.write(" " * 2 * fixturedef.scopenum) + tw.write( + "{step} {scope} {fixture}".format( + step=msg.ljust(8), # align the output to TEARDOWN + scope=fixturedef.scope[0].upper(), + fixture=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("[{}]".format(saferepr(fixturedef.cached_param, maxsize=42))) # type: ignore[attr-defined] + + tw.flush() + + if capman: + capman.resume_global_capture() + + +@pytest.hookimpl(tryfirst=True) +def pytest_cmdline_main(config: Config) -> Optional[Union[int, ExitCode]]: + 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..9ba81cc --- /dev/null +++ b/venv/Lib/site-packages/_pytest/setupplan.py @@ -0,0 +1,40 @@ +from typing import Optional +from typing import Union + +import pytest +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 + + +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 +) -> Optional[object]: + # 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) -> Optional[Union[int, ExitCode]]: + 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..9aacfec --- /dev/null +++ b/venv/Lib/site-packages/_pytest/skipping.py @@ -0,0 +1,324 @@ +"""Support for skip/xfail functions and markers.""" +import os +import platform +import sys +import traceback +from collections.abc import Mapping +from typing import Generator +from typing import Optional +from typing import Tuple +from typing import Type + +import attr + +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.reports import BaseReport +from _pytest.runner import CallInfo +from _pytest.store import StoreKey + + +def pytest_addoption(parser: Parser) -> None: + group = parser.getgroup("general") + group.addoption( + "--runxfail", + action="store_true", + dest="runxfail", + default=False, + help="report the results of xfail tests as if they were not marked", + ) + + parser.addini( + "xfail_strict", + "default for the strict parameter of xfail " + "markers when not given explicitly (default: False)", + default=False, + type="bool", + ) + + +def pytest_configure(config: Config) -> None: + if config.option.runxfail: + # yay a hack + import pytest + + old = pytest.xfail + config._cleanup.append(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.html#pytest-mark-skipif", + ) + config.addinivalue_line( + "markers", + "xfail(condition, ..., *, reason=..., run=True, raises=None, strict=xfail_strict): " + "mark the test function as an expected failure if any of the conditions " + "evaluate to True. Optionally specify a reason for better reporting " + "and run=False if you don't even want to execute the test function. " + "If only specific exception(s) are expected, you can list them in " + "raises, and if the test fails in other ways, it will be reported as " + "a true failure. See https://docs.pytest.org/en/stable/reference.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( + "pytest_markeval_namespace() needs to return a dict, got {!r}".format( + dictionary + ) + ) + globals_.update(dictionary) + if hasattr(item, "obj"): + globals_.update(item.obj.__globals__) # type: ignore[attr-defined] + try: + filename = f"<{mark.name} condition>" + condition_code = compile(condition, filename, "eval") + result = eval(condition_code, globals_) + except SyntaxError as exc: + msglines = [ + "Error evaluating %r condition" % mark.name, + " " + condition, + " " + " " * (exc.offset or 0) + "^", + "SyntaxError: invalid syntax", + ] + fail("\n".join(msglines), pytrace=False) + except Exception as exc: + msglines = [ + "Error evaluating %r condition" % mark.name, + " " + 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 = [ + "Error evaluating %r condition as a boolean" % mark.name, + *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 = ( + "Error evaluating %r: " % mark.name + + "you need to specify reason=STRING when using booleans as conditions." + ) + fail(msg, pytrace=False) + + return result, reason + + +@attr.s(slots=True, frozen=True) +class Skip: + """The result of evaluate_skip_marks().""" + + reason = attr.ib(type=str) + + +def evaluate_skip_marks(item: Item) -> Optional[Skip]: + """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"): + if "reason" in mark.kwargs: + reason = mark.kwargs["reason"] + elif mark.args: + reason = mark.args[0] + else: + reason = "unconditional skip" + return Skip(reason) + + return None + + +@attr.s(slots=True, frozen=True) +class Xfail: + """The result of evaluate_xfail_marks().""" + + reason = attr.ib(type=str) + run = attr.ib(type=bool) + strict = attr.ib(type=bool) + raises = attr.ib(type=Optional[Tuple[Type[BaseException], ...]]) + + +def evaluate_xfail_marks(item: Item) -> Optional[Xfail]: + """Evaluate xfail marks on item, returning Xfail if triggered.""" + for mark in item.iter_markers(name="xfail"): + run = mark.kwargs.get("run", True) + strict = mark.kwargs.get("strict", item.config.getini("xfail_strict")) + raises = mark.kwargs.get("raises", None) + if "condition" not in mark.kwargs: + conditions = mark.args + else: + conditions = (mark.kwargs["condition"],) + + # Unconditional. + if not conditions: + reason = mark.kwargs.get("reason", "") + return Xfail(reason, run, strict, raises) + + # If any of the conditions are true. + for condition in conditions: + result, reason = evaluate_condition(item, mark, condition) + if result: + return Xfail(reason, run, strict, raises) + + return None + + +# Whether skipped due to skip or skipif marks. +skipped_by_mark_key = StoreKey[bool]() +# Saves the xfail mark evaluation. Can be refreshed during call if None. +xfailed_key = StoreKey[Optional[Xfail]]() +unexpectedsuccess_key = StoreKey[str]() + + +@hookimpl(tryfirst=True) +def pytest_runtest_setup(item: Item) -> None: + skipped = evaluate_skip_marks(item) + item._store[skipped_by_mark_key] = skipped is not None + if skipped: + skip(skipped.reason) + + item._store[xfailed_key] = xfailed = evaluate_xfail_marks(item) + if xfailed and not item.config.option.runxfail and not xfailed.run: + xfail("[NOTRUN] " + xfailed.reason) + + +@hookimpl(hookwrapper=True) +def pytest_runtest_call(item: Item) -> Generator[None, None, None]: + xfailed = item._store.get(xfailed_key, None) + if xfailed is None: + item._store[xfailed_key] = xfailed = evaluate_xfail_marks(item) + + if xfailed and not item.config.option.runxfail and not xfailed.run: + xfail("[NOTRUN] " + xfailed.reason) + + yield + + # The test run may have added an xfail mark dynamically. + xfailed = item._store.get(xfailed_key, None) + if xfailed is None: + item._store[xfailed_key] = xfailed = evaluate_xfail_marks(item) + + +@hookimpl(hookwrapper=True) +def pytest_runtest_makereport(item: Item, call: CallInfo[None]): + outcome = yield + rep = outcome.get_result() + xfailed = item._store.get(xfailed_key, None) + # unittest special case, see setting of unexpectedsuccess_key + if unexpectedsuccess_key in item._store and rep.when == "call": + reason = item._store[unexpectedsuccess_key] + if reason: + rep.longrepr = f"Unexpected success: {reason}" + else: + rep.longrepr = "Unexpected success" + rep.outcome = "failed" + elif 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 = "reason: " + call.excinfo.value.msg + rep.outcome = "skipped" + elif not rep.skipped and xfailed: + if call.excinfo: + raises = xfailed.raises + if raises is not None and not isinstance(call.excinfo.value, raises): + rep.outcome = "failed" + else: + rep.outcome = "skipped" + rep.wasxfail = xfailed.reason + elif call.when == "call": + if xfailed.strict: + rep.outcome = "failed" + rep.longrepr = "[XPASS(strict)] " + xfailed.reason + else: + rep.outcome = "passed" + rep.wasxfail = xfailed.reason + + if ( + item._store.get(skipped_by_mark_key, True) + and rep.skipped + and type(rep.longrepr) is tuple + ): + # Skipped by mark.skipif; change the location of the failure + # to point to the item definition, otherwise it will display + # the location of where the skip exception was raised within pytest. + _, _, reason = rep.longrepr + filename, line = item.reportinfo()[:2] + assert line is not None + rep.longrepr = str(filename), line + 1, reason + + +def pytest_report_teststatus(report: BaseReport) -> Optional[Tuple[str, str, str]]: + 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/stepwise.py b/venv/Lib/site-packages/_pytest/stepwise.py new file mode 100644 index 0000000..197577c --- /dev/null +++ b/venv/Lib/site-packages/_pytest/stepwise.py @@ -0,0 +1,119 @@ +from typing import List +from typing import Optional +from typing import TYPE_CHECKING + +import pytest +from _pytest import nodes +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 _pytest.cacheprovider import Cache + +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", + ) + + +@pytest.hookimpl +def pytest_configure(config: Config) -> None: + # We should always have a cache as cache provider plugin uses tryfirst=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 + # Clear the list of failing tests if the plugin is not active. + session.config.cache.set(STEPWISE_CACHE_DIR, []) + + +class StepwisePlugin: + def __init__(self, config: Config) -> None: + self.config = config + self.session: Optional[Session] = None + self.report_status = "" + assert config.cache is not None + self.cache: Cache = config.cache + self.lastfailed: Optional[str] = self.cache.get(STEPWISE_CACHE_DIR, None) + self.skip: bool = config.getoption("stepwise_skip") + + def pytest_sessionstart(self, session: Session) -> None: + self.session = session + + def pytest_collection_modifyitems( + self, config: Config, items: List[nodes.Item] + ) -> None: + if not self.lastfailed: + self.report_status = "no previously failed tests, not skipping." + 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.lastfailed: + 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 = "previously failed test not found, not skipping." + else: + self.report_status = f"skipping {failed_index} already passed items." + 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.lastfailed: + self.lastfailed = None + + self.skip = False + else: + # Mark test as the last failing and interrupt the test session. + self.lastfailed = 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.lastfailed: + self.lastfailed = None + + def pytest_report_collectionfinish(self) -> Optional[str]: + if self.config.getoption("verbose") >= 0 and self.report_status: + return f"stepwise: {self.report_status}" + return None + + def pytest_sessionfinish(self) -> None: + self.cache.set(STEPWISE_CACHE_DIR, self.lastfailed) diff --git a/venv/Lib/site-packages/_pytest/store.py b/venv/Lib/site-packages/_pytest/store.py new file mode 100644 index 0000000..e5008cf --- /dev/null +++ b/venv/Lib/site-packages/_pytest/store.py @@ -0,0 +1,125 @@ +from typing import Any +from typing import cast +from typing import Dict +from typing import Generic +from typing import TypeVar +from typing import Union + + +__all__ = ["Store", "StoreKey"] + + +T = TypeVar("T") +D = TypeVar("D") + + +class StoreKey(Generic[T]): + """StoreKey is an object used as a key to a Store. + + A StoreKey is associated with the type T of the value of the key. + + A StoreKey is unique and cannot conflict with another key. + """ + + __slots__ = () + + +class Store: + """Store is a type-safe heterogenous mutable mapping that + allows keys and value types to be defined separately from + where it (the Store) is created. + + Usually you will be given an object which has a ``Store``: + + .. code-block:: python + + store: Store = some_object.store + + If a module wants to store data in this Store, it creates StoreKeys + for its keys (at the module level): + + .. code-block:: python + + some_str_key = StoreKey[str]() + some_bool_key = StoreKey[bool]() + + To store information: + + .. code-block:: python + + # Value type must match the key. + store[some_str_key] = "value" + store[some_bool_key] = True + + To retrieve the information: + + .. code-block:: python + + # The static type of some_str is str. + some_str = store[some_str_key] + # The static type of some_bool is bool. + some_bool = store[some_bool_key] + + Why use this? + ------------- + + Problem: module Internal defines an object. Module External, which + module Internal doesn't know about, receives the object and wants to + attach information to it, to be retrieved later given the object. + + Bad solution 1: Module External assigns private attributes directly on + the object. This doesn't work well because the type checker doesn't + know about these attributes and it complains about undefined attributes. + + Bad solution 2: module Internal adds a ``Dict[str, Any]`` attribute to + the object. Module External stores its data in private keys of this dict. + This doesn't work well because retrieved values are untyped. + + Good solution: module Internal adds a ``Store`` to the object. Module + External mints StoreKeys for its own keys. Module External stores and + retrieves its data using these keys. + """ + + __slots__ = ("_store",) + + def __init__(self) -> None: + self._store: Dict[StoreKey[Any], object] = {} + + def __setitem__(self, key: StoreKey[T], value: T) -> None: + """Set a value for key.""" + self._store[key] = value + + def __getitem__(self, key: StoreKey[T]) -> T: + """Get the value for key. + + Raises ``KeyError`` if the key wasn't set before. + """ + return cast(T, self._store[key]) + + def get(self, key: StoreKey[T], default: D) -> Union[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: StoreKey[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: StoreKey[T]) -> None: + """Delete the value for key. + + Raises ``KeyError`` if the key wasn't set before. + """ + del self._store[key] + + def __contains__(self, key: StoreKey[T]) -> bool: + """Return whether key was set.""" + return key in self._store diff --git a/venv/Lib/site-packages/_pytest/terminal.py b/venv/Lib/site-packages/_pytest/terminal.py new file mode 100644 index 0000000..fbfb09a --- /dev/null +++ b/venv/Lib/site-packages/_pytest/terminal.py @@ -0,0 +1,1405 @@ +"""Terminal reporting of the full testing process. + +This is a good source for looking at the various reporting hooks. +""" +import argparse +import datetime +import inspect +import platform +import sys +import warnings +from collections import Counter +from functools import partial +from pathlib import Path +from typing import Any +from typing import Callable +from typing import cast +from typing import Dict +from typing import Generator +from typing import List +from typing import Mapping +from typing import Optional +from typing import Sequence +from typing import Set +from typing import TextIO +from typing import Tuple +from typing import TYPE_CHECKING +from typing import Union + +import attr +import pluggy +import py + +import _pytest._version +from _pytest import nodes +from _pytest import timing +from _pytest._code import ExceptionInfo +from _pytest._code.code import ExceptionRepr +from _pytest._io.wcwidth import wcswidth +from _pytest.compat import final +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 typing_extensions import Literal + + from _pytest.main import Session + + +REPORT_COLLECTING_RESOLUTION = 0.5 + +KNOWN_TYPES = ( + "failed", + "passed", + "skipped", + "deselected", + "xfailed", + "xpassed", + "warnings", + "error", +) + +_REPORTCHARS_DEFAULT = "fE" + + +class MoreQuietAction(argparse.Action): + """A modified copy of the argparse count action which counts down and updates + the legacy quiet attribute at the same time. + + Used to unify verbosity handling. + """ + + def __init__( + self, + option_strings: Sequence[str], + dest: str, + default: object = None, + required: bool = False, + help: Optional[str] = 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: Union[str, Sequence[object], None], + option_string: Optional[str] = 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 + + +def pytest_addoption(parser: Parser) -> None: + group = parser.getgroup("terminal reporting", "reporting", after="general") + group._addoption( + "-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( + "-q", + "--quiet", + action=MoreQuietAction, + default=0, + dest="verbose", + help="decrease verbosity.", + ) + group._addoption( + "--verbosity", + dest="verbose", + type=int, + default=0, + help="set verbosity. Default is 0.", + ) + group._addoption( + "-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( + "-l", + "--showlocals", + action="store_true", + dest="showlocals", + default=False, + help="show locals in tracebacks (disabled by default).", + ) + 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( + "--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 is '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)", + ) + + parser.addini( + "console_output_style", + help='console output: "classic", or with additional progress information ("progress" (percentage) | "count").', + default="progress", + ) + + +def pytest_configure(config: Config) -> None: + reporter = TerminalReporter(config, sys.stdout) + config.pluginmanager.register(reporter, "terminalreporter") + if config.option.debug or config.option.traceconfig: + + def mywriter(tags, args): + msg = " ".join(map(str, args)) + reporter.write_line("[traceconfig] " + msg) + + config.trace.root.setprocessor("pytest:config", mywriter) + + +def getreportopt(config: Config) -> str: + reportchars: str = config.option.reportchars + + old_aliases = {"F", "S"} + reportopts = "" + for char in reportchars: + if char in old_aliases: + char = char.lower() + if char == "a": + reportopts = "sxXEf" + elif char == "A": + reportopts = "PpsxXEf" + elif char == "N": + reportopts = "" + elif char not in reportopts: + reportopts += char + + if not config.option.disable_warnings and "w" not in reportopts: + reportopts = "w" + reportopts + elif config.option.disable_warnings and "w" in reportopts: + reportopts = reportopts.replace("w", "") + + return reportopts + + +@hookimpl(trylast=True) # after _pytest.runner +def pytest_report_teststatus(report: BaseReport) -> Tuple[str, str, str]: + letter = "F" + if report.passed: + letter = "." + elif report.skipped: + letter = "s" + + outcome: str = report.outcome + if report.when in ("collect", "setup", "teardown") and outcome == "failed": + outcome = "error" + letter = "E" + + return outcome, letter, outcome.upper() + + +@attr.s +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|py.path.local fslocation: + File system location of the source of the warning (see ``get_location``). + """ + + message = attr.ib(type=str) + nodeid = attr.ib(type=Optional[str], default=None) + fslocation = attr.ib( + type=Optional[Union[Tuple[str, int], py.path.local]], default=None + ) + count_towards_summary = True + + def get_location(self, config: Config) -> Optional[str]: + """Return the more user-friendly information about the location of a warning, or None.""" + if self.nodeid: + return self.nodeid + if self.fslocation: + if isinstance(self.fslocation, tuple) and len(self.fslocation) >= 2: + filename, linenum = self.fslocation[:2] + relpath = bestrelpath( + config.invocation_params.dir, absolutepath(filename) + ) + return f"{relpath}:{linenum}" + else: + return str(self.fslocation) + return None + + +@final +class TerminalReporter: + def __init__(self, config: Config, file: Optional[TextIO] = None) -> None: + import _pytest.config + + self.config = config + self._numcollected = 0 + self._session: Optional[Session] = None + self._showfspath: Optional[bool] = None + + self.stats: Dict[str, List[Any]] = {} + self._main_color: Optional[str] = None + self._known_types: Optional[List[str]] = None + self.startdir = config.invocation_dir + 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: Union[None, Path, str, int] = None + self.reportchars = getreportopt(config) + self.hasmarkup = self._tw.hasmarkup + self.isatty = file.isatty() + self._progress_nodeids_reported: Set[str] = set() + self._show_progress_info = self._determine_show_progress_info() + self._collect_report_last_write: Optional[float] = None + self._already_displayed_warnings: Optional[int] = None + self._keyboardinterrupt_memo: Optional[ExceptionRepr] = None + + def _determine_show_progress_info(self) -> "Literal['progress', 'count', False]": + """Return whether we should display progress information based on the current config.""" + # do not show progress if we are not capturing output (#3038) + if self.config.getoption("capture", "no") == "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 == "progress": + return "progress" + elif cfg == "count": + return "count" + 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.verbosity >= 0 + return self._showfspath + + @showfspath.setter + def showfspath(self, value: Optional[bool]) -> None: + self._showfspath = value + + @property + def showlongtestinfo(self) -> bool: + return self.verbosity > 0 + + def hasopt(self, char: str) -> bool: + char = {"xfailed": "x", "skipped": "s"}.get(char, char) + return char in self.reportchars + + def write_fspath_result(self, nodeid: str, res, **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 write(self, content: str, *, flush: bool = False, **markup: bool) -> None: + self._tw.write(content, flush=flush, **markup) + + def flush(self) -> None: + self._tw.flush() + + def write_line(self, line: Union[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: Optional[str] = None, + fullwidth: Optional[int] = 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, Optional[int], str] + ) -> None: + # Ensure that the path is printed before the + # 1st test of a module starts running. + if self.showlongtestinfo: + line = self._locationline(nodeid, *location) + 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: Tuple[ + str, str, Union[str, Tuple[str, Mapping[str, bool]]] + ] = self.config.hook.pytest_report_teststatus(report=rep, config=self.config) + category, letter, word = res + 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 + running_xdist = hasattr(rep, "node") + 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 = {} + if self.verbosity <= 0: + self._tw.write(letter, **markup) + else: + self._progress_nodeids_reported.add(rep.nodeid) + line = self._locationline(rep.nodeid, *rep.location) + if not running_xdist: + self.write_ensure_prefix(line, word, **markup) + if rep.skipped or hasattr(report, "wasxfail"): + available_width = ( + (self._tw.fullwidth - self._tw.width_of_current_line) + - len(" [100%]") + - 1 + ) + reason = _get_raw_skip_reason(rep) + reason_ = _format_trimmed(" ({})", reason, available_width) + if reason and reason_ is not None: + self._tw.write(reason_) + if self._show_progress_info: + self._write_progress_information_filling_space() + else: + self.ensure_newline() + self._tw.write("[%s]" % rep.node.gateway.id) + if self._show_progress_info: + self._tw.write( + self._get_progress_information_message() + " ", cyan=True + ) + else: + self._tw.write(" ") + self._tw.write(word, **markup) + self._tw.write(" " + line) + self.currentfspath = -2 + self.flush() + + @property + def _is_last_item(self) -> bool: + assert self._session is not None + return len(self._progress_nodeids_reported) == self._session.testscollected + + def pytest_runtest_logfinish(self, nodeid: str) -> None: + assert self._session + if self.verbosity <= 0 and self._show_progress_info: + if self._show_progress_info == "count": + num_tests = self._session.testscollected + progress_length = len(" [{}/{}]".format(str(num_tests), str(num_tests))) + else: + progress_length = len(" [100%]") + + self._progress_nodeids_reported.add(nodeid) + + if self._is_last_item: + self._write_progress_information_filling_space() + else: + main_color, _ = self._get_main_color() + w = self._width_of_current_line + past_edge = w + progress_length + 1 >= self._screen_width + if past_edge: + msg = self._get_progress_information_message() + self._tw.write(msg + "\n", **{main_color: True}) + + def _get_progress_information_message(self) -> str: + assert self._session + collected = self._session.testscollected + if self._show_progress_info == "count": + if collected: + progress = self._progress_nodeids_reported + counter_format = "{{:{}d}}".format(len(str(collected))) + format_string = f" [{counter_format}/{{}}]" + return format_string.format(len(progress), collected) + return f" [ {collected} / {collected} ]" + else: + if collected: + return " [{:3d}%]".format( + len(self._progress_nodeids_reported) * 100 // collected + ) + return " [100%]" + + 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) + self._collect_report_last_write = timing.time() + 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 "collecting" report every 0.5s. + t = timing.time() + if ( + self._collect_report_last_write is not None + and self._collect_report_last_write > t - REPORT_COLLECTING_RESOLUTION + ): + return + self._collect_report_last_write = t + + errors = len(self.stats.get("error", [])) + skipped = len(self.stats.get("skipped", [])) + deselected = len(self.stats.get("deselected", [])) + selected = self._numcollected - errors - skipped - deselected + if final: + line = "collected " + else: + line = "collecting " + line += ( + str(self._numcollected) + " item" + ("" if self._numcollected == 1 else "s") + ) + if errors: + line += " / %d error%s" % (errors, "s" if errors != 1 else "") + if deselected: + line += " / %d deselected" % deselected + if skipped: + line += " / %d skipped" % skipped + if self._numcollected > selected > 0: + line += " / %d 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._sessionstarttime = timing.time() + 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 += "[pypy-{}-{}]".format(verinfo, pypy_version_info[3]) + msg += ", pytest-{}, py-{}, pluggy-{}".format( + _pytest._version.version, py.__version__, 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, startdir=self.startdir + ) + self._write_report_lines_from_hooks(lines) + + def _write_report_lines_from_hooks( + self, lines: Sequence[Union[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]: + line = "rootdir: %s" % config.rootpath + + if config.inipath: + line += ", configfile: " + bestrelpath(config.rootpath, config.inipath) + + testpaths: List[str] = config.getini("testpaths") + if config.invocation_params.dir == config.rootpath and config.args == testpaths: + line += ", testpaths: {}".format(", ".join(testpaths)) + + result = [line] + + plugininfo = config.pluginmanager.list_plugin_distinfo() + if plugininfo: + result.append("plugins: %s" % ", ".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, startdir=self.startdir, 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: + # To print out items and their parent collectors + # we take care to leave out Instances aka () + # because later versions are going to get rid of them anyway. + if self.config.option.verbose < 0: + if self.config.option.verbose < -1: + counts = Counter(item.nodeid.split("::", 1)[0] for item in items) + for name, count in sorted(counts.items()): + self._tw.line("%s: %d" % (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) + if col.name == "()": # Skip Instances. + continue + indent = (len(stack) - 1) * " " + self._tw.line(f"{indent}{col}") + if self.config.option.verbose >= 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(hookwrapper=True) + def pytest_sessionfinish( + self, session: "Session", exitstatus: Union[int, ExitCode] + ): + outcome = yield + outcome.get_result() + 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() + + @hookimpl(hookwrapper=True) + def pytest_terminal_summary(self) -> Generator[None, None, None]: + self.summary_errors() + self.summary_failures() + self.summary_warnings() + self.summary_passes() + yield + 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, fspath, lineno, domain): + def mkrel(nodeid): + 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 + + # collect_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, 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): + values = [] + for x in self.stats.get(name, []): + if not hasattr(x, "_pdbshown"): + values.append(x) + return values + + def summary_warnings(self) -> None: + if self.hasopt("w"): + all_warnings: Optional[List[WarningReport]] = 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/warnings.html") + + def summary_passes(self) -> None: + if self.config.option.tbstyle != "no": + if self.hasopt("P"): + reports: List[TestReport] = self.getreports("passed") + if not reports: + return + self.write_sep("=", "PASSES") + 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: + if self.config.option.tbstyle != "no": + reports: List[BaseReport] = self.getreports("failed") + if not reports: + return + self.write_sep("=", "FAILURES") + if self.config.option.tbstyle == "line": + for rep in reports: + line = self._getcrashline(rep) + self.write_line(line) + else: + for rep in reports: + msg = self._getfailureheadline(rep) + self.write_sep("_", msg, red=True, bold=True) + self._outrep_summary(rep) + self._handle_teardown_sections(rep.nodeid) + + def summary_errors(self) -> None: + if self.config.option.tbstyle != "no": + reports: List[BaseReport] = self.getreports("error") + if not reports: + return + self.write_sep("=", "ERRORS") + for rep in self.stats["error"]: + msg = self._getfailureheadline(rep) + if rep.when == "collect": + msg = "ERROR collecting " + msg + else: + msg = f"ERROR at {rep.when} of {msg}" + self.write_sep("_", msg, red=True, bold=True) + self._outrep_summary(rep) + + def _outrep_summary(self, rep: BaseReport) -> None: + rep.toterminal(self._tw) + showcapture = self.config.option.showcapture + if showcapture == "no": + return + for secname, content in rep.sections: + if showcapture != "all" and showcapture not in secname: + continue + self._tw.sep("-", secname) + if content[-1:] == "\n": + content = content[:-1] + self._tw.line(content) + + def summary_stats(self) -> None: + if self.verbosity < -1: + return + + session_duration = timing.time() - self._sessionstarttime + (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 = " in {}".format(format_session_duration(session_duration)) + 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(stat, lines: List[str]) -> None: + failed = self.stats.get(stat, []) + if not failed: + return + termwidth = self._tw.fullwidth + config = self.config + for rep in failed: + line = _get_line_with_reprcrash_message(config, rep, termwidth) + lines.append(line) + + def show_xfailed(lines: List[str]) -> None: + xfailed = self.stats.get("xfailed", []) + for rep in xfailed: + verbose_word = rep._get_verbose_word(self.config) + pos = _get_pos(self.config, rep) + lines.append(f"{verbose_word} {pos}") + reason = rep.wasxfail + if reason: + lines.append(" " + str(reason)) + + def show_xpassed(lines: List[str]) -> None: + xpassed = self.stats.get("xpassed", []) + for rep in xpassed: + verbose_word = rep._get_verbose_word(self.config) + pos = _get_pos(self.config, rep) + reason = rep.wasxfail + lines.append(f"{verbose_word} {pos} {reason}") + + def show_skipped(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 = skipped[0]._get_verbose_word(self.config) + for num, fspath, lineno, reason in fskips: + if reason.startswith("Skipped: "): + reason = reason[9:] + if lineno is not None: + lines.append( + "%s [%d] %s:%d: %s" + % (verbose_word, num, fspath, lineno, reason) + ) + else: + lines.append("%s [%d] %s: %s" % (verbose_word, num, fspath, reason)) + + REPORTCHAR_ACTIONS: Mapping[str, Callable[[List[str]], None]] = { + "x": show_xfailed, + "X": show_xpassed, + "f": partial(show_simple, "failed"), + "s": show_skipped, + "p": partial(show_simple, "passed"), + "E": partial(show_simple, "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") + 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.keys(): + 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)) + + 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") + 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})] + + return parts, main_color + + +def _get_pos(config: Config, rep: BaseReport): + nodeid = config.cwd_relative_nodeid(rep.nodeid) + return nodeid + + +def _format_trimmed(format: str, msg: str, available_width: int) -> Optional[str]: + """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, termwidth: int +) -> str: + """Get summary line for a report, trying to add reprcrash message.""" + verbose_word = rep._get_verbose_word(config) + pos = _get_pos(config, rep) + + line = f"{verbose_word} {pos}" + line_width = wcswidth(line) + + try: + # Type ignored intentionally -- possible AttributeError expected. + msg = rep.longrepr.reprcrash.message # type: ignore[union-attr] + except AttributeError: + pass + else: + available_width = termwidth - 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, Optional[int], str]]: + d: Dict[Tuple[str, Optional[int], 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, Optional[int], str] = (fspath, None, reason) + else: + key = (fspath, lineno, reason) + d.setdefault(key, []).append(event) + values: List[Tuple[int, str, Optional[int], str]] = [] + for key, events in d.items(): + values.append((len(events), *key)) + return values + + +_color_for_type = { + "failed": "red", + "error": "red", + "warnings": "yellow", + "passed": "green", +} +_color_for_type_default = "yellow" + + +def pluralize(count: int, noun: str) -> Tuple[int, str]: + # No need to pluralize words such as `failed` or `passed`. + if noun not in ["error", "warnings", "test"]: + return count, noun + + # The `warnings` key is plural. To avoid API breakage, we keep it that way but + # set it to singular here so we can determine plurality in the same way as we do + # for `error`. + noun = noun.replace("warnings", "warning") + + return count, noun + "s" if count != 1 else noun + + +def _plugin_nameversions(plugininfo) -> List[str]: + values: List[str] = [] + for plugin, dist in plugininfo: + # Gets us name and version! + name = "{dist.project_name}-{dist.version}".format(dist=dist) + # 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 _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 = cast(str, report.wasxfail) + if reason.startswith("reason: "): + reason = reason[len("reason: ") :] + return reason + else: + assert report.skipped + assert isinstance(report.longrepr, tuple) + _, _, reason = report.longrepr + if reason.startswith("Skipped: "): + reason = reason[len("Skipped: ") :] + elif reason == "Skipped": + reason = "" + return reason diff --git a/venv/Lib/site-packages/_pytest/threadexception.py b/venv/Lib/site-packages/_pytest/threadexception.py new file mode 100644 index 0000000..1c1f62f --- /dev/null +++ b/venv/Lib/site-packages/_pytest/threadexception.py @@ -0,0 +1,90 @@ +import threading +import traceback +import warnings +from types import TracebackType +from typing import Any +from typing import Callable +from typing import Generator +from typing import Optional +from typing import Type + +import pytest + + +# Copied from cpython/Lib/test/support/threading_helper.py, with modifications. +class catch_threading_exception: + """Context manager catching threading.Thread exception using + threading.excepthook. + + Storing exc_value using a custom hook can create a reference cycle. The + reference cycle is broken explicitly when the context manager exits. + + Storing thread using a custom hook can resurrect it if it is set to an + object which is being finalized. Exiting the context manager clears the + stored object. + + Usage: + with threading_helper.catch_threading_exception() as cm: + # code spawning a thread which raises an exception + ... + # check the thread exception: use cm.args + ... + # cm.args attribute no longer exists at this point + # (to break a reference cycle) + """ + + def __init__(self) -> None: + # See https://github.com/python/typeshed/issues/4767 regarding the underscore. + self.args: Optional["threading._ExceptHookArgs"] = None + self._old_hook: Optional[Callable[["threading._ExceptHookArgs"], Any]] = None + + def _hook(self, args: "threading._ExceptHookArgs") -> None: + self.args = args + + def __enter__(self) -> "catch_threading_exception": + self._old_hook = threading.excepthook + threading.excepthook = self._hook + return self + + def __exit__( + self, + exc_type: Optional[Type[BaseException]], + exc_val: Optional[BaseException], + exc_tb: Optional[TracebackType], + ) -> None: + assert self._old_hook is not None + threading.excepthook = self._old_hook + self._old_hook = None + del self.args + + +def thread_exception_runtest_hook() -> Generator[None, None, None]: + with catch_threading_exception() as cm: + yield + if cm.args: + if cm.args.thread is not None: + thread_name = cm.args.thread.name + else: + thread_name = "" + msg = f"Exception in thread {thread_name}\n\n" + msg += "".join( + traceback.format_exception( + cm.args.exc_type, cm.args.exc_value, cm.args.exc_traceback, + ) + ) + warnings.warn(pytest.PytestUnhandledThreadExceptionWarning(msg)) + + +@pytest.hookimpl(hookwrapper=True, trylast=True) +def pytest_runtest_setup() -> Generator[None, None, None]: + yield from thread_exception_runtest_hook() + + +@pytest.hookimpl(hookwrapper=True, tryfirst=True) +def pytest_runtest_call() -> Generator[None, None, None]: + yield from thread_exception_runtest_hook() + + +@pytest.hookimpl(hookwrapper=True, tryfirst=True) +def pytest_runtest_teardown() -> Generator[None, None, None]: + yield from thread_exception_runtest_hook() diff --git a/venv/Lib/site-packages/_pytest/timing.py b/venv/Lib/site-packages/_pytest/timing.py new file mode 100644 index 0000000..925163a --- /dev/null +++ b/venv/Lib/site-packages/_pytest/timing.py @@ -0,0 +1,12 @@ +"""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 time import perf_counter +from time import sleep +from time import 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..3fe1758 --- /dev/null +++ b/venv/Lib/site-packages/_pytest/tmpdir.py @@ -0,0 +1,253 @@ +"""Support for providing temporary directories to test functions.""" +import os +import re +import tempfile +from pathlib import Path +from typing import Optional + +import attr +import py + +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 final +from _pytest.config import Config +from _pytest.deprecated import check_ispytest +from _pytest.fixtures import fixture +from _pytest.fixtures import FixtureRequest +from _pytest.monkeypatch import MonkeyPatch + + +@final +@attr.s(init=False) +class TempPathFactory: + """Factory for temporary directories under the common base temp directory. + + The base directory can be configured using the ``--basetemp`` option. + """ + + _given_basetemp = attr.ib(type=Optional[Path]) + _trace = attr.ib() + _basetemp = attr.ib(type=Optional[Path]) + + def __init__( + self, + given_basetemp: Optional[Path], + trace, + basetemp: Optional[Path] = 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._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) + return cls( + given_basetemp=config.option.basetemp, + trace=config.trace.get("tmpdir"), + _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.""" + 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}") + 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. + if hasattr(os, "getuid"): + rootdir_stat = rootdir.stat() + uid = os.getuid() + # getuid shouldn't fail, but cpython defines such a case. + # Let's hope for the best. + if uid != -1: + 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) + basetemp = make_numbered_dir_with_cleanup( + prefix="pytest-", + root=rootdir, + keep=3, + lock_timeout=LOCK_TIMEOUT, + mode=0o700, + ) + assert basetemp is not None, basetemp + self._basetemp = basetemp + self._trace("new basetemp", basetemp) + return basetemp + + +@final +@attr.s(init=False) +class TempdirFactory: + """Backward comptibility wrapper that implements :class:``py.path.local`` + for :class:``TempPathFactory``.""" + + _tmppath_factory = attr.ib(type=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) -> py.path.local: + """Same as :meth:`TempPathFactory.mktemp`, but returns a ``py.path.local`` object.""" + return py.path.local(self._tmppath_factory.mktemp(basename, numbered).resolve()) + + def getbasetemp(self) -> py.path.local: + """Backward compat wrapper for ``_tmppath_factory.getbasetemp``.""" + return py.path.local(self._tmppath_factory.getbasetemp().resolve()) + + +def get_user() -> Optional[str]: + """Return the current user name, or None if getuser() does not work + in the current environment (see #1010).""" + import getpass + + try: + return getpass.getuser() + except (ImportError, KeyError): + return None + + +def pytest_configure(config: Config) -> None: + """Create a TempdirFactory 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. + """ + mp = MonkeyPatch() + tmppath_handler = TempPathFactory.from_config(config, _ispytest=True) + t = TempdirFactory(tmppath_handler, _ispytest=True) + config._cleanup.append(mp.undo) + mp.setattr(config, "_tmp_path_factory", tmppath_handler, raising=False) + mp.setattr(config, "_tmpdirhandler", t, raising=False) + + +@fixture(scope="session") +def tmpdir_factory(request: FixtureRequest) -> TempdirFactory: + """Return a :class:`_pytest.tmpdir.TempdirFactory` instance for the test session.""" + # Set dynamically by pytest_configure() above. + return request.config._tmpdirhandler # type: ignore + + +@fixture(scope="session") +def tmp_path_factory(request: FixtureRequest) -> TempPathFactory: + """Return a :class:`_pytest.tmpdir.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 tmpdir(tmp_path: Path) -> py.path.local: + """Return a temporary directory path object which is unique to each test + function invocation, created as a sub directory of the base temporary + directory. + + By default, a new base temporary directory is created each test session, + and old bases are removed after 3 sessions, to aid in debugging. If + ``--basetemp`` is used then it is cleared each session. See :ref:`base + temporary directory`. + + The returned object is a `py.path.local`_ path object. + + .. _`py.path.local`: https://py.readthedocs.io/en/latest/path.html + """ + return py.path.local(tmp_path) + + +@fixture +def tmp_path(request: FixtureRequest, tmp_path_factory: TempPathFactory) -> Path: + """Return a temporary directory path object which is unique to each test + function invocation, created as a sub directory of the base temporary + directory. + + By default, a new base temporary directory is created each test session, + and old bases are removed after 3 sessions, to aid in debugging. If + ``--basetemp`` is used then it is cleared each session. See :ref:`base + temporary directory`. + + The returned object is a :class:`pathlib.Path` object. + """ + + return _mk_tmp(request, tmp_path_factory) diff --git a/venv/Lib/site-packages/_pytest/unittest.py b/venv/Lib/site-packages/_pytest/unittest.py new file mode 100644 index 0000000..55f15ef --- /dev/null +++ b/venv/Lib/site-packages/_pytest/unittest.py @@ -0,0 +1,405 @@ +"""Discover and run std-library "unittest" style tests.""" +import sys +import traceback +import types +from typing import Any +from typing import Callable +from typing import Generator +from typing import Iterable +from typing import List +from typing import Optional +from typing import Tuple +from typing import Type +from typing import TYPE_CHECKING +from typing import Union + +import _pytest._code +import pytest +from _pytest.compat import getimfunc +from _pytest.compat import is_async_function +from _pytest.config import hookimpl +from _pytest.fixtures import FixtureRequest +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 PyCollector +from _pytest.runner import CallInfo +from _pytest.skipping import skipped_by_mark_key +from _pytest.skipping import unexpectedsuccess_key + +if TYPE_CHECKING: + import unittest + + from _pytest.fixtures import _Scope + + _SysExcInfoType = Union[ + Tuple[Type[BaseException], BaseException, types.TracebackType], + Tuple[None, None, None], + ] + + +def pytest_pycollect_makeitem( + collector: PyCollector, name: str, obj: object +) -> Optional["UnitTestCase"]: + # Has unittest been imported and is obj a subclass of its TestCase? + try: + ut = sys.modules["unittest"] + # Type ignored because `ut` is an opaque module. + if not issubclass(obj, ut.TestCase): # type: ignore + return None + except Exception: + return None + # Yes, so let's collect it. + item: UnitTestCase = UnitTestCase.from_parent(collector, name=name, obj=obj) + return item + + +class UnitTestCase(Class): + # Marker for fixturemanger.getfixtureinfo() + # to declare that our children do not support funcargs. + nofuncargs = True + + def collect(self) -> Iterable[Union[Item, Collector]]: + from unittest import TestLoader + + cls = self.obj + if not getattr(cls, "__test__", True): + return + + skipped = _is_skipped(cls) + if not skipped: + self._inject_setup_teardown_fixtures(cls) + self._inject_setup_class_fixture() + + self.session._fixturemanager.parsefactories(self, unittest=True) + loader = TestLoader() + foundsomething = False + for name in loader.getTestCaseNames(self.obj): + x = getattr(self.obj, name) + if not getattr(x, "__test__", True): + continue + funcobj = getimfunc(x) + yield TestCaseFunction.from_parent(self, name=name, callobj=funcobj) + foundsomething = True + + if not foundsomething: + runtest = getattr(self.obj, "runTest", None) + if runtest is not None: + ut = sys.modules.get("twisted.trial.unittest", None) + # Type ignored because `ut` is an opaque module. + if ut is None or runtest != ut.TestCase.runTest: # type: ignore + yield TestCaseFunction.from_parent(self, name="runTest") + + def _inject_setup_teardown_fixtures(self, cls: type) -> None: + """Injects a hidden auto-use fixture to invoke setUpClass/setup_method and corresponding + teardown functions (#517).""" + class_fixture = _make_xunit_fixture( + cls, + "setUpClass", + "tearDownClass", + "doClassCleanups", + scope="class", + pass_self=False, + ) + if class_fixture: + cls.__pytest_class_setup = class_fixture # type: ignore[attr-defined] + + method_fixture = _make_xunit_fixture( + cls, + "setup_method", + "teardown_method", + None, + scope="function", + pass_self=True, + ) + if method_fixture: + cls.__pytest_method_setup = method_fixture # type: ignore[attr-defined] + + +def _make_xunit_fixture( + obj: type, + setup_name: str, + teardown_name: str, + cleanup_name: Optional[str], + scope: "_Scope", + pass_self: bool, +): + setup = getattr(obj, setup_name, None) + teardown = getattr(obj, teardown_name, None) + if setup is None and teardown is None: + return None + + if cleanup_name: + cleanup = getattr(obj, cleanup_name, lambda *args: None) + else: + + def cleanup(*args): + pass + + @pytest.fixture( + scope=scope, + autouse=True, + # Use a unique name to speed up lookup. + name=f"unittest_{setup_name}_fixture_{obj.__qualname__}", + ) + def fixture(self, request: FixtureRequest) -> Generator[None, None, None]: + if _is_skipped(self): + reason = self.__unittest_skip_why__ + pytest.skip(reason) + if setup is not None: + try: + if pass_self: + setup(self, request.function) + else: + setup() + # unittest does not call the cleanup function for every BaseException, so we + # follow this here. + except Exception: + if pass_self: + cleanup(self) + else: + cleanup() + + raise + yield + try: + if teardown is not None: + if pass_self: + teardown(self, request.function) + else: + teardown() + finally: + if pass_self: + cleanup(self) + else: + cleanup() + + return fixture + + +class TestCaseFunction(Function): + nofuncargs = True + _excinfo: Optional[List[_pytest._code.ExceptionInfo[BaseException]]] = None + _testcase: Optional["unittest.TestCase"] = None + + def setup(self) -> None: + # A bound method to be called during teardown() if set (see 'runtest()'). + self._explicit_tearDown: Optional[Callable[[], None]] = None + assert self.parent is not None + self._testcase = self.parent.obj(self.name) # type: ignore[attr-defined] + self._obj = getattr(self._testcase, self.name) + if hasattr(self, "_request"): + self._request._fillfixtures() + + def teardown(self) -> None: + if self._explicit_tearDown is not None: + self._explicit_tearDown() + self._explicit_tearDown = None + self._testcase = None + self._obj = None + + def startTest(self, testcase: "unittest.TestCase") -> None: + pass + + def _addexcinfo(self, rawexcinfo: "_SysExcInfoType") -> None: + # Unwrap potential exception info (see twisted trial support below). + rawexcinfo = getattr(rawexcinfo, "_rawexcinfo", rawexcinfo) + try: + excinfo = _pytest._code.ExceptionInfo(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 " + "representation:\n%r" % (rawexcinfo,), + pytrace=False, + ) + except KeyboardInterrupt: + raise + except fail.Exception: + excinfo = _pytest._code.ExceptionInfo.from_current() + self.__dict__.setdefault("_excinfo", []).append(excinfo) + + def addError( + self, testcase: "unittest.TestCase", rawexcinfo: "_SysExcInfoType" + ) -> None: + try: + if isinstance(rawexcinfo[1], exit.Exception): + exit(rawexcinfo[1].msg) + except TypeError: + pass + self._addexcinfo(rawexcinfo) + + def addFailure( + self, testcase: "unittest.TestCase", rawexcinfo: "_SysExcInfoType" + ) -> None: + self._addexcinfo(rawexcinfo) + + def addSkip(self, testcase: "unittest.TestCase", reason: str) -> None: + try: + skip(reason) + except skip.Exception: + self._store[skipped_by_mark_key] = True + self._addexcinfo(sys.exc_info()) + + def addExpectedFailure( + self, + testcase: "unittest.TestCase", + rawexcinfo: "_SysExcInfoType", + reason: str = "", + ) -> None: + try: + xfail(str(reason)) + except xfail.Exception: + self._addexcinfo(sys.exc_info()) + + def addUnexpectedSuccess( + self, testcase: "unittest.TestCase", reason: str = "" + ) -> None: + self._store[unexpectedsuccess_key] = reason + + def addSuccess(self, testcase: "unittest.TestCase") -> None: + pass + + def stopTest(self, testcase: "unittest.TestCase") -> None: + pass + + def _expecting_failure(self, test_method) -> bool: + """Return True if the given unittest method (or the entire class) is marked + with @expectedFailure.""" + expecting_failure_method = getattr( + test_method, "__unittest_expecting_failure__", False + ) + expecting_failure_class = getattr(self, "__unittest_expecting_failure__", False) + return bool(expecting_failure_class or expecting_failure_method) + + def runtest(self) -> None: + from _pytest.debugging import maybe_wrap_pytest_function_for_tracing + + assert self._testcase is not None + + maybe_wrap_pytest_function_for_tracing(self) + + # Let the unittest framework handle async functions. + if is_async_function(self.obj): + # Type ignored because self acts as the TestResult, but is not actually one. + self._testcase(result=self) # type: ignore[arg-type] + 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. + if self.config.getoption("usepdb") and not _is_skipped(self.obj): + self._explicit_tearDown = self._testcase.tearDown + setattr(self._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(self._testcase, self.name, self.obj) + try: + self._testcase(result=self) # type: ignore[arg-type] + finally: + delattr(self._testcase, self.name) + + def _prunetraceback( + self, excinfo: _pytest._code.ExceptionInfo[BaseException] + ) -> None: + Function._prunetraceback(self, excinfo) + traceback = excinfo.traceback.filter( + lambda x: not x.frame.f_globals.get("__unittest") + ) + if traceback: + excinfo.traceback = traceback + + +@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 + + unittest = sys.modules.get("unittest") + if ( + unittest + and call.excinfo + and isinstance(call.excinfo.value, unittest.SkipTest) # type: ignore[attr-defined] + ): + excinfo = call.excinfo + # Let's substitute the excinfo with a pytest.skip one. + call2 = CallInfo[None].from_call( + lambda: pytest.skip(str(excinfo.value)), call.when + ) + call.excinfo = call2.excinfo + + +# Twisted trial support. + + +@hookimpl(hookwrapper=True) +def pytest_runtest_protocol(item: Item) -> Generator[None, None, None]: + if isinstance(item, TestCaseFunction) and "twisted.trial.unittest" in sys.modules: + ut: Any = sys.modules["twisted.python.failure"] + Failure__init__ = ut.Failure.__init__ + check_testcase_implements_trial_reporter() + + def excstore( + self, exc_value=None, exc_type=None, exc_tb=None, captureVars=None + ): + if exc_value is None: + self._rawexcinfo = sys.exc_info() + else: + if exc_type is None: + exc_type = type(exc_value) + self._rawexcinfo = (exc_type, exc_value, exc_tb) + try: + Failure__init__( + self, exc_value, exc_type, exc_tb, captureVars=captureVars + ) + except TypeError: + Failure__init__(self, exc_value, exc_type, exc_tb) + + ut.Failure.__init__ = excstore + yield + ut.Failure.__init__ = Failure__init__ + else: + yield + + +def check_testcase_implements_trial_reporter(done: List[int] = []) -> None: + if done: + return + from zope.interface import classImplements + from twisted.trial.itrial import IReporter + + classImplements(TestCaseFunction, IReporter) + done.append(1) + + +def _is_skipped(obj) -> bool: + """Return True if the given object has been marked with @unittest.skip.""" + return bool(getattr(obj, "__unittest_skip__", False)) diff --git a/venv/Lib/site-packages/_pytest/unraisableexception.py b/venv/Lib/site-packages/_pytest/unraisableexception.py new file mode 100644 index 0000000..fcb5d82 --- /dev/null +++ b/venv/Lib/site-packages/_pytest/unraisableexception.py @@ -0,0 +1,93 @@ +import sys +import traceback +import warnings +from types import TracebackType +from typing import Any +from typing import Callable +from typing import Generator +from typing import Optional +from typing import Type + +import pytest + + +# Copied from cpython/Lib/test/support/__init__.py, with modifications. +class catch_unraisable_exception: + """Context manager catching unraisable exception using sys.unraisablehook. + + Storing the exception value (cm.unraisable.exc_value) creates a reference + cycle. The reference cycle is broken explicitly when the context manager + exits. + + Storing the object (cm.unraisable.object) can resurrect it if it is set to + an object which is being finalized. Exiting the context manager clears the + stored object. + + Usage: + with catch_unraisable_exception() as cm: + # code creating an "unraisable exception" + ... + # check the unraisable exception: use cm.unraisable + ... + # cm.unraisable attribute no longer exists at this point + # (to break a reference cycle) + """ + + def __init__(self) -> None: + self.unraisable: Optional["sys.UnraisableHookArgs"] = None + self._old_hook: Optional[Callable[["sys.UnraisableHookArgs"], Any]] = None + + def _hook(self, unraisable: "sys.UnraisableHookArgs") -> None: + # Storing unraisable.object can resurrect an object which is being + # finalized. Storing unraisable.exc_value creates a reference cycle. + self.unraisable = unraisable + + def __enter__(self) -> "catch_unraisable_exception": + self._old_hook = sys.unraisablehook + sys.unraisablehook = self._hook + return self + + def __exit__( + self, + exc_type: Optional[Type[BaseException]], + exc_val: Optional[BaseException], + exc_tb: Optional[TracebackType], + ) -> None: + assert self._old_hook is not None + sys.unraisablehook = self._old_hook + self._old_hook = None + del self.unraisable + + +def unraisable_exception_runtest_hook() -> Generator[None, None, None]: + with catch_unraisable_exception() as cm: + yield + if cm.unraisable: + if cm.unraisable.err_msg is not None: + err_msg = cm.unraisable.err_msg + else: + err_msg = "Exception ignored in" + msg = f"{err_msg}: {cm.unraisable.object!r}\n\n" + msg += "".join( + traceback.format_exception( + cm.unraisable.exc_type, + cm.unraisable.exc_value, + cm.unraisable.exc_traceback, + ) + ) + warnings.warn(pytest.PytestUnraisableExceptionWarning(msg)) + + +@pytest.hookimpl(hookwrapper=True, tryfirst=True) +def pytest_runtest_setup() -> Generator[None, None, None]: + yield from unraisable_exception_runtest_hook() + + +@pytest.hookimpl(hookwrapper=True, tryfirst=True) +def pytest_runtest_call() -> Generator[None, None, None]: + yield from unraisable_exception_runtest_hook() + + +@pytest.hookimpl(hookwrapper=True, tryfirst=True) +def pytest_runtest_teardown() -> Generator[None, None, None]: + yield from unraisable_exception_runtest_hook() 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..2eadd9f --- /dev/null +++ b/venv/Lib/site-packages/_pytest/warning_types.py @@ -0,0 +1,132 @@ +from typing import Any +from typing import Generic +from typing import Type +from typing import TypeVar + +import attr + +from _pytest.compat import final + + +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" + + +@final +class PytestDeprecationWarning(PytestWarning, DeprecationWarning): + """Warning class for features that will be removed in a future version.""" + + __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( + "{apiname} is an experimental api that may change over time".format( + apiname=apiname + ) + ) + + +@final +class PytestUnhandledCoroutineWarning(PytestWarning): + """Warning emitted for an unhandled coroutine. + + A coroutine was encountered when collecting test functions, but was not + handled by any async-aware plugin. + Coroutine test functions are not natively supported. + """ + + __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 +@attr.s +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 = attr.ib(type=Type["_W"]) + template = attr.ib(type=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)) diff --git a/venv/Lib/site-packages/_pytest/warnings.py b/venv/Lib/site-packages/_pytest/warnings.py new file mode 100644 index 0000000..35eed96 --- /dev/null +++ b/venv/Lib/site-packages/_pytest/warnings.py @@ -0,0 +1,139 @@ +import sys +import warnings +from contextlib import contextmanager +from typing import Generator +from typing import Optional +from typing import TYPE_CHECKING + +import pytest +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 + +if TYPE_CHECKING: + from typing_extensions import Literal + + +def pytest_configure(config: Config) -> None: + config.addinivalue_line( + "markers", + "filterwarnings(warning): add a warning filter to the given test. " + "see https://docs.pytest.org/en/stable/warnings.html#pytest-mark-filterwarnings ", + ) + + +@contextmanager +def catch_warnings_for_item( + config: Config, + ihook, + when: "Literal['config', 'collect', 'runtest']", + item: Optional[Item], +) -> Generator[None, None, 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=True) as log: + # mypy can't infer that record=True means log is not None; help it. + assert log is not None + + 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) + + 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)) + + yield + + for warning_message in log: + ihook.pytest_warning_captured.call_historic( + kwargs=dict( + warning_message=warning_message, + when=when, + item=item, + location=None, + ) + ) + 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.""" + warn_msg = warning_message.message + msg = warnings.formatwarning( + str(warn_msg), + warning_message.category, + warning_message.filename, + warning_message.lineno, + warning_message.line, + ) + return msg + + +@pytest.hookimpl(hookwrapper=True, tryfirst=True) +def pytest_runtest_protocol(item: Item) -> Generator[None, None, None]: + with catch_warnings_for_item( + config=item.config, ihook=item.ihook, when="runtest", item=item + ): + yield + + +@pytest.hookimpl(hookwrapper=True, tryfirst=True) +def pytest_collection(session: Session) -> Generator[None, None, None]: + config = session.config + with catch_warnings_for_item( + config=config, ihook=config.hook, when="collect", item=None + ): + yield + + +@pytest.hookimpl(hookwrapper=True) +def pytest_terminal_summary( + terminalreporter: TerminalReporter, +) -> Generator[None, None, None]: + config = terminalreporter.config + with catch_warnings_for_item( + config=config, ihook=config.hook, when="config", item=None + ): + yield + + +@pytest.hookimpl(hookwrapper=True) +def pytest_sessionfinish(session: Session) -> Generator[None, None, None]: + config = session.config + with catch_warnings_for_item( + config=config, ihook=config.hook, when="config", item=None + ): + yield + + +@pytest.hookimpl(hookwrapper=True) +def pytest_load_initial_conftests( + early_config: "Config", +) -> Generator[None, None, None]: + with catch_warnings_for_item( + config=early_config, ihook=early_config.hook, when="config", item=None + ): + yield diff --git a/venv/Lib/site-packages/atomicwrites-1.4.0.dist-info/INSTALLER b/venv/Lib/site-packages/atomicwrites-1.4.0.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/venv/Lib/site-packages/atomicwrites-1.4.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/Lib/site-packages/atomicwrites-1.4.0.dist-info/LICENSE b/venv/Lib/site-packages/atomicwrites-1.4.0.dist-info/LICENSE new file mode 100644 index 0000000..3bbadc3 --- /dev/null +++ b/venv/Lib/site-packages/atomicwrites-1.4.0.dist-info/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2015-2016 Markus Unterwaditzer + +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/atomicwrites-1.4.0.dist-info/METADATA b/venv/Lib/site-packages/atomicwrites-1.4.0.dist-info/METADATA new file mode 100644 index 0000000..ca2c892 --- /dev/null +++ b/venv/Lib/site-packages/atomicwrites-1.4.0.dist-info/METADATA @@ -0,0 +1,145 @@ +Metadata-Version: 2.1 +Name: atomicwrites +Version: 1.4.0 +Summary: Atomic file writes. +Home-page: https://github.com/untitaker/python-atomicwrites +Author: Markus Unterwaditzer +Author-email: markus@unterwaditzer.net +License: MIT +Platform: UNKNOWN +Classifier: License :: OSI Approved :: MIT License +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.4 +Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: Implementation :: CPython +Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.* + +=================== +python-atomicwrites +=================== + +.. image:: https://travis-ci.org/untitaker/python-atomicwrites.svg?branch=master + :target: https://travis-ci.org/untitaker/python-atomicwrites + +.. image:: https://ci.appveyor.com/api/projects/status/vadc4le3c27to59x/branch/master?svg=true + :target: https://ci.appveyor.com/project/untitaker/python-atomicwrites/branch/master + +Atomic file writes. + +.. code-block:: python + + from atomicwrites import atomic_write + + with atomic_write('foo.txt', overwrite=True) as f: + f.write('Hello world.') + # "foo.txt" doesn't exist yet. + + # Now it does. + + +Features that distinguish it from other similar libraries (see `Alternatives and Credit`_): + +- Race-free assertion that the target file doesn't yet exist. This can be + controlled with the ``overwrite`` parameter. + +- Windows support, although not well-tested. The MSDN resources are not very + explicit about which operations are atomic. I'm basing my assumptions off `a + comment + `_ + by `Doug Crook + `_, who appears + to be a Microsoft employee: + + FAQ: Is MoveFileEx atomic + Frequently asked question: Is MoveFileEx atomic if the existing and new + files are both on the same drive? + + The simple answer is "usually, but in some cases it will silently fall-back + to a non-atomic method, so don't count on it". + + The implementation of MoveFileEx looks something like this: [...] + + The problem is if the rename fails, you might end up with a CopyFile, which + is definitely not atomic. + + If you really need atomic-or-nothing, you can try calling + NtSetInformationFile, which is unsupported but is much more likely to be + atomic. + +- Simple high-level API that wraps a very flexible class-based API. + +- Consistent error handling across platforms. + + +How it works +============ + +It uses a temporary file in the same directory as the given path. This ensures +that the temporary file resides on the same filesystem. + +The temporary file will then be atomically moved to the target location: On +POSIX, it will use ``rename`` if files should be overwritten, otherwise a +combination of ``link`` and ``unlink``. On Windows, it uses MoveFileEx_ through +stdlib's ``ctypes`` with the appropriate flags. + +Note that with ``link`` and ``unlink``, there's a timewindow where the file +might be available under two entries in the filesystem: The name of the +temporary file, and the name of the target file. + +Also note that the permissions of the target file may change this way. In some +situations a ``chmod`` can be issued without any concurrency problems, but +since that is not always the case, this library doesn't do it by itself. + +.. _MoveFileEx: https://msdn.microsoft.com/en-us/library/windows/desktop/aa365240%28v=vs.85%29.aspx + +fsync +----- + +On POSIX, ``fsync`` is invoked on the temporary file after it is written (to +flush file content and metadata), and on the parent directory after the file is +moved (to flush filename). + +``fsync`` does not take care of disks' internal buffers, but there don't seem +to be any standard POSIX APIs for that. On OS X, ``fcntl`` is used with +``F_FULLFSYNC`` instead of ``fsync`` for that reason. + +On Windows, `_commit `_ +is used, but there are no guarantees about disk internal buffers. + +Alternatives and Credit +======================= + +Atomicwrites is directly inspired by the following libraries (and shares a +minimal amount of code): + +- The Trac project's `utility functions + `_, + also used in `Werkzeug `_ and + `mitsuhiko/python-atomicfile + `_. The idea to use + ``ctypes`` instead of ``PyWin32`` originated there. + +- `abarnert/fatomic `_. Windows support + (based on ``PyWin32``) was originally taken from there. + +Other alternatives to atomicwrites include: + +- `sashka/atomicfile `_. Originally I + considered using that, but at the time it was lacking a lot of features I + needed (Windows support, overwrite-parameter, overriding behavior through + subclassing). + +- The `Boltons library collection `_ + features a class for atomic file writes, which seems to have a very similar + ``overwrite`` parameter. It is lacking Windows support though. + +License +======= + +Licensed under the MIT, see ``LICENSE``. + + diff --git a/venv/Lib/site-packages/atomicwrites-1.4.0.dist-info/RECORD b/venv/Lib/site-packages/atomicwrites-1.4.0.dist-info/RECORD new file mode 100644 index 0000000..52f0715 --- /dev/null +++ b/venv/Lib/site-packages/atomicwrites-1.4.0.dist-info/RECORD @@ -0,0 +1,8 @@ +atomicwrites-1.4.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +atomicwrites-1.4.0.dist-info/LICENSE,sha256=h4Mp8L2HitAVEpzovagvSB6G7C6Agx6QnA1nFx2SLnM,1069 +atomicwrites-1.4.0.dist-info/METADATA,sha256=C0889LUauSNbRgzOwLjcI-RFU-Q7ICAvPPxSk_pFN4Q,5585 +atomicwrites-1.4.0.dist-info/RECORD,, +atomicwrites-1.4.0.dist-info/WHEEL,sha256=kGT74LWyRUZrL4VgLh6_g12IeVl_9u9ZVhadrgXZUEY,110 +atomicwrites-1.4.0.dist-info/top_level.txt,sha256=ks64zKVUkrl2ZrrP046CsytXlSGf8gLG-IcoXpNyeoc,13 +atomicwrites/__init__.py,sha256=N_LFjMO0nQ9NXMyGQTod3my4OodSCX-FUshHUThV2_4,6794 +atomicwrites/__pycache__/__init__.cpython-39.pyc,, diff --git a/venv/Lib/site-packages/atomicwrites-1.4.0.dist-info/WHEEL b/venv/Lib/site-packages/atomicwrites-1.4.0.dist-info/WHEEL new file mode 100644 index 0000000..ef99c6c --- /dev/null +++ b/venv/Lib/site-packages/atomicwrites-1.4.0.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.34.2) +Root-Is-Purelib: true +Tag: py2-none-any +Tag: py3-none-any + diff --git a/venv/Lib/site-packages/atomicwrites-1.4.0.dist-info/top_level.txt b/venv/Lib/site-packages/atomicwrites-1.4.0.dist-info/top_level.txt new file mode 100644 index 0000000..5fa5a87 --- /dev/null +++ b/venv/Lib/site-packages/atomicwrites-1.4.0.dist-info/top_level.txt @@ -0,0 +1 @@ +atomicwrites diff --git a/venv/Lib/site-packages/atomicwrites/__init__.py b/venv/Lib/site-packages/atomicwrites/__init__.py new file mode 100644 index 0000000..623826e --- /dev/null +++ b/venv/Lib/site-packages/atomicwrites/__init__.py @@ -0,0 +1,226 @@ +import contextlib +import io +import os +import sys +import tempfile + +try: + import fcntl +except ImportError: + fcntl = None + +# `fspath` was added in Python 3.6 +try: + from os import fspath +except ImportError: + fspath = None + +__version__ = '1.4.0' + + +PY2 = sys.version_info[0] == 2 + +text_type = unicode if PY2 else str # noqa + + +def _path_to_unicode(x): + if not isinstance(x, text_type): + return x.decode(sys.getfilesystemencoding()) + return x + + +DEFAULT_MODE = "wb" if PY2 else "w" + + +_proper_fsync = os.fsync + + +if sys.platform != 'win32': + if hasattr(fcntl, 'F_FULLFSYNC'): + def _proper_fsync(fd): + # https://lists.apple.com/archives/darwin-dev/2005/Feb/msg00072.html + # https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man2/fsync.2.html + # https://github.com/untitaker/python-atomicwrites/issues/6 + fcntl.fcntl(fd, fcntl.F_FULLFSYNC) + + def _sync_directory(directory): + # Ensure that filenames are written to disk + fd = os.open(directory, 0) + try: + _proper_fsync(fd) + finally: + os.close(fd) + + def _replace_atomic(src, dst): + os.rename(src, dst) + _sync_directory(os.path.normpath(os.path.dirname(dst))) + + def _move_atomic(src, dst): + os.link(src, dst) + os.unlink(src) + + src_dir = os.path.normpath(os.path.dirname(src)) + dst_dir = os.path.normpath(os.path.dirname(dst)) + _sync_directory(dst_dir) + if src_dir != dst_dir: + _sync_directory(src_dir) +else: + from ctypes import windll, WinError + + _MOVEFILE_REPLACE_EXISTING = 0x1 + _MOVEFILE_WRITE_THROUGH = 0x8 + _windows_default_flags = _MOVEFILE_WRITE_THROUGH + + def _handle_errors(rv): + if not rv: + raise WinError() + + def _replace_atomic(src, dst): + _handle_errors(windll.kernel32.MoveFileExW( + _path_to_unicode(src), _path_to_unicode(dst), + _windows_default_flags | _MOVEFILE_REPLACE_EXISTING + )) + + def _move_atomic(src, dst): + _handle_errors(windll.kernel32.MoveFileExW( + _path_to_unicode(src), _path_to_unicode(dst), + _windows_default_flags + )) + + +def replace_atomic(src, dst): + ''' + Move ``src`` to ``dst``. If ``dst`` exists, it will be silently + overwritten. + + Both paths must reside on the same filesystem for the operation to be + atomic. + ''' + return _replace_atomic(src, dst) + + +def move_atomic(src, dst): + ''' + Move ``src`` to ``dst``. There might a timewindow where both filesystem + entries exist. If ``dst`` already exists, :py:exc:`FileExistsError` will be + raised. + + Both paths must reside on the same filesystem for the operation to be + atomic. + ''' + return _move_atomic(src, dst) + + +class AtomicWriter(object): + ''' + A helper class for performing atomic writes. Usage:: + + with AtomicWriter(path).open() as f: + f.write(...) + + :param path: The destination filepath. May or may not exist. + :param mode: The filemode for the temporary file. This defaults to `wb` in + Python 2 and `w` in Python 3. + :param overwrite: If set to false, an error is raised if ``path`` exists. + Errors are only raised after the file has been written to. Either way, + the operation is atomic. + + If you need further control over the exact behavior, you are encouraged to + subclass. + ''' + + def __init__(self, path, mode=DEFAULT_MODE, overwrite=False, + **open_kwargs): + if 'a' in mode: + raise ValueError( + 'Appending to an existing file is not supported, because that ' + 'would involve an expensive `copy`-operation to a temporary ' + 'file. Open the file in normal `w`-mode and copy explicitly ' + 'if that\'s what you\'re after.' + ) + if 'x' in mode: + raise ValueError('Use the `overwrite`-parameter instead.') + if 'w' not in mode: + raise ValueError('AtomicWriters can only be written to.') + + # Attempt to convert `path` to `str` or `bytes` + if fspath is not None: + path = fspath(path) + + self._path = path + self._mode = mode + self._overwrite = overwrite + self._open_kwargs = open_kwargs + + def open(self): + ''' + Open the temporary file. + ''' + return self._open(self.get_fileobject) + + @contextlib.contextmanager + def _open(self, get_fileobject): + f = None # make sure f exists even if get_fileobject() fails + try: + success = False + with get_fileobject(**self._open_kwargs) as f: + yield f + self.sync(f) + self.commit(f) + success = True + finally: + if not success: + try: + self.rollback(f) + except Exception: + pass + + def get_fileobject(self, suffix="", prefix=tempfile.gettempprefix(), + dir=None, **kwargs): + '''Return the temporary file to use.''' + if dir is None: + dir = os.path.normpath(os.path.dirname(self._path)) + descriptor, name = tempfile.mkstemp(suffix=suffix, prefix=prefix, + dir=dir) + # io.open() will take either the descriptor or the name, but we need + # the name later for commit()/replace_atomic() and couldn't find a way + # to get the filename from the descriptor. + os.close(descriptor) + kwargs['mode'] = self._mode + kwargs['file'] = name + return io.open(**kwargs) + + def sync(self, f): + '''responsible for clearing as many file caches as possible before + commit''' + f.flush() + _proper_fsync(f.fileno()) + + def commit(self, f): + '''Move the temporary file to the target location.''' + if self._overwrite: + replace_atomic(f.name, self._path) + else: + move_atomic(f.name, self._path) + + def rollback(self, f): + '''Clean up all temporary resources.''' + os.unlink(f.name) + + +def atomic_write(path, writer_cls=AtomicWriter, **cls_kwargs): + ''' + Simple atomic writes. This wraps :py:class:`AtomicWriter`:: + + with atomic_write(path) as f: + f.write(...) + + :param path: The target path to write to. + :param writer_cls: The writer class to use. This parameter is useful if you + subclassed :py:class:`AtomicWriter` to change some behavior and want to + use that new subclass. + + Additional keyword arguments are passed to the writer class. See + :py:class:`AtomicWriter`. + ''' + return writer_cls(path, **cls_kwargs).open() diff --git a/venv/Lib/site-packages/atomicwrites/__pycache__/__init__.cpython-39.pyc b/venv/Lib/site-packages/atomicwrites/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fc51765a63033084f2cbeee25cff1061d953cbff GIT binary patch literal 6582 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" + +__author__ = "Hynek Schlawack" +__email__ = "hs@ox.cx" + +__license__ = "MIT" +__copyright__ = "Copyright (c) 2015 Hynek Schlawack" + + +s = attributes = attrs +ib = attr = attrib +dataclass = partial(attrs, auto_attribs=True) # happy Easter ;) + +__all__ = [ + "Attribute", + "Factory", + "NOTHING", + "asdict", + "assoc", + "astuple", + "attr", + "attrib", + "attributes", + "attrs", + "cmp_using", + "converters", + "evolve", + "exceptions", + "fields", + "fields_dict", + "filters", + "get_run_validators", + "has", + "ib", + "make_class", + "resolve_types", + "s", + "set_run_validators", + "setters", + "validate", + "validators", +] + +if sys.version_info[:2] >= (3, 6): + from ._next_gen import define, field, frozen, mutable + + __all__.extend((define, field, frozen, mutable)) diff --git a/venv/Lib/site-packages/attr/__init__.pyi b/venv/Lib/site-packages/attr/__init__.pyi new file mode 100644 index 0000000..3503b07 --- /dev/null +++ b/venv/Lib/site-packages/attr/__init__.pyi @@ -0,0 +1,475 @@ +import sys + +from typing import ( + Any, + Callable, + Dict, + Generic, + List, + Mapping, + Optional, + Sequence, + Tuple, + Type, + TypeVar, + Union, + overload, +) + +# `import X as X` is required to make these public +from . import converters as converters +from . import exceptions as exceptions +from . import filters as filters +from . import setters as setters +from . import validators as validators +from ._version_info import VersionInfo + + +__version__: str +__version_info__: VersionInfo +__title__: str +__description__: str +__url__: str +__uri__: str +__author__: str +__email__: str +__license__: str +__copyright__: str + +_T = TypeVar("_T") +_C = TypeVar("_C", bound=type) + +_EqOrderType = Union[bool, Callable[[Any], Any]] +_ValidatorType = Callable[[Any, Attribute[_T], _T], Any] +_ConverterType = Callable[[Any], Any] +_FilterType = Callable[[Attribute[_T], _T], bool] +_ReprType = Callable[[Any], str] +_ReprArgType = Union[bool, _ReprType] +_OnSetAttrType = Callable[[Any, Attribute[Any], Any], Any] +_OnSetAttrArgType = Union[ + _OnSetAttrType, List[_OnSetAttrType], setters._NoOpType +] +_FieldTransformer = Callable[[type, List[Attribute[Any]]], List[Attribute[Any]]] +# FIXME: in reality, if multiple validators are passed they must be in a list +# or tuple, but those are invariant and so would prevent subtypes of +# _ValidatorType from working when passed in a list or tuple. +_ValidatorArgType = Union[_ValidatorType[_T], Sequence[_ValidatorType[_T]]] + +# _make -- + +NOTHING: object + +# NOTE: Factory lies about its return type to make this possible: +# `x: List[int] # = Factory(list)` +# Work around mypy issue #4554 in the common case by using an overload. +if sys.version_info >= (3, 8): + from typing import Literal + + @overload + def Factory(factory: Callable[[], _T]) -> _T: ... + @overload + def Factory( + factory: Callable[[Any], _T], + takes_self: Literal[True], + ) -> _T: ... + @overload + def Factory( + factory: Callable[[], _T], + takes_self: Literal[False], + ) -> _T: ... +else: + @overload + def Factory(factory: Callable[[], _T]) -> _T: ... + @overload + def Factory( + factory: Union[Callable[[Any], _T], Callable[[], _T]], + takes_self: bool = ..., + ) -> _T: ... + +# Static type inference support via __dataclass_transform__ implemented as per: +# https://github.com/microsoft/pyright/blob/1.1.135/specs/dataclass_transforms.md +# This annotation must be applied to all overloads of "define" and "attrs" +# +# NOTE: This is a typing construct and does not exist at runtime. Extensions +# wrapping attrs decorators should declare a separate __dataclass_transform__ +# signature in the extension module using the specification linked above to +# provide pyright support. +def __dataclass_transform__( + *, + eq_default: bool = True, + order_default: bool = False, + kw_only_default: bool = False, + field_descriptors: Tuple[Union[type, Callable[..., Any]], ...] = (()), +) -> Callable[[_T], _T]: ... + +class Attribute(Generic[_T]): + name: str + default: Optional[_T] + validator: Optional[_ValidatorType[_T]] + repr: _ReprArgType + cmp: _EqOrderType + eq: _EqOrderType + order: _EqOrderType + hash: Optional[bool] + init: bool + converter: Optional[_ConverterType] + metadata: Dict[Any, Any] + type: Optional[Type[_T]] + kw_only: bool + on_setattr: _OnSetAttrType + + def evolve(self, **changes: Any) -> "Attribute[Any]": ... + +# NOTE: We had several choices for the annotation to use for type arg: +# 1) Type[_T] +# - Pros: Handles simple cases correctly +# - Cons: Might produce less informative errors in the case of conflicting +# TypeVars e.g. `attr.ib(default='bad', type=int)` +# 2) Callable[..., _T] +# - Pros: Better error messages than #1 for conflicting TypeVars +# - Cons: Terrible error messages for validator checks. +# e.g. attr.ib(type=int, validator=validate_str) +# -> error: Cannot infer function type argument +# 3) type (and do all of the work in the mypy plugin) +# - Pros: Simple here, and we could customize the plugin with our own errors. +# - Cons: Would need to write mypy plugin code to handle all the cases. +# We chose option #1. + +# `attr` lies about its return type to make the following possible: +# attr() -> Any +# attr(8) -> int +# attr(validator=) -> Whatever the callable expects. +# This makes this type of assignments possible: +# x: int = attr(8) +# +# This form catches explicit None or no default but with no other arguments +# returns Any. +@overload +def attrib( + default: None = ..., + validator: None = ..., + repr: _ReprArgType = ..., + cmp: Optional[_EqOrderType] = ..., + hash: Optional[bool] = ..., + init: bool = ..., + metadata: Optional[Mapping[Any, Any]] = ..., + type: None = ..., + converter: None = ..., + factory: None = ..., + kw_only: bool = ..., + eq: Optional[_EqOrderType] = ..., + order: Optional[_EqOrderType] = ..., + on_setattr: Optional[_OnSetAttrArgType] = ..., +) -> Any: ... + +# This form catches an explicit None or no default and infers the type from the +# other arguments. +@overload +def attrib( + default: None = ..., + validator: Optional[_ValidatorArgType[_T]] = ..., + repr: _ReprArgType = ..., + cmp: Optional[_EqOrderType] = ..., + hash: Optional[bool] = ..., + init: bool = ..., + metadata: Optional[Mapping[Any, Any]] = ..., + type: Optional[Type[_T]] = ..., + converter: Optional[_ConverterType] = ..., + factory: Optional[Callable[[], _T]] = ..., + kw_only: bool = ..., + eq: Optional[_EqOrderType] = ..., + order: Optional[_EqOrderType] = ..., + on_setattr: Optional[_OnSetAttrArgType] = ..., +) -> _T: ... + +# This form catches an explicit default argument. +@overload +def attrib( + default: _T, + validator: Optional[_ValidatorArgType[_T]] = ..., + repr: _ReprArgType = ..., + cmp: Optional[_EqOrderType] = ..., + hash: Optional[bool] = ..., + init: bool = ..., + metadata: Optional[Mapping[Any, Any]] = ..., + type: Optional[Type[_T]] = ..., + converter: Optional[_ConverterType] = ..., + factory: Optional[Callable[[], _T]] = ..., + kw_only: bool = ..., + eq: Optional[_EqOrderType] = ..., + order: Optional[_EqOrderType] = ..., + on_setattr: Optional[_OnSetAttrArgType] = ..., +) -> _T: ... + +# This form covers type=non-Type: e.g. forward references (str), Any +@overload +def attrib( + default: Optional[_T] = ..., + validator: Optional[_ValidatorArgType[_T]] = ..., + repr: _ReprArgType = ..., + cmp: Optional[_EqOrderType] = ..., + hash: Optional[bool] = ..., + init: bool = ..., + metadata: Optional[Mapping[Any, Any]] = ..., + type: object = ..., + converter: Optional[_ConverterType] = ..., + factory: Optional[Callable[[], _T]] = ..., + kw_only: bool = ..., + eq: Optional[_EqOrderType] = ..., + order: Optional[_EqOrderType] = ..., + on_setattr: Optional[_OnSetAttrArgType] = ..., +) -> Any: ... +@overload +def field( + *, + default: None = ..., + validator: None = ..., + repr: _ReprArgType = ..., + hash: Optional[bool] = ..., + init: bool = ..., + metadata: Optional[Mapping[Any, Any]] = ..., + converter: None = ..., + factory: None = ..., + kw_only: bool = ..., + eq: Optional[bool] = ..., + order: Optional[bool] = ..., + on_setattr: Optional[_OnSetAttrArgType] = ..., +) -> Any: ... + +# This form catches an explicit None or no default and infers the type from the +# other arguments. +@overload +def field( + *, + default: None = ..., + validator: Optional[_ValidatorArgType[_T]] = ..., + repr: _ReprArgType = ..., + hash: Optional[bool] = ..., + init: bool = ..., + metadata: Optional[Mapping[Any, Any]] = ..., + converter: Optional[_ConverterType] = ..., + factory: Optional[Callable[[], _T]] = ..., + kw_only: bool = ..., + eq: Optional[_EqOrderType] = ..., + order: Optional[_EqOrderType] = ..., + on_setattr: Optional[_OnSetAttrArgType] = ..., +) -> _T: ... + +# This form catches an explicit default argument. +@overload +def field( + *, + default: _T, + validator: Optional[_ValidatorArgType[_T]] = ..., + repr: _ReprArgType = ..., + hash: Optional[bool] = ..., + init: bool = ..., + metadata: Optional[Mapping[Any, Any]] = ..., + converter: Optional[_ConverterType] = ..., + factory: Optional[Callable[[], _T]] = ..., + kw_only: bool = ..., + eq: Optional[_EqOrderType] = ..., + order: Optional[_EqOrderType] = ..., + on_setattr: Optional[_OnSetAttrArgType] = ..., +) -> _T: ... + +# This form covers type=non-Type: e.g. forward references (str), Any +@overload +def field( + *, + default: Optional[_T] = ..., + validator: Optional[_ValidatorArgType[_T]] = ..., + repr: _ReprArgType = ..., + hash: Optional[bool] = ..., + init: bool = ..., + metadata: Optional[Mapping[Any, Any]] = ..., + converter: Optional[_ConverterType] = ..., + factory: Optional[Callable[[], _T]] = ..., + kw_only: bool = ..., + eq: Optional[_EqOrderType] = ..., + order: Optional[_EqOrderType] = ..., + on_setattr: Optional[_OnSetAttrArgType] = ..., +) -> Any: ... +@overload +@__dataclass_transform__(order_default=True, field_descriptors=(attrib, field)) +def attrs( + maybe_cls: _C, + these: Optional[Dict[str, Any]] = ..., + repr_ns: Optional[str] = ..., + repr: bool = ..., + cmp: Optional[_EqOrderType] = ..., + hash: Optional[bool] = ..., + init: bool = ..., + slots: bool = ..., + frozen: bool = ..., + weakref_slot: bool = ..., + str: bool = ..., + auto_attribs: bool = ..., + kw_only: bool = ..., + cache_hash: bool = ..., + auto_exc: bool = ..., + eq: Optional[_EqOrderType] = ..., + order: Optional[_EqOrderType] = ..., + auto_detect: bool = ..., + collect_by_mro: bool = ..., + getstate_setstate: Optional[bool] = ..., + on_setattr: Optional[_OnSetAttrArgType] = ..., + field_transformer: Optional[_FieldTransformer] = ..., +) -> _C: ... +@overload +@__dataclass_transform__(order_default=True, field_descriptors=(attrib, field)) +def attrs( + maybe_cls: None = ..., + these: Optional[Dict[str, Any]] = ..., + repr_ns: Optional[str] = ..., + repr: bool = ..., + cmp: Optional[_EqOrderType] = ..., + hash: Optional[bool] = ..., + init: bool = ..., + slots: bool = ..., + frozen: bool = ..., + weakref_slot: bool = ..., + str: bool = ..., + auto_attribs: bool = ..., + kw_only: bool = ..., + cache_hash: bool = ..., + auto_exc: bool = ..., + eq: Optional[_EqOrderType] = ..., + order: Optional[_EqOrderType] = ..., + auto_detect: bool = ..., + collect_by_mro: bool = ..., + getstate_setstate: Optional[bool] = ..., + on_setattr: Optional[_OnSetAttrArgType] = ..., + field_transformer: Optional[_FieldTransformer] = ..., +) -> Callable[[_C], _C]: ... +@overload +@__dataclass_transform__(field_descriptors=(attrib, field)) +def define( + maybe_cls: _C, + *, + these: Optional[Dict[str, Any]] = ..., + repr: bool = ..., + hash: Optional[bool] = ..., + init: bool = ..., + slots: bool = ..., + frozen: bool = ..., + weakref_slot: bool = ..., + str: bool = ..., + auto_attribs: bool = ..., + kw_only: bool = ..., + cache_hash: bool = ..., + auto_exc: bool = ..., + eq: Optional[bool] = ..., + order: Optional[bool] = ..., + auto_detect: bool = ..., + getstate_setstate: Optional[bool] = ..., + on_setattr: Optional[_OnSetAttrArgType] = ..., + field_transformer: Optional[_FieldTransformer] = ..., +) -> _C: ... +@overload +@__dataclass_transform__(field_descriptors=(attrib, field)) +def define( + maybe_cls: None = ..., + *, + these: Optional[Dict[str, Any]] = ..., + repr: bool = ..., + hash: Optional[bool] = ..., + init: bool = ..., + slots: bool = ..., + frozen: bool = ..., + weakref_slot: bool = ..., + str: bool = ..., + auto_attribs: bool = ..., + kw_only: bool = ..., + cache_hash: bool = ..., + auto_exc: bool = ..., + eq: Optional[bool] = ..., + order: Optional[bool] = ..., + auto_detect: bool = ..., + getstate_setstate: Optional[bool] = ..., + on_setattr: Optional[_OnSetAttrArgType] = ..., + field_transformer: Optional[_FieldTransformer] = ..., +) -> Callable[[_C], _C]: ... + +mutable = define +frozen = define # they differ only in their defaults + +# TODO: add support for returning NamedTuple from the mypy plugin +class _Fields(Tuple[Attribute[Any], ...]): + def __getattr__(self, name: str) -> Attribute[Any]: ... + +def fields(cls: type) -> _Fields: ... +def fields_dict(cls: type) -> Dict[str, Attribute[Any]]: ... +def validate(inst: Any) -> None: ... +def resolve_types( + cls: _C, + globalns: Optional[Dict[str, Any]] = ..., + localns: Optional[Dict[str, Any]] = ..., + attribs: Optional[List[Attribute[Any]]] = ..., +) -> _C: ... + +# TODO: add support for returning a proper attrs class from the mypy plugin +# we use Any instead of _CountingAttr so that e.g. `make_class('Foo', +# [attr.ib()])` is valid +def make_class( + name: str, + attrs: Union[List[str], Tuple[str, ...], Dict[str, Any]], + bases: Tuple[type, ...] = ..., + repr_ns: Optional[str] = ..., + repr: bool = ..., + cmp: Optional[_EqOrderType] = ..., + hash: Optional[bool] = ..., + init: bool = ..., + slots: bool = ..., + frozen: bool = ..., + weakref_slot: bool = ..., + str: bool = ..., + auto_attribs: bool = ..., + kw_only: bool = ..., + cache_hash: bool = ..., + auto_exc: bool = ..., + eq: Optional[_EqOrderType] = ..., + order: Optional[_EqOrderType] = ..., + collect_by_mro: bool = ..., + on_setattr: Optional[_OnSetAttrArgType] = ..., + field_transformer: Optional[_FieldTransformer] = ..., +) -> type: ... + +# _funcs -- + +# TODO: add support for returning TypedDict from the mypy plugin +# FIXME: asdict/astuple do not honor their factory args. Waiting on one of +# these: +# https://github.com/python/mypy/issues/4236 +# https://github.com/python/typing/issues/253 +def asdict( + inst: Any, + recurse: bool = ..., + filter: Optional[_FilterType[Any]] = ..., + dict_factory: Type[Mapping[Any, Any]] = ..., + retain_collection_types: bool = ..., + value_serializer: Optional[Callable[[type, Attribute[Any], Any], Any]] = ..., +) -> Dict[str, Any]: ... + +# TODO: add support for returning NamedTuple from the mypy plugin +def astuple( + inst: Any, + recurse: bool = ..., + filter: Optional[_FilterType[Any]] = ..., + tuple_factory: Type[Sequence[Any]] = ..., + retain_collection_types: bool = ..., +) -> Tuple[Any, ...]: ... +def has(cls: type) -> bool: ... +def assoc(inst: _T, **changes: Any) -> _T: ... +def evolve(inst: _T, **changes: Any) -> _T: ... + +# _config -- + +def set_run_validators(run: bool) -> None: ... +def get_run_validators() -> bool: ... + +# aliases -- + +s = attributes = attrs +ib = attr = attrib +dataclass = attrs # Technically, partial(attrs, auto_attribs=True) ;) diff --git a/venv/Lib/site-packages/attr/__pycache__/__init__.cpython-39.pyc b/venv/Lib/site-packages/attr/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..165eb3611733a9ba408eda154027f87242ed3045 GIT binary patch literal 1697 zcmZ8hTW=IM6rP#A-;zzTxnEd9Xc;aWNP(7+A}CS|Qh`dKwmMqLos7M^CicwA_HL4W z=dn_M0`#qa$*+Bi`VV;O@hlL;tmgCa@v(irZ_e4M(WrX()ZhH~$Frj6{p%*5Kj`xw z;;Xy9=TVRP!jnGuGJrtlAm=+@Ao7sMF((SL2u1YeX+e}^8OpK(6 zNN1Nlc5T_C&1Da+(>b_7=b=LvV3)SwCfmw;a0~X>CXV00ZB}GkbdfIo8Ni*sN0$$M~5?!&&k5BKE(9QYo)`}TWwkL|Pj?4Xx_ z=VN}sOD|kMbGnB)I~yJ9RE!KGTn=*}t+xCj zNro(Hzanqs`!wmLlj7d3&R*xHT190h>yKS9)9v558KfiA{vqXpsiBArn=B2C8S4Gr z-Q(lqPIhf4RsG#b&^~zYKbXuuIZfD6`(->3(Qy8r{1<8*kc zcz5Po3yQq?bc?V?&YYx^OR2(!MEFjo`AF2&EYq^=P)r_0CwpW`DY6xcmLKvLoob7r( z%-bSmJ)SUI$b`r5pwbCTY)OtxbSPN(z*Y&Cq8TYhh;j!gw_W8CY)+qQTgeJFO@&qk z=lqa!mTs10mV~?C<_Xqc#>lB=- zcCJt+ha(RSg3aP8hHxwywS_)gBcGfES_FB^PY#Xcn)Ge+JjCrI#8wC~+z6Dy&JseI zjup>Z4;>{!MoN4%_|%|jj0Q@GRWV2v+IR$$5qE)@g5WVrv^%dsP>~M>hDz@^LfZ~5 zIk<=b73||^+75VGB-|PG&zV7mB<#eHK1;>weR3``2srT;8t+d4KwAI?KJevS0S=L3yj?uLS=Cp90=V literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/attr/__pycache__/_cmp.cpython-39.pyc b/venv/Lib/site-packages/attr/__pycache__/_cmp.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..465dd0788a32b25274b8026fb76cd9531ca8ad4d GIT binary patch literal 3845 zcmb_f&2JmW72jDd$t5LGvZdHgQv{O&jw@GX;-DXfAPYt98Vyh=Z7j4XD`R)b8B#0l z2Q@R4DweubE}Hg~Lk>OUU?2St^pDv?E_)bIEh*F;_Tq9K}M?&E5wA#RA4SbV}dO|c}dJaIa6;;LB2cV4WBSMXgB z*Ti*vTf%+htiJjJ_d2VUVlnFKJQ*1o#OW|srtn3480$FCiq=rYnF;!%tY$~Q`Am;U+9~@jGtH-Slg2X!4h>lDdi0U!I8Bb< zeI+9!dBm+TxY>;i??oB!N6K8{|AV+bM@ZIQ; zMC*?**3$62&+V18EtO_B7n;X?%L8I3GSb+SN!k~Nze&s**4Fp{-+??Ku`)50o?fT2vC+5r9AMN6?Qk`?BH(J&_)_9|ao_AriD9dTzl=dHQmH(ex zb5*{dH1gj`F!)OE9$4tA|K;g}uk`dw7k}C7FPgoZ=Lt}LFp8B7bd<`#91kJJFLq_d z!)-N^@S_=yEK)xY^)vHZ-%!$w6i#mC=G}CdNOCtRLiocBbr#}4gasp9j4tJL=}X|Q zje1;qUyxzzI^UuLK(Y-DZT>*^BlxKG>09UV_tuo%BPrn{XTBZsK2($Da5EuI6esWk z`+|0xA4)~pArkPt^>zNvo%Wsf+m={V$))6~S2Th^C%Mrg};$RX;(y>Kf`r@LvzNj zffS51B4K+Y$q}0LCI;oM7YOdo_<=m&DMBxWav}RMVg`bR1(l7;uv8)uyUt+NS9!`W z2hH~Ce9Mo>YGqJTq?)1@4e>@%ECsz?+1o$Ie9`NKqwU2oxvrU#IR361#DxSi*+>Av)(xk9o{x?mufjMy`)pi#74}S?k~Q zH!2>RuY8TwDme3*=QUmR7P$0?)$B(oa?$RiX}U?EJbB`r08L2HmGK+a9j4iNhW}~O zD+7Cvj?aEHruGP}1SOj|0hofBJHB%kI^Km9W27o1<7ompip?&+ZN2Vx$(ZsNaI8>s z;&9<zDoTT;=7VF{0j0gwOFe-Y-){#BXZeZKLLc z>!Vs1ynDh@!qn9jER))RDmI zO>~-?m9{$HoQL1kKJz_G`GQ5s-qr&+22iQ>@dSfJE{n*E2PGQL}S z1;rCSEsA((m59Wrr82wFgSZa9`z&gVK#o>N;}m)Wq3-?w@IqoHo@Pvl4Fb7hvW5K^%fyc*`oLAE8YhRMC93zD2)nwMdgi zgm*+lloFHx=~0THugEa!Tj*eWn_x{D;j0#NWyrrvdnmA3Z?y)tda~D)QgEW$&cfN> jNOLhtqF81T?+qZAu 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/attr/__pycache__/_config.cpython-39.pyc b/venv/Lib/site-packages/attr/__pycache__/_config.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a143a3ca405eec55971c7839bfb1395c5efea9da GIT binary patch literal 724 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%0D-g{Q&0;lzFh1MoSuZ9#NyQM9eq8Z8d{cl(UD zN@xUnLA2}G`AV%pjHSeW!27*)VHkGC>$-(?{F|+=^%fZspbJ_+%S%ip*@OOS6LrWn`qAXn+FJBSFTk z(`CiFU2I4KT*AAbbQdo3`f&7}%Tsre`tmU%z|Q?2LVhi&Bg-%!@sBQ!J<80MWbt|^msxp5Q_zzsw)jG?c5#J;5_yEVHE3ox5ZmOzHzm4<>RYY zet50*15!u@a<40~aJlYE3!nCJZ=$YqOi_Jw%+t#FUu5L^UaqwyL@B|lzVvro;OnGxLN+PR%sk-oNv6{ OxY{_|IM}G*b^aIhvkPqi literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/click/__pycache__/formatting.cpython-39.pyc b/venv/Lib/site-packages/click/__pycache__/formatting.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a3a861d375b9206f0ad714d7f895a41377b1e690 GIT binary patch literal 9391 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/click/__pycache__/globals.cpython-39.pyc b/venv/Lib/site-packages/click/__pycache__/globals.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..465851635dc7182fef68b7e57c498b9d77054c29 GIT binary patch literal 2388 zcmah~-HzKt6!thyHk-}<(QVmnxfmo61Qk<(s8CcP(5j^?so?2EAx6%RMi_Blwo9iZ|c}&KcY7wq3+X9?#G5neTja&S$o~-149;{_@xG z_XW@U6DOxz9VXwvWB!1Sdz?tGU%d&;ed*6?{TlHE*$?zpXHoArc#Q{gVb<(7VW;-U z>n{p#zs29>4ZgsePkg?}TaW9HNPo$#Z;Cd2zswi5Jh$7fcKOyDJIlD!Uji?LuYjLb zzVMP$w1?NlT;)lBToMKa&J6_54|9R6v_q6cgu%w8np z=)q?bsYZY$Opo$&6JZBYrZqqrL(f4h>796tiz!6 z`j$WZ#>ILK2FDxLdrCO5{(g=7Ti!GO2Z%oKY+&nS|L7_KYKV8@na?d`$RqNQ-1idy zipK-Ekve+_$nQEz4YgB2%iyRBot7bnlQYuZ+%=7BrrvKzD2lZxWIqVK$x%IKUz6jl1ZFQXlxBwOxZjJpCg8>z`?JvSTGQ_oSR)m)67iI zvbapUGhwIQVOa{HiBu|vrx`Dugdv>+b_kJiE_k;~zYMPw{<|sDY(|Sp>C$*ijj%}H za93yG43QP(L+4CpK)IpAo=RW~Y!g*ow*!lJdaB_)%~>qjNEUY|fcr7aB$8B805))# zX0Z{Jo#mt}4&esxW{Hhw;=0yK4?}0?s{F-2(2n2V#q>aE0Eod|LI%8^jJ?i6PHY}T z%zz#YoRs8sW1(vEm2a)p@hG!}`@FT2aB=94%G;+8*8f#_7qIZKroC04Y>+h)kQE;U zs215n-GJq{$`KXEmE$H1thY~|`kYLl5TDjgyk~@fU_+_Zjz75rmoG5i6LC;+U1fJw zcP@-juYs#xC(x_#&YOb)k0U!6m8Qjjm+A$kLhUP+ur4awV>X;qzsuu@4B zf`o5eBrtrfp^CXkR$x#>@(jeoiMN0J;axFPa|i(Q$f}e|F&9u!Sk|~yK<{;G4z&v* zucazI1?{EAqAt|fEjkQzEZGFqWoKOG^vlvg zB|=58ADObOU4}Fmv9TK{}*ICaS_9z8C)61}x zFWRXVjN=^1&X;%Z-?={6zIlE7`<>o*{q@3j0KaRIpbDAXw*rb0B-~YTnX-^5S}Mlf z&nyo{O3A!=J4I<`QezToylMyM%mis(n<<`2@iqS04We$6CTaVPqOS#HBM8XFU~_Y= HzEb}e$sT0v literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/click/__pycache__/parser.cpython-39.pyc b/venv/Lib/site-packages/click/__pycache__/parser.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fbe57f79552bad0c7d85ba683bec2b9db20706d8 GIT binary patch literal 13545 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/click/__pycache__/testing.cpython-39.pyc b/venv/Lib/site-packages/click/__pycache__/testing.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7e7ac7daabee78ec35ef40eacdd8cfd4533ba0b9 GIT binary patch literal 15095 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

    A zAf}DC1?0i_u_oV7?^fgt0YPZ;{prs15EK+;4f`|?L!n)UqTOr2(b}2BRlyaKE|Hv9 zhkqS@co=i$TV^kVJi80y8!+o+H>3Tyk|z!cl(Jy*y}dITIXAPq+y&$6+WeU{VVpbMdptg=f(BfNqhJ88$|hWhRrxody9?Ug{=;EUT{@Xj zp~zwK1IIuH!(D>`^KCF>oTU%rnyS%^>VmKf&YxsXHXKihb2h7)&kL)_8q%NbVK3ViI??mwBz5vHA z-IU*A7CkK@JH;yz7m_eSy?QC0OzxA2g8CyCmGe&GYz!@6o zgHT^&kw0M~HUY1wqUSe(w& zBwjLSKPb{X<{tlV3+^_SIQ;(PuNbpQJ=6H9uBqRNiTz*2z_wU_-pqd>J;Iao*X&(p z!=b8onEVBkuQB-^68sP1wh-@OJ~%vP7V48+;zOkP6EBSTRd7lSD5C8}iWV^b#qp&V zPQ#mX`qXo0)}EohQGv>{hIo7ahId>cC6ry&RRHn&NAJZUq?dvEaA++a;Uj(0qMT$p w`%yN|pV0q~agw??TtR|=F&Oz4p)BMJHB!t>9<$A(W3#hMru{*2escAH04=|AtpET3 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/click/__pycache__/types.cpython-39.pyc b/venv/Lib/site-packages/click/__pycache__/types.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f12d66a534b612c8c9080672524827037ae157d7 GIT binary patch literal 32885 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 J=bC?+{{tL`KJNek literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/click/__pycache__/utils.cpython-39.pyc b/venv/Lib/site-packages/click/__pycache__/utils.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8950093c2290ba8e6f7482bb512f5308da5c4a49 GIT binary patch literal 17749 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 YUK)S0hC4v}Y5f(c`!`HZ%33}AzcaIV8UO$Q literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/click/_compat.py b/venv/Lib/site-packages/click/_compat.py new file mode 100644 index 0000000..b9e1f0d --- /dev/null +++ b/venv/Lib/site-packages/click/_compat.py @@ -0,0 +1,627 @@ +import codecs +import io +import os +import re +import sys +import typing as t +from weakref import WeakKeyDictionary + +CYGWIN = sys.platform.startswith("cygwin") +MSYS2 = sys.platform.startswith("win") and ("GCC" in sys.version) +# Determine local App Engine environment, per Google's own suggestion +APP_ENGINE = "APPENGINE_RUNTIME" in os.environ and "Development/" in os.environ.get( + "SERVER_SOFTWARE", "" +) +WIN = sys.platform.startswith("win") and not APP_ENGINE and not MSYS2 +auto_wrap_for_ansi: t.Optional[t.Callable[[t.TextIO], t.TextIO]] = None +_ansi_re = re.compile(r"\033\[[;?0-9]*[a-zA-Z]") + + +def get_filesystem_encoding() -> str: + return sys.getfilesystemencoding() or sys.getdefaultencoding() + + +def _make_text_stream( + stream: t.BinaryIO, + encoding: t.Optional[str], + errors: t.Optional[str], + force_readable: bool = False, + force_writable: bool = False, +) -> t.TextIO: + if encoding is None: + encoding = get_best_encoding(stream) + if errors is None: + errors = "replace" + return _NonClosingTextIOWrapper( + stream, + encoding, + errors, + line_buffering=True, + force_readable=force_readable, + force_writable=force_writable, + ) + + +def is_ascii_encoding(encoding: str) -> bool: + """Checks if a given encoding is ascii.""" + try: + return codecs.lookup(encoding).name == "ascii" + except LookupError: + return False + + +def get_best_encoding(stream: t.IO) -> str: + """Returns the default stream encoding if not found.""" + rv = getattr(stream, "encoding", None) or sys.getdefaultencoding() + if is_ascii_encoding(rv): + return "utf-8" + return rv + + +class _NonClosingTextIOWrapper(io.TextIOWrapper): + def __init__( + self, + stream: t.BinaryIO, + encoding: t.Optional[str], + errors: t.Optional[str], + force_readable: bool = False, + force_writable: bool = False, + **extra: t.Any, + ) -> None: + self._stream = stream = t.cast( + t.BinaryIO, _FixupStream(stream, force_readable, force_writable) + ) + super().__init__(stream, encoding, errors, **extra) + + def __del__(self) -> None: + try: + self.detach() + except Exception: + pass + + def isatty(self) -> bool: + # https://bitbucket.org/pypy/pypy/issue/1803 + return self._stream.isatty() + + +class _FixupStream: + """The new io interface needs more from streams than streams + traditionally implement. As such, this fix-up code is necessary in + some circumstances. + + The forcing of readable and writable flags are there because some tools + put badly patched objects on sys (one such offender are certain version + of jupyter notebook). + """ + + def __init__( + self, + stream: t.BinaryIO, + force_readable: bool = False, + force_writable: bool = False, + ): + self._stream = stream + self._force_readable = force_readable + self._force_writable = force_writable + + def __getattr__(self, name: str) -> t.Any: + return getattr(self._stream, name) + + def read1(self, size: int) -> bytes: + f = getattr(self._stream, "read1", None) + + if f is not None: + return t.cast(bytes, f(size)) + + return self._stream.read(size) + + def readable(self) -> bool: + if self._force_readable: + return True + x = getattr(self._stream, "readable", None) + if x is not None: + return t.cast(bool, x()) + try: + self._stream.read(0) + except Exception: + return False + return True + + def writable(self) -> bool: + if self._force_writable: + return True + x = getattr(self._stream, "writable", None) + if x is not None: + return t.cast(bool, x()) + try: + self._stream.write("") # type: ignore + except Exception: + try: + self._stream.write(b"") + except Exception: + return False + return True + + def seekable(self) -> bool: + x = getattr(self._stream, "seekable", None) + if x is not None: + return t.cast(bool, x()) + try: + self._stream.seek(self._stream.tell()) + except Exception: + return False + return True + + +def _is_binary_reader(stream: t.IO, default: bool = False) -> bool: + try: + return isinstance(stream.read(0), bytes) + except Exception: + return default + # This happens in some cases where the stream was already + # closed. In this case, we assume the default. + + +def _is_binary_writer(stream: t.IO, default: bool = False) -> bool: + try: + stream.write(b"") + except Exception: + try: + stream.write("") + return False + except Exception: + pass + return default + return True + + +def _find_binary_reader(stream: t.IO) -> t.Optional[t.BinaryIO]: + # We need to figure out if the given stream is already binary. + # This can happen because the official docs recommend detaching + # the streams to get binary streams. Some code might do this, so + # we need to deal with this case explicitly. + if _is_binary_reader(stream, False): + return t.cast(t.BinaryIO, stream) + + buf = getattr(stream, "buffer", None) + + # Same situation here; this time we assume that the buffer is + # actually binary in case it's closed. + if buf is not None and _is_binary_reader(buf, True): + return t.cast(t.BinaryIO, buf) + + return None + + +def _find_binary_writer(stream: t.IO) -> t.Optional[t.BinaryIO]: + # We need to figure out if the given stream is already binary. + # This can happen because the official docs recommend detaching + # the streams to get binary streams. Some code might do this, so + # we need to deal with this case explicitly. + if _is_binary_writer(stream, False): + return t.cast(t.BinaryIO, stream) + + buf = getattr(stream, "buffer", None) + + # Same situation here; this time we assume that the buffer is + # actually binary in case it's closed. + if buf is not None and _is_binary_writer(buf, True): + return t.cast(t.BinaryIO, buf) + + return None + + +def _stream_is_misconfigured(stream: t.TextIO) -> bool: + """A stream is misconfigured if its encoding is ASCII.""" + # If the stream does not have an encoding set, we assume it's set + # to ASCII. This appears to happen in certain unittest + # environments. It's not quite clear what the correct behavior is + # but this at least will force Click to recover somehow. + return is_ascii_encoding(getattr(stream, "encoding", None) or "ascii") + + +def _is_compat_stream_attr(stream: t.TextIO, attr: str, value: t.Optional[str]) -> bool: + """A stream attribute is compatible if it is equal to the + desired value or the desired value is unset and the attribute + has a value. + """ + stream_value = getattr(stream, attr, None) + return stream_value == value or (value is None and stream_value is not None) + + +def _is_compatible_text_stream( + stream: t.TextIO, encoding: t.Optional[str], errors: t.Optional[str] +) -> bool: + """Check if a stream's encoding and errors attributes are + compatible with the desired values. + """ + return _is_compat_stream_attr( + stream, "encoding", encoding + ) and _is_compat_stream_attr(stream, "errors", errors) + + +def _force_correct_text_stream( + text_stream: t.IO, + encoding: t.Optional[str], + errors: t.Optional[str], + is_binary: t.Callable[[t.IO, bool], bool], + find_binary: t.Callable[[t.IO], t.Optional[t.BinaryIO]], + force_readable: bool = False, + force_writable: bool = False, +) -> t.TextIO: + if is_binary(text_stream, False): + binary_reader = t.cast(t.BinaryIO, text_stream) + else: + text_stream = t.cast(t.TextIO, text_stream) + # If the stream looks compatible, and won't default to a + # misconfigured ascii encoding, return it as-is. + if _is_compatible_text_stream(text_stream, encoding, errors) and not ( + encoding is None and _stream_is_misconfigured(text_stream) + ): + return text_stream + + # Otherwise, get the underlying binary reader. + possible_binary_reader = find_binary(text_stream) + + # If that's not possible, silently use the original reader + # and get mojibake instead of exceptions. + if possible_binary_reader is None: + return text_stream + + binary_reader = possible_binary_reader + + # Default errors to replace instead of strict in order to get + # something that works. + if errors is None: + errors = "replace" + + # Wrap the binary stream in a text stream with the correct + # encoding parameters. + return _make_text_stream( + binary_reader, + encoding, + errors, + force_readable=force_readable, + force_writable=force_writable, + ) + + +def _force_correct_text_reader( + text_reader: t.IO, + encoding: t.Optional[str], + errors: t.Optional[str], + force_readable: bool = False, +) -> t.TextIO: + return _force_correct_text_stream( + text_reader, + encoding, + errors, + _is_binary_reader, + _find_binary_reader, + force_readable=force_readable, + ) + + +def _force_correct_text_writer( + text_writer: t.IO, + encoding: t.Optional[str], + errors: t.Optional[str], + force_writable: bool = False, +) -> t.TextIO: + return _force_correct_text_stream( + text_writer, + encoding, + errors, + _is_binary_writer, + _find_binary_writer, + force_writable=force_writable, + ) + + +def get_binary_stdin() -> t.BinaryIO: + reader = _find_binary_reader(sys.stdin) + if reader is None: + raise RuntimeError("Was not able to determine binary stream for sys.stdin.") + return reader + + +def get_binary_stdout() -> t.BinaryIO: + writer = _find_binary_writer(sys.stdout) + if writer is None: + raise RuntimeError("Was not able to determine binary stream for sys.stdout.") + return writer + + +def get_binary_stderr() -> t.BinaryIO: + writer = _find_binary_writer(sys.stderr) + if writer is None: + raise RuntimeError("Was not able to determine binary stream for sys.stderr.") + return writer + + +def get_text_stdin( + encoding: t.Optional[str] = None, errors: t.Optional[str] = None +) -> t.TextIO: + rv = _get_windows_console_stream(sys.stdin, encoding, errors) + if rv is not None: + return rv + return _force_correct_text_reader(sys.stdin, encoding, errors, force_readable=True) + + +def get_text_stdout( + encoding: t.Optional[str] = None, errors: t.Optional[str] = None +) -> t.TextIO: + rv = _get_windows_console_stream(sys.stdout, encoding, errors) + if rv is not None: + return rv + return _force_correct_text_writer(sys.stdout, encoding, errors, force_writable=True) + + +def get_text_stderr( + encoding: t.Optional[str] = None, errors: t.Optional[str] = None +) -> t.TextIO: + rv = _get_windows_console_stream(sys.stderr, encoding, errors) + if rv is not None: + return rv + return _force_correct_text_writer(sys.stderr, encoding, errors, force_writable=True) + + +def _wrap_io_open( + file: t.Union[str, os.PathLike, int], + mode: str, + encoding: t.Optional[str], + errors: t.Optional[str], +) -> t.IO: + """Handles not passing ``encoding`` and ``errors`` in binary mode.""" + if "b" in mode: + return open(file, mode) + + return open(file, mode, encoding=encoding, errors=errors) + + +def open_stream( + filename: str, + mode: str = "r", + encoding: t.Optional[str] = None, + errors: t.Optional[str] = "strict", + atomic: bool = False, +) -> t.Tuple[t.IO, bool]: + binary = "b" in mode + + # Standard streams first. These are simple because they don't need + # special handling for the atomic flag. It's entirely ignored. + if filename == "-": + if any(m in mode for m in ["w", "a", "x"]): + if binary: + return get_binary_stdout(), False + return get_text_stdout(encoding=encoding, errors=errors), False + if binary: + return get_binary_stdin(), False + return get_text_stdin(encoding=encoding, errors=errors), False + + # Non-atomic writes directly go out through the regular open functions. + if not atomic: + return _wrap_io_open(filename, mode, encoding, errors), True + + # Some usability stuff for atomic writes + if "a" in mode: + raise ValueError( + "Appending to an existing file is not supported, because that" + " would involve an expensive `copy`-operation to a temporary" + " file. Open the file in normal `w`-mode and copy explicitly" + " if that's what you're after." + ) + if "x" in mode: + raise ValueError("Use the `overwrite`-parameter instead.") + if "w" not in mode: + raise ValueError("Atomic writes only make sense with `w`-mode.") + + # Atomic writes are more complicated. They work by opening a file + # as a proxy in the same folder and then using the fdopen + # functionality to wrap it in a Python file. Then we wrap it in an + # atomic file that moves the file over on close. + import errno + import random + + try: + perm: t.Optional[int] = os.stat(filename).st_mode + except OSError: + perm = None + + flags = os.O_RDWR | os.O_CREAT | os.O_EXCL + + if binary: + flags |= getattr(os, "O_BINARY", 0) + + while True: + tmp_filename = os.path.join( + os.path.dirname(filename), + f".__atomic-write{random.randrange(1 << 32):08x}", + ) + try: + fd = os.open(tmp_filename, flags, 0o666 if perm is None else perm) + break + except OSError as e: + if e.errno == errno.EEXIST or ( + os.name == "nt" + and e.errno == errno.EACCES + and os.path.isdir(e.filename) + and os.access(e.filename, os.W_OK) + ): + continue + raise + + if perm is not None: + os.chmod(tmp_filename, perm) # in case perm includes bits in umask + + f = _wrap_io_open(fd, mode, encoding, errors) + af = _AtomicFile(f, tmp_filename, os.path.realpath(filename)) + return t.cast(t.IO, af), True + + +class _AtomicFile: + def __init__(self, f: t.IO, tmp_filename: str, real_filename: str) -> None: + self._f = f + self._tmp_filename = tmp_filename + self._real_filename = real_filename + self.closed = False + + @property + def name(self) -> str: + return self._real_filename + + def close(self, delete: bool = False) -> None: + if self.closed: + return + self._f.close() + os.replace(self._tmp_filename, self._real_filename) + self.closed = True + + def __getattr__(self, name: str) -> t.Any: + return getattr(self._f, name) + + def __enter__(self) -> "_AtomicFile": + return self + + def __exit__(self, exc_type, exc_value, tb): # type: ignore + self.close(delete=exc_type is not None) + + def __repr__(self) -> str: + return repr(self._f) + + +def strip_ansi(value: str) -> str: + return _ansi_re.sub("", value) + + +def _is_jupyter_kernel_output(stream: t.IO) -> bool: + while isinstance(stream, (_FixupStream, _NonClosingTextIOWrapper)): + stream = stream._stream + + return stream.__class__.__module__.startswith("ipykernel.") + + +def should_strip_ansi( + stream: t.Optional[t.IO] = None, color: t.Optional[bool] = None +) -> bool: + if color is None: + if stream is None: + stream = sys.stdin + return not isatty(stream) and not _is_jupyter_kernel_output(stream) + return not color + + +# On Windows, wrap the output streams with colorama to support ANSI +# color codes. +# NOTE: double check is needed so mypy does not analyze this on Linux +if sys.platform.startswith("win") and WIN: + from ._winconsole import _get_windows_console_stream + + def _get_argv_encoding() -> str: + import locale + + return locale.getpreferredencoding() + + _ansi_stream_wrappers: t.MutableMapping[t.TextIO, t.TextIO] = WeakKeyDictionary() + + def auto_wrap_for_ansi( + stream: t.TextIO, color: t.Optional[bool] = None + ) -> t.TextIO: + """Support ANSI color and style codes on Windows by wrapping a + stream with colorama. + """ + try: + cached = _ansi_stream_wrappers.get(stream) + except Exception: + cached = None + + if cached is not None: + return cached + + import colorama + + strip = should_strip_ansi(stream, color) + ansi_wrapper = colorama.AnsiToWin32(stream, strip=strip) + rv = t.cast(t.TextIO, ansi_wrapper.stream) + _write = rv.write + + def _safe_write(s): + try: + return _write(s) + except BaseException: + ansi_wrapper.reset_all() + raise + + rv.write = _safe_write + + try: + _ansi_stream_wrappers[stream] = rv + except Exception: + pass + + return rv + + +else: + + def _get_argv_encoding() -> str: + return getattr(sys.stdin, "encoding", None) or get_filesystem_encoding() + + def _get_windows_console_stream( + f: t.TextIO, encoding: t.Optional[str], errors: t.Optional[str] + ) -> t.Optional[t.TextIO]: + return None + + +def term_len(x: str) -> int: + return len(strip_ansi(x)) + + +def isatty(stream: t.IO) -> bool: + try: + return stream.isatty() + except Exception: + return False + + +def _make_cached_stream_func( + src_func: t.Callable[[], t.TextIO], wrapper_func: t.Callable[[], t.TextIO] +) -> t.Callable[[], t.TextIO]: + cache: t.MutableMapping[t.TextIO, t.TextIO] = WeakKeyDictionary() + + def func() -> t.TextIO: + stream = src_func() + try: + rv = cache.get(stream) + except Exception: + rv = None + if rv is not None: + return rv + rv = wrapper_func() + try: + cache[stream] = rv + except Exception: + pass + return rv + + return func + + +_default_text_stdin = _make_cached_stream_func(lambda: sys.stdin, get_text_stdin) +_default_text_stdout = _make_cached_stream_func(lambda: sys.stdout, get_text_stdout) +_default_text_stderr = _make_cached_stream_func(lambda: sys.stderr, get_text_stderr) + + +binary_streams: t.Mapping[str, t.Callable[[], t.BinaryIO]] = { + "stdin": get_binary_stdin, + "stdout": get_binary_stdout, + "stderr": get_binary_stderr, +} + +text_streams: t.Mapping[ + str, t.Callable[[t.Optional[str], t.Optional[str]], t.TextIO] +] = { + "stdin": get_text_stdin, + "stdout": get_text_stdout, + "stderr": get_text_stderr, +} diff --git a/venv/Lib/site-packages/click/_termui_impl.py b/venv/Lib/site-packages/click/_termui_impl.py new file mode 100644 index 0000000..06cf2b7 --- /dev/null +++ b/venv/Lib/site-packages/click/_termui_impl.py @@ -0,0 +1,717 @@ +""" +This module contains implementations for the termui module. To keep the +import time of Click down, some infrequently used functionality is +placed in this module and only imported as needed. +""" +import contextlib +import math +import os +import sys +import time +import typing as t +from gettext import gettext as _ + +from ._compat import _default_text_stdout +from ._compat import CYGWIN +from ._compat import get_best_encoding +from ._compat import isatty +from ._compat import open_stream +from ._compat import strip_ansi +from ._compat import term_len +from ._compat import WIN +from .exceptions import ClickException +from .utils import echo + +V = t.TypeVar("V") + +if os.name == "nt": + BEFORE_BAR = "\r" + AFTER_BAR = "\n" +else: + BEFORE_BAR = "\r\033[?25l" + AFTER_BAR = "\033[?25h\n" + + +class ProgressBar(t.Generic[V]): + def __init__( + self, + iterable: t.Optional[t.Iterable[V]], + length: t.Optional[int] = None, + fill_char: str = "#", + empty_char: str = " ", + bar_template: str = "%(bar)s", + info_sep: str = " ", + show_eta: bool = True, + show_percent: t.Optional[bool] = None, + show_pos: bool = False, + item_show_func: t.Optional[t.Callable[[t.Optional[V]], t.Optional[str]]] = None, + label: t.Optional[str] = None, + file: t.Optional[t.TextIO] = None, + color: t.Optional[bool] = None, + update_min_steps: int = 1, + width: int = 30, + ) -> None: + self.fill_char = fill_char + self.empty_char = empty_char + self.bar_template = bar_template + self.info_sep = info_sep + self.show_eta = show_eta + self.show_percent = show_percent + self.show_pos = show_pos + self.item_show_func = item_show_func + self.label = label or "" + if file is None: + file = _default_text_stdout() + self.file = file + self.color = color + self.update_min_steps = update_min_steps + self._completed_intervals = 0 + self.width = width + self.autowidth = width == 0 + + if length is None: + from operator import length_hint + + length = length_hint(iterable, -1) + + if length == -1: + length = None + if iterable is None: + if length is None: + raise TypeError("iterable or length is required") + iterable = t.cast(t.Iterable[V], range(length)) + self.iter = iter(iterable) + self.length = length + self.pos = 0 + self.avg: t.List[float] = [] + self.start = self.last_eta = time.time() + self.eta_known = False + self.finished = False + self.max_width: t.Optional[int] = None + self.entered = False + self.current_item: t.Optional[V] = None + self.is_hidden = not isatty(self.file) + self._last_line: t.Optional[str] = None + + def __enter__(self) -> "ProgressBar": + self.entered = True + self.render_progress() + return self + + def __exit__(self, exc_type, exc_value, tb): # type: ignore + self.render_finish() + + def __iter__(self) -> t.Iterator[V]: + if not self.entered: + raise RuntimeError("You need to use progress bars in a with block.") + self.render_progress() + return self.generator() + + def __next__(self) -> V: + # Iteration is defined in terms of a generator function, + # returned by iter(self); use that to define next(). This works + # because `self.iter` is an iterable consumed by that generator, + # so it is re-entry safe. Calling `next(self.generator())` + # twice works and does "what you want". + return next(iter(self)) + + def render_finish(self) -> None: + if self.is_hidden: + return + self.file.write(AFTER_BAR) + self.file.flush() + + @property + def pct(self) -> float: + if self.finished: + return 1.0 + return min(self.pos / (float(self.length or 1) or 1), 1.0) + + @property + def time_per_iteration(self) -> float: + if not self.avg: + return 0.0 + return sum(self.avg) / float(len(self.avg)) + + @property + def eta(self) -> float: + if self.length is not None and not self.finished: + return self.time_per_iteration * (self.length - self.pos) + return 0.0 + + def format_eta(self) -> str: + if self.eta_known: + t = int(self.eta) + seconds = t % 60 + t //= 60 + minutes = t % 60 + t //= 60 + hours = t % 24 + t //= 24 + if t > 0: + return f"{t}d {hours:02}:{minutes:02}:{seconds:02}" + else: + return f"{hours:02}:{minutes:02}:{seconds:02}" + return "" + + def format_pos(self) -> str: + pos = str(self.pos) + if self.length is not None: + pos += f"/{self.length}" + return pos + + def format_pct(self) -> str: + return f"{int(self.pct * 100): 4}%"[1:] + + def format_bar(self) -> str: + if self.length is not None: + bar_length = int(self.pct * self.width) + bar = self.fill_char * bar_length + bar += self.empty_char * (self.width - bar_length) + elif self.finished: + bar = self.fill_char * self.width + else: + chars = list(self.empty_char * (self.width or 1)) + if self.time_per_iteration != 0: + chars[ + int( + (math.cos(self.pos * self.time_per_iteration) / 2.0 + 0.5) + * self.width + ) + ] = self.fill_char + bar = "".join(chars) + return bar + + def format_progress_line(self) -> str: + show_percent = self.show_percent + + info_bits = [] + if self.length is not None and show_percent is None: + show_percent = not self.show_pos + + if self.show_pos: + info_bits.append(self.format_pos()) + if show_percent: + info_bits.append(self.format_pct()) + if self.show_eta and self.eta_known and not self.finished: + info_bits.append(self.format_eta()) + if self.item_show_func is not None: + item_info = self.item_show_func(self.current_item) + if item_info is not None: + info_bits.append(item_info) + + return ( + self.bar_template + % { + "label": self.label, + "bar": self.format_bar(), + "info": self.info_sep.join(info_bits), + } + ).rstrip() + + def render_progress(self) -> None: + import shutil + + if self.is_hidden: + # Only output the label as it changes if the output is not a + # TTY. Use file=stderr if you expect to be piping stdout. + if self._last_line != self.label: + self._last_line = self.label + echo(self.label, file=self.file, color=self.color) + + return + + buf = [] + # Update width in case the terminal has been resized + if self.autowidth: + old_width = self.width + self.width = 0 + clutter_length = term_len(self.format_progress_line()) + new_width = max(0, shutil.get_terminal_size().columns - clutter_length) + if new_width < old_width: + buf.append(BEFORE_BAR) + buf.append(" " * self.max_width) # type: ignore + self.max_width = new_width + self.width = new_width + + clear_width = self.width + if self.max_width is not None: + clear_width = self.max_width + + buf.append(BEFORE_BAR) + line = self.format_progress_line() + line_len = term_len(line) + if self.max_width is None or self.max_width < line_len: + self.max_width = line_len + + buf.append(line) + buf.append(" " * (clear_width - line_len)) + line = "".join(buf) + # Render the line only if it changed. + + if line != self._last_line: + self._last_line = line + echo(line, file=self.file, color=self.color, nl=False) + self.file.flush() + + def make_step(self, n_steps: int) -> None: + self.pos += n_steps + if self.length is not None and self.pos >= self.length: + self.finished = True + + if (time.time() - self.last_eta) < 1.0: + return + + self.last_eta = time.time() + + # self.avg is a rolling list of length <= 7 of steps where steps are + # defined as time elapsed divided by the total progress through + # self.length. + if self.pos: + step = (time.time() - self.start) / self.pos + else: + step = time.time() - self.start + + self.avg = self.avg[-6:] + [step] + + self.eta_known = self.length is not None + + def update(self, n_steps: int, current_item: t.Optional[V] = None) -> None: + """Update the progress bar by advancing a specified number of + steps, and optionally set the ``current_item`` for this new + position. + + :param n_steps: Number of steps to advance. + :param current_item: Optional item to set as ``current_item`` + for the updated position. + + .. versionchanged:: 8.0 + Added the ``current_item`` optional parameter. + + .. versionchanged:: 8.0 + Only render when the number of steps meets the + ``update_min_steps`` threshold. + """ + if current_item is not None: + self.current_item = current_item + + self._completed_intervals += n_steps + + if self._completed_intervals >= self.update_min_steps: + self.make_step(self._completed_intervals) + self.render_progress() + self._completed_intervals = 0 + + def finish(self) -> None: + self.eta_known = False + self.current_item = None + self.finished = True + + def generator(self) -> t.Iterator[V]: + """Return a generator which yields the items added to the bar + during construction, and updates the progress bar *after* the + yielded block returns. + """ + # WARNING: the iterator interface for `ProgressBar` relies on + # this and only works because this is a simple generator which + # doesn't create or manage additional state. If this function + # changes, the impact should be evaluated both against + # `iter(bar)` and `next(bar)`. `next()` in particular may call + # `self.generator()` repeatedly, and this must remain safe in + # order for that interface to work. + if not self.entered: + raise RuntimeError("You need to use progress bars in a with block.") + + if self.is_hidden: + yield from self.iter + else: + for rv in self.iter: + self.current_item = rv + + # This allows show_item_func to be updated before the + # item is processed. Only trigger at the beginning of + # the update interval. + if self._completed_intervals == 0: + self.render_progress() + + yield rv + self.update(1) + + self.finish() + self.render_progress() + + +def pager(generator: t.Iterable[str], color: t.Optional[bool] = None) -> None: + """Decide what method to use for paging through text.""" + stdout = _default_text_stdout() + if not isatty(sys.stdin) or not isatty(stdout): + return _nullpager(stdout, generator, color) + pager_cmd = (os.environ.get("PAGER", None) or "").strip() + if pager_cmd: + if WIN: + return _tempfilepager(generator, pager_cmd, color) + return _pipepager(generator, pager_cmd, color) + if os.environ.get("TERM") in ("dumb", "emacs"): + return _nullpager(stdout, generator, color) + if WIN or sys.platform.startswith("os2"): + return _tempfilepager(generator, "more <", color) + if hasattr(os, "system") and os.system("(less) 2>/dev/null") == 0: + return _pipepager(generator, "less", color) + + import tempfile + + fd, filename = tempfile.mkstemp() + os.close(fd) + try: + if hasattr(os, "system") and os.system(f'more "{filename}"') == 0: + return _pipepager(generator, "more", color) + return _nullpager(stdout, generator, color) + finally: + os.unlink(filename) + + +def _pipepager(generator: t.Iterable[str], cmd: str, color: t.Optional[bool]) -> None: + """Page through text by feeding it to another program. Invoking a + pager through this might support colors. + """ + import subprocess + + env = dict(os.environ) + + # If we're piping to less we might support colors under the + # condition that + cmd_detail = cmd.rsplit("/", 1)[-1].split() + if color is None and cmd_detail[0] == "less": + less_flags = f"{os.environ.get('LESS', '')}{' '.join(cmd_detail[1:])}" + if not less_flags: + env["LESS"] = "-R" + color = True + elif "r" in less_flags or "R" in less_flags: + color = True + + c = subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE, env=env) + stdin = t.cast(t.BinaryIO, c.stdin) + encoding = get_best_encoding(stdin) + try: + for text in generator: + if not color: + text = strip_ansi(text) + + stdin.write(text.encode(encoding, "replace")) + except (OSError, KeyboardInterrupt): + pass + else: + stdin.close() + + # Less doesn't respect ^C, but catches it for its own UI purposes (aborting + # search or other commands inside less). + # + # That means when the user hits ^C, the parent process (click) terminates, + # but less is still alive, paging the output and messing up the terminal. + # + # If the user wants to make the pager exit on ^C, they should set + # `LESS='-K'`. It's not our decision to make. + while True: + try: + c.wait() + except KeyboardInterrupt: + pass + else: + break + + +def _tempfilepager( + generator: t.Iterable[str], cmd: str, color: t.Optional[bool] +) -> None: + """Page through text by invoking a program on a temporary file.""" + import tempfile + + _, filename = tempfile.mkstemp() + # TODO: This never terminates if the passed generator never terminates. + text = "".join(generator) + if not color: + text = strip_ansi(text) + encoding = get_best_encoding(sys.stdout) + with open_stream(filename, "wb")[0] as f: + f.write(text.encode(encoding)) + try: + os.system(f'{cmd} "{filename}"') + finally: + os.unlink(filename) + + +def _nullpager( + stream: t.TextIO, generator: t.Iterable[str], color: t.Optional[bool] +) -> None: + """Simply print unformatted text. This is the ultimate fallback.""" + for text in generator: + if not color: + text = strip_ansi(text) + stream.write(text) + + +class Editor: + def __init__( + self, + editor: t.Optional[str] = None, + env: t.Optional[t.Mapping[str, str]] = None, + require_save: bool = True, + extension: str = ".txt", + ) -> None: + self.editor = editor + self.env = env + self.require_save = require_save + self.extension = extension + + def get_editor(self) -> str: + if self.editor is not None: + return self.editor + for key in "VISUAL", "EDITOR": + rv = os.environ.get(key) + if rv: + return rv + if WIN: + return "notepad" + for editor in "sensible-editor", "vim", "nano": + if os.system(f"which {editor} >/dev/null 2>&1") == 0: + return editor + return "vi" + + def edit_file(self, filename: str) -> None: + import subprocess + + editor = self.get_editor() + environ: t.Optional[t.Dict[str, str]] = None + + if self.env: + environ = os.environ.copy() + environ.update(self.env) + + try: + c = subprocess.Popen(f'{editor} "{filename}"', env=environ, shell=True) + exit_code = c.wait() + if exit_code != 0: + raise ClickException( + _("{editor}: Editing failed").format(editor=editor) + ) + except OSError as e: + raise ClickException( + _("{editor}: Editing failed: {e}").format(editor=editor, e=e) + ) + + def edit(self, text: t.Optional[t.AnyStr]) -> t.Optional[t.AnyStr]: + import tempfile + + if not text: + data = b"" + elif isinstance(text, (bytes, bytearray)): + data = text + else: + if text and not text.endswith("\n"): + text += "\n" + + if WIN: + data = text.replace("\n", "\r\n").encode("utf-8-sig") + else: + data = text.encode("utf-8") + + fd, name = tempfile.mkstemp(prefix="editor-", suffix=self.extension) + f: t.BinaryIO + + try: + with os.fdopen(fd, "wb") as f: + f.write(data) + + # If the filesystem resolution is 1 second, like Mac OS + # 10.12 Extended, or 2 seconds, like FAT32, and the editor + # closes very fast, require_save can fail. Set the modified + # time to be 2 seconds in the past to work around this. + os.utime(name, (os.path.getatime(name), os.path.getmtime(name) - 2)) + # Depending on the resolution, the exact value might not be + # recorded, so get the new recorded value. + timestamp = os.path.getmtime(name) + + self.edit_file(name) + + if self.require_save and os.path.getmtime(name) == timestamp: + return None + + with open(name, "rb") as f: + rv = f.read() + + if isinstance(text, (bytes, bytearray)): + return rv + + return rv.decode("utf-8-sig").replace("\r\n", "\n") # type: ignore + finally: + os.unlink(name) + + +def open_url(url: str, wait: bool = False, locate: bool = False) -> int: + import subprocess + + def _unquote_file(url: str) -> str: + from urllib.parse import unquote + + if url.startswith("file://"): + url = unquote(url[7:]) + + return url + + if sys.platform == "darwin": + args = ["open"] + if wait: + args.append("-W") + if locate: + args.append("-R") + args.append(_unquote_file(url)) + null = open("/dev/null", "w") + try: + return subprocess.Popen(args, stderr=null).wait() + finally: + null.close() + elif WIN: + if locate: + url = _unquote_file(url.replace('"', "")) + args = f'explorer /select,"{url}"' + else: + url = url.replace('"', "") + wait_str = "/WAIT" if wait else "" + args = f'start {wait_str} "" "{url}"' + return os.system(args) + elif CYGWIN: + if locate: + url = os.path.dirname(_unquote_file(url).replace('"', "")) + args = f'cygstart "{url}"' + else: + url = url.replace('"', "") + wait_str = "-w" if wait else "" + args = f'cygstart {wait_str} "{url}"' + return os.system(args) + + try: + if locate: + url = os.path.dirname(_unquote_file(url)) or "." + else: + url = _unquote_file(url) + c = subprocess.Popen(["xdg-open", url]) + if wait: + return c.wait() + return 0 + except OSError: + if url.startswith(("http://", "https://")) and not locate and not wait: + import webbrowser + + webbrowser.open(url) + return 0 + return 1 + + +def _translate_ch_to_exc(ch: str) -> t.Optional[BaseException]: + if ch == "\x03": + raise KeyboardInterrupt() + + if ch == "\x04" and not WIN: # Unix-like, Ctrl+D + raise EOFError() + + if ch == "\x1a" and WIN: # Windows, Ctrl+Z + raise EOFError() + + return None + + +if WIN: + import msvcrt + + @contextlib.contextmanager + def raw_terminal() -> t.Iterator[int]: + yield -1 + + def getchar(echo: bool) -> str: + # The function `getch` will return a bytes object corresponding to + # the pressed character. Since Windows 10 build 1803, it will also + # return \x00 when called a second time after pressing a regular key. + # + # `getwch` does not share this probably-bugged behavior. Moreover, it + # returns a Unicode object by default, which is what we want. + # + # Either of these functions will return \x00 or \xe0 to indicate + # a special key, and you need to call the same function again to get + # the "rest" of the code. The fun part is that \u00e0 is + # "latin small letter a with grave", so if you type that on a French + # keyboard, you _also_ get a \xe0. + # E.g., consider the Up arrow. This returns \xe0 and then \x48. The + # resulting Unicode string reads as "a with grave" + "capital H". + # This is indistinguishable from when the user actually types + # "a with grave" and then "capital H". + # + # When \xe0 is returned, we assume it's part of a special-key sequence + # and call `getwch` again, but that means that when the user types + # the \u00e0 character, `getchar` doesn't return until a second + # character is typed. + # The alternative is returning immediately, but that would mess up + # cross-platform handling of arrow keys and others that start with + # \xe0. Another option is using `getch`, but then we can't reliably + # read non-ASCII characters, because return values of `getch` are + # limited to the current 8-bit codepage. + # + # Anyway, Click doesn't claim to do this Right(tm), and using `getwch` + # is doing the right thing in more situations than with `getch`. + func: t.Callable[[], str] + + if echo: + func = msvcrt.getwche # type: ignore + else: + func = msvcrt.getwch # type: ignore + + rv = func() + + if rv in ("\x00", "\xe0"): + # \x00 and \xe0 are control characters that indicate special key, + # see above. + rv += func() + + _translate_ch_to_exc(rv) + return rv + + +else: + import tty + import termios + + @contextlib.contextmanager + def raw_terminal() -> t.Iterator[int]: + f: t.Optional[t.TextIO] + fd: int + + if not isatty(sys.stdin): + f = open("/dev/tty") + fd = f.fileno() + else: + fd = sys.stdin.fileno() + f = None + + try: + old_settings = termios.tcgetattr(fd) + + try: + tty.setraw(fd) + yield fd + finally: + termios.tcsetattr(fd, termios.TCSADRAIN, old_settings) + sys.stdout.flush() + + if f is not None: + f.close() + except termios.error: + pass + + def getchar(echo: bool) -> str: + with raw_terminal() as fd: + ch = os.read(fd, 32).decode(get_best_encoding(sys.stdin), "replace") + + if echo and isatty(sys.stdout): + sys.stdout.write(ch) + + _translate_ch_to_exc(ch) + return ch diff --git a/venv/Lib/site-packages/click/_textwrap.py b/venv/Lib/site-packages/click/_textwrap.py new file mode 100644 index 0000000..b47dcbd --- /dev/null +++ b/venv/Lib/site-packages/click/_textwrap.py @@ -0,0 +1,49 @@ +import textwrap +import typing as t +from contextlib import contextmanager + + +class TextWrapper(textwrap.TextWrapper): + def _handle_long_word( + self, + reversed_chunks: t.List[str], + cur_line: t.List[str], + cur_len: int, + width: int, + ) -> None: + space_left = max(width - cur_len, 1) + + if self.break_long_words: + last = reversed_chunks[-1] + cut = last[:space_left] + res = last[space_left:] + cur_line.append(cut) + reversed_chunks[-1] = res + elif not cur_line: + cur_line.append(reversed_chunks.pop()) + + @contextmanager + def extra_indent(self, indent: str) -> t.Iterator[None]: + old_initial_indent = self.initial_indent + old_subsequent_indent = self.subsequent_indent + self.initial_indent += indent + self.subsequent_indent += indent + + try: + yield + finally: + self.initial_indent = old_initial_indent + self.subsequent_indent = old_subsequent_indent + + def indent_only(self, text: str) -> str: + rv = [] + + for idx, line in enumerate(text.splitlines()): + indent = self.initial_indent + + if idx > 0: + indent = self.subsequent_indent + + rv.append(f"{indent}{line}") + + return "\n".join(rv) diff --git a/venv/Lib/site-packages/click/_unicodefun.py b/venv/Lib/site-packages/click/_unicodefun.py new file mode 100644 index 0000000..9cb30c3 --- /dev/null +++ b/venv/Lib/site-packages/click/_unicodefun.py @@ -0,0 +1,100 @@ +import codecs +import os +from gettext import gettext as _ + + +def _verify_python_env() -> None: + """Ensures that the environment is good for Unicode.""" + try: + from locale import getpreferredencoding + + fs_enc = codecs.lookup(getpreferredencoding()).name + except Exception: + fs_enc = "ascii" + + if fs_enc != "ascii": + return + + extra = [ + _( + "Click will abort further execution because Python was" + " configured to use ASCII as encoding for the environment." + " Consult https://click.palletsprojects.com/unicode-support/" + " for mitigation steps." + ) + ] + + if os.name == "posix": + import subprocess + + try: + rv = subprocess.Popen( + ["locale", "-a"], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + encoding="ascii", + errors="replace", + ).communicate()[0] + except OSError: + rv = "" + + good_locales = set() + has_c_utf8 = False + + for line in rv.splitlines(): + locale = line.strip() + + if locale.lower().endswith((".utf-8", ".utf8")): + good_locales.add(locale) + + if locale.lower() in ("c.utf8", "c.utf-8"): + has_c_utf8 = True + + if not good_locales: + extra.append( + _( + "Additional information: on this system no suitable" + " UTF-8 locales were discovered. This most likely" + " requires resolving by reconfiguring the locale" + " system." + ) + ) + elif has_c_utf8: + extra.append( + _( + "This system supports the C.UTF-8 locale which is" + " recommended. You might be able to resolve your" + " issue by exporting the following environment" + " variables:" + ) + ) + extra.append(" export LC_ALL=C.UTF-8\n export LANG=C.UTF-8") + else: + extra.append( + _( + "This system lists some UTF-8 supporting locales" + " that you can pick from. The following suitable" + " locales were discovered: {locales}" + ).format(locales=", ".join(sorted(good_locales))) + ) + + bad_locale = None + + for env_locale in os.environ.get("LC_ALL"), os.environ.get("LANG"): + if env_locale and env_locale.lower().endswith((".utf-8", ".utf8")): + bad_locale = env_locale + + if env_locale is not None: + break + + if bad_locale is not None: + extra.append( + _( + "Click discovered that you exported a UTF-8 locale" + " but the locale system could not pick up from it" + " because it does not exist. The exported locale is" + " {locale!r} but it is not supported." + ).format(locale=bad_locale) + ) + + raise RuntimeError("\n\n".join(extra)) diff --git a/venv/Lib/site-packages/click/_winconsole.py b/venv/Lib/site-packages/click/_winconsole.py new file mode 100644 index 0000000..6b20df3 --- /dev/null +++ b/venv/Lib/site-packages/click/_winconsole.py @@ -0,0 +1,279 @@ +# This module is based on the excellent work by Adam Bartoš who +# provided a lot of what went into the implementation here in +# the discussion to issue1602 in the Python bug tracker. +# +# There are some general differences in regards to how this works +# compared to the original patches as we do not need to patch +# the entire interpreter but just work in our little world of +# echo and prompt. +import io +import sys +import time +import typing as t +from ctypes import byref +from ctypes import c_char +from ctypes import c_char_p +from ctypes import c_int +from ctypes import c_ssize_t +from ctypes import c_ulong +from ctypes import c_void_p +from ctypes import POINTER +from ctypes import py_object +from ctypes import Structure +from ctypes.wintypes import DWORD +from ctypes.wintypes import HANDLE +from ctypes.wintypes import LPCWSTR +from ctypes.wintypes import LPWSTR + +from ._compat import _NonClosingTextIOWrapper + +assert sys.platform == "win32" +import msvcrt # noqa: E402 +from ctypes import windll # noqa: E402 +from ctypes import WINFUNCTYPE # noqa: E402 + +c_ssize_p = POINTER(c_ssize_t) + +kernel32 = windll.kernel32 +GetStdHandle = kernel32.GetStdHandle +ReadConsoleW = kernel32.ReadConsoleW +WriteConsoleW = kernel32.WriteConsoleW +GetConsoleMode = kernel32.GetConsoleMode +GetLastError = kernel32.GetLastError +GetCommandLineW = WINFUNCTYPE(LPWSTR)(("GetCommandLineW", windll.kernel32)) +CommandLineToArgvW = WINFUNCTYPE(POINTER(LPWSTR), LPCWSTR, POINTER(c_int))( + ("CommandLineToArgvW", windll.shell32) +) +LocalFree = WINFUNCTYPE(c_void_p, c_void_p)(("LocalFree", windll.kernel32)) + +STDIN_HANDLE = GetStdHandle(-10) +STDOUT_HANDLE = GetStdHandle(-11) +STDERR_HANDLE = GetStdHandle(-12) + +PyBUF_SIMPLE = 0 +PyBUF_WRITABLE = 1 + +ERROR_SUCCESS = 0 +ERROR_NOT_ENOUGH_MEMORY = 8 +ERROR_OPERATION_ABORTED = 995 + +STDIN_FILENO = 0 +STDOUT_FILENO = 1 +STDERR_FILENO = 2 + +EOF = b"\x1a" +MAX_BYTES_WRITTEN = 32767 + +try: + from ctypes import pythonapi +except ImportError: + # On PyPy we cannot get buffers so our ability to operate here is + # severely limited. + get_buffer = None +else: + + class Py_buffer(Structure): + _fields_ = [ + ("buf", c_void_p), + ("obj", py_object), + ("len", c_ssize_t), + ("itemsize", c_ssize_t), + ("readonly", c_int), + ("ndim", c_int), + ("format", c_char_p), + ("shape", c_ssize_p), + ("strides", c_ssize_p), + ("suboffsets", c_ssize_p), + ("internal", c_void_p), + ] + + PyObject_GetBuffer = pythonapi.PyObject_GetBuffer + PyBuffer_Release = pythonapi.PyBuffer_Release + + def get_buffer(obj, writable=False): + buf = Py_buffer() + flags = PyBUF_WRITABLE if writable else PyBUF_SIMPLE + PyObject_GetBuffer(py_object(obj), byref(buf), flags) + + try: + buffer_type = c_char * buf.len + return buffer_type.from_address(buf.buf) + finally: + PyBuffer_Release(byref(buf)) + + +class _WindowsConsoleRawIOBase(io.RawIOBase): + def __init__(self, handle): + self.handle = handle + + def isatty(self): + super().isatty() + return True + + +class _WindowsConsoleReader(_WindowsConsoleRawIOBase): + def readable(self): + return True + + def readinto(self, b): + bytes_to_be_read = len(b) + if not bytes_to_be_read: + return 0 + elif bytes_to_be_read % 2: + raise ValueError( + "cannot read odd number of bytes from UTF-16-LE encoded console" + ) + + buffer = get_buffer(b, writable=True) + code_units_to_be_read = bytes_to_be_read // 2 + code_units_read = c_ulong() + + rv = ReadConsoleW( + HANDLE(self.handle), + buffer, + code_units_to_be_read, + byref(code_units_read), + None, + ) + if GetLastError() == ERROR_OPERATION_ABORTED: + # wait for KeyboardInterrupt + time.sleep(0.1) + if not rv: + raise OSError(f"Windows error: {GetLastError()}") + + if buffer[0] == EOF: + return 0 + return 2 * code_units_read.value + + +class _WindowsConsoleWriter(_WindowsConsoleRawIOBase): + def writable(self): + return True + + @staticmethod + def _get_error_message(errno): + if errno == ERROR_SUCCESS: + return "ERROR_SUCCESS" + elif errno == ERROR_NOT_ENOUGH_MEMORY: + return "ERROR_NOT_ENOUGH_MEMORY" + return f"Windows error {errno}" + + def write(self, b): + bytes_to_be_written = len(b) + buf = get_buffer(b) + code_units_to_be_written = min(bytes_to_be_written, MAX_BYTES_WRITTEN) // 2 + code_units_written = c_ulong() + + WriteConsoleW( + HANDLE(self.handle), + buf, + code_units_to_be_written, + byref(code_units_written), + None, + ) + bytes_written = 2 * code_units_written.value + + if bytes_written == 0 and bytes_to_be_written > 0: + raise OSError(self._get_error_message(GetLastError())) + return bytes_written + + +class ConsoleStream: + def __init__(self, text_stream: t.TextIO, byte_stream: t.BinaryIO) -> None: + self._text_stream = text_stream + self.buffer = byte_stream + + @property + def name(self) -> str: + return self.buffer.name + + def write(self, x: t.AnyStr) -> int: + if isinstance(x, str): + return self._text_stream.write(x) + try: + self.flush() + except Exception: + pass + return self.buffer.write(x) + + def writelines(self, lines: t.Iterable[t.AnyStr]) -> None: + for line in lines: + self.write(line) + + def __getattr__(self, name: str) -> t.Any: + return getattr(self._text_stream, name) + + def isatty(self) -> bool: + return self.buffer.isatty() + + def __repr__(self): + return f"" + + +def _get_text_stdin(buffer_stream: t.BinaryIO) -> t.TextIO: + text_stream = _NonClosingTextIOWrapper( + io.BufferedReader(_WindowsConsoleReader(STDIN_HANDLE)), + "utf-16-le", + "strict", + line_buffering=True, + ) + return t.cast(t.TextIO, ConsoleStream(text_stream, buffer_stream)) + + +def _get_text_stdout(buffer_stream: t.BinaryIO) -> t.TextIO: + text_stream = _NonClosingTextIOWrapper( + io.BufferedWriter(_WindowsConsoleWriter(STDOUT_HANDLE)), + "utf-16-le", + "strict", + line_buffering=True, + ) + return t.cast(t.TextIO, ConsoleStream(text_stream, buffer_stream)) + + +def _get_text_stderr(buffer_stream: t.BinaryIO) -> t.TextIO: + text_stream = _NonClosingTextIOWrapper( + io.BufferedWriter(_WindowsConsoleWriter(STDERR_HANDLE)), + "utf-16-le", + "strict", + line_buffering=True, + ) + return t.cast(t.TextIO, ConsoleStream(text_stream, buffer_stream)) + + +_stream_factories: t.Mapping[int, t.Callable[[t.BinaryIO], t.TextIO]] = { + 0: _get_text_stdin, + 1: _get_text_stdout, + 2: _get_text_stderr, +} + + +def _is_console(f: t.TextIO) -> bool: + if not hasattr(f, "fileno"): + return False + + try: + fileno = f.fileno() + except (OSError, io.UnsupportedOperation): + return False + + handle = msvcrt.get_osfhandle(fileno) + return bool(GetConsoleMode(handle, byref(DWORD()))) + + +def _get_windows_console_stream( + f: t.TextIO, encoding: t.Optional[str], errors: t.Optional[str] +) -> t.Optional[t.TextIO]: + if ( + get_buffer is not None + and encoding in {"utf-16-le", None} + and errors in {"strict", None} + and _is_console(f) + ): + func = _stream_factories.get(f.fileno()) + if func is not None: + b = getattr(f, "buffer", None) + + if b is None: + return None + + return func(b) diff --git a/venv/Lib/site-packages/click/core.py b/venv/Lib/site-packages/click/core.py new file mode 100644 index 0000000..e2ccf59 --- /dev/null +++ b/venv/Lib/site-packages/click/core.py @@ -0,0 +1,2957 @@ +import enum +import errno +import os +import sys +import typing +import typing as t +from collections import abc +from contextlib import contextmanager +from contextlib import ExitStack +from functools import partial +from functools import update_wrapper +from gettext import gettext as _ +from gettext import ngettext +from itertools import repeat + +from . import types +from ._unicodefun import _verify_python_env +from .exceptions import Abort +from .exceptions import BadParameter +from .exceptions import ClickException +from .exceptions import Exit +from .exceptions import MissingParameter +from .exceptions import UsageError +from .formatting import HelpFormatter +from .formatting import join_options +from .globals import pop_context +from .globals import push_context +from .parser import _flag_needs_value +from .parser import OptionParser +from .parser import split_opt +from .termui import confirm +from .termui import prompt +from .termui import style +from .utils import _detect_program_name +from .utils import _expand_args +from .utils import echo +from .utils import make_default_short_help +from .utils import make_str +from .utils import PacifyFlushWrapper + +if t.TYPE_CHECKING: + import typing_extensions as te + from .shell_completion import CompletionItem + +F = t.TypeVar("F", bound=t.Callable[..., t.Any]) +V = t.TypeVar("V") + + +def _fast_exit(code: int) -> "te.NoReturn": + """Low-level exit that skips Python's cleanup but speeds up exit by + about 10ms for things like shell completion. + + :param code: Exit code. + """ + sys.stdout.flush() + sys.stderr.flush() + os._exit(code) + + +def _complete_visible_commands( + ctx: "Context", incomplete: str +) -> t.Iterator[t.Tuple[str, "Command"]]: + """List all the subcommands of a group that start with the + incomplete value and aren't hidden. + + :param ctx: Invocation context for the group. + :param incomplete: Value being completed. May be empty. + """ + multi = t.cast(MultiCommand, ctx.command) + + for name in multi.list_commands(ctx): + if name.startswith(incomplete): + command = multi.get_command(ctx, name) + + if command is not None and not command.hidden: + yield name, command + + +def _check_multicommand( + base_command: "MultiCommand", cmd_name: str, cmd: "Command", register: bool = False +) -> None: + if not base_command.chain or not isinstance(cmd, MultiCommand): + return + if register: + hint = ( + "It is not possible to add multi commands as children to" + " another multi command that is in chain mode." + ) + else: + hint = ( + "Found a multi command as subcommand to a multi command" + " that is in chain mode. This is not supported." + ) + raise RuntimeError( + f"{hint}. Command {base_command.name!r} is set to chain and" + f" {cmd_name!r} was added as a subcommand but it in itself is a" + f" multi command. ({cmd_name!r} is a {type(cmd).__name__}" + f" within a chained {type(base_command).__name__} named" + f" {base_command.name!r})." + ) + + +def batch(iterable: t.Iterable[V], batch_size: int) -> t.List[t.Tuple[V, ...]]: + return list(zip(*repeat(iter(iterable), batch_size))) + + +@contextmanager +def augment_usage_errors( + ctx: "Context", param: t.Optional["Parameter"] = None +) -> t.Iterator[None]: + """Context manager that attaches extra information to exceptions.""" + try: + yield + except BadParameter as e: + if e.ctx is None: + e.ctx = ctx + if param is not None and e.param is None: + e.param = param + raise + except UsageError as e: + if e.ctx is None: + e.ctx = ctx + raise + + +def iter_params_for_processing( + invocation_order: t.Sequence["Parameter"], + declaration_order: t.Sequence["Parameter"], +) -> t.List["Parameter"]: + """Given a sequence of parameters in the order as should be considered + for processing and an iterable of parameters that exist, this returns + a list in the correct order as they should be processed. + """ + + def sort_key(item: "Parameter") -> t.Tuple[bool, float]: + try: + idx: float = invocation_order.index(item) + except ValueError: + idx = float("inf") + + return not item.is_eager, idx + + return sorted(declaration_order, key=sort_key) + + +class ParameterSource(enum.Enum): + """This is an :class:`~enum.Enum` that indicates the source of a + parameter's value. + + Use :meth:`click.Context.get_parameter_source` to get the + source for a parameter by name. + + .. versionchanged:: 8.0 + Use :class:`~enum.Enum` and drop the ``validate`` method. + + .. versionchanged:: 8.0 + Added the ``PROMPT`` value. + """ + + COMMANDLINE = enum.auto() + """The value was provided by the command line args.""" + ENVIRONMENT = enum.auto() + """The value was provided with an environment variable.""" + DEFAULT = enum.auto() + """Used the default specified by the parameter.""" + DEFAULT_MAP = enum.auto() + """Used a default provided by :attr:`Context.default_map`.""" + PROMPT = enum.auto() + """Used a prompt to confirm a default or provide a value.""" + + +class Context: + """The context is a special internal object that holds state relevant + for the script execution at every single level. It's normally invisible + to commands unless they opt-in to getting access to it. + + The context is useful as it can pass internal objects around and can + control special execution features such as reading data from + environment variables. + + A context can be used as context manager in which case it will call + :meth:`close` on teardown. + + :param command: the command class for this context. + :param parent: the parent context. + :param info_name: the info name for this invocation. Generally this + is the most descriptive name for the script or + command. For the toplevel script it is usually + the name of the script, for commands below it it's + the name of the script. + :param obj: an arbitrary object of user data. + :param auto_envvar_prefix: the prefix to use for automatic environment + variables. If this is `None` then reading + from environment variables is disabled. This + does not affect manually set environment + variables which are always read. + :param default_map: a dictionary (like object) with default values + for parameters. + :param terminal_width: the width of the terminal. The default is + inherit from parent context. If no context + defines the terminal width then auto + detection will be applied. + :param max_content_width: the maximum width for content rendered by + Click (this currently only affects help + pages). This defaults to 80 characters if + not overridden. In other words: even if the + terminal is larger than that, Click will not + format things wider than 80 characters by + default. In addition to that, formatters might + add some safety mapping on the right. + :param resilient_parsing: if this flag is enabled then Click will + parse without any interactivity or callback + invocation. Default values will also be + ignored. This is useful for implementing + things such as completion support. + :param allow_extra_args: if this is set to `True` then extra arguments + at the end will not raise an error and will be + kept on the context. The default is to inherit + from the command. + :param allow_interspersed_args: if this is set to `False` then options + and arguments cannot be mixed. The + default is to inherit from the command. + :param ignore_unknown_options: instructs click to ignore options it does + not know and keeps them for later + processing. + :param help_option_names: optionally a list of strings that define how + the default help parameter is named. The + default is ``['--help']``. + :param token_normalize_func: an optional function that is used to + normalize tokens (options, choices, + etc.). This for instance can be used to + implement case insensitive behavior. + :param color: controls if the terminal supports ANSI colors or not. The + default is autodetection. This is only needed if ANSI + codes are used in texts that Click prints which is by + default not the case. This for instance would affect + help output. + :param show_default: Show defaults for all options. If not set, + defaults to the value from a parent context. Overrides an + option's ``show_default`` argument. + + .. versionchanged:: 8.0 + The ``show_default`` parameter defaults to the value from the + parent context. + + .. versionchanged:: 7.1 + Added the ``show_default`` parameter. + + .. versionchanged:: 4.0 + Added the ``color``, ``ignore_unknown_options``, and + ``max_content_width`` parameters. + + .. versionchanged:: 3.0 + Added the ``allow_extra_args`` and ``allow_interspersed_args`` + parameters. + + .. versionchanged:: 2.0 + Added the ``resilient_parsing``, ``help_option_names``, and + ``token_normalize_func`` parameters. + """ + + #: The formatter class to create with :meth:`make_formatter`. + #: + #: .. versionadded:: 8.0 + formatter_class: t.Type["HelpFormatter"] = HelpFormatter + + def __init__( + self, + command: "Command", + parent: t.Optional["Context"] = None, + info_name: t.Optional[str] = None, + obj: t.Optional[t.Any] = None, + auto_envvar_prefix: t.Optional[str] = None, + default_map: t.Optional[t.Dict[str, t.Any]] = None, + terminal_width: t.Optional[int] = None, + max_content_width: t.Optional[int] = None, + resilient_parsing: bool = False, + allow_extra_args: t.Optional[bool] = None, + allow_interspersed_args: t.Optional[bool] = None, + ignore_unknown_options: t.Optional[bool] = None, + help_option_names: t.Optional[t.List[str]] = None, + token_normalize_func: t.Optional[t.Callable[[str], str]] = None, + color: t.Optional[bool] = None, + show_default: t.Optional[bool] = None, + ) -> None: + #: the parent context or `None` if none exists. + self.parent = parent + #: the :class:`Command` for this context. + self.command = command + #: the descriptive information name + self.info_name = info_name + #: Map of parameter names to their parsed values. Parameters + #: with ``expose_value=False`` are not stored. + self.params: t.Dict[str, t.Any] = {} + #: the leftover arguments. + self.args: t.List[str] = [] + #: protected arguments. These are arguments that are prepended + #: to `args` when certain parsing scenarios are encountered but + #: must be never propagated to another arguments. This is used + #: to implement nested parsing. + self.protected_args: t.List[str] = [] + + if obj is None and parent is not None: + obj = parent.obj + + #: the user object stored. + self.obj: t.Any = obj + self._meta: t.Dict[str, t.Any] = getattr(parent, "meta", {}) + + #: A dictionary (-like object) with defaults for parameters. + if ( + default_map is None + and info_name is not None + and parent is not None + and parent.default_map is not None + ): + default_map = parent.default_map.get(info_name) + + self.default_map: t.Optional[t.Dict[str, t.Any]] = default_map + + #: This flag indicates if a subcommand is going to be executed. A + #: group callback can use this information to figure out if it's + #: being executed directly or because the execution flow passes + #: onwards to a subcommand. By default it's None, but it can be + #: the name of the subcommand to execute. + #: + #: If chaining is enabled this will be set to ``'*'`` in case + #: any commands are executed. It is however not possible to + #: figure out which ones. If you require this knowledge you + #: should use a :func:`result_callback`. + self.invoked_subcommand: t.Optional[str] = None + + if terminal_width is None and parent is not None: + terminal_width = parent.terminal_width + + #: The width of the terminal (None is autodetection). + self.terminal_width: t.Optional[int] = terminal_width + + if max_content_width is None and parent is not None: + max_content_width = parent.max_content_width + + #: The maximum width of formatted content (None implies a sensible + #: default which is 80 for most things). + self.max_content_width: t.Optional[int] = max_content_width + + if allow_extra_args is None: + allow_extra_args = command.allow_extra_args + + #: Indicates if the context allows extra args or if it should + #: fail on parsing. + #: + #: .. versionadded:: 3.0 + self.allow_extra_args = allow_extra_args + + if allow_interspersed_args is None: + allow_interspersed_args = command.allow_interspersed_args + + #: Indicates if the context allows mixing of arguments and + #: options or not. + #: + #: .. versionadded:: 3.0 + self.allow_interspersed_args: bool = allow_interspersed_args + + if ignore_unknown_options is None: + ignore_unknown_options = command.ignore_unknown_options + + #: Instructs click to ignore options that a command does not + #: understand and will store it on the context for later + #: processing. This is primarily useful for situations where you + #: want to call into external programs. Generally this pattern is + #: strongly discouraged because it's not possibly to losslessly + #: forward all arguments. + #: + #: .. versionadded:: 4.0 + self.ignore_unknown_options: bool = ignore_unknown_options + + if help_option_names is None: + if parent is not None: + help_option_names = parent.help_option_names + else: + help_option_names = ["--help"] + + #: The names for the help options. + self.help_option_names: t.List[str] = help_option_names + + if token_normalize_func is None and parent is not None: + token_normalize_func = parent.token_normalize_func + + #: An optional normalization function for tokens. This is + #: options, choices, commands etc. + self.token_normalize_func: t.Optional[ + t.Callable[[str], str] + ] = token_normalize_func + + #: Indicates if resilient parsing is enabled. In that case Click + #: will do its best to not cause any failures and default values + #: will be ignored. Useful for completion. + self.resilient_parsing: bool = resilient_parsing + + # If there is no envvar prefix yet, but the parent has one and + # the command on this level has a name, we can expand the envvar + # prefix automatically. + if auto_envvar_prefix is None: + if ( + parent is not None + and parent.auto_envvar_prefix is not None + and self.info_name is not None + ): + auto_envvar_prefix = ( + f"{parent.auto_envvar_prefix}_{self.info_name.upper()}" + ) + else: + auto_envvar_prefix = auto_envvar_prefix.upper() + + if auto_envvar_prefix is not None: + auto_envvar_prefix = auto_envvar_prefix.replace("-", "_") + + self.auto_envvar_prefix: t.Optional[str] = auto_envvar_prefix + + if color is None and parent is not None: + color = parent.color + + #: Controls if styling output is wanted or not. + self.color: t.Optional[bool] = color + + if show_default is None and parent is not None: + show_default = parent.show_default + + #: Show option default values when formatting help text. + self.show_default: t.Optional[bool] = show_default + + self._close_callbacks: t.List[t.Callable[[], t.Any]] = [] + self._depth = 0 + self._parameter_source: t.Dict[str, ParameterSource] = {} + self._exit_stack = ExitStack() + + def to_info_dict(self) -> t.Dict[str, t.Any]: + """Gather information that could be useful for a tool generating + user-facing documentation. This traverses the entire CLI + structure. + + .. code-block:: python + + with Context(cli) as ctx: + info = ctx.to_info_dict() + + .. versionadded:: 8.0 + """ + return { + "command": self.command.to_info_dict(self), + "info_name": self.info_name, + "allow_extra_args": self.allow_extra_args, + "allow_interspersed_args": self.allow_interspersed_args, + "ignore_unknown_options": self.ignore_unknown_options, + "auto_envvar_prefix": self.auto_envvar_prefix, + } + + def __enter__(self) -> "Context": + self._depth += 1 + push_context(self) + return self + + def __exit__(self, exc_type, exc_value, tb): # type: ignore + self._depth -= 1 + if self._depth == 0: + self.close() + pop_context() + + @contextmanager + def scope(self, cleanup: bool = True) -> t.Iterator["Context"]: + """This helper method can be used with the context object to promote + it to the current thread local (see :func:`get_current_context`). + The default behavior of this is to invoke the cleanup functions which + can be disabled by setting `cleanup` to `False`. The cleanup + functions are typically used for things such as closing file handles. + + If the cleanup is intended the context object can also be directly + used as a context manager. + + Example usage:: + + with ctx.scope(): + assert get_current_context() is ctx + + This is equivalent:: + + with ctx: + assert get_current_context() is ctx + + .. versionadded:: 5.0 + + :param cleanup: controls if the cleanup functions should be run or + not. The default is to run these functions. In + some situations the context only wants to be + temporarily pushed in which case this can be disabled. + Nested pushes automatically defer the cleanup. + """ + if not cleanup: + self._depth += 1 + try: + with self as rv: + yield rv + finally: + if not cleanup: + self._depth -= 1 + + @property + def meta(self) -> t.Dict[str, t.Any]: + """This is a dictionary which is shared with all the contexts + that are nested. It exists so that click utilities can store some + state here if they need to. It is however the responsibility of + that code to manage this dictionary well. + + The keys are supposed to be unique dotted strings. For instance + module paths are a good choice for it. What is stored in there is + irrelevant for the operation of click. However what is important is + that code that places data here adheres to the general semantics of + the system. + + Example usage:: + + LANG_KEY = f'{__name__}.lang' + + def set_language(value): + ctx = get_current_context() + ctx.meta[LANG_KEY] = value + + def get_language(): + return get_current_context().meta.get(LANG_KEY, 'en_US') + + .. versionadded:: 5.0 + """ + return self._meta + + def make_formatter(self) -> HelpFormatter: + """Creates the :class:`~click.HelpFormatter` for the help and + usage output. + + To quickly customize the formatter class used without overriding + this method, set the :attr:`formatter_class` attribute. + + .. versionchanged:: 8.0 + Added the :attr:`formatter_class` attribute. + """ + return self.formatter_class( + width=self.terminal_width, max_width=self.max_content_width + ) + + def with_resource(self, context_manager: t.ContextManager[V]) -> V: + """Register a resource as if it were used in a ``with`` + statement. The resource will be cleaned up when the context is + popped. + + Uses :meth:`contextlib.ExitStack.enter_context`. It calls the + resource's ``__enter__()`` method and returns the result. When + the context is popped, it closes the stack, which calls the + resource's ``__exit__()`` method. + + To register a cleanup function for something that isn't a + context manager, use :meth:`call_on_close`. Or use something + from :mod:`contextlib` to turn it into a context manager first. + + .. code-block:: python + + @click.group() + @click.option("--name") + @click.pass_context + def cli(ctx): + ctx.obj = ctx.with_resource(connect_db(name)) + + :param context_manager: The context manager to enter. + :return: Whatever ``context_manager.__enter__()`` returns. + + .. versionadded:: 8.0 + """ + return self._exit_stack.enter_context(context_manager) + + def call_on_close(self, f: t.Callable[..., t.Any]) -> t.Callable[..., t.Any]: + """Register a function to be called when the context tears down. + + This can be used to close resources opened during the script + execution. Resources that support Python's context manager + protocol which would be used in a ``with`` statement should be + registered with :meth:`with_resource` instead. + + :param f: The function to execute on teardown. + """ + return self._exit_stack.callback(f) + + def close(self) -> None: + """Invoke all close callbacks registered with + :meth:`call_on_close`, and exit all context managers entered + with :meth:`with_resource`. + """ + self._exit_stack.close() + # In case the context is reused, create a new exit stack. + self._exit_stack = ExitStack() + + @property + def command_path(self) -> str: + """The computed command path. This is used for the ``usage`` + information on the help page. It's automatically created by + combining the info names of the chain of contexts to the root. + """ + rv = "" + if self.info_name is not None: + rv = self.info_name + if self.parent is not None: + parent_command_path = [self.parent.command_path] + + if isinstance(self.parent.command, Command): + for param in self.parent.command.get_params(self): + parent_command_path.extend(param.get_usage_pieces(self)) + + rv = f"{' '.join(parent_command_path)} {rv}" + return rv.lstrip() + + def find_root(self) -> "Context": + """Finds the outermost context.""" + node = self + while node.parent is not None: + node = node.parent + return node + + def find_object(self, object_type: t.Type[V]) -> t.Optional[V]: + """Finds the closest object of a given type.""" + node: t.Optional["Context"] = self + + while node is not None: + if isinstance(node.obj, object_type): + return node.obj + + node = node.parent + + return None + + def ensure_object(self, object_type: t.Type[V]) -> V: + """Like :meth:`find_object` but sets the innermost object to a + new instance of `object_type` if it does not exist. + """ + rv = self.find_object(object_type) + if rv is None: + self.obj = rv = object_type() + return rv + + @typing.overload + def lookup_default( + self, name: str, call: "te.Literal[True]" = True + ) -> t.Optional[t.Any]: + ... + + @typing.overload + def lookup_default( + self, name: str, call: "te.Literal[False]" = ... + ) -> t.Optional[t.Union[t.Any, t.Callable[[], t.Any]]]: + ... + + def lookup_default(self, name: str, call: bool = True) -> t.Optional[t.Any]: + """Get the default for a parameter from :attr:`default_map`. + + :param name: Name of the parameter. + :param call: If the default is a callable, call it. Disable to + return the callable instead. + + .. versionchanged:: 8.0 + Added the ``call`` parameter. + """ + if self.default_map is not None: + value = self.default_map.get(name) + + if call and callable(value): + return value() + + return value + + return None + + def fail(self, message: str) -> "te.NoReturn": + """Aborts the execution of the program with a specific error + message. + + :param message: the error message to fail with. + """ + raise UsageError(message, self) + + def abort(self) -> "te.NoReturn": + """Aborts the script.""" + raise Abort() + + def exit(self, code: int = 0) -> "te.NoReturn": + """Exits the application with a given exit code.""" + raise Exit(code) + + def get_usage(self) -> str: + """Helper method to get formatted usage string for the current + context and command. + """ + return self.command.get_usage(self) + + def get_help(self) -> str: + """Helper method to get formatted help page for the current + context and command. + """ + return self.command.get_help(self) + + def _make_sub_context(self, command: "Command") -> "Context": + """Create a new context of the same type as this context, but + for a new command. + + :meta private: + """ + return type(self)(command, info_name=command.name, parent=self) + + def invoke( + __self, # noqa: B902 + __callback: t.Union["Command", t.Callable[..., t.Any]], + *args: t.Any, + **kwargs: t.Any, + ) -> t.Any: + """Invokes a command callback in exactly the way it expects. There + are two ways to invoke this method: + + 1. the first argument can be a callback and all other arguments and + keyword arguments are forwarded directly to the function. + 2. the first argument is a click command object. In that case all + arguments are forwarded as well but proper click parameters + (options and click arguments) must be keyword arguments and Click + will fill in defaults. + + Note that before Click 3.2 keyword arguments were not properly filled + in against the intention of this code and no context was created. For + more information about this change and why it was done in a bugfix + release see :ref:`upgrade-to-3.2`. + + .. versionchanged:: 8.0 + All ``kwargs`` are tracked in :attr:`params` so they will be + passed if :meth:`forward` is called at multiple levels. + """ + if isinstance(__callback, Command): + other_cmd = __callback + + if other_cmd.callback is None: + raise TypeError( + "The given command does not have a callback that can be invoked." + ) + else: + __callback = other_cmd.callback + + ctx = __self._make_sub_context(other_cmd) + + for param in other_cmd.params: + if param.name not in kwargs and param.expose_value: + kwargs[param.name] = param.get_default(ctx) # type: ignore + + # Track all kwargs as params, so that forward() will pass + # them on in subsequent calls. + ctx.params.update(kwargs) + else: + ctx = __self + + with augment_usage_errors(__self): + with ctx: + return __callback(*args, **kwargs) + + def forward( + __self, __cmd: "Command", *args: t.Any, **kwargs: t.Any # noqa: B902 + ) -> t.Any: + """Similar to :meth:`invoke` but fills in default keyword + arguments from the current context if the other command expects + it. This cannot invoke callbacks directly, only other commands. + + .. versionchanged:: 8.0 + All ``kwargs`` are tracked in :attr:`params` so they will be + passed if ``forward`` is called at multiple levels. + """ + # Can only forward to other commands, not direct callbacks. + if not isinstance(__cmd, Command): + raise TypeError("Callback is not a command.") + + for param in __self.params: + if param not in kwargs: + kwargs[param] = __self.params[param] + + return __self.invoke(__cmd, *args, **kwargs) + + def set_parameter_source(self, name: str, source: ParameterSource) -> None: + """Set the source of a parameter. This indicates the location + from which the value of the parameter was obtained. + + :param name: The name of the parameter. + :param source: A member of :class:`~click.core.ParameterSource`. + """ + self._parameter_source[name] = source + + def get_parameter_source(self, name: str) -> t.Optional[ParameterSource]: + """Get the source of a parameter. This indicates the location + from which the value of the parameter was obtained. + + This can be useful for determining when a user specified a value + on the command line that is the same as the default value. It + will be :attr:`~click.core.ParameterSource.DEFAULT` only if the + value was actually taken from the default. + + :param name: The name of the parameter. + :rtype: ParameterSource + + .. versionchanged:: 8.0 + Returns ``None`` if the parameter was not provided from any + source. + """ + return self._parameter_source.get(name) + + +class BaseCommand: + """The base command implements the minimal API contract of commands. + Most code will never use this as it does not implement a lot of useful + functionality but it can act as the direct subclass of alternative + parsing methods that do not depend on the Click parser. + + For instance, this can be used to bridge Click and other systems like + argparse or docopt. + + Because base commands do not implement a lot of the API that other + parts of Click take for granted, they are not supported for all + operations. For instance, they cannot be used with the decorators + usually and they have no built-in callback system. + + .. versionchanged:: 2.0 + Added the `context_settings` parameter. + + :param name: the name of the command to use unless a group overrides it. + :param context_settings: an optional dictionary with defaults that are + passed to the context object. + """ + + #: The context class to create with :meth:`make_context`. + #: + #: .. versionadded:: 8.0 + context_class: t.Type[Context] = Context + #: the default for the :attr:`Context.allow_extra_args` flag. + allow_extra_args = False + #: the default for the :attr:`Context.allow_interspersed_args` flag. + allow_interspersed_args = True + #: the default for the :attr:`Context.ignore_unknown_options` flag. + ignore_unknown_options = False + + def __init__( + self, + name: t.Optional[str], + context_settings: t.Optional[t.Dict[str, t.Any]] = None, + ) -> None: + #: the name the command thinks it has. Upon registering a command + #: on a :class:`Group` the group will default the command name + #: with this information. You should instead use the + #: :class:`Context`\'s :attr:`~Context.info_name` attribute. + self.name = name + + if context_settings is None: + context_settings = {} + + #: an optional dictionary with defaults passed to the context. + self.context_settings: t.Dict[str, t.Any] = context_settings + + def to_info_dict(self, ctx: Context) -> t.Dict[str, t.Any]: + """Gather information that could be useful for a tool generating + user-facing documentation. This traverses the entire structure + below this command. + + Use :meth:`click.Context.to_info_dict` to traverse the entire + CLI structure. + + :param ctx: A :class:`Context` representing this command. + + .. versionadded:: 8.0 + """ + return {"name": self.name} + + def __repr__(self) -> str: + return f"<{self.__class__.__name__} {self.name}>" + + def get_usage(self, ctx: Context) -> str: + raise NotImplementedError("Base commands cannot get usage") + + def get_help(self, ctx: Context) -> str: + raise NotImplementedError("Base commands cannot get help") + + def make_context( + self, + info_name: t.Optional[str], + args: t.List[str], + parent: t.Optional[Context] = None, + **extra: t.Any, + ) -> Context: + """This function when given an info name and arguments will kick + off the parsing and create a new :class:`Context`. It does not + invoke the actual command callback though. + + To quickly customize the context class used without overriding + this method, set the :attr:`context_class` attribute. + + :param info_name: the info name for this invocation. Generally this + is the most descriptive name for the script or + command. For the toplevel script it's usually + the name of the script, for commands below it it's + the name of the command. + :param args: the arguments to parse as list of strings. + :param parent: the parent context if available. + :param extra: extra keyword arguments forwarded to the context + constructor. + + .. versionchanged:: 8.0 + Added the :attr:`context_class` attribute. + """ + for key, value in self.context_settings.items(): + if key not in extra: + extra[key] = value + + ctx = self.context_class( + self, info_name=info_name, parent=parent, **extra # type: ignore + ) + + with ctx.scope(cleanup=False): + self.parse_args(ctx, args) + return ctx + + def parse_args(self, ctx: Context, args: t.List[str]) -> t.List[str]: + """Given a context and a list of arguments this creates the parser + and parses the arguments, then modifies the context as necessary. + This is automatically invoked by :meth:`make_context`. + """ + raise NotImplementedError("Base commands do not know how to parse arguments.") + + def invoke(self, ctx: Context) -> t.Any: + """Given a context, this invokes the command. The default + implementation is raising a not implemented error. + """ + raise NotImplementedError("Base commands are not invokable by default") + + def shell_complete(self, ctx: Context, incomplete: str) -> t.List["CompletionItem"]: + """Return a list of completions for the incomplete value. Looks + at the names of chained multi-commands. + + Any command could be part of a chained multi-command, so sibling + commands are valid at any point during command completion. Other + command classes will return more completions. + + :param ctx: Invocation context for this command. + :param incomplete: Value being completed. May be empty. + + .. versionadded:: 8.0 + """ + from click.shell_completion import CompletionItem + + results: t.List["CompletionItem"] = [] + + while ctx.parent is not None: + ctx = ctx.parent + + if isinstance(ctx.command, MultiCommand) and ctx.command.chain: + results.extend( + CompletionItem(name, help=command.get_short_help_str()) + for name, command in _complete_visible_commands(ctx, incomplete) + if name not in ctx.protected_args + ) + + return results + + @typing.overload + def main( + self, + args: t.Optional[t.Sequence[str]] = None, + prog_name: t.Optional[str] = None, + complete_var: t.Optional[str] = None, + standalone_mode: "te.Literal[True]" = True, + **extra: t.Any, + ) -> "te.NoReturn": + ... + + @typing.overload + def main( + self, + args: t.Optional[t.Sequence[str]] = None, + prog_name: t.Optional[str] = None, + complete_var: t.Optional[str] = None, + standalone_mode: bool = ..., + **extra: t.Any, + ) -> t.Any: + ... + + def main( + self, + args: t.Optional[t.Sequence[str]] = None, + prog_name: t.Optional[str] = None, + complete_var: t.Optional[str] = None, + standalone_mode: bool = True, + windows_expand_args: bool = True, + **extra: t.Any, + ) -> t.Any: + """This is the way to invoke a script with all the bells and + whistles as a command line application. This will always terminate + the application after a call. If this is not wanted, ``SystemExit`` + needs to be caught. + + This method is also available by directly calling the instance of + a :class:`Command`. + + :param args: the arguments that should be used for parsing. If not + provided, ``sys.argv[1:]`` is used. + :param prog_name: the program name that should be used. By default + the program name is constructed by taking the file + name from ``sys.argv[0]``. + :param complete_var: the environment variable that controls the + bash completion support. The default is + ``"__COMPLETE"`` with prog_name in + uppercase. + :param standalone_mode: the default behavior is to invoke the script + in standalone mode. Click will then + handle exceptions and convert them into + error messages and the function will never + return but shut down the interpreter. If + this is set to `False` they will be + propagated to the caller and the return + value of this function is the return value + of :meth:`invoke`. + :param windows_expand_args: Expand glob patterns, user dir, and + env vars in command line args on Windows. + :param extra: extra keyword arguments are forwarded to the context + constructor. See :class:`Context` for more information. + + .. versionchanged:: 8.0.1 + Added the ``windows_expand_args`` parameter to allow + disabling command line arg expansion on Windows. + + .. versionchanged:: 8.0 + When taking arguments from ``sys.argv`` on Windows, glob + patterns, user dir, and env vars are expanded. + + .. versionchanged:: 3.0 + Added the ``standalone_mode`` parameter. + """ + # Verify that the environment is configured correctly, or reject + # further execution to avoid a broken script. + _verify_python_env() + + if args is None: + args = sys.argv[1:] + + if os.name == "nt" and windows_expand_args: + args = _expand_args(args) + else: + args = list(args) + + if prog_name is None: + prog_name = _detect_program_name() + + # Process shell completion requests and exit early. + self._main_shell_completion(extra, prog_name, complete_var) + + try: + try: + with self.make_context(prog_name, args, **extra) as ctx: + rv = self.invoke(ctx) + if not standalone_mode: + return rv + # it's not safe to `ctx.exit(rv)` here! + # note that `rv` may actually contain data like "1" which + # has obvious effects + # more subtle case: `rv=[None, None]` can come out of + # chained commands which all returned `None` -- so it's not + # even always obvious that `rv` indicates success/failure + # by its truthiness/falsiness + ctx.exit() + except (EOFError, KeyboardInterrupt): + echo(file=sys.stderr) + raise Abort() + except ClickException as e: + if not standalone_mode: + raise + e.show() + sys.exit(e.exit_code) + except OSError as e: + if e.errno == errno.EPIPE: + sys.stdout = t.cast(t.TextIO, PacifyFlushWrapper(sys.stdout)) + sys.stderr = t.cast(t.TextIO, PacifyFlushWrapper(sys.stderr)) + sys.exit(1) + else: + raise + except Exit as e: + if standalone_mode: + sys.exit(e.exit_code) + else: + # in non-standalone mode, return the exit code + # note that this is only reached if `self.invoke` above raises + # an Exit explicitly -- thus bypassing the check there which + # would return its result + # the results of non-standalone execution may therefore be + # somewhat ambiguous: if there are codepaths which lead to + # `ctx.exit(1)` and to `return 1`, the caller won't be able to + # tell the difference between the two + return e.exit_code + except Abort: + if not standalone_mode: + raise + echo(_("Aborted!"), file=sys.stderr) + sys.exit(1) + + def _main_shell_completion( + self, + ctx_args: t.Dict[str, t.Any], + prog_name: str, + complete_var: t.Optional[str] = None, + ) -> None: + """Check if the shell is asking for tab completion, process + that, then exit early. Called from :meth:`main` before the + program is invoked. + + :param prog_name: Name of the executable in the shell. + :param complete_var: Name of the environment variable that holds + the completion instruction. Defaults to + ``_{PROG_NAME}_COMPLETE``. + """ + if complete_var is None: + complete_var = f"_{prog_name}_COMPLETE".replace("-", "_").upper() + + instruction = os.environ.get(complete_var) + + if not instruction: + return + + from .shell_completion import shell_complete + + rv = shell_complete(self, ctx_args, prog_name, complete_var, instruction) + _fast_exit(rv) + + def __call__(self, *args: t.Any, **kwargs: t.Any) -> t.Any: + """Alias for :meth:`main`.""" + return self.main(*args, **kwargs) + + +class Command(BaseCommand): + """Commands are the basic building block of command line interfaces in + Click. A basic command handles command line parsing and might dispatch + more parsing to commands nested below it. + + .. versionchanged:: 2.0 + Added the `context_settings` parameter. + .. versionchanged:: 8.0 + Added repr showing the command name + .. versionchanged:: 7.1 + Added the `no_args_is_help` parameter. + + :param name: the name of the command to use unless a group overrides it. + :param context_settings: an optional dictionary with defaults that are + passed to the context object. + :param callback: the callback to invoke. This is optional. + :param params: the parameters to register with this command. This can + be either :class:`Option` or :class:`Argument` objects. + :param help: the help string to use for this command. + :param epilog: like the help string but it's printed at the end of the + help page after everything else. + :param short_help: the short help to use for this command. This is + shown on the command listing of the parent command. + :param add_help_option: by default each command registers a ``--help`` + option. This can be disabled by this parameter. + :param no_args_is_help: this controls what happens if no arguments are + provided. This option is disabled by default. + If enabled this will add ``--help`` as argument + if no arguments are passed + :param hidden: hide this command from help outputs. + + :param deprecated: issues a message indicating that + the command is deprecated. + """ + + def __init__( + self, + name: t.Optional[str], + context_settings: t.Optional[t.Dict[str, t.Any]] = None, + callback: t.Optional[t.Callable[..., t.Any]] = None, + params: t.Optional[t.List["Parameter"]] = None, + help: t.Optional[str] = None, + epilog: t.Optional[str] = None, + short_help: t.Optional[str] = None, + options_metavar: t.Optional[str] = "[OPTIONS]", + add_help_option: bool = True, + no_args_is_help: bool = False, + hidden: bool = False, + deprecated: bool = False, + ) -> None: + super().__init__(name, context_settings) + #: the callback to execute when the command fires. This might be + #: `None` in which case nothing happens. + self.callback = callback + #: the list of parameters for this command in the order they + #: should show up in the help page and execute. Eager parameters + #: will automatically be handled before non eager ones. + self.params: t.List["Parameter"] = params or [] + + # if a form feed (page break) is found in the help text, truncate help + # text to the content preceding the first form feed + if help and "\f" in help: + help = help.split("\f", 1)[0] + + self.help = help + self.epilog = epilog + self.options_metavar = options_metavar + self.short_help = short_help + self.add_help_option = add_help_option + self.no_args_is_help = no_args_is_help + self.hidden = hidden + self.deprecated = deprecated + + def to_info_dict(self, ctx: Context) -> t.Dict[str, t.Any]: + info_dict = super().to_info_dict(ctx) + info_dict.update( + params=[param.to_info_dict() for param in self.get_params(ctx)], + help=self.help, + epilog=self.epilog, + short_help=self.short_help, + hidden=self.hidden, + deprecated=self.deprecated, + ) + return info_dict + + def get_usage(self, ctx: Context) -> str: + """Formats the usage line into a string and returns it. + + Calls :meth:`format_usage` internally. + """ + formatter = ctx.make_formatter() + self.format_usage(ctx, formatter) + return formatter.getvalue().rstrip("\n") + + def get_params(self, ctx: Context) -> t.List["Parameter"]: + rv = self.params + help_option = self.get_help_option(ctx) + + if help_option is not None: + rv = [*rv, help_option] + + return rv + + def format_usage(self, ctx: Context, formatter: HelpFormatter) -> None: + """Writes the usage line into the formatter. + + This is a low-level method called by :meth:`get_usage`. + """ + pieces = self.collect_usage_pieces(ctx) + formatter.write_usage(ctx.command_path, " ".join(pieces)) + + def collect_usage_pieces(self, ctx: Context) -> t.List[str]: + """Returns all the pieces that go into the usage line and returns + it as a list of strings. + """ + rv = [self.options_metavar] if self.options_metavar else [] + + for param in self.get_params(ctx): + rv.extend(param.get_usage_pieces(ctx)) + + return rv + + def get_help_option_names(self, ctx: Context) -> t.List[str]: + """Returns the names for the help option.""" + all_names = set(ctx.help_option_names) + for param in self.params: + all_names.difference_update(param.opts) + all_names.difference_update(param.secondary_opts) + return list(all_names) + + def get_help_option(self, ctx: Context) -> t.Optional["Option"]: + """Returns the help option object.""" + help_options = self.get_help_option_names(ctx) + + if not help_options or not self.add_help_option: + return None + + def show_help(ctx: Context, param: "Parameter", value: str) -> None: + if value and not ctx.resilient_parsing: + echo(ctx.get_help(), color=ctx.color) + ctx.exit() + + return Option( + help_options, + is_flag=True, + is_eager=True, + expose_value=False, + callback=show_help, + help=_("Show this message and exit."), + ) + + def make_parser(self, ctx: Context) -> OptionParser: + """Creates the underlying option parser for this command.""" + parser = OptionParser(ctx) + for param in self.get_params(ctx): + param.add_to_parser(parser, ctx) + return parser + + def get_help(self, ctx: Context) -> str: + """Formats the help into a string and returns it. + + Calls :meth:`format_help` internally. + """ + formatter = ctx.make_formatter() + self.format_help(ctx, formatter) + return formatter.getvalue().rstrip("\n") + + def get_short_help_str(self, limit: int = 45) -> str: + """Gets short help for the command or makes it by shortening the + long help string. + """ + text = self.short_help or "" + + if not text and self.help: + text = make_default_short_help(self.help, limit) + + if self.deprecated: + text = _("(Deprecated) {text}").format(text=text) + + return text.strip() + + def format_help(self, ctx: Context, formatter: HelpFormatter) -> None: + """Writes the help into the formatter if it exists. + + This is a low-level method called by :meth:`get_help`. + + This calls the following methods: + + - :meth:`format_usage` + - :meth:`format_help_text` + - :meth:`format_options` + - :meth:`format_epilog` + """ + self.format_usage(ctx, formatter) + self.format_help_text(ctx, formatter) + self.format_options(ctx, formatter) + self.format_epilog(ctx, formatter) + + def format_help_text(self, ctx: Context, formatter: HelpFormatter) -> None: + """Writes the help text to the formatter if it exists.""" + text = self.help or "" + + if self.deprecated: + text = _("(Deprecated) {text}").format(text=text) + + if text: + formatter.write_paragraph() + + with formatter.indentation(): + formatter.write_text(text) + + def format_options(self, ctx: Context, formatter: HelpFormatter) -> None: + """Writes all the options into the formatter if they exist.""" + opts = [] + for param in self.get_params(ctx): + rv = param.get_help_record(ctx) + if rv is not None: + opts.append(rv) + + if opts: + with formatter.section(_("Options")): + formatter.write_dl(opts) + + def format_epilog(self, ctx: Context, formatter: HelpFormatter) -> None: + """Writes the epilog into the formatter if it exists.""" + if self.epilog: + formatter.write_paragraph() + with formatter.indentation(): + formatter.write_text(self.epilog) + + def parse_args(self, ctx: Context, args: t.List[str]) -> t.List[str]: + if not args and self.no_args_is_help and not ctx.resilient_parsing: + echo(ctx.get_help(), color=ctx.color) + ctx.exit() + + parser = self.make_parser(ctx) + opts, args, param_order = parser.parse_args(args=args) + + for param in iter_params_for_processing(param_order, self.get_params(ctx)): + value, args = param.handle_parse_result(ctx, opts, args) + + if args and not ctx.allow_extra_args and not ctx.resilient_parsing: + ctx.fail( + ngettext( + "Got unexpected extra argument ({args})", + "Got unexpected extra arguments ({args})", + len(args), + ).format(args=" ".join(map(str, args))) + ) + + ctx.args = args + return args + + def invoke(self, ctx: Context) -> t.Any: + """Given a context, this invokes the attached callback (if it exists) + in the right way. + """ + if self.deprecated: + message = _( + "DeprecationWarning: The command {name!r} is deprecated." + ).format(name=self.name) + echo(style(message, fg="red"), err=True) + + if self.callback is not None: + return ctx.invoke(self.callback, **ctx.params) + + def shell_complete(self, ctx: Context, incomplete: str) -> t.List["CompletionItem"]: + """Return a list of completions for the incomplete value. Looks + at the names of options and chained multi-commands. + + :param ctx: Invocation context for this command. + :param incomplete: Value being completed. May be empty. + + .. versionadded:: 8.0 + """ + from click.shell_completion import CompletionItem + + results: t.List["CompletionItem"] = [] + + if incomplete and not incomplete[0].isalnum(): + for param in self.get_params(ctx): + if ( + not isinstance(param, Option) + or param.hidden + or ( + not param.multiple + and ctx.get_parameter_source(param.name) # type: ignore + is ParameterSource.COMMANDLINE + ) + ): + continue + + results.extend( + CompletionItem(name, help=param.help) + for name in [*param.opts, *param.secondary_opts] + if name.startswith(incomplete) + ) + + results.extend(super().shell_complete(ctx, incomplete)) + return results + + +class MultiCommand(Command): + """A multi command is the basic implementation of a command that + dispatches to subcommands. The most common version is the + :class:`Group`. + + :param invoke_without_command: this controls how the multi command itself + is invoked. By default it's only invoked + if a subcommand is provided. + :param no_args_is_help: this controls what happens if no arguments are + provided. This option is enabled by default if + `invoke_without_command` is disabled or disabled + if it's enabled. If enabled this will add + ``--help`` as argument if no arguments are + passed. + :param subcommand_metavar: the string that is used in the documentation + to indicate the subcommand place. + :param chain: if this is set to `True` chaining of multiple subcommands + is enabled. This restricts the form of commands in that + they cannot have optional arguments but it allows + multiple commands to be chained together. + :param result_callback: The result callback to attach to this multi + command. This can be set or changed later with the + :meth:`result_callback` decorator. + """ + + allow_extra_args = True + allow_interspersed_args = False + + def __init__( + self, + name: t.Optional[str] = None, + invoke_without_command: bool = False, + no_args_is_help: t.Optional[bool] = None, + subcommand_metavar: t.Optional[str] = None, + chain: bool = False, + result_callback: t.Optional[t.Callable[..., t.Any]] = None, + **attrs: t.Any, + ) -> None: + super().__init__(name, **attrs) + + if no_args_is_help is None: + no_args_is_help = not invoke_without_command + + self.no_args_is_help = no_args_is_help + self.invoke_without_command = invoke_without_command + + if subcommand_metavar is None: + if chain: + subcommand_metavar = "COMMAND1 [ARGS]... [COMMAND2 [ARGS]...]..." + else: + subcommand_metavar = "COMMAND [ARGS]..." + + self.subcommand_metavar = subcommand_metavar + self.chain = chain + # The result callback that is stored. This can be set or + # overridden with the :func:`result_callback` decorator. + self._result_callback = result_callback + + if self.chain: + for param in self.params: + if isinstance(param, Argument) and not param.required: + raise RuntimeError( + "Multi commands in chain mode cannot have" + " optional arguments." + ) + + def to_info_dict(self, ctx: Context) -> t.Dict[str, t.Any]: + info_dict = super().to_info_dict(ctx) + commands = {} + + for name in self.list_commands(ctx): + command = self.get_command(ctx, name) + + if command is None: + continue + + sub_ctx = ctx._make_sub_context(command) + + with sub_ctx.scope(cleanup=False): + commands[name] = command.to_info_dict(sub_ctx) + + info_dict.update(commands=commands, chain=self.chain) + return info_dict + + def collect_usage_pieces(self, ctx: Context) -> t.List[str]: + rv = super().collect_usage_pieces(ctx) + rv.append(self.subcommand_metavar) + return rv + + def format_options(self, ctx: Context, formatter: HelpFormatter) -> None: + super().format_options(ctx, formatter) + self.format_commands(ctx, formatter) + + def result_callback(self, replace: bool = False) -> t.Callable[[F], F]: + """Adds a result callback to the command. By default if a + result callback is already registered this will chain them but + this can be disabled with the `replace` parameter. The result + callback is invoked with the return value of the subcommand + (or the list of return values from all subcommands if chaining + is enabled) as well as the parameters as they would be passed + to the main callback. + + Example:: + + @click.group() + @click.option('-i', '--input', default=23) + def cli(input): + return 42 + + @cli.result_callback() + def process_result(result, input): + return result + input + + :param replace: if set to `True` an already existing result + callback will be removed. + + .. versionchanged:: 8.0 + Renamed from ``resultcallback``. + + .. versionadded:: 3.0 + """ + + def decorator(f: F) -> F: + old_callback = self._result_callback + + if old_callback is None or replace: + self._result_callback = f + return f + + def function(__value, *args, **kwargs): # type: ignore + inner = old_callback(__value, *args, **kwargs) # type: ignore + return f(inner, *args, **kwargs) + + self._result_callback = rv = update_wrapper(t.cast(F, function), f) + return rv + + return decorator + + def resultcallback(self, replace: bool = False) -> t.Callable[[F], F]: + import warnings + + warnings.warn( + "'resultcallback' has been renamed to 'result_callback'." + " The old name will be removed in Click 8.1.", + DeprecationWarning, + stacklevel=2, + ) + return self.result_callback(replace=replace) + + def format_commands(self, ctx: Context, formatter: HelpFormatter) -> None: + """Extra format methods for multi methods that adds all the commands + after the options. + """ + commands = [] + for subcommand in self.list_commands(ctx): + cmd = self.get_command(ctx, subcommand) + # What is this, the tool lied about a command. Ignore it + if cmd is None: + continue + if cmd.hidden: + continue + + commands.append((subcommand, cmd)) + + # allow for 3 times the default spacing + if len(commands): + limit = formatter.width - 6 - max(len(cmd[0]) for cmd in commands) + + rows = [] + for subcommand, cmd in commands: + help = cmd.get_short_help_str(limit) + rows.append((subcommand, help)) + + if rows: + with formatter.section(_("Commands")): + formatter.write_dl(rows) + + def parse_args(self, ctx: Context, args: t.List[str]) -> t.List[str]: + if not args and self.no_args_is_help and not ctx.resilient_parsing: + echo(ctx.get_help(), color=ctx.color) + ctx.exit() + + rest = super().parse_args(ctx, args) + + if self.chain: + ctx.protected_args = rest + ctx.args = [] + elif rest: + ctx.protected_args, ctx.args = rest[:1], rest[1:] + + return ctx.args + + def invoke(self, ctx: Context) -> t.Any: + def _process_result(value: t.Any) -> t.Any: + if self._result_callback is not None: + value = ctx.invoke(self._result_callback, value, **ctx.params) + return value + + if not ctx.protected_args: + if self.invoke_without_command: + # No subcommand was invoked, so the result callback is + # invoked with None for regular groups, or an empty list + # for chained groups. + with ctx: + super().invoke(ctx) + return _process_result([] if self.chain else None) + ctx.fail(_("Missing command.")) + + # Fetch args back out + args = [*ctx.protected_args, *ctx.args] + ctx.args = [] + ctx.protected_args = [] + + # If we're not in chain mode, we only allow the invocation of a + # single command but we also inform the current context about the + # name of the command to invoke. + if not self.chain: + # Make sure the context is entered so we do not clean up + # resources until the result processor has worked. + with ctx: + cmd_name, cmd, args = self.resolve_command(ctx, args) + assert cmd is not None + ctx.invoked_subcommand = cmd_name + super().invoke(ctx) + sub_ctx = cmd.make_context(cmd_name, args, parent=ctx) + with sub_ctx: + return _process_result(sub_ctx.command.invoke(sub_ctx)) + + # In chain mode we create the contexts step by step, but after the + # base command has been invoked. Because at that point we do not + # know the subcommands yet, the invoked subcommand attribute is + # set to ``*`` to inform the command that subcommands are executed + # but nothing else. + with ctx: + ctx.invoked_subcommand = "*" if args else None + super().invoke(ctx) + + # Otherwise we make every single context and invoke them in a + # chain. In that case the return value to the result processor + # is the list of all invoked subcommand's results. + contexts = [] + while args: + cmd_name, cmd, args = self.resolve_command(ctx, args) + assert cmd is not None + sub_ctx = cmd.make_context( + cmd_name, + args, + parent=ctx, + allow_extra_args=True, + allow_interspersed_args=False, + ) + contexts.append(sub_ctx) + args, sub_ctx.args = sub_ctx.args, [] + + rv = [] + for sub_ctx in contexts: + with sub_ctx: + rv.append(sub_ctx.command.invoke(sub_ctx)) + return _process_result(rv) + + def resolve_command( + self, ctx: Context, args: t.List[str] + ) -> t.Tuple[t.Optional[str], t.Optional[Command], t.List[str]]: + cmd_name = make_str(args[0]) + original_cmd_name = cmd_name + + # Get the command + cmd = self.get_command(ctx, cmd_name) + + # If we can't find the command but there is a normalization + # function available, we try with that one. + if cmd is None and ctx.token_normalize_func is not None: + cmd_name = ctx.token_normalize_func(cmd_name) + cmd = self.get_command(ctx, cmd_name) + + # If we don't find the command we want to show an error message + # to the user that it was not provided. However, there is + # something else we should do: if the first argument looks like + # an option we want to kick off parsing again for arguments to + # resolve things like --help which now should go to the main + # place. + if cmd is None and not ctx.resilient_parsing: + if split_opt(cmd_name)[0]: + self.parse_args(ctx, ctx.args) + ctx.fail(_("No such command {name!r}.").format(name=original_cmd_name)) + return cmd_name if cmd else None, cmd, args[1:] + + def get_command(self, ctx: Context, cmd_name: str) -> t.Optional[Command]: + """Given a context and a command name, this returns a + :class:`Command` object if it exists or returns `None`. + """ + raise NotImplementedError + + def list_commands(self, ctx: Context) -> t.List[str]: + """Returns a list of subcommand names in the order they should + appear. + """ + return [] + + def shell_complete(self, ctx: Context, incomplete: str) -> t.List["CompletionItem"]: + """Return a list of completions for the incomplete value. Looks + at the names of options, subcommands, and chained + multi-commands. + + :param ctx: Invocation context for this command. + :param incomplete: Value being completed. May be empty. + + .. versionadded:: 8.0 + """ + from click.shell_completion import CompletionItem + + results = [ + CompletionItem(name, help=command.get_short_help_str()) + for name, command in _complete_visible_commands(ctx, incomplete) + ] + results.extend(super().shell_complete(ctx, incomplete)) + return results + + +class Group(MultiCommand): + """A group allows a command to have subcommands attached. This is + the most common way to implement nesting in Click. + + :param name: The name of the group command. + :param commands: A dict mapping names to :class:`Command` objects. + Can also be a list of :class:`Command`, which will use + :attr:`Command.name` to create the dict. + :param attrs: Other command arguments described in + :class:`MultiCommand`, :class:`Command`, and + :class:`BaseCommand`. + + .. versionchanged:: 8.0 + The ``commmands`` argument can be a list of command objects. + """ + + #: If set, this is used by the group's :meth:`command` decorator + #: as the default :class:`Command` class. This is useful to make all + #: subcommands use a custom command class. + #: + #: .. versionadded:: 8.0 + command_class: t.Optional[t.Type[Command]] = None + + #: If set, this is used by the group's :meth:`group` decorator + #: as the default :class:`Group` class. This is useful to make all + #: subgroups use a custom group class. + #: + #: If set to the special value :class:`type` (literally + #: ``group_class = type``), this group's class will be used as the + #: default class. This makes a custom group class continue to make + #: custom groups. + #: + #: .. versionadded:: 8.0 + group_class: t.Optional[t.Union[t.Type["Group"], t.Type[type]]] = None + # Literal[type] isn't valid, so use Type[type] + + def __init__( + self, + name: t.Optional[str] = None, + commands: t.Optional[t.Union[t.Dict[str, Command], t.Sequence[Command]]] = None, + **attrs: t.Any, + ) -> None: + super().__init__(name, **attrs) + + if commands is None: + commands = {} + elif isinstance(commands, abc.Sequence): + commands = {c.name: c for c in commands if c.name is not None} + + #: The registered subcommands by their exported names. + self.commands: t.Dict[str, Command] = commands + + def add_command(self, cmd: Command, name: t.Optional[str] = None) -> None: + """Registers another :class:`Command` with this group. If the name + is not provided, the name of the command is used. + """ + name = name or cmd.name + if name is None: + raise TypeError("Command has no name.") + _check_multicommand(self, name, cmd, register=True) + self.commands[name] = cmd + + def command( + self, *args: t.Any, **kwargs: t.Any + ) -> t.Callable[[t.Callable[..., t.Any]], Command]: + """A shortcut decorator for declaring and attaching a command to + the group. This takes the same arguments as :func:`command` and + immediately registers the created command with this group by + calling :meth:`add_command`. + + To customize the command class used, set the + :attr:`command_class` attribute. + + .. versionchanged:: 8.0 + Added the :attr:`command_class` attribute. + """ + from .decorators import command + + if self.command_class is not None and "cls" not in kwargs: + kwargs["cls"] = self.command_class + + def decorator(f: t.Callable[..., t.Any]) -> Command: + cmd = command(*args, **kwargs)(f) + self.add_command(cmd) + return cmd + + return decorator + + def group( + self, *args: t.Any, **kwargs: t.Any + ) -> t.Callable[[t.Callable[..., t.Any]], "Group"]: + """A shortcut decorator for declaring and attaching a group to + the group. This takes the same arguments as :func:`group` and + immediately registers the created group with this group by + calling :meth:`add_command`. + + To customize the group class used, set the :attr:`group_class` + attribute. + + .. versionchanged:: 8.0 + Added the :attr:`group_class` attribute. + """ + from .decorators import group + + if self.group_class is not None and "cls" not in kwargs: + if self.group_class is type: + kwargs["cls"] = type(self) + else: + kwargs["cls"] = self.group_class + + def decorator(f: t.Callable[..., t.Any]) -> "Group": + cmd = group(*args, **kwargs)(f) + self.add_command(cmd) + return cmd + + return decorator + + def get_command(self, ctx: Context, cmd_name: str) -> t.Optional[Command]: + return self.commands.get(cmd_name) + + def list_commands(self, ctx: Context) -> t.List[str]: + return sorted(self.commands) + + +class CommandCollection(MultiCommand): + """A command collection is a multi command that merges multiple multi + commands together into one. This is a straightforward implementation + that accepts a list of different multi commands as sources and + provides all the commands for each of them. + """ + + def __init__( + self, + name: t.Optional[str] = None, + sources: t.Optional[t.List[MultiCommand]] = None, + **attrs: t.Any, + ) -> None: + super().__init__(name, **attrs) + #: The list of registered multi commands. + self.sources: t.List[MultiCommand] = sources or [] + + def add_source(self, multi_cmd: MultiCommand) -> None: + """Adds a new multi command to the chain dispatcher.""" + self.sources.append(multi_cmd) + + def get_command(self, ctx: Context, cmd_name: str) -> t.Optional[Command]: + for source in self.sources: + rv = source.get_command(ctx, cmd_name) + + if rv is not None: + if self.chain: + _check_multicommand(self, cmd_name, rv) + + return rv + + return None + + def list_commands(self, ctx: Context) -> t.List[str]: + rv: t.Set[str] = set() + + for source in self.sources: + rv.update(source.list_commands(ctx)) + + return sorted(rv) + + +def _check_iter(value: t.Any) -> t.Iterator[t.Any]: + """Check if the value is iterable but not a string. Raises a type + error, or return an iterator over the value. + """ + if isinstance(value, str): + raise TypeError + + return iter(value) + + +class Parameter: + r"""A parameter to a command comes in two versions: they are either + :class:`Option`\s or :class:`Argument`\s. Other subclasses are currently + not supported by design as some of the internals for parsing are + intentionally not finalized. + + Some settings are supported by both options and arguments. + + :param param_decls: the parameter declarations for this option or + argument. This is a list of flags or argument + names. + :param type: the type that should be used. Either a :class:`ParamType` + or a Python type. The later is converted into the former + automatically if supported. + :param required: controls if this is optional or not. + :param default: the default value if omitted. This can also be a callable, + in which case it's invoked when the default is needed + without any arguments. + :param callback: A function to further process or validate the value + after type conversion. It is called as ``f(ctx, param, value)`` + and must return the value. It is called for all sources, + including prompts. + :param nargs: the number of arguments to match. If not ``1`` the return + value is a tuple instead of single value. The default for + nargs is ``1`` (except if the type is a tuple, then it's + the arity of the tuple). If ``nargs=-1``, all remaining + parameters are collected. + :param metavar: how the value is represented in the help page. + :param expose_value: if this is `True` then the value is passed onwards + to the command callback and stored on the context, + otherwise it's skipped. + :param is_eager: eager values are processed before non eager ones. This + should not be set for arguments or it will inverse the + order of processing. + :param envvar: a string or list of strings that are environment variables + that should be checked. + :param shell_complete: A function that returns custom shell + completions. Used instead of the param's type completion if + given. Takes ``ctx, param, incomplete`` and must return a list + of :class:`~click.shell_completion.CompletionItem` or a list of + strings. + + .. versionchanged:: 8.0 + ``process_value`` validates required parameters and bounded + ``nargs``, and invokes the parameter callback before returning + the value. This allows the callback to validate prompts. + ``full_process_value`` is removed. + + .. versionchanged:: 8.0 + ``autocompletion`` is renamed to ``shell_complete`` and has new + semantics described above. The old name is deprecated and will + be removed in 8.1, until then it will be wrapped to match the + new requirements. + + .. versionchanged:: 8.0 + For ``multiple=True, nargs>1``, the default must be a list of + tuples. + + .. versionchanged:: 8.0 + Setting a default is no longer required for ``nargs>1``, it will + default to ``None``. ``multiple=True`` or ``nargs=-1`` will + default to ``()``. + + .. versionchanged:: 7.1 + Empty environment variables are ignored rather than taking the + empty string value. This makes it possible for scripts to clear + variables if they can't unset them. + + .. versionchanged:: 2.0 + Changed signature for parameter callback to also be passed the + parameter. The old callback format will still work, but it will + raise a warning to give you a chance to migrate the code easier. + """ + + param_type_name = "parameter" + + def __init__( + self, + param_decls: t.Optional[t.Sequence[str]] = None, + type: t.Optional[t.Union[types.ParamType, t.Any]] = None, + required: bool = False, + default: t.Optional[t.Union[t.Any, t.Callable[[], t.Any]]] = None, + callback: t.Optional[t.Callable[[Context, "Parameter", t.Any], t.Any]] = None, + nargs: t.Optional[int] = None, + multiple: bool = False, + metavar: t.Optional[str] = None, + expose_value: bool = True, + is_eager: bool = False, + envvar: t.Optional[t.Union[str, t.Sequence[str]]] = None, + shell_complete: t.Optional[ + t.Callable[ + [Context, "Parameter", str], + t.Union[t.List["CompletionItem"], t.List[str]], + ] + ] = None, + autocompletion: t.Optional[ + t.Callable[ + [Context, t.List[str], str], t.List[t.Union[t.Tuple[str, str], str]] + ] + ] = None, + ) -> None: + self.name, self.opts, self.secondary_opts = self._parse_decls( + param_decls or (), expose_value + ) + self.type = types.convert_type(type, default) + + # Default nargs to what the type tells us if we have that + # information available. + if nargs is None: + if self.type.is_composite: + nargs = self.type.arity + else: + nargs = 1 + + self.required = required + self.callback = callback + self.nargs = nargs + self.multiple = multiple + self.expose_value = expose_value + self.default = default + self.is_eager = is_eager + self.metavar = metavar + self.envvar = envvar + + if autocompletion is not None: + import warnings + + warnings.warn( + "'autocompletion' is renamed to 'shell_complete'. The old name is" + " deprecated and will be removed in Click 8.1. See the docs about" + " 'Parameter' for information about new behavior.", + DeprecationWarning, + stacklevel=2, + ) + + def shell_complete( + ctx: Context, param: "Parameter", incomplete: str + ) -> t.List["CompletionItem"]: + from click.shell_completion import CompletionItem + + out = [] + + for c in autocompletion(ctx, [], incomplete): # type: ignore + if isinstance(c, tuple): + c = CompletionItem(c[0], help=c[1]) + elif isinstance(c, str): + c = CompletionItem(c) + + if c.value.startswith(incomplete): + out.append(c) + + return out + + self._custom_shell_complete = shell_complete + + if __debug__: + if self.type.is_composite and nargs != self.type.arity: + raise ValueError( + f"'nargs' must be {self.type.arity} (or None) for" + f" type {self.type!r}, but it was {nargs}." + ) + + # Skip no default or callable default. + check_default = default if not callable(default) else None + + if check_default is not None: + if multiple: + try: + # Only check the first value against nargs. + check_default = next(_check_iter(check_default), None) + except TypeError: + raise ValueError( + "'default' must be a list when 'multiple' is true." + ) from None + + # Can be None for multiple with empty default. + if nargs != 1 and check_default is not None: + try: + _check_iter(check_default) + except TypeError: + if multiple: + message = ( + "'default' must be a list of lists when 'multiple' is" + " true and 'nargs' != 1." + ) + else: + message = "'default' must be a list when 'nargs' != 1." + + raise ValueError(message) from None + + if nargs > 1 and len(check_default) != nargs: + subject = "item length" if multiple else "length" + raise ValueError( + f"'default' {subject} must match nargs={nargs}." + ) + + def to_info_dict(self) -> t.Dict[str, t.Any]: + """Gather information that could be useful for a tool generating + user-facing documentation. + + Use :meth:`click.Context.to_info_dict` to traverse the entire + CLI structure. + + .. versionadded:: 8.0 + """ + return { + "name": self.name, + "param_type_name": self.param_type_name, + "opts": self.opts, + "secondary_opts": self.secondary_opts, + "type": self.type.to_info_dict(), + "required": self.required, + "nargs": self.nargs, + "multiple": self.multiple, + "default": self.default, + "envvar": self.envvar, + } + + def __repr__(self) -> str: + return f"<{self.__class__.__name__} {self.name}>" + + def _parse_decls( + self, decls: t.Sequence[str], expose_value: bool + ) -> t.Tuple[t.Optional[str], t.List[str], t.List[str]]: + raise NotImplementedError() + + @property + def human_readable_name(self) -> str: + """Returns the human readable name of this parameter. This is the + same as the name for options, but the metavar for arguments. + """ + return self.name # type: ignore + + def make_metavar(self) -> str: + if self.metavar is not None: + return self.metavar + + metavar = self.type.get_metavar(self) + + if metavar is None: + metavar = self.type.name.upper() + + if self.nargs != 1: + metavar += "..." + + return metavar + + @typing.overload + def get_default( + self, ctx: Context, call: "te.Literal[True]" = True + ) -> t.Optional[t.Any]: + ... + + @typing.overload + def get_default( + self, ctx: Context, call: bool = ... + ) -> t.Optional[t.Union[t.Any, t.Callable[[], t.Any]]]: + ... + + def get_default( + self, ctx: Context, call: bool = True + ) -> t.Optional[t.Union[t.Any, t.Callable[[], t.Any]]]: + """Get the default for the parameter. Tries + :meth:`Context.lookup_value` first, then the local default. + + :param ctx: Current context. + :param call: If the default is a callable, call it. Disable to + return the callable instead. + + .. versionchanged:: 8.0.1 + Type casting can fail in resilient parsing mode. Invalid + defaults will not prevent showing help text. + + .. versionchanged:: 8.0 + Looks at ``ctx.default_map`` first. + + .. versionchanged:: 8.0 + Added the ``call`` parameter. + """ + value = ctx.lookup_default(self.name, call=False) # type: ignore + + if value is None: + value = self.default + + if callable(value): + if not call: + # Don't type cast the callable. + return value + + value = value() + + try: + return self.type_cast_value(ctx, value) + except BadParameter: + if ctx.resilient_parsing: + return value + + raise + + def add_to_parser(self, parser: OptionParser, ctx: Context) -> None: + raise NotImplementedError() + + def consume_value( + self, ctx: Context, opts: t.Mapping[str, t.Any] + ) -> t.Tuple[t.Any, ParameterSource]: + value = opts.get(self.name) # type: ignore + source = ParameterSource.COMMANDLINE + + if value is None: + value = self.value_from_envvar(ctx) + source = ParameterSource.ENVIRONMENT + + if value is None: + value = ctx.lookup_default(self.name) # type: ignore + source = ParameterSource.DEFAULT_MAP + + if value is None: + value = self.get_default(ctx) + source = ParameterSource.DEFAULT + + return value, source + + def type_cast_value(self, ctx: Context, value: t.Any) -> t.Any: + """Convert and validate a value against the option's + :attr:`type`, :attr:`multiple`, and :attr:`nargs`. + """ + if value is None: + return () if self.multiple or self.nargs == -1 else None + + def check_iter(value: t.Any) -> t.Iterator: + try: + return _check_iter(value) + except TypeError: + # This should only happen when passing in args manually, + # the parser should construct an iterable when parsing + # the command line. + raise BadParameter( + _("Value must be an iterable."), ctx=ctx, param=self + ) from None + + if self.nargs == 1 or self.type.is_composite: + convert: t.Callable[[t.Any], t.Any] = partial( + self.type, param=self, ctx=ctx + ) + elif self.nargs == -1: + + def convert(value: t.Any) -> t.Tuple: + return tuple(self.type(x, self, ctx) for x in check_iter(value)) + + else: # nargs > 1 + + def convert(value: t.Any) -> t.Tuple: + value = tuple(check_iter(value)) + + if len(value) != self.nargs: + raise BadParameter( + ngettext( + "Takes {nargs} values but 1 was given.", + "Takes {nargs} values but {len} were given.", + len(value), + ).format(nargs=self.nargs, len=len(value)), + ctx=ctx, + param=self, + ) + + return tuple(self.type(x, self, ctx) for x in value) + + if self.multiple: + return tuple(convert(x) for x in check_iter(value)) + + return convert(value) + + def value_is_missing(self, value: t.Any) -> bool: + if value is None: + return True + + if (self.nargs != 1 or self.multiple) and value == (): + return True + + return False + + def process_value(self, ctx: Context, value: t.Any) -> t.Any: + if value is not None: + value = self.type_cast_value(ctx, value) + + if self.required and self.value_is_missing(value): + raise MissingParameter(ctx=ctx, param=self) + + if self.callback is not None: + value = self.callback(ctx, self, value) + + return value + + def resolve_envvar_value(self, ctx: Context) -> t.Optional[str]: + if self.envvar is None: + return None + + if isinstance(self.envvar, str): + rv = os.environ.get(self.envvar) + + if rv: + return rv + else: + for envvar in self.envvar: + rv = os.environ.get(envvar) + + if rv: + return rv + + return None + + def value_from_envvar(self, ctx: Context) -> t.Optional[t.Any]: + rv: t.Optional[t.Any] = self.resolve_envvar_value(ctx) + + if rv is not None and self.nargs != 1: + rv = self.type.split_envvar_value(rv) + + return rv + + def handle_parse_result( + self, ctx: Context, opts: t.Mapping[str, t.Any], args: t.List[str] + ) -> t.Tuple[t.Any, t.List[str]]: + with augment_usage_errors(ctx, param=self): + value, source = self.consume_value(ctx, opts) + ctx.set_parameter_source(self.name, source) # type: ignore + + try: + value = self.process_value(ctx, value) + except Exception: + if not ctx.resilient_parsing: + raise + + value = None + + if self.expose_value: + ctx.params[self.name] = value # type: ignore + + return value, args + + def get_help_record(self, ctx: Context) -> t.Optional[t.Tuple[str, str]]: + pass + + def get_usage_pieces(self, ctx: Context) -> t.List[str]: + return [] + + def get_error_hint(self, ctx: Context) -> str: + """Get a stringified version of the param for use in error messages to + indicate which param caused the error. + """ + hint_list = self.opts or [self.human_readable_name] + return " / ".join(f"'{x}'" for x in hint_list) + + def shell_complete(self, ctx: Context, incomplete: str) -> t.List["CompletionItem"]: + """Return a list of completions for the incomplete value. If a + ``shell_complete`` function was given during init, it is used. + Otherwise, the :attr:`type` + :meth:`~click.types.ParamType.shell_complete` function is used. + + :param ctx: Invocation context for this command. + :param incomplete: Value being completed. May be empty. + + .. versionadded:: 8.0 + """ + if self._custom_shell_complete is not None: + results = self._custom_shell_complete(ctx, self, incomplete) + + if results and isinstance(results[0], str): + from click.shell_completion import CompletionItem + + results = [CompletionItem(c) for c in results] + + return t.cast(t.List["CompletionItem"], results) + + return self.type.shell_complete(ctx, self, incomplete) + + +class Option(Parameter): + """Options are usually optional values on the command line and + have some extra features that arguments don't have. + + All other parameters are passed onwards to the parameter constructor. + + :param show_default: controls if the default value should be shown on the + help page. Normally, defaults are not shown. If this + value is a string, it shows the string instead of the + value. This is particularly useful for dynamic options. + :param show_envvar: controls if an environment variable should be shown on + the help page. Normally, environment variables + are not shown. + :param prompt: if set to `True` or a non empty string then the user will be + prompted for input. If set to `True` the prompt will be the + option name capitalized. + :param confirmation_prompt: Prompt a second time to confirm the + value if it was prompted for. Can be set to a string instead of + ``True`` to customize the message. + :param prompt_required: If set to ``False``, the user will be + prompted for input only when the option was specified as a flag + without a value. + :param hide_input: if this is `True` then the input on the prompt will be + hidden from the user. This is useful for password + input. + :param is_flag: forces this option to act as a flag. The default is + auto detection. + :param flag_value: which value should be used for this flag if it's + enabled. This is set to a boolean automatically if + the option string contains a slash to mark two options. + :param multiple: if this is set to `True` then the argument is accepted + multiple times and recorded. This is similar to ``nargs`` + in how it works but supports arbitrary number of + arguments. + :param count: this flag makes an option increment an integer. + :param allow_from_autoenv: if this is enabled then the value of this + parameter will be pulled from an environment + variable in case a prefix is defined on the + context. + :param help: the help string. + :param hidden: hide this option from help outputs. + + .. versionchanged:: 8.0.1 + ``type`` is detected from ``flag_value`` if given. + """ + + param_type_name = "option" + + def __init__( + self, + param_decls: t.Optional[t.Sequence[str]] = None, + show_default: bool = False, + prompt: t.Union[bool, str] = False, + confirmation_prompt: t.Union[bool, str] = False, + prompt_required: bool = True, + hide_input: bool = False, + is_flag: t.Optional[bool] = None, + flag_value: t.Optional[t.Any] = None, + multiple: bool = False, + count: bool = False, + allow_from_autoenv: bool = True, + type: t.Optional[t.Union[types.ParamType, t.Any]] = None, + help: t.Optional[str] = None, + hidden: bool = False, + show_choices: bool = True, + show_envvar: bool = False, + **attrs: t.Any, + ) -> None: + default_is_missing = "default" not in attrs + super().__init__(param_decls, type=type, multiple=multiple, **attrs) + + if prompt is True: + if self.name is None: + raise TypeError("'name' is required with 'prompt=True'.") + + prompt_text: t.Optional[str] = self.name.replace("_", " ").capitalize() + elif prompt is False: + prompt_text = None + else: + prompt_text = t.cast(str, prompt) + + self.prompt = prompt_text + self.confirmation_prompt = confirmation_prompt + self.prompt_required = prompt_required + self.hide_input = hide_input + self.hidden = hidden + + # If prompt is enabled but not required, then the option can be + # used as a flag to indicate using prompt or flag_value. + self._flag_needs_value = self.prompt is not None and not self.prompt_required + + if is_flag is None: + if flag_value is not None: + # Implicitly a flag because flag_value was set. + is_flag = True + elif self._flag_needs_value: + # Not a flag, but when used as a flag it shows a prompt. + is_flag = False + else: + # Implicitly a flag because flag options were given. + is_flag = bool(self.secondary_opts) + elif is_flag is False and not self._flag_needs_value: + # Not a flag, and prompt is not enabled, can be used as a + # flag if flag_value is set. + self._flag_needs_value = flag_value is not None + + if is_flag and default_is_missing: + self.default: t.Union[t.Any, t.Callable[[], t.Any]] = False + + if flag_value is None: + flag_value = not self.default + + if is_flag and type is None: + # Re-guess the type from the flag value instead of the + # default. + self.type = types.convert_type(None, flag_value) + + self.is_flag: bool = is_flag + self.is_bool_flag = isinstance(self.type, types.BoolParamType) + self.flag_value: t.Any = flag_value + + # Counting + self.count = count + if count: + if type is None: + self.type = types.IntRange(min=0) + if default_is_missing: + self.default = 0 + + self.allow_from_autoenv = allow_from_autoenv + self.help = help + self.show_default = show_default + self.show_choices = show_choices + self.show_envvar = show_envvar + + if __debug__: + if self.nargs == -1: + raise TypeError("nargs=-1 is not supported for options.") + + if self.prompt and self.is_flag and not self.is_bool_flag: + raise TypeError("'prompt' is not valid for non-boolean flag.") + + if not self.is_bool_flag and self.secondary_opts: + raise TypeError("Secondary flag is not valid for non-boolean flag.") + + if self.is_bool_flag and self.hide_input and self.prompt is not None: + raise TypeError( + "'prompt' with 'hide_input' is not valid for boolean flag." + ) + + if self.count: + if self.multiple: + raise TypeError("'count' is not valid with 'multiple'.") + + if self.is_flag: + raise TypeError("'count' is not valid with 'is_flag'.") + + def to_info_dict(self) -> t.Dict[str, t.Any]: + info_dict = super().to_info_dict() + info_dict.update( + help=self.help, + prompt=self.prompt, + is_flag=self.is_flag, + flag_value=self.flag_value, + count=self.count, + hidden=self.hidden, + ) + return info_dict + + def _parse_decls( + self, decls: t.Sequence[str], expose_value: bool + ) -> t.Tuple[t.Optional[str], t.List[str], t.List[str]]: + opts = [] + secondary_opts = [] + name = None + possible_names = [] + + for decl in decls: + if decl.isidentifier(): + if name is not None: + raise TypeError("Name defined twice") + name = decl + else: + split_char = ";" if decl[:1] == "/" else "/" + if split_char in decl: + first, second = decl.split(split_char, 1) + first = first.rstrip() + if first: + possible_names.append(split_opt(first)) + opts.append(first) + second = second.lstrip() + if second: + secondary_opts.append(second.lstrip()) + if first == second: + raise ValueError( + f"Boolean option {decl!r} cannot use the" + " same flag for true/false." + ) + else: + possible_names.append(split_opt(decl)) + opts.append(decl) + + if name is None and possible_names: + possible_names.sort(key=lambda x: -len(x[0])) # group long options first + name = possible_names[0][1].replace("-", "_").lower() + if not name.isidentifier(): + name = None + + if name is None: + if not expose_value: + return None, opts, secondary_opts + raise TypeError("Could not determine name for option") + + if not opts and not secondary_opts: + raise TypeError( + f"No options defined but a name was passed ({name})." + " Did you mean to declare an argument instead? Did" + f" you mean to pass '--{name}'?" + ) + + return name, opts, secondary_opts + + def add_to_parser(self, parser: OptionParser, ctx: Context) -> None: + if self.multiple: + action = "append" + elif self.count: + action = "count" + else: + action = "store" + + if self.is_flag: + action = f"{action}_const" + + if self.is_bool_flag and self.secondary_opts: + parser.add_option( + obj=self, opts=self.opts, dest=self.name, action=action, const=True + ) + parser.add_option( + obj=self, + opts=self.secondary_opts, + dest=self.name, + action=action, + const=False, + ) + else: + parser.add_option( + obj=self, + opts=self.opts, + dest=self.name, + action=action, + const=self.flag_value, + ) + else: + parser.add_option( + obj=self, + opts=self.opts, + dest=self.name, + action=action, + nargs=self.nargs, + ) + + def get_help_record(self, ctx: Context) -> t.Optional[t.Tuple[str, str]]: + if self.hidden: + return None + + any_prefix_is_slash = False + + def _write_opts(opts: t.Sequence[str]) -> str: + nonlocal any_prefix_is_slash + + rv, any_slashes = join_options(opts) + + if any_slashes: + any_prefix_is_slash = True + + if not self.is_flag and not self.count: + rv += f" {self.make_metavar()}" + + return rv + + rv = [_write_opts(self.opts)] + + if self.secondary_opts: + rv.append(_write_opts(self.secondary_opts)) + + help = self.help or "" + extra = [] + + if self.show_envvar: + envvar = self.envvar + + if envvar is None: + if ( + self.allow_from_autoenv + and ctx.auto_envvar_prefix is not None + and self.name is not None + ): + envvar = f"{ctx.auto_envvar_prefix}_{self.name.upper()}" + + if envvar is not None: + var_str = ( + envvar + if isinstance(envvar, str) + else ", ".join(str(d) for d in envvar) + ) + extra.append(_("env var: {var}").format(var=var_str)) + + # Temporarily enable resilient parsing to avoid type casting + # failing for the default. Might be possible to extend this to + # help formatting in general. + resilient = ctx.resilient_parsing + ctx.resilient_parsing = True + + try: + default_value = self.get_default(ctx, call=False) + finally: + ctx.resilient_parsing = resilient + + show_default_is_str = isinstance(self.show_default, str) + + if show_default_is_str or ( + default_value is not None and (self.show_default or ctx.show_default) + ): + if show_default_is_str: + default_string = f"({self.show_default})" + elif isinstance(default_value, (list, tuple)): + default_string = ", ".join(str(d) for d in default_value) + elif callable(default_value): + default_string = _("(dynamic)") + elif self.is_bool_flag and self.secondary_opts: + # For boolean flags that have distinct True/False opts, + # use the opt without prefix instead of the value. + default_string = split_opt( + (self.opts if self.default else self.secondary_opts)[0] + )[1] + else: + default_string = str(default_value) + + extra.append(_("default: {default}").format(default=default_string)) + + if isinstance(self.type, types._NumberRangeBase): + range_str = self.type._describe_range() + + if range_str: + extra.append(range_str) + + if self.required: + extra.append(_("required")) + + if extra: + extra_str = ";".join(extra) + help = f"{help} [{extra_str}]" if help else f"[{extra_str}]" + + return ("; " if any_prefix_is_slash else " / ").join(rv), help + + @typing.overload + def get_default( + self, ctx: Context, call: "te.Literal[True]" = True + ) -> t.Optional[t.Any]: + ... + + @typing.overload + def get_default( + self, ctx: Context, call: bool = ... + ) -> t.Optional[t.Union[t.Any, t.Callable[[], t.Any]]]: + ... + + def get_default( + self, ctx: Context, call: bool = True + ) -> t.Optional[t.Union[t.Any, t.Callable[[], t.Any]]]: + # If we're a non boolean flag our default is more complex because + # we need to look at all flags in the same group to figure out + # if we're the the default one in which case we return the flag + # value as default. + if self.is_flag and not self.is_bool_flag: + for param in ctx.command.params: + if param.name == self.name and param.default: + return param.flag_value # type: ignore + + return None + + return super().get_default(ctx, call=call) + + def prompt_for_value(self, ctx: Context) -> t.Any: + """This is an alternative flow that can be activated in the full + value processing if a value does not exist. It will prompt the + user until a valid value exists and then returns the processed + value as result. + """ + assert self.prompt is not None + + # Calculate the default before prompting anything to be stable. + default = self.get_default(ctx) + + # If this is a prompt for a flag we need to handle this + # differently. + if self.is_bool_flag: + return confirm(self.prompt, default) + + return prompt( + self.prompt, + default=default, + type=self.type, + hide_input=self.hide_input, + show_choices=self.show_choices, + confirmation_prompt=self.confirmation_prompt, + value_proc=lambda x: self.process_value(ctx, x), + ) + + def resolve_envvar_value(self, ctx: Context) -> t.Optional[str]: + rv = super().resolve_envvar_value(ctx) + + if rv is not None: + return rv + + if ( + self.allow_from_autoenv + and ctx.auto_envvar_prefix is not None + and self.name is not None + ): + envvar = f"{ctx.auto_envvar_prefix}_{self.name.upper()}" + rv = os.environ.get(envvar) + + return rv + + def value_from_envvar(self, ctx: Context) -> t.Optional[t.Any]: + rv: t.Optional[t.Any] = self.resolve_envvar_value(ctx) + + if rv is None: + return None + + value_depth = (self.nargs != 1) + bool(self.multiple) + + if value_depth > 0: + rv = self.type.split_envvar_value(rv) + + if self.multiple and self.nargs != 1: + rv = batch(rv, self.nargs) + + return rv + + def consume_value( + self, ctx: Context, opts: t.Mapping[str, "Parameter"] + ) -> t.Tuple[t.Any, ParameterSource]: + value, source = super().consume_value(ctx, opts) + + # The parser will emit a sentinel value if the option can be + # given as a flag without a value. This is different from None + # to distinguish from the flag not being given at all. + if value is _flag_needs_value: + if self.prompt is not None and not ctx.resilient_parsing: + value = self.prompt_for_value(ctx) + source = ParameterSource.PROMPT + else: + value = self.flag_value + source = ParameterSource.COMMANDLINE + + # The value wasn't set, or used the param's default, prompt if + # prompting is enabled. + elif ( + source in {None, ParameterSource.DEFAULT} + and self.prompt is not None + and (self.required or self.prompt_required) + and not ctx.resilient_parsing + ): + value = self.prompt_for_value(ctx) + source = ParameterSource.PROMPT + + return value, source + + +class Argument(Parameter): + """Arguments are positional parameters to a command. They generally + provide fewer features than options but can have infinite ``nargs`` + and are required by default. + + All parameters are passed onwards to the parameter constructor. + """ + + param_type_name = "argument" + + def __init__( + self, + param_decls: t.Sequence[str], + required: t.Optional[bool] = None, + **attrs: t.Any, + ) -> None: + if required is None: + if attrs.get("default") is not None: + required = False + else: + required = attrs.get("nargs", 1) > 0 + + if "multiple" in attrs: + raise TypeError("__init__() got an unexpected keyword argument 'multiple'.") + + super().__init__(param_decls, required=required, **attrs) + + if __debug__: + if self.default is not None and self.nargs == -1: + raise TypeError("'default' is not supported for nargs=-1.") + + @property + def human_readable_name(self) -> str: + if self.metavar is not None: + return self.metavar + return self.name.upper() # type: ignore + + def make_metavar(self) -> str: + if self.metavar is not None: + return self.metavar + var = self.type.get_metavar(self) + if not var: + var = self.name.upper() # type: ignore + if not self.required: + var = f"[{var}]" + if self.nargs != 1: + var += "..." + return var + + def _parse_decls( + self, decls: t.Sequence[str], expose_value: bool + ) -> t.Tuple[t.Optional[str], t.List[str], t.List[str]]: + if not decls: + if not expose_value: + return None, [], [] + raise TypeError("Could not determine name for argument") + if len(decls) == 1: + name = arg = decls[0] + name = name.replace("-", "_").lower() + else: + raise TypeError( + "Arguments take exactly one parameter declaration, got" + f" {len(decls)}." + ) + return name, [arg], [] + + def get_usage_pieces(self, ctx: Context) -> t.List[str]: + return [self.make_metavar()] + + def get_error_hint(self, ctx: Context) -> str: + return f"'{self.make_metavar()}'" + + def add_to_parser(self, parser: OptionParser, ctx: Context) -> None: + parser.add_argument(dest=self.name, nargs=self.nargs, obj=self) diff --git a/venv/Lib/site-packages/click/decorators.py b/venv/Lib/site-packages/click/decorators.py new file mode 100644 index 0000000..5940e69 --- /dev/null +++ b/venv/Lib/site-packages/click/decorators.py @@ -0,0 +1,437 @@ +import inspect +import types +import typing as t +from functools import update_wrapper +from gettext import gettext as _ + +from .core import Argument +from .core import Command +from .core import Context +from .core import Group +from .core import Option +from .core import Parameter +from .globals import get_current_context +from .utils import echo + +F = t.TypeVar("F", bound=t.Callable[..., t.Any]) +FC = t.TypeVar("FC", t.Callable[..., t.Any], Command) + + +def pass_context(f: F) -> F: + """Marks a callback as wanting to receive the current context + object as first argument. + """ + + def new_func(*args, **kwargs): # type: ignore + return f(get_current_context(), *args, **kwargs) + + return update_wrapper(t.cast(F, new_func), f) + + +def pass_obj(f: F) -> F: + """Similar to :func:`pass_context`, but only pass the object on the + context onwards (:attr:`Context.obj`). This is useful if that object + represents the state of a nested system. + """ + + def new_func(*args, **kwargs): # type: ignore + return f(get_current_context().obj, *args, **kwargs) + + return update_wrapper(t.cast(F, new_func), f) + + +def make_pass_decorator( + object_type: t.Type, ensure: bool = False +) -> "t.Callable[[F], F]": + """Given an object type this creates a decorator that will work + similar to :func:`pass_obj` but instead of passing the object of the + current context, it will find the innermost context of type + :func:`object_type`. + + This generates a decorator that works roughly like this:: + + from functools import update_wrapper + + def decorator(f): + @pass_context + def new_func(ctx, *args, **kwargs): + obj = ctx.find_object(object_type) + return ctx.invoke(f, obj, *args, **kwargs) + return update_wrapper(new_func, f) + return decorator + + :param object_type: the type of the object to pass. + :param ensure: if set to `True`, a new object will be created and + remembered on the context if it's not there yet. + """ + + def decorator(f: F) -> F: + def new_func(*args, **kwargs): # type: ignore + ctx = get_current_context() + + if ensure: + obj = ctx.ensure_object(object_type) + else: + obj = ctx.find_object(object_type) + + if obj is None: + raise RuntimeError( + "Managed to invoke callback without a context" + f" object of type {object_type.__name__!r}" + " existing." + ) + + return ctx.invoke(f, obj, *args, **kwargs) + + return update_wrapper(t.cast(F, new_func), f) + + return decorator + + +def pass_meta_key( + key: str, *, doc_description: t.Optional[str] = None +) -> "t.Callable[[F], F]": + """Create a decorator that passes a key from + :attr:`click.Context.meta` as the first argument to the decorated + function. + + :param key: Key in ``Context.meta`` to pass. + :param doc_description: Description of the object being passed, + inserted into the decorator's docstring. Defaults to "the 'key' + key from Context.meta". + + .. versionadded:: 8.0 + """ + + def decorator(f: F) -> F: + def new_func(*args, **kwargs): # type: ignore + ctx = get_current_context() + obj = ctx.meta[key] + return ctx.invoke(f, obj, *args, **kwargs) + + return update_wrapper(t.cast(F, new_func), f) + + if doc_description is None: + doc_description = f"the {key!r} key from :attr:`click.Context.meta`" + + decorator.__doc__ = ( + f"Decorator that passes {doc_description} as the first argument" + " to the decorated function." + ) + return decorator + + +def _make_command( + f: F, + name: t.Optional[str], + attrs: t.MutableMapping[str, t.Any], + cls: t.Type[Command], +) -> Command: + if isinstance(f, Command): + raise TypeError("Attempted to convert a callback into a command twice.") + + try: + params = f.__click_params__ # type: ignore + params.reverse() + del f.__click_params__ # type: ignore + except AttributeError: + params = [] + + help = attrs.get("help") + + if help is None: + help = inspect.getdoc(f) + else: + help = inspect.cleandoc(help) + + attrs["help"] = help + return cls( + name=name or f.__name__.lower().replace("_", "-"), + callback=f, + params=params, + **attrs, + ) + + +def command( + name: t.Optional[str] = None, + cls: t.Optional[t.Type[Command]] = None, + **attrs: t.Any, +) -> t.Callable[[F], Command]: + r"""Creates a new :class:`Command` and uses the decorated function as + callback. This will also automatically attach all decorated + :func:`option`\s and :func:`argument`\s as parameters to the command. + + The name of the command defaults to the name of the function with + underscores replaced by dashes. If you want to change that, you can + pass the intended name as the first argument. + + All keyword arguments are forwarded to the underlying command class. + + Once decorated the function turns into a :class:`Command` instance + that can be invoked as a command line utility or be attached to a + command :class:`Group`. + + :param name: the name of the command. This defaults to the function + name with underscores replaced by dashes. + :param cls: the command class to instantiate. This defaults to + :class:`Command`. + """ + if cls is None: + cls = Command + + def decorator(f: t.Callable[..., t.Any]) -> Command: + cmd = _make_command(f, name, attrs, cls) # type: ignore + cmd.__doc__ = f.__doc__ + return cmd + + return decorator + + +def group(name: t.Optional[str] = None, **attrs: t.Any) -> t.Callable[[F], Group]: + """Creates a new :class:`Group` with a function as callback. This + works otherwise the same as :func:`command` just that the `cls` + parameter is set to :class:`Group`. + """ + attrs.setdefault("cls", Group) + return t.cast(Group, command(name, **attrs)) + + +def _param_memo(f: FC, param: Parameter) -> None: + if isinstance(f, Command): + f.params.append(param) + else: + if not hasattr(f, "__click_params__"): + f.__click_params__ = [] # type: ignore + + f.__click_params__.append(param) # type: ignore + + +def argument(*param_decls: str, **attrs: t.Any) -> t.Callable[[FC], FC]: + """Attaches an argument to the command. All positional arguments are + passed as parameter declarations to :class:`Argument`; all keyword + arguments are forwarded unchanged (except ``cls``). + This is equivalent to creating an :class:`Argument` instance manually + and attaching it to the :attr:`Command.params` list. + + :param cls: the argument class to instantiate. This defaults to + :class:`Argument`. + """ + + def decorator(f: FC) -> FC: + ArgumentClass = attrs.pop("cls", Argument) + _param_memo(f, ArgumentClass(param_decls, **attrs)) + return f + + return decorator + + +def option(*param_decls: str, **attrs: t.Any) -> t.Callable[[FC], FC]: + """Attaches an option to the command. All positional arguments are + passed as parameter declarations to :class:`Option`; all keyword + arguments are forwarded unchanged (except ``cls``). + This is equivalent to creating an :class:`Option` instance manually + and attaching it to the :attr:`Command.params` list. + + :param cls: the option class to instantiate. This defaults to + :class:`Option`. + """ + + def decorator(f: FC) -> FC: + # Issue 926, copy attrs, so pre-defined options can re-use the same cls= + option_attrs = attrs.copy() + + if "help" in option_attrs: + option_attrs["help"] = inspect.cleandoc(option_attrs["help"]) + OptionClass = option_attrs.pop("cls", Option) + _param_memo(f, OptionClass(param_decls, **option_attrs)) + return f + + return decorator + + +def confirmation_option(*param_decls: str, **kwargs: t.Any) -> t.Callable[[FC], FC]: + """Add a ``--yes`` option which shows a prompt before continuing if + not passed. If the prompt is declined, the program will exit. + + :param param_decls: One or more option names. Defaults to the single + value ``"--yes"``. + :param kwargs: Extra arguments are passed to :func:`option`. + """ + + def callback(ctx: Context, param: Parameter, value: bool) -> None: + if not value: + ctx.abort() + + if not param_decls: + param_decls = ("--yes",) + + kwargs.setdefault("is_flag", True) + kwargs.setdefault("callback", callback) + kwargs.setdefault("expose_value", False) + kwargs.setdefault("prompt", "Do you want to continue?") + kwargs.setdefault("help", "Confirm the action without prompting.") + return option(*param_decls, **kwargs) + + +def password_option(*param_decls: str, **kwargs: t.Any) -> t.Callable[[FC], FC]: + """Add a ``--password`` option which prompts for a password, hiding + input and asking to enter the value again for confirmation. + + :param param_decls: One or more option names. Defaults to the single + value ``"--password"``. + :param kwargs: Extra arguments are passed to :func:`option`. + """ + if not param_decls: + param_decls = ("--password",) + + kwargs.setdefault("prompt", True) + kwargs.setdefault("confirmation_prompt", True) + kwargs.setdefault("hide_input", True) + return option(*param_decls, **kwargs) + + +def version_option( + version: t.Optional[str] = None, + *param_decls: str, + package_name: t.Optional[str] = None, + prog_name: t.Optional[str] = None, + message: t.Optional[str] = None, + **kwargs: t.Any, +) -> t.Callable[[FC], FC]: + """Add a ``--version`` option which immediately prints the version + number and exits the program. + + If ``version`` is not provided, Click will try to detect it using + :func:`importlib.metadata.version` to get the version for the + ``package_name``. On Python < 3.8, the ``importlib_metadata`` + backport must be installed. + + If ``package_name`` is not provided, Click will try to detect it by + inspecting the stack frames. This will be used to detect the + version, so it must match the name of the installed package. + + :param version: The version number to show. If not provided, Click + will try to detect it. + :param param_decls: One or more option names. Defaults to the single + value ``"--version"``. + :param package_name: The package name to detect the version from. If + not provided, Click will try to detect it. + :param prog_name: The name of the CLI to show in the message. If not + provided, it will be detected from the command. + :param message: The message to show. The values ``%(prog)s``, + ``%(package)s``, and ``%(version)s`` are available. Defaults to + ``"%(prog)s, version %(version)s"``. + :param kwargs: Extra arguments are passed to :func:`option`. + :raise RuntimeError: ``version`` could not be detected. + + .. versionchanged:: 8.0 + Add the ``package_name`` parameter, and the ``%(package)s`` + value for messages. + + .. versionchanged:: 8.0 + Use :mod:`importlib.metadata` instead of ``pkg_resources``. The + version is detected based on the package name, not the entry + point name. The Python package name must match the installed + package name, or be passed with ``package_name=``. + """ + if message is None: + message = _("%(prog)s, version %(version)s") + + if version is None and package_name is None: + frame = inspect.currentframe() + assert frame is not None + assert frame.f_back is not None + f_globals = frame.f_back.f_globals if frame is not None else None + # break reference cycle + # https://docs.python.org/3/library/inspect.html#the-interpreter-stack + del frame + + if f_globals is not None: + package_name = f_globals.get("__name__") + + if package_name == "__main__": + package_name = f_globals.get("__package__") + + if package_name: + package_name = package_name.partition(".")[0] + + def callback(ctx: Context, param: Parameter, value: bool) -> None: + if not value or ctx.resilient_parsing: + return + + nonlocal prog_name + nonlocal version + + if prog_name is None: + prog_name = ctx.find_root().info_name + + if version is None and package_name is not None: + metadata: t.Optional[types.ModuleType] + + try: + from importlib import metadata # type: ignore + except ImportError: + # Python < 3.8 + import importlib_metadata as metadata # type: ignore + + try: + version = metadata.version(package_name) # type: ignore + except metadata.PackageNotFoundError: # type: ignore + raise RuntimeError( + f"{package_name!r} is not installed. Try passing" + " 'package_name' instead." + ) + + if version is None: + raise RuntimeError( + f"Could not determine the version for {package_name!r} automatically." + ) + + echo( + t.cast(str, message) + % {"prog": prog_name, "package": package_name, "version": version}, + color=ctx.color, + ) + ctx.exit() + + if not param_decls: + param_decls = ("--version",) + + kwargs.setdefault("is_flag", True) + kwargs.setdefault("expose_value", False) + kwargs.setdefault("is_eager", True) + kwargs.setdefault("help", _("Show the version and exit.")) + kwargs["callback"] = callback + return option(*param_decls, **kwargs) + + +def help_option(*param_decls: str, **kwargs: t.Any) -> t.Callable[[FC], FC]: + """Add a ``--help`` option which immediately prints the help page + and exits the program. + + This is usually unnecessary, as the ``--help`` option is added to + each command automatically unless ``add_help_option=False`` is + passed. + + :param param_decls: One or more option names. Defaults to the single + value ``"--help"``. + :param kwargs: Extra arguments are passed to :func:`option`. + """ + + def callback(ctx: Context, param: Parameter, value: bool) -> None: + if not value or ctx.resilient_parsing: + return + + echo(ctx.get_help(), color=ctx.color) + ctx.exit() + + if not param_decls: + param_decls = ("--help",) + + kwargs.setdefault("is_flag", True) + kwargs.setdefault("expose_value", False) + kwargs.setdefault("is_eager", True) + kwargs.setdefault("help", _("Show this message and exit.")) + kwargs["callback"] = callback + return option(*param_decls, **kwargs) diff --git a/venv/Lib/site-packages/click/exceptions.py b/venv/Lib/site-packages/click/exceptions.py new file mode 100644 index 0000000..9e20b3e --- /dev/null +++ b/venv/Lib/site-packages/click/exceptions.py @@ -0,0 +1,287 @@ +import os +import typing as t +from gettext import gettext as _ +from gettext import ngettext + +from ._compat import get_text_stderr +from .utils import echo + +if t.TYPE_CHECKING: + from .core import Context + from .core import Parameter + + +def _join_param_hints( + param_hint: t.Optional[t.Union[t.Sequence[str], str]] +) -> t.Optional[str]: + if param_hint is not None and not isinstance(param_hint, str): + return " / ".join(repr(x) for x in param_hint) + + return param_hint + + +class ClickException(Exception): + """An exception that Click can handle and show to the user.""" + + #: The exit code for this exception. + exit_code = 1 + + def __init__(self, message: str) -> None: + super().__init__(message) + self.message = message + + def format_message(self) -> str: + return self.message + + def __str__(self) -> str: + return self.message + + def show(self, file: t.Optional[t.IO] = None) -> None: + if file is None: + file = get_text_stderr() + + echo(_("Error: {message}").format(message=self.format_message()), file=file) + + +class UsageError(ClickException): + """An internal exception that signals a usage error. This typically + aborts any further handling. + + :param message: the error message to display. + :param ctx: optionally the context that caused this error. Click will + fill in the context automatically in some situations. + """ + + exit_code = 2 + + def __init__(self, message: str, ctx: t.Optional["Context"] = None) -> None: + super().__init__(message) + self.ctx = ctx + self.cmd = self.ctx.command if self.ctx else None + + def show(self, file: t.Optional[t.IO] = None) -> None: + if file is None: + file = get_text_stderr() + color = None + hint = "" + if ( + self.ctx is not None + and self.ctx.command.get_help_option(self.ctx) is not None + ): + hint = _("Try '{command} {option}' for help.").format( + command=self.ctx.command_path, option=self.ctx.help_option_names[0] + ) + hint = f"{hint}\n" + if self.ctx is not None: + color = self.ctx.color + echo(f"{self.ctx.get_usage()}\n{hint}", file=file, color=color) + echo( + _("Error: {message}").format(message=self.format_message()), + file=file, + color=color, + ) + + +class BadParameter(UsageError): + """An exception that formats out a standardized error message for a + bad parameter. This is useful when thrown from a callback or type as + Click will attach contextual information to it (for instance, which + parameter it is). + + .. versionadded:: 2.0 + + :param param: the parameter object that caused this error. This can + be left out, and Click will attach this info itself + if possible. + :param param_hint: a string that shows up as parameter name. This + can be used as alternative to `param` in cases + where custom validation should happen. If it is + a string it's used as such, if it's a list then + each item is quoted and separated. + """ + + def __init__( + self, + message: str, + ctx: t.Optional["Context"] = None, + param: t.Optional["Parameter"] = None, + param_hint: t.Optional[str] = None, + ) -> None: + super().__init__(message, ctx) + self.param = param + self.param_hint = param_hint + + def format_message(self) -> str: + if self.param_hint is not None: + param_hint = self.param_hint + elif self.param is not None: + param_hint = self.param.get_error_hint(self.ctx) # type: ignore + else: + return _("Invalid value: {message}").format(message=self.message) + + return _("Invalid value for {param_hint}: {message}").format( + param_hint=_join_param_hints(param_hint), message=self.message + ) + + +class MissingParameter(BadParameter): + """Raised if click required an option or argument but it was not + provided when invoking the script. + + .. versionadded:: 4.0 + + :param param_type: a string that indicates the type of the parameter. + The default is to inherit the parameter type from + the given `param`. Valid values are ``'parameter'``, + ``'option'`` or ``'argument'``. + """ + + def __init__( + self, + message: t.Optional[str] = None, + ctx: t.Optional["Context"] = None, + param: t.Optional["Parameter"] = None, + param_hint: t.Optional[str] = None, + param_type: t.Optional[str] = None, + ) -> None: + super().__init__(message or "", ctx, param, param_hint) + self.param_type = param_type + + def format_message(self) -> str: + if self.param_hint is not None: + param_hint: t.Optional[str] = self.param_hint + elif self.param is not None: + param_hint = self.param.get_error_hint(self.ctx) # type: ignore + else: + param_hint = None + + param_hint = _join_param_hints(param_hint) + param_hint = f" {param_hint}" if param_hint else "" + + param_type = self.param_type + if param_type is None and self.param is not None: + param_type = self.param.param_type_name + + msg = self.message + if self.param is not None: + msg_extra = self.param.type.get_missing_message(self.param) + if msg_extra: + if msg: + msg += f". {msg_extra}" + else: + msg = msg_extra + + msg = f" {msg}" if msg else "" + + # Translate param_type for known types. + if param_type == "argument": + missing = _("Missing argument") + elif param_type == "option": + missing = _("Missing option") + elif param_type == "parameter": + missing = _("Missing parameter") + else: + missing = _("Missing {param_type}").format(param_type=param_type) + + return f"{missing}{param_hint}.{msg}" + + def __str__(self) -> str: + if not self.message: + param_name = self.param.name if self.param else None + return _("Missing parameter: {param_name}").format(param_name=param_name) + else: + return self.message + + +class NoSuchOption(UsageError): + """Raised if click attempted to handle an option that does not + exist. + + .. versionadded:: 4.0 + """ + + def __init__( + self, + option_name: str, + message: t.Optional[str] = None, + possibilities: t.Optional[t.Sequence[str]] = None, + ctx: t.Optional["Context"] = None, + ) -> None: + if message is None: + message = _("No such option: {name}").format(name=option_name) + + super().__init__(message, ctx) + self.option_name = option_name + self.possibilities = possibilities + + def format_message(self) -> str: + if not self.possibilities: + return self.message + + possibility_str = ", ".join(sorted(self.possibilities)) + suggest = ngettext( + "Did you mean {possibility}?", + "(Possible options: {possibilities})", + len(self.possibilities), + ).format(possibility=possibility_str, possibilities=possibility_str) + return f"{self.message} {suggest}" + + +class BadOptionUsage(UsageError): + """Raised if an option is generally supplied but the use of the option + was incorrect. This is for instance raised if the number of arguments + for an option is not correct. + + .. versionadded:: 4.0 + + :param option_name: the name of the option being used incorrectly. + """ + + def __init__( + self, option_name: str, message: str, ctx: t.Optional["Context"] = None + ) -> None: + super().__init__(message, ctx) + self.option_name = option_name + + +class BadArgumentUsage(UsageError): + """Raised if an argument is generally supplied but the use of the argument + was incorrect. This is for instance raised if the number of values + for an argument is not correct. + + .. versionadded:: 6.0 + """ + + +class FileError(ClickException): + """Raised if a file cannot be opened.""" + + def __init__(self, filename: str, hint: t.Optional[str] = None) -> None: + if hint is None: + hint = _("unknown error") + + super().__init__(hint) + self.ui_filename = os.fsdecode(filename) + self.filename = filename + + def format_message(self) -> str: + return _("Could not open file {filename!r}: {message}").format( + filename=self.ui_filename, message=self.message + ) + + +class Abort(RuntimeError): + """An internal signalling exception that signals Click to abort.""" + + +class Exit(RuntimeError): + """An exception that indicates that the application should exit with some + status code. + + :param code: the status code to exit with. + """ + + __slots__ = ("exit_code",) + + def __init__(self, code: int = 0) -> None: + self.exit_code = code diff --git a/venv/Lib/site-packages/click/formatting.py b/venv/Lib/site-packages/click/formatting.py new file mode 100644 index 0000000..ddd2a2f --- /dev/null +++ b/venv/Lib/site-packages/click/formatting.py @@ -0,0 +1,301 @@ +import typing as t +from contextlib import contextmanager +from gettext import gettext as _ + +from ._compat import term_len +from .parser import split_opt + +# Can force a width. This is used by the test system +FORCED_WIDTH: t.Optional[int] = None + + +def measure_table(rows: t.Iterable[t.Tuple[str, str]]) -> t.Tuple[int, ...]: + widths: t.Dict[int, int] = {} + + for row in rows: + for idx, col in enumerate(row): + widths[idx] = max(widths.get(idx, 0), term_len(col)) + + return tuple(y for x, y in sorted(widths.items())) + + +def iter_rows( + rows: t.Iterable[t.Tuple[str, str]], col_count: int +) -> t.Iterator[t.Tuple[str, ...]]: + for row in rows: + yield row + ("",) * (col_count - len(row)) + + +def wrap_text( + text: str, + width: int = 78, + initial_indent: str = "", + subsequent_indent: str = "", + preserve_paragraphs: bool = False, +) -> str: + """A helper function that intelligently wraps text. By default, it + assumes that it operates on a single paragraph of text but if the + `preserve_paragraphs` parameter is provided it will intelligently + handle paragraphs (defined by two empty lines). + + If paragraphs are handled, a paragraph can be prefixed with an empty + line containing the ``\\b`` character (``\\x08``) to indicate that + no rewrapping should happen in that block. + + :param text: the text that should be rewrapped. + :param width: the maximum width for the text. + :param initial_indent: the initial indent that should be placed on the + first line as a string. + :param subsequent_indent: the indent string that should be placed on + each consecutive line. + :param preserve_paragraphs: if this flag is set then the wrapping will + intelligently handle paragraphs. + """ + from ._textwrap import TextWrapper + + text = text.expandtabs() + wrapper = TextWrapper( + width, + initial_indent=initial_indent, + subsequent_indent=subsequent_indent, + replace_whitespace=False, + ) + if not preserve_paragraphs: + return wrapper.fill(text) + + p: t.List[t.Tuple[int, bool, str]] = [] + buf: t.List[str] = [] + indent = None + + def _flush_par() -> None: + if not buf: + return + if buf[0].strip() == "\b": + p.append((indent or 0, True, "\n".join(buf[1:]))) + else: + p.append((indent or 0, False, " ".join(buf))) + del buf[:] + + for line in text.splitlines(): + if not line: + _flush_par() + indent = None + else: + if indent is None: + orig_len = term_len(line) + line = line.lstrip() + indent = orig_len - term_len(line) + buf.append(line) + _flush_par() + + rv = [] + for indent, raw, text in p: + with wrapper.extra_indent(" " * indent): + if raw: + rv.append(wrapper.indent_only(text)) + else: + rv.append(wrapper.fill(text)) + + return "\n\n".join(rv) + + +class HelpFormatter: + """This class helps with formatting text-based help pages. It's + usually just needed for very special internal cases, but it's also + exposed so that developers can write their own fancy outputs. + + At present, it always writes into memory. + + :param indent_increment: the additional increment for each level. + :param width: the width for the text. This defaults to the terminal + width clamped to a maximum of 78. + """ + + def __init__( + self, + indent_increment: int = 2, + width: t.Optional[int] = None, + max_width: t.Optional[int] = None, + ) -> None: + import shutil + + self.indent_increment = indent_increment + if max_width is None: + max_width = 80 + if width is None: + width = FORCED_WIDTH + if width is None: + width = max(min(shutil.get_terminal_size().columns, max_width) - 2, 50) + self.width = width + self.current_indent = 0 + self.buffer: t.List[str] = [] + + def write(self, string: str) -> None: + """Writes a unicode string into the internal buffer.""" + self.buffer.append(string) + + def indent(self) -> None: + """Increases the indentation.""" + self.current_indent += self.indent_increment + + def dedent(self) -> None: + """Decreases the indentation.""" + self.current_indent -= self.indent_increment + + def write_usage( + self, prog: str, args: str = "", prefix: t.Optional[str] = None + ) -> None: + """Writes a usage line into the buffer. + + :param prog: the program name. + :param args: whitespace separated list of arguments. + :param prefix: The prefix for the first line. Defaults to + ``"Usage: "``. + """ + if prefix is None: + prefix = f"{_('Usage:')} " + + usage_prefix = f"{prefix:>{self.current_indent}}{prog} " + text_width = self.width - self.current_indent + + if text_width >= (term_len(usage_prefix) + 20): + # The arguments will fit to the right of the prefix. + indent = " " * term_len(usage_prefix) + self.write( + wrap_text( + args, + text_width, + initial_indent=usage_prefix, + subsequent_indent=indent, + ) + ) + else: + # The prefix is too long, put the arguments on the next line. + self.write(usage_prefix) + self.write("\n") + indent = " " * (max(self.current_indent, term_len(prefix)) + 4) + self.write( + wrap_text( + args, text_width, initial_indent=indent, subsequent_indent=indent + ) + ) + + self.write("\n") + + def write_heading(self, heading: str) -> None: + """Writes a heading into the buffer.""" + self.write(f"{'':>{self.current_indent}}{heading}:\n") + + def write_paragraph(self) -> None: + """Writes a paragraph into the buffer.""" + if self.buffer: + self.write("\n") + + def write_text(self, text: str) -> None: + """Writes re-indented text into the buffer. This rewraps and + preserves paragraphs. + """ + indent = " " * self.current_indent + self.write( + wrap_text( + text, + self.width, + initial_indent=indent, + subsequent_indent=indent, + preserve_paragraphs=True, + ) + ) + self.write("\n") + + def write_dl( + self, + rows: t.Sequence[t.Tuple[str, str]], + col_max: int = 30, + col_spacing: int = 2, + ) -> None: + """Writes a definition list into the buffer. This is how options + and commands are usually formatted. + + :param rows: a list of two item tuples for the terms and values. + :param col_max: the maximum width of the first column. + :param col_spacing: the number of spaces between the first and + second column. + """ + rows = list(rows) + widths = measure_table(rows) + if len(widths) != 2: + raise TypeError("Expected two columns for definition list") + + first_col = min(widths[0], col_max) + col_spacing + + for first, second in iter_rows(rows, len(widths)): + self.write(f"{'':>{self.current_indent}}{first}") + if not second: + self.write("\n") + continue + if term_len(first) <= first_col - col_spacing: + self.write(" " * (first_col - term_len(first))) + else: + self.write("\n") + self.write(" " * (first_col + self.current_indent)) + + text_width = max(self.width - first_col - 2, 10) + wrapped_text = wrap_text(second, text_width, preserve_paragraphs=True) + lines = wrapped_text.splitlines() + + if lines: + self.write(f"{lines[0]}\n") + + for line in lines[1:]: + self.write(f"{'':>{first_col + self.current_indent}}{line}\n") + else: + self.write("\n") + + @contextmanager + def section(self, name: str) -> t.Iterator[None]: + """Helpful context manager that writes a paragraph, a heading, + and the indents. + + :param name: the section name that is written as heading. + """ + self.write_paragraph() + self.write_heading(name) + self.indent() + try: + yield + finally: + self.dedent() + + @contextmanager + def indentation(self) -> t.Iterator[None]: + """A context manager that increases the indentation.""" + self.indent() + try: + yield + finally: + self.dedent() + + def getvalue(self) -> str: + """Returns the buffer contents.""" + return "".join(self.buffer) + + +def join_options(options: t.Sequence[str]) -> t.Tuple[str, bool]: + """Given a list of option strings this joins them in the most appropriate + way and returns them in the form ``(formatted_string, + any_prefix_is_slash)`` where the second item in the tuple is a flag that + indicates if any of the option prefixes was a slash. + """ + rv = [] + any_prefix_is_slash = False + + for opt in options: + prefix = split_opt(opt)[0] + + if prefix == "/": + any_prefix_is_slash = True + + rv.append((len(prefix), opt)) + + rv.sort(key=lambda x: x[0]) + return ", ".join(x[1] for x in rv), any_prefix_is_slash diff --git a/venv/Lib/site-packages/click/globals.py b/venv/Lib/site-packages/click/globals.py new file mode 100644 index 0000000..cfcade1 --- /dev/null +++ b/venv/Lib/site-packages/click/globals.py @@ -0,0 +1,69 @@ +import typing +import typing as t +from threading import local + +if t.TYPE_CHECKING: + import typing_extensions as te + from .core import Context + +_local = local() + + +@typing.overload +def get_current_context(silent: "te.Literal[False]" = False) -> "Context": + ... + + +@typing.overload +def get_current_context(silent: bool = ...) -> t.Optional["Context"]: + ... + + +def get_current_context(silent: bool = False) -> t.Optional["Context"]: + """Returns the current click context. This can be used as a way to + access the current context object from anywhere. This is a more implicit + alternative to the :func:`pass_context` decorator. This function is + primarily useful for helpers such as :func:`echo` which might be + interested in changing its behavior based on the current context. + + To push the current context, :meth:`Context.scope` can be used. + + .. versionadded:: 5.0 + + :param silent: if set to `True` the return value is `None` if no context + is available. The default behavior is to raise a + :exc:`RuntimeError`. + """ + try: + return t.cast("Context", _local.stack[-1]) + except (AttributeError, IndexError): + if not silent: + raise RuntimeError("There is no active click context.") + + return None + + +def push_context(ctx: "Context") -> None: + """Pushes a new context to the current stack.""" + _local.__dict__.setdefault("stack", []).append(ctx) + + +def pop_context() -> None: + """Removes the top level from the stack.""" + _local.stack.pop() + + +def resolve_color_default(color: t.Optional[bool] = None) -> t.Optional[bool]: + """Internal helper to get the default value of the color flag. If a + value is passed it's returned unchanged, otherwise it's looked up from + the current context. + """ + if color is not None: + return color + + ctx = get_current_context(silent=True) + + if ctx is not None: + return ctx.color + + return None diff --git a/venv/Lib/site-packages/click/parser.py b/venv/Lib/site-packages/click/parser.py new file mode 100644 index 0000000..7d995f7 --- /dev/null +++ b/venv/Lib/site-packages/click/parser.py @@ -0,0 +1,529 @@ +""" +This module started out as largely a copy paste from the stdlib's +optparse module with the features removed that we do not need from +optparse because we implement them in Click on a higher level (for +instance type handling, help formatting and a lot more). + +The plan is to remove more and more from here over time. + +The reason this is a different module and not optparse from the stdlib +is that there are differences in 2.x and 3.x about the error messages +generated and optparse in the stdlib uses gettext for no good reason +and might cause us issues. + +Click uses parts of optparse written by Gregory P. Ward and maintained +by the Python Software Foundation. This is limited to code in parser.py. + +Copyright 2001-2006 Gregory P. Ward. All rights reserved. +Copyright 2002-2006 Python Software Foundation. All rights reserved. +""" +# This code uses parts of optparse written by Gregory P. Ward and +# maintained by the Python Software Foundation. +# Copyright 2001-2006 Gregory P. Ward +# Copyright 2002-2006 Python Software Foundation +import typing as t +from collections import deque +from gettext import gettext as _ +from gettext import ngettext + +from .exceptions import BadArgumentUsage +from .exceptions import BadOptionUsage +from .exceptions import NoSuchOption +from .exceptions import UsageError + +if t.TYPE_CHECKING: + import typing_extensions as te + from .core import Argument as CoreArgument + from .core import Context + from .core import Option as CoreOption + from .core import Parameter as CoreParameter + +V = t.TypeVar("V") + +# Sentinel value that indicates an option was passed as a flag without a +# value but is not a flag option. Option.consume_value uses this to +# prompt or use the flag_value. +_flag_needs_value = object() + + +def _unpack_args( + args: t.Sequence[str], nargs_spec: t.Sequence[int] +) -> t.Tuple[t.Sequence[t.Union[str, t.Sequence[t.Optional[str]], None]], t.List[str]]: + """Given an iterable of arguments and an iterable of nargs specifications, + it returns a tuple with all the unpacked arguments at the first index + and all remaining arguments as the second. + + The nargs specification is the number of arguments that should be consumed + or `-1` to indicate that this position should eat up all the remainders. + + Missing items are filled with `None`. + """ + args = deque(args) + nargs_spec = deque(nargs_spec) + rv: t.List[t.Union[str, t.Tuple[t.Optional[str], ...], None]] = [] + spos: t.Optional[int] = None + + def _fetch(c: "te.Deque[V]") -> t.Optional[V]: + try: + if spos is None: + return c.popleft() + else: + return c.pop() + except IndexError: + return None + + while nargs_spec: + nargs = _fetch(nargs_spec) + + if nargs is None: + continue + + if nargs == 1: + rv.append(_fetch(args)) + elif nargs > 1: + x = [_fetch(args) for _ in range(nargs)] + + # If we're reversed, we're pulling in the arguments in reverse, + # so we need to turn them around. + if spos is not None: + x.reverse() + + rv.append(tuple(x)) + elif nargs < 0: + if spos is not None: + raise TypeError("Cannot have two nargs < 0") + + spos = len(rv) + rv.append(None) + + # spos is the position of the wildcard (star). If it's not `None`, + # we fill it with the remainder. + if spos is not None: + rv[spos] = tuple(args) + args = [] + rv[spos + 1 :] = reversed(rv[spos + 1 :]) + + return tuple(rv), list(args) + + +def split_opt(opt: str) -> t.Tuple[str, str]: + first = opt[:1] + if first.isalnum(): + return "", opt + if opt[1:2] == first: + return opt[:2], opt[2:] + return first, opt[1:] + + +def normalize_opt(opt: str, ctx: t.Optional["Context"]) -> str: + if ctx is None or ctx.token_normalize_func is None: + return opt + prefix, opt = split_opt(opt) + return f"{prefix}{ctx.token_normalize_func(opt)}" + + +def split_arg_string(string: str) -> t.List[str]: + """Split an argument string as with :func:`shlex.split`, but don't + fail if the string is incomplete. Ignores a missing closing quote or + incomplete escape sequence and uses the partial token as-is. + + .. code-block:: python + + split_arg_string("example 'my file") + ["example", "my file"] + + split_arg_string("example my\\") + ["example", "my"] + + :param string: String to split. + """ + import shlex + + lex = shlex.shlex(string, posix=True) + lex.whitespace_split = True + lex.commenters = "" + out = [] + + try: + for token in lex: + out.append(token) + except ValueError: + # Raised when end-of-string is reached in an invalid state. Use + # the partial token as-is. The quote or escape character is in + # lex.state, not lex.token. + out.append(lex.token) + + return out + + +class Option: + def __init__( + self, + obj: "CoreOption", + opts: t.Sequence[str], + dest: t.Optional[str], + action: t.Optional[str] = None, + nargs: int = 1, + const: t.Optional[t.Any] = None, + ): + self._short_opts = [] + self._long_opts = [] + self.prefixes = set() + + for opt in opts: + prefix, value = split_opt(opt) + if not prefix: + raise ValueError(f"Invalid start character for option ({opt})") + self.prefixes.add(prefix[0]) + if len(prefix) == 1 and len(value) == 1: + self._short_opts.append(opt) + else: + self._long_opts.append(opt) + self.prefixes.add(prefix) + + if action is None: + action = "store" + + self.dest = dest + self.action = action + self.nargs = nargs + self.const = const + self.obj = obj + + @property + def takes_value(self) -> bool: + return self.action in ("store", "append") + + def process(self, value: str, state: "ParsingState") -> None: + if self.action == "store": + state.opts[self.dest] = value # type: ignore + elif self.action == "store_const": + state.opts[self.dest] = self.const # type: ignore + elif self.action == "append": + state.opts.setdefault(self.dest, []).append(value) # type: ignore + elif self.action == "append_const": + state.opts.setdefault(self.dest, []).append(self.const) # type: ignore + elif self.action == "count": + state.opts[self.dest] = state.opts.get(self.dest, 0) + 1 # type: ignore + else: + raise ValueError(f"unknown action '{self.action}'") + state.order.append(self.obj) + + +class Argument: + def __init__(self, obj: "CoreArgument", dest: t.Optional[str], nargs: int = 1): + self.dest = dest + self.nargs = nargs + self.obj = obj + + def process( + self, + value: t.Union[t.Optional[str], t.Sequence[t.Optional[str]]], + state: "ParsingState", + ) -> None: + if self.nargs > 1: + assert value is not None + holes = sum(1 for x in value if x is None) + if holes == len(value): + value = None + elif holes != 0: + raise BadArgumentUsage( + _("Argument {name!r} takes {nargs} values.").format( + name=self.dest, nargs=self.nargs + ) + ) + + if self.nargs == -1 and self.obj.envvar is not None and value == (): + # Replace empty tuple with None so that a value from the + # environment may be tried. + value = None + + state.opts[self.dest] = value # type: ignore + state.order.append(self.obj) + + +class ParsingState: + def __init__(self, rargs: t.List[str]) -> None: + self.opts: t.Dict[str, t.Any] = {} + self.largs: t.List[str] = [] + self.rargs = rargs + self.order: t.List["CoreParameter"] = [] + + +class OptionParser: + """The option parser is an internal class that is ultimately used to + parse options and arguments. It's modelled after optparse and brings + a similar but vastly simplified API. It should generally not be used + directly as the high level Click classes wrap it for you. + + It's not nearly as extensible as optparse or argparse as it does not + implement features that are implemented on a higher level (such as + types or defaults). + + :param ctx: optionally the :class:`~click.Context` where this parser + should go with. + """ + + def __init__(self, ctx: t.Optional["Context"] = None) -> None: + #: The :class:`~click.Context` for this parser. This might be + #: `None` for some advanced use cases. + self.ctx = ctx + #: This controls how the parser deals with interspersed arguments. + #: If this is set to `False`, the parser will stop on the first + #: non-option. Click uses this to implement nested subcommands + #: safely. + self.allow_interspersed_args = True + #: This tells the parser how to deal with unknown options. By + #: default it will error out (which is sensible), but there is a + #: second mode where it will ignore it and continue processing + #: after shifting all the unknown options into the resulting args. + self.ignore_unknown_options = False + + if ctx is not None: + self.allow_interspersed_args = ctx.allow_interspersed_args + self.ignore_unknown_options = ctx.ignore_unknown_options + + self._short_opt: t.Dict[str, Option] = {} + self._long_opt: t.Dict[str, Option] = {} + self._opt_prefixes = {"-", "--"} + self._args: t.List[Argument] = [] + + def add_option( + self, + obj: "CoreOption", + opts: t.Sequence[str], + dest: t.Optional[str], + action: t.Optional[str] = None, + nargs: int = 1, + const: t.Optional[t.Any] = None, + ) -> None: + """Adds a new option named `dest` to the parser. The destination + is not inferred (unlike with optparse) and needs to be explicitly + provided. Action can be any of ``store``, ``store_const``, + ``append``, ``appnd_const`` or ``count``. + + The `obj` can be used to identify the option in the order list + that is returned from the parser. + """ + opts = [normalize_opt(opt, self.ctx) for opt in opts] + option = Option(obj, opts, dest, action=action, nargs=nargs, const=const) + self._opt_prefixes.update(option.prefixes) + for opt in option._short_opts: + self._short_opt[opt] = option + for opt in option._long_opts: + self._long_opt[opt] = option + + def add_argument( + self, obj: "CoreArgument", dest: t.Optional[str], nargs: int = 1 + ) -> None: + """Adds a positional argument named `dest` to the parser. + + The `obj` can be used to identify the option in the order list + that is returned from the parser. + """ + self._args.append(Argument(obj, dest=dest, nargs=nargs)) + + def parse_args( + self, args: t.List[str] + ) -> t.Tuple[t.Dict[str, t.Any], t.List[str], t.List["CoreParameter"]]: + """Parses positional arguments and returns ``(values, args, order)`` + for the parsed options and arguments as well as the leftover + arguments if there are any. The order is a list of objects as they + appear on the command line. If arguments appear multiple times they + will be memorized multiple times as well. + """ + state = ParsingState(args) + try: + self._process_args_for_options(state) + self._process_args_for_args(state) + except UsageError: + if self.ctx is None or not self.ctx.resilient_parsing: + raise + return state.opts, state.largs, state.order + + def _process_args_for_args(self, state: ParsingState) -> None: + pargs, args = _unpack_args( + state.largs + state.rargs, [x.nargs for x in self._args] + ) + + for idx, arg in enumerate(self._args): + arg.process(pargs[idx], state) + + state.largs = args + state.rargs = [] + + def _process_args_for_options(self, state: ParsingState) -> None: + while state.rargs: + arg = state.rargs.pop(0) + arglen = len(arg) + # Double dashes always handled explicitly regardless of what + # prefixes are valid. + if arg == "--": + return + elif arg[:1] in self._opt_prefixes and arglen > 1: + self._process_opts(arg, state) + elif self.allow_interspersed_args: + state.largs.append(arg) + else: + state.rargs.insert(0, arg) + return + + # Say this is the original argument list: + # [arg0, arg1, ..., arg(i-1), arg(i), arg(i+1), ..., arg(N-1)] + # ^ + # (we are about to process arg(i)). + # + # Then rargs is [arg(i), ..., arg(N-1)] and largs is a *subset* of + # [arg0, ..., arg(i-1)] (any options and their arguments will have + # been removed from largs). + # + # The while loop will usually consume 1 or more arguments per pass. + # If it consumes 1 (eg. arg is an option that takes no arguments), + # then after _process_arg() is done the situation is: + # + # largs = subset of [arg0, ..., arg(i)] + # rargs = [arg(i+1), ..., arg(N-1)] + # + # If allow_interspersed_args is false, largs will always be + # *empty* -- still a subset of [arg0, ..., arg(i-1)], but + # not a very interesting subset! + + def _match_long_opt( + self, opt: str, explicit_value: t.Optional[str], state: ParsingState + ) -> None: + if opt not in self._long_opt: + from difflib import get_close_matches + + possibilities = get_close_matches(opt, self._long_opt) + raise NoSuchOption(opt, possibilities=possibilities, ctx=self.ctx) + + option = self._long_opt[opt] + if option.takes_value: + # At this point it's safe to modify rargs by injecting the + # explicit value, because no exception is raised in this + # branch. This means that the inserted value will be fully + # consumed. + if explicit_value is not None: + state.rargs.insert(0, explicit_value) + + value = self._get_value_from_state(opt, option, state) + + elif explicit_value is not None: + raise BadOptionUsage( + opt, _("Option {name!r} does not take a value.").format(name=opt) + ) + + else: + value = None + + option.process(value, state) + + def _match_short_opt(self, arg: str, state: ParsingState) -> None: + stop = False + i = 1 + prefix = arg[0] + unknown_options = [] + + for ch in arg[1:]: + opt = normalize_opt(f"{prefix}{ch}", self.ctx) + option = self._short_opt.get(opt) + i += 1 + + if not option: + if self.ignore_unknown_options: + unknown_options.append(ch) + continue + raise NoSuchOption(opt, ctx=self.ctx) + if option.takes_value: + # Any characters left in arg? Pretend they're the + # next arg, and stop consuming characters of arg. + if i < len(arg): + state.rargs.insert(0, arg[i:]) + stop = True + + value = self._get_value_from_state(opt, option, state) + + else: + value = None + + option.process(value, state) + + if stop: + break + + # If we got any unknown options we re-combinate the string of the + # remaining options and re-attach the prefix, then report that + # to the state as new larg. This way there is basic combinatorics + # that can be achieved while still ignoring unknown arguments. + if self.ignore_unknown_options and unknown_options: + state.largs.append(f"{prefix}{''.join(unknown_options)}") + + def _get_value_from_state( + self, option_name: str, option: Option, state: ParsingState + ) -> t.Any: + nargs = option.nargs + + if len(state.rargs) < nargs: + if option.obj._flag_needs_value: + # Option allows omitting the value. + value = _flag_needs_value + else: + raise BadOptionUsage( + option_name, + ngettext( + "Option {name!r} requires an argument.", + "Option {name!r} requires {nargs} arguments.", + nargs, + ).format(name=option_name, nargs=nargs), + ) + elif nargs == 1: + next_rarg = state.rargs[0] + + if ( + option.obj._flag_needs_value + and isinstance(next_rarg, str) + and next_rarg[:1] in self._opt_prefixes + and len(next_rarg) > 1 + ): + # The next arg looks like the start of an option, don't + # use it as the value if omitting the value is allowed. + value = _flag_needs_value + else: + value = state.rargs.pop(0) + else: + value = tuple(state.rargs[:nargs]) + del state.rargs[:nargs] + + return value + + def _process_opts(self, arg: str, state: ParsingState) -> None: + explicit_value = None + # Long option handling happens in two parts. The first part is + # supporting explicitly attached values. In any case, we will try + # to long match the option first. + if "=" in arg: + long_opt, explicit_value = arg.split("=", 1) + else: + long_opt = arg + norm_long_opt = normalize_opt(long_opt, self.ctx) + + # At this point we will match the (assumed) long option through + # the long option matching code. Note that this allows options + # like "-foo" to be matched as long options. + try: + self._match_long_opt(norm_long_opt, explicit_value, state) + except NoSuchOption: + # At this point the long option matching failed, and we need + # to try with short options. However there is a special rule + # which says, that if we have a two character options prefix + # (applies to "--foo" for instance), we do not dispatch to the + # short option code and will instead raise the no option + # error. + if arg[:2] not in self._opt_prefixes: + self._match_short_opt(arg, state) + return + + if not self.ignore_unknown_options: + raise + + state.largs.append(arg) diff --git a/venv/Lib/site-packages/click/py.typed b/venv/Lib/site-packages/click/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/venv/Lib/site-packages/click/shell_completion.py b/venv/Lib/site-packages/click/shell_completion.py new file mode 100644 index 0000000..706fb69 --- /dev/null +++ b/venv/Lib/site-packages/click/shell_completion.py @@ -0,0 +1,574 @@ +import os +import re +import typing as t +from gettext import gettext as _ + +from .core import Argument +from .core import BaseCommand +from .core import Context +from .core import MultiCommand +from .core import Option +from .core import Parameter +from .core import ParameterSource +from .parser import split_arg_string +from .utils import echo + + +def shell_complete( + cli: BaseCommand, + ctx_args: t.Dict[str, t.Any], + prog_name: str, + complete_var: str, + instruction: str, +) -> int: + """Perform shell completion for the given CLI program. + + :param cli: Command being called. + :param ctx_args: Extra arguments to pass to + ``cli.make_context``. + :param prog_name: Name of the executable in the shell. + :param complete_var: Name of the environment variable that holds + the completion instruction. + :param instruction: Value of ``complete_var`` with the completion + instruction and shell, in the form ``instruction_shell``. + :return: Status code to exit with. + """ + shell, _, instruction = instruction.partition("_") + comp_cls = get_completion_class(shell) + + if comp_cls is None: + return 1 + + comp = comp_cls(cli, ctx_args, prog_name, complete_var) + + if instruction == "source": + echo(comp.source()) + return 0 + + if instruction == "complete": + echo(comp.complete()) + return 0 + + return 1 + + +class CompletionItem: + """Represents a completion value and metadata about the value. The + default metadata is ``type`` to indicate special shell handling, + and ``help`` if a shell supports showing a help string next to the + value. + + Arbitrary parameters can be passed when creating the object, and + accessed using ``item.attr``. If an attribute wasn't passed, + accessing it returns ``None``. + + :param value: The completion suggestion. + :param type: Tells the shell script to provide special completion + support for the type. Click uses ``"dir"`` and ``"file"``. + :param help: String shown next to the value if supported. + :param kwargs: Arbitrary metadata. The built-in implementations + don't use this, but custom type completions paired with custom + shell support could use it. + """ + + __slots__ = ("value", "type", "help", "_info") + + def __init__( + self, + value: t.Any, + type: str = "plain", + help: t.Optional[str] = None, + **kwargs: t.Any, + ) -> None: + self.value = value + self.type = type + self.help = help + self._info = kwargs + + def __getattr__(self, name: str) -> t.Any: + return self._info.get(name) + + +# Only Bash >= 4.4 has the nosort option. +_SOURCE_BASH = """\ +%(complete_func)s() { + local IFS=$'\\n' + local response + + response=$(env COMP_WORDS="${COMP_WORDS[*]}" COMP_CWORD=$COMP_CWORD \ +%(complete_var)s=bash_complete $1) + + for completion in $response; do + IFS=',' read type value <<< "$completion" + + if [[ $type == 'dir' ]]; then + COMREPLY=() + compopt -o dirnames + elif [[ $type == 'file' ]]; then + COMREPLY=() + compopt -o default + elif [[ $type == 'plain' ]]; then + COMPREPLY+=($value) + fi + done + + return 0 +} + +%(complete_func)s_setup() { + complete -o nosort -F %(complete_func)s %(prog_name)s +} + +%(complete_func)s_setup; +""" + +_SOURCE_ZSH = """\ +#compdef %(prog_name)s + +%(complete_func)s() { + local -a completions + local -a completions_with_descriptions + local -a response + (( ! $+commands[%(prog_name)s] )) && return 1 + + response=("${(@f)$(env COMP_WORDS="${words[*]}" COMP_CWORD=$((CURRENT-1)) \ +%(complete_var)s=zsh_complete %(prog_name)s)}") + + for type key descr in ${response}; do + if [[ "$type" == "plain" ]]; then + if [[ "$descr" == "_" ]]; then + completions+=("$key") + else + completions_with_descriptions+=("$key":"$descr") + fi + elif [[ "$type" == "dir" ]]; then + _path_files -/ + elif [[ "$type" == "file" ]]; then + _path_files -f + fi + done + + if [ -n "$completions_with_descriptions" ]; then + _describe -V unsorted completions_with_descriptions -U + fi + + if [ -n "$completions" ]; then + compadd -U -V unsorted -a completions + fi +} + +compdef %(complete_func)s %(prog_name)s; +""" + +_SOURCE_FISH = """\ +function %(complete_func)s; + set -l response; + + for value in (env %(complete_var)s=fish_complete COMP_WORDS=(commandline -cp) \ +COMP_CWORD=(commandline -t) %(prog_name)s); + set response $response $value; + end; + + for completion in $response; + set -l metadata (string split "," $completion); + + if test $metadata[1] = "dir"; + __fish_complete_directories $metadata[2]; + else if test $metadata[1] = "file"; + __fish_complete_path $metadata[2]; + else if test $metadata[1] = "plain"; + echo $metadata[2]; + end; + end; +end; + +complete --no-files --command %(prog_name)s --arguments \ +"(%(complete_func)s)"; +""" + + +class ShellComplete: + """Base class for providing shell completion support. A subclass for + a given shell will override attributes and methods to implement the + completion instructions (``source`` and ``complete``). + + :param cli: Command being called. + :param prog_name: Name of the executable in the shell. + :param complete_var: Name of the environment variable that holds + the completion instruction. + + .. versionadded:: 8.0 + """ + + name: t.ClassVar[str] + """Name to register the shell as with :func:`add_completion_class`. + This is used in completion instructions (``{name}_source`` and + ``{name}_complete``). + """ + + source_template: t.ClassVar[str] + """Completion script template formatted by :meth:`source`. This must + be provided by subclasses. + """ + + def __init__( + self, + cli: BaseCommand, + ctx_args: t.Dict[str, t.Any], + prog_name: str, + complete_var: str, + ) -> None: + self.cli = cli + self.ctx_args = ctx_args + self.prog_name = prog_name + self.complete_var = complete_var + + @property + def func_name(self) -> str: + """The name of the shell function defined by the completion + script. + """ + safe_name = re.sub(r"\W*", "", self.prog_name.replace("-", "_"), re.ASCII) + return f"_{safe_name}_completion" + + def source_vars(self) -> t.Dict[str, t.Any]: + """Vars for formatting :attr:`source_template`. + + By default this provides ``complete_func``, ``complete_var``, + and ``prog_name``. + """ + return { + "complete_func": self.func_name, + "complete_var": self.complete_var, + "prog_name": self.prog_name, + } + + def source(self) -> str: + """Produce the shell script that defines the completion + function. By default this ``%``-style formats + :attr:`source_template` with the dict returned by + :meth:`source_vars`. + """ + return self.source_template % self.source_vars() + + def get_completion_args(self) -> t.Tuple[t.List[str], str]: + """Use the env vars defined by the shell script to return a + tuple of ``args, incomplete``. This must be implemented by + subclasses. + """ + raise NotImplementedError + + def get_completions( + self, args: t.List[str], incomplete: str + ) -> t.List[CompletionItem]: + """Determine the context and last complete command or parameter + from the complete args. Call that object's ``shell_complete`` + method to get the completions for the incomplete value. + + :param args: List of complete args before the incomplete value. + :param incomplete: Value being completed. May be empty. + """ + ctx = _resolve_context(self.cli, self.ctx_args, self.prog_name, args) + obj, incomplete = _resolve_incomplete(ctx, args, incomplete) + return obj.shell_complete(ctx, incomplete) + + def format_completion(self, item: CompletionItem) -> str: + """Format a completion item into the form recognized by the + shell script. This must be implemented by subclasses. + + :param item: Completion item to format. + """ + raise NotImplementedError + + def complete(self) -> str: + """Produce the completion data to send back to the shell. + + By default this calls :meth:`get_completion_args`, gets the + completions, then calls :meth:`format_completion` for each + completion. + """ + args, incomplete = self.get_completion_args() + completions = self.get_completions(args, incomplete) + out = [self.format_completion(item) for item in completions] + return "\n".join(out) + + +class BashComplete(ShellComplete): + """Shell completion for Bash.""" + + name = "bash" + source_template = _SOURCE_BASH + + def _check_version(self) -> None: + import subprocess + + output = subprocess.run(["bash", "--version"], stdout=subprocess.PIPE) + match = re.search(r"version (\d)\.(\d)\.\d", output.stdout.decode()) + + if match is not None: + major, minor = match.groups() + + if major < "4" or major == "4" and minor < "4": + raise RuntimeError( + _( + "Shell completion is not supported for Bash" + " versions older than 4.4." + ) + ) + else: + raise RuntimeError( + _("Couldn't detect Bash version, shell completion is not supported.") + ) + + def source(self) -> str: + self._check_version() + return super().source() + + def get_completion_args(self) -> t.Tuple[t.List[str], str]: + cwords = split_arg_string(os.environ["COMP_WORDS"]) + cword = int(os.environ["COMP_CWORD"]) + args = cwords[1:cword] + + try: + incomplete = cwords[cword] + except IndexError: + incomplete = "" + + return args, incomplete + + def format_completion(self, item: CompletionItem) -> str: + return f"{item.type},{item.value}" + + +class ZshComplete(ShellComplete): + """Shell completion for Zsh.""" + + name = "zsh" + source_template = _SOURCE_ZSH + + def get_completion_args(self) -> t.Tuple[t.List[str], str]: + cwords = split_arg_string(os.environ["COMP_WORDS"]) + cword = int(os.environ["COMP_CWORD"]) + args = cwords[1:cword] + + try: + incomplete = cwords[cword] + except IndexError: + incomplete = "" + + return args, incomplete + + def format_completion(self, item: CompletionItem) -> str: + return f"{item.type}\n{item.value}\n{item.help if item.help else '_'}" + + +class FishComplete(ShellComplete): + """Shell completion for Fish.""" + + name = "fish" + source_template = _SOURCE_FISH + + def get_completion_args(self) -> t.Tuple[t.List[str], str]: + cwords = split_arg_string(os.environ["COMP_WORDS"]) + incomplete = os.environ["COMP_CWORD"] + args = cwords[1:] + + # Fish stores the partial word in both COMP_WORDS and + # COMP_CWORD, remove it from complete args. + if incomplete and args and args[-1] == incomplete: + args.pop() + + return args, incomplete + + def format_completion(self, item: CompletionItem) -> str: + if item.help: + return f"{item.type},{item.value}\t{item.help}" + + return f"{item.type},{item.value}" + + +_available_shells: t.Dict[str, t.Type[ShellComplete]] = { + "bash": BashComplete, + "fish": FishComplete, + "zsh": ZshComplete, +} + + +def add_completion_class( + cls: t.Type[ShellComplete], name: t.Optional[str] = None +) -> None: + """Register a :class:`ShellComplete` subclass under the given name. + The name will be provided by the completion instruction environment + variable during completion. + + :param cls: The completion class that will handle completion for the + shell. + :param name: Name to register the class under. Defaults to the + class's ``name`` attribute. + """ + if name is None: + name = cls.name + + _available_shells[name] = cls + + +def get_completion_class(shell: str) -> t.Optional[t.Type[ShellComplete]]: + """Look up a registered :class:`ShellComplete` subclass by the name + provided by the completion instruction environment variable. If the + name isn't registered, returns ``None``. + + :param shell: Name the class is registered under. + """ + return _available_shells.get(shell) + + +def _is_incomplete_argument(ctx: Context, param: Parameter) -> bool: + """Determine if the given parameter is an argument that can still + accept values. + + :param ctx: Invocation context for the command represented by the + parsed complete args. + :param param: Argument object being checked. + """ + if not isinstance(param, Argument): + return False + + assert param.name is not None + value = ctx.params[param.name] + return ( + param.nargs == -1 + or ctx.get_parameter_source(param.name) is not ParameterSource.COMMANDLINE + or ( + param.nargs > 1 + and isinstance(value, (tuple, list)) + and len(value) < param.nargs + ) + ) + + +def _start_of_option(value: str) -> bool: + """Check if the value looks like the start of an option.""" + return not value[0].isalnum() if value else False + + +def _is_incomplete_option(args: t.List[str], param: Parameter) -> bool: + """Determine if the given parameter is an option that needs a value. + + :param args: List of complete args before the incomplete value. + :param param: Option object being checked. + """ + if not isinstance(param, Option): + return False + + if param.is_flag: + return False + + last_option = None + + for index, arg in enumerate(reversed(args)): + if index + 1 > param.nargs: + break + + if _start_of_option(arg): + last_option = arg + + return last_option is not None and last_option in param.opts + + +def _resolve_context( + cli: BaseCommand, ctx_args: t.Dict[str, t.Any], prog_name: str, args: t.List[str] +) -> Context: + """Produce the context hierarchy starting with the command and + traversing the complete arguments. This only follows the commands, + it doesn't trigger input prompts or callbacks. + + :param cli: Command being called. + :param prog_name: Name of the executable in the shell. + :param args: List of complete args before the incomplete value. + """ + ctx_args["resilient_parsing"] = True + ctx = cli.make_context(prog_name, args.copy(), **ctx_args) + args = ctx.protected_args + ctx.args + + while args: + command = ctx.command + + if isinstance(command, MultiCommand): + if not command.chain: + name, cmd, args = command.resolve_command(ctx, args) + + if cmd is None: + return ctx + + ctx = cmd.make_context(name, args, parent=ctx, resilient_parsing=True) + args = ctx.protected_args + ctx.args + else: + while args: + name, cmd, args = command.resolve_command(ctx, args) + + if cmd is None: + return ctx + + sub_ctx = cmd.make_context( + name, + args, + parent=ctx, + allow_extra_args=True, + allow_interspersed_args=False, + resilient_parsing=True, + ) + args = sub_ctx.args + + ctx = sub_ctx + args = [*sub_ctx.protected_args, *sub_ctx.args] + else: + break + + return ctx + + +def _resolve_incomplete( + ctx: Context, args: t.List[str], incomplete: str +) -> t.Tuple[t.Union[BaseCommand, Parameter], str]: + """Find the Click object that will handle the completion of the + incomplete value. Return the object and the incomplete value. + + :param ctx: Invocation context for the command represented by + the parsed complete args. + :param args: List of complete args before the incomplete value. + :param incomplete: Value being completed. May be empty. + """ + # Different shells treat an "=" between a long option name and + # value differently. Might keep the value joined, return the "=" + # as a separate item, or return the split name and value. Always + # split and discard the "=" to make completion easier. + if incomplete == "=": + incomplete = "" + elif "=" in incomplete and _start_of_option(incomplete): + name, _, incomplete = incomplete.partition("=") + args.append(name) + + # The "--" marker tells Click to stop treating values as options + # even if they start with the option character. If it hasn't been + # given and the incomplete arg looks like an option, the current + # command will provide option name completions. + if "--" not in args and _start_of_option(incomplete): + return ctx.command, incomplete + + params = ctx.command.get_params(ctx) + + # If the last complete arg is an option name with an incomplete + # value, the option will provide value completions. + for param in params: + if _is_incomplete_option(args, param): + return param, incomplete + + # It's not an option name or value. The first argument without a + # parsed value will provide value completions. + for param in params: + if _is_incomplete_argument(ctx, param): + return param, incomplete + + # There were no unparsed arguments, the command may be a group that + # will provide command name completions. + return ctx.command, incomplete diff --git a/venv/Lib/site-packages/click/termui.py b/venv/Lib/site-packages/click/termui.py new file mode 100644 index 0000000..034fe6e --- /dev/null +++ b/venv/Lib/site-packages/click/termui.py @@ -0,0 +1,807 @@ +import inspect +import io +import itertools +import os +import sys +import typing +import typing as t +from gettext import gettext as _ + +from ._compat import isatty +from ._compat import strip_ansi +from ._compat import WIN +from .exceptions import Abort +from .exceptions import UsageError +from .globals import resolve_color_default +from .types import Choice +from .types import convert_type +from .types import ParamType +from .utils import echo +from .utils import LazyFile + +if t.TYPE_CHECKING: + from ._termui_impl import ProgressBar + +V = t.TypeVar("V") + +# The prompt functions to use. The doc tools currently override these +# functions to customize how they work. +visible_prompt_func: t.Callable[[str], str] = input + +_ansi_colors = { + "black": 30, + "red": 31, + "green": 32, + "yellow": 33, + "blue": 34, + "magenta": 35, + "cyan": 36, + "white": 37, + "reset": 39, + "bright_black": 90, + "bright_red": 91, + "bright_green": 92, + "bright_yellow": 93, + "bright_blue": 94, + "bright_magenta": 95, + "bright_cyan": 96, + "bright_white": 97, +} +_ansi_reset_all = "\033[0m" + + +def hidden_prompt_func(prompt: str) -> str: + import getpass + + return getpass.getpass(prompt) + + +def _build_prompt( + text: str, + suffix: str, + show_default: bool = False, + default: t.Optional[t.Any] = None, + show_choices: bool = True, + type: t.Optional[ParamType] = None, +) -> str: + prompt = text + if type is not None and show_choices and isinstance(type, Choice): + prompt += f" ({', '.join(map(str, type.choices))})" + if default is not None and show_default: + prompt = f"{prompt} [{_format_default(default)}]" + return f"{prompt}{suffix}" + + +def _format_default(default: t.Any) -> t.Any: + if isinstance(default, (io.IOBase, LazyFile)) and hasattr(default, "name"): + return default.name # type: ignore + + return default + + +def prompt( + text: str, + default: t.Optional[t.Any] = None, + hide_input: bool = False, + confirmation_prompt: t.Union[bool, str] = False, + type: t.Optional[ParamType] = None, + value_proc: t.Optional[t.Callable[[str], t.Any]] = None, + prompt_suffix: str = ": ", + show_default: bool = True, + err: bool = False, + show_choices: bool = True, +) -> t.Any: + """Prompts a user for input. This is a convenience function that can + be used to prompt a user for input later. + + If the user aborts the input by sending a interrupt signal, this + function will catch it and raise a :exc:`Abort` exception. + + :param text: the text to show for the prompt. + :param default: the default value to use if no input happens. If this + is not given it will prompt until it's aborted. + :param hide_input: if this is set to true then the input value will + be hidden. + :param confirmation_prompt: Prompt a second time to confirm the + value. Can be set to a string instead of ``True`` to customize + the message. + :param type: the type to use to check the value against. + :param value_proc: if this parameter is provided it's a function that + is invoked instead of the type conversion to + convert a value. + :param prompt_suffix: a suffix that should be added to the prompt. + :param show_default: shows or hides the default value in the prompt. + :param err: if set to true the file defaults to ``stderr`` instead of + ``stdout``, the same as with echo. + :param show_choices: Show or hide choices if the passed type is a Choice. + For example if type is a Choice of either day or week, + show_choices is true and text is "Group by" then the + prompt will be "Group by (day, week): ". + + .. versionadded:: 8.0 + ``confirmation_prompt`` can be a custom string. + + .. versionadded:: 7.0 + Added the ``show_choices`` parameter. + + .. versionadded:: 6.0 + Added unicode support for cmd.exe on Windows. + + .. versionadded:: 4.0 + Added the `err` parameter. + + """ + + def prompt_func(text: str) -> str: + f = hidden_prompt_func if hide_input else visible_prompt_func + try: + # Write the prompt separately so that we get nice + # coloring through colorama on Windows + echo(text.rstrip(" "), nl=False, err=err) + # Echo a space to stdout to work around an issue where + # readline causes backspace to clear the whole line. + return f(" ") + except (KeyboardInterrupt, EOFError): + # getpass doesn't print a newline if the user aborts input with ^C. + # Allegedly this behavior is inherited from getpass(3). + # A doc bug has been filed at https://bugs.python.org/issue24711 + if hide_input: + echo(None, err=err) + raise Abort() + + if value_proc is None: + value_proc = convert_type(type, default) + + prompt = _build_prompt( + text, prompt_suffix, show_default, default, show_choices, type + ) + + if confirmation_prompt: + if confirmation_prompt is True: + confirmation_prompt = _("Repeat for confirmation") + + confirmation_prompt = t.cast(str, confirmation_prompt) + confirmation_prompt = _build_prompt(confirmation_prompt, prompt_suffix) + + while True: + while True: + value = prompt_func(prompt) + if value: + break + elif default is not None: + value = default + break + try: + result = value_proc(value) + except UsageError as e: + if hide_input: + echo(_("Error: The value you entered was invalid."), err=err) + else: + echo(_("Error: {e.message}").format(e=e), err=err) # noqa: B306 + continue + if not confirmation_prompt: + return result + while True: + confirmation_prompt = t.cast(str, confirmation_prompt) + value2 = prompt_func(confirmation_prompt) + if value2: + break + if value == value2: + return result + echo(_("Error: The two entered values do not match."), err=err) + + +def confirm( + text: str, + default: t.Optional[bool] = False, + abort: bool = False, + prompt_suffix: str = ": ", + show_default: bool = True, + err: bool = False, +) -> bool: + """Prompts for confirmation (yes/no question). + + If the user aborts the input by sending a interrupt signal this + function will catch it and raise a :exc:`Abort` exception. + + :param text: the question to ask. + :param default: The default value to use when no input is given. If + ``None``, repeat until input is given. + :param abort: if this is set to `True` a negative answer aborts the + exception by raising :exc:`Abort`. + :param prompt_suffix: a suffix that should be added to the prompt. + :param show_default: shows or hides the default value in the prompt. + :param err: if set to true the file defaults to ``stderr`` instead of + ``stdout``, the same as with echo. + + .. versionchanged:: 8.0 + Repeat until input is given if ``default`` is ``None``. + + .. versionadded:: 4.0 + Added the ``err`` parameter. + """ + prompt = _build_prompt( + text, + prompt_suffix, + show_default, + "y/n" if default is None else ("Y/n" if default else "y/N"), + ) + + while True: + try: + # Write the prompt separately so that we get nice + # coloring through colorama on Windows + echo(prompt, nl=False, err=err) + value = visible_prompt_func("").lower().strip() + except (KeyboardInterrupt, EOFError): + raise Abort() + if value in ("y", "yes"): + rv = True + elif value in ("n", "no"): + rv = False + elif default is not None and value == "": + rv = default + else: + echo(_("Error: invalid input"), err=err) + continue + break + if abort and not rv: + raise Abort() + return rv + + +def get_terminal_size() -> os.terminal_size: + """Returns the current size of the terminal as tuple in the form + ``(width, height)`` in columns and rows. + + .. deprecated:: 8.0 + Will be removed in Click 8.1. Use + :func:`shutil.get_terminal_size` instead. + """ + import shutil + import warnings + + warnings.warn( + "'click.get_terminal_size()' is deprecated and will be removed" + " in Click 8.1. Use 'shutil.get_terminal_size()' instead.", + DeprecationWarning, + stacklevel=2, + ) + return shutil.get_terminal_size() + + +def echo_via_pager( + text_or_generator: t.Union[t.Iterable[str], t.Callable[[], t.Iterable[str]], str], + color: t.Optional[bool] = None, +) -> None: + """This function takes a text and shows it via an environment specific + pager on stdout. + + .. versionchanged:: 3.0 + Added the `color` flag. + + :param text_or_generator: the text to page, or alternatively, a + generator emitting the text to page. + :param color: controls if the pager supports ANSI colors or not. The + default is autodetection. + """ + color = resolve_color_default(color) + + if inspect.isgeneratorfunction(text_or_generator): + i = t.cast(t.Callable[[], t.Iterable[str]], text_or_generator)() + elif isinstance(text_or_generator, str): + i = [text_or_generator] + else: + i = iter(t.cast(t.Iterable[str], text_or_generator)) + + # convert every element of i to a text type if necessary + text_generator = (el if isinstance(el, str) else str(el) for el in i) + + from ._termui_impl import pager + + return pager(itertools.chain(text_generator, "\n"), color) + + +def progressbar( + iterable: t.Optional[t.Iterable[V]] = None, + length: t.Optional[int] = None, + label: t.Optional[str] = None, + show_eta: bool = True, + show_percent: t.Optional[bool] = None, + show_pos: bool = False, + item_show_func: t.Optional[t.Callable[[t.Optional[V]], t.Optional[str]]] = None, + fill_char: str = "#", + empty_char: str = "-", + bar_template: str = "%(label)s [%(bar)s] %(info)s", + info_sep: str = " ", + width: int = 36, + file: t.Optional[t.TextIO] = None, + color: t.Optional[bool] = None, + update_min_steps: int = 1, +) -> "ProgressBar[V]": + """This function creates an iterable context manager that can be used + to iterate over something while showing a progress bar. It will + either iterate over the `iterable` or `length` items (that are counted + up). While iteration happens, this function will print a rendered + progress bar to the given `file` (defaults to stdout) and will attempt + to calculate remaining time and more. By default, this progress bar + will not be rendered if the file is not a terminal. + + The context manager creates the progress bar. When the context + manager is entered the progress bar is already created. With every + iteration over the progress bar, the iterable passed to the bar is + advanced and the bar is updated. When the context manager exits, + a newline is printed and the progress bar is finalized on screen. + + Note: The progress bar is currently designed for use cases where the + total progress can be expected to take at least several seconds. + Because of this, the ProgressBar class object won't display + progress that is considered too fast, and progress where the time + between steps is less than a second. + + No printing must happen or the progress bar will be unintentionally + destroyed. + + Example usage:: + + with progressbar(items) as bar: + for item in bar: + do_something_with(item) + + Alternatively, if no iterable is specified, one can manually update the + progress bar through the `update()` method instead of directly + iterating over the progress bar. The update method accepts the number + of steps to increment the bar with:: + + with progressbar(length=chunks.total_bytes) as bar: + for chunk in chunks: + process_chunk(chunk) + bar.update(chunks.bytes) + + The ``update()`` method also takes an optional value specifying the + ``current_item`` at the new position. This is useful when used + together with ``item_show_func`` to customize the output for each + manual step:: + + with click.progressbar( + length=total_size, + label='Unzipping archive', + item_show_func=lambda a: a.filename + ) as bar: + for archive in zip_file: + archive.extract() + bar.update(archive.size, archive) + + :param iterable: an iterable to iterate over. If not provided the length + is required. + :param length: the number of items to iterate over. By default the + progressbar will attempt to ask the iterator about its + length, which might or might not work. If an iterable is + also provided this parameter can be used to override the + length. If an iterable is not provided the progress bar + will iterate over a range of that length. + :param label: the label to show next to the progress bar. + :param show_eta: enables or disables the estimated time display. This is + automatically disabled if the length cannot be + determined. + :param show_percent: enables or disables the percentage display. The + default is `True` if the iterable has a length or + `False` if not. + :param show_pos: enables or disables the absolute position display. The + default is `False`. + :param item_show_func: A function called with the current item which + can return a string to show next to the progress bar. If the + function returns ``None`` nothing is shown. The current item can + be ``None``, such as when entering and exiting the bar. + :param fill_char: the character to use to show the filled part of the + progress bar. + :param empty_char: the character to use to show the non-filled part of + the progress bar. + :param bar_template: the format string to use as template for the bar. + The parameters in it are ``label`` for the label, + ``bar`` for the progress bar and ``info`` for the + info section. + :param info_sep: the separator between multiple info items (eta etc.) + :param width: the width of the progress bar in characters, 0 means full + terminal width + :param file: The file to write to. If this is not a terminal then + only the label is printed. + :param color: controls if the terminal supports ANSI colors or not. The + default is autodetection. This is only needed if ANSI + codes are included anywhere in the progress bar output + which is not the case by default. + :param update_min_steps: Render only when this many updates have + completed. This allows tuning for very fast iterators. + + .. versionchanged:: 8.0 + Output is shown even if execution time is less than 0.5 seconds. + + .. versionchanged:: 8.0 + ``item_show_func`` shows the current item, not the previous one. + + .. versionchanged:: 8.0 + Labels are echoed if the output is not a TTY. Reverts a change + in 7.0 that removed all output. + + .. versionadded:: 8.0 + Added the ``update_min_steps`` parameter. + + .. versionchanged:: 4.0 + Added the ``color`` parameter. Added the ``update`` method to + the object. + + .. versionadded:: 2.0 + """ + from ._termui_impl import ProgressBar + + color = resolve_color_default(color) + return ProgressBar( + iterable=iterable, + length=length, + show_eta=show_eta, + show_percent=show_percent, + show_pos=show_pos, + item_show_func=item_show_func, + fill_char=fill_char, + empty_char=empty_char, + bar_template=bar_template, + info_sep=info_sep, + file=file, + label=label, + width=width, + color=color, + update_min_steps=update_min_steps, + ) + + +def clear() -> None: + """Clears the terminal screen. This will have the effect of clearing + the whole visible space of the terminal and moving the cursor to the + top left. This does not do anything if not connected to a terminal. + + .. versionadded:: 2.0 + """ + if not isatty(sys.stdout): + return + if WIN: + os.system("cls") + else: + sys.stdout.write("\033[2J\033[1;1H") + + +def _interpret_color( + color: t.Union[int, t.Tuple[int, int, int], str], offset: int = 0 +) -> str: + if isinstance(color, int): + return f"{38 + offset};5;{color:d}" + + if isinstance(color, (tuple, list)): + r, g, b = color + return f"{38 + offset};2;{r:d};{g:d};{b:d}" + + return str(_ansi_colors[color] + offset) + + +def style( + text: t.Any, + fg: t.Optional[t.Union[int, t.Tuple[int, int, int], str]] = None, + bg: t.Optional[t.Union[int, t.Tuple[int, int, int], str]] = None, + bold: t.Optional[bool] = None, + dim: t.Optional[bool] = None, + underline: t.Optional[bool] = None, + overline: t.Optional[bool] = None, + italic: t.Optional[bool] = None, + blink: t.Optional[bool] = None, + reverse: t.Optional[bool] = None, + strikethrough: t.Optional[bool] = None, + reset: bool = True, +) -> str: + """Styles a text with ANSI styles and returns the new string. By + default the styling is self contained which means that at the end + of the string a reset code is issued. This can be prevented by + passing ``reset=False``. + + Examples:: + + click.echo(click.style('Hello World!', fg='green')) + click.echo(click.style('ATTENTION!', blink=True)) + click.echo(click.style('Some things', reverse=True, fg='cyan')) + click.echo(click.style('More colors', fg=(255, 12, 128), bg=117)) + + Supported color names: + + * ``black`` (might be a gray) + * ``red`` + * ``green`` + * ``yellow`` (might be an orange) + * ``blue`` + * ``magenta`` + * ``cyan`` + * ``white`` (might be light gray) + * ``bright_black`` + * ``bright_red`` + * ``bright_green`` + * ``bright_yellow`` + * ``bright_blue`` + * ``bright_magenta`` + * ``bright_cyan`` + * ``bright_white`` + * ``reset`` (reset the color code only) + + If the terminal supports it, color may also be specified as: + + - An integer in the interval [0, 255]. The terminal must support + 8-bit/256-color mode. + - An RGB tuple of three integers in [0, 255]. The terminal must + support 24-bit/true-color mode. + + See https://en.wikipedia.org/wiki/ANSI_color and + https://gist.github.com/XVilka/8346728 for more information. + + :param text: the string to style with ansi codes. + :param fg: if provided this will become the foreground color. + :param bg: if provided this will become the background color. + :param bold: if provided this will enable or disable bold mode. + :param dim: if provided this will enable or disable dim mode. This is + badly supported. + :param underline: if provided this will enable or disable underline. + :param overline: if provided this will enable or disable overline. + :param italic: if provided this will enable or disable italic. + :param blink: if provided this will enable or disable blinking. + :param reverse: if provided this will enable or disable inverse + rendering (foreground becomes background and the + other way round). + :param strikethrough: if provided this will enable or disable + striking through text. + :param reset: by default a reset-all code is added at the end of the + string which means that styles do not carry over. This + can be disabled to compose styles. + + .. versionchanged:: 8.0 + A non-string ``message`` is converted to a string. + + .. versionchanged:: 8.0 + Added support for 256 and RGB color codes. + + .. versionchanged:: 8.0 + Added the ``strikethrough``, ``italic``, and ``overline`` + parameters. + + .. versionchanged:: 7.0 + Added support for bright colors. + + .. versionadded:: 2.0 + """ + if not isinstance(text, str): + text = str(text) + + bits = [] + + if fg: + try: + bits.append(f"\033[{_interpret_color(fg)}m") + except KeyError: + raise TypeError(f"Unknown color {fg!r}") + + if bg: + try: + bits.append(f"\033[{_interpret_color(bg, 10)}m") + except KeyError: + raise TypeError(f"Unknown color {bg!r}") + + if bold is not None: + bits.append(f"\033[{1 if bold else 22}m") + if dim is not None: + bits.append(f"\033[{2 if dim else 22}m") + if underline is not None: + bits.append(f"\033[{4 if underline else 24}m") + if overline is not None: + bits.append(f"\033[{53 if underline else 55}m") + if italic is not None: + bits.append(f"\033[{5 if underline else 23}m") + if blink is not None: + bits.append(f"\033[{5 if blink else 25}m") + if reverse is not None: + bits.append(f"\033[{7 if reverse else 27}m") + if strikethrough is not None: + bits.append(f"\033[{9 if strikethrough else 29}m") + bits.append(text) + if reset: + bits.append(_ansi_reset_all) + return "".join(bits) + + +def unstyle(text: str) -> str: + """Removes ANSI styling information from a string. Usually it's not + necessary to use this function as Click's echo function will + automatically remove styling if necessary. + + .. versionadded:: 2.0 + + :param text: the text to remove style information from. + """ + return strip_ansi(text) + + +def secho( + message: t.Optional[t.Any] = None, + file: t.Optional[t.IO] = None, + nl: bool = True, + err: bool = False, + color: t.Optional[bool] = None, + **styles: t.Any, +) -> None: + """This function combines :func:`echo` and :func:`style` into one + call. As such the following two calls are the same:: + + click.secho('Hello World!', fg='green') + click.echo(click.style('Hello World!', fg='green')) + + All keyword arguments are forwarded to the underlying functions + depending on which one they go with. + + Non-string types will be converted to :class:`str`. However, + :class:`bytes` are passed directly to :meth:`echo` without applying + style. If you want to style bytes that represent text, call + :meth:`bytes.decode` first. + + .. versionchanged:: 8.0 + A non-string ``message`` is converted to a string. Bytes are + passed through without style applied. + + .. versionadded:: 2.0 + """ + if message is not None and not isinstance(message, (bytes, bytearray)): + message = style(message, **styles) + + return echo(message, file=file, nl=nl, err=err, color=color) + + +def edit( + text: t.Optional[t.AnyStr] = None, + editor: t.Optional[str] = None, + env: t.Optional[t.Mapping[str, str]] = None, + require_save: bool = True, + extension: str = ".txt", + filename: t.Optional[str] = None, +) -> t.Optional[t.AnyStr]: + r"""Edits the given text in the defined editor. If an editor is given + (should be the full path to the executable but the regular operating + system search path is used for finding the executable) it overrides + the detected editor. Optionally, some environment variables can be + used. If the editor is closed without changes, `None` is returned. In + case a file is edited directly the return value is always `None` and + `require_save` and `extension` are ignored. + + If the editor cannot be opened a :exc:`UsageError` is raised. + + Note for Windows: to simplify cross-platform usage, the newlines are + automatically converted from POSIX to Windows and vice versa. As such, + the message here will have ``\n`` as newline markers. + + :param text: the text to edit. + :param editor: optionally the editor to use. Defaults to automatic + detection. + :param env: environment variables to forward to the editor. + :param require_save: if this is true, then not saving in the editor + will make the return value become `None`. + :param extension: the extension to tell the editor about. This defaults + to `.txt` but changing this might change syntax + highlighting. + :param filename: if provided it will edit this file instead of the + provided text contents. It will not use a temporary + file as an indirection in that case. + """ + from ._termui_impl import Editor + + ed = Editor(editor=editor, env=env, require_save=require_save, extension=extension) + + if filename is None: + return ed.edit(text) + + ed.edit_file(filename) + return None + + +def launch(url: str, wait: bool = False, locate: bool = False) -> int: + """This function launches the given URL (or filename) in the default + viewer application for this file type. If this is an executable, it + might launch the executable in a new session. The return value is + the exit code of the launched application. Usually, ``0`` indicates + success. + + Examples:: + + click.launch('https://click.palletsprojects.com/') + click.launch('/my/downloaded/file', locate=True) + + .. versionadded:: 2.0 + + :param url: URL or filename of the thing to launch. + :param wait: Wait for the program to exit before returning. This + only works if the launched program blocks. In particular, + ``xdg-open`` on Linux does not block. + :param locate: if this is set to `True` then instead of launching the + application associated with the URL it will attempt to + launch a file manager with the file located. This + might have weird effects if the URL does not point to + the filesystem. + """ + from ._termui_impl import open_url + + return open_url(url, wait=wait, locate=locate) + + +# If this is provided, getchar() calls into this instead. This is used +# for unittesting purposes. +_getchar: t.Optional[t.Callable[[bool], str]] = None + + +def getchar(echo: bool = False) -> str: + """Fetches a single character from the terminal and returns it. This + will always return a unicode character and under certain rare + circumstances this might return more than one character. The + situations which more than one character is returned is when for + whatever reason multiple characters end up in the terminal buffer or + standard input was not actually a terminal. + + Note that this will always read from the terminal, even if something + is piped into the standard input. + + Note for Windows: in rare cases when typing non-ASCII characters, this + function might wait for a second character and then return both at once. + This is because certain Unicode characters look like special-key markers. + + .. versionadded:: 2.0 + + :param echo: if set to `True`, the character read will also show up on + the terminal. The default is to not show it. + """ + global _getchar + + if _getchar is None: + from ._termui_impl import getchar as f + + _getchar = f + + return _getchar(echo) + + +def raw_terminal() -> t.ContextManager[int]: + from ._termui_impl import raw_terminal as f + + return f() + + +def pause(info: t.Optional[str] = None, err: bool = False) -> None: + """This command stops execution and waits for the user to press any + key to continue. This is similar to the Windows batch "pause" + command. If the program is not run through a terminal, this command + will instead do nothing. + + .. versionadded:: 2.0 + + .. versionadded:: 4.0 + Added the `err` parameter. + + :param info: The message to print before pausing. Defaults to + ``"Press any key to continue..."``. + :param err: if set to message goes to ``stderr`` instead of + ``stdout``, the same as with echo. + """ + if not isatty(sys.stdin) or not isatty(sys.stdout): + return + + if info is None: + info = _("Press any key to continue...") + + try: + if info: + echo(info, nl=False, err=err) + try: + getchar() + except (KeyboardInterrupt, EOFError): + pass + finally: + if info: + echo(err=err) diff --git a/venv/Lib/site-packages/click/testing.py b/venv/Lib/site-packages/click/testing.py new file mode 100644 index 0000000..d19b850 --- /dev/null +++ b/venv/Lib/site-packages/click/testing.py @@ -0,0 +1,479 @@ +import contextlib +import io +import os +import shlex +import shutil +import sys +import tempfile +import typing as t +from types import TracebackType + +from . import formatting +from . import termui +from . import utils +from ._compat import _find_binary_reader + +if t.TYPE_CHECKING: + from .core import BaseCommand + + +class EchoingStdin: + def __init__(self, input: t.BinaryIO, output: t.BinaryIO) -> None: + self._input = input + self._output = output + self._paused = False + + def __getattr__(self, x: str) -> t.Any: + return getattr(self._input, x) + + def _echo(self, rv: bytes) -> bytes: + if not self._paused: + self._output.write(rv) + + return rv + + def read(self, n: int = -1) -> bytes: + return self._echo(self._input.read(n)) + + def read1(self, n: int = -1) -> bytes: + return self._echo(self._input.read1(n)) # type: ignore + + def readline(self, n: int = -1) -> bytes: + return self._echo(self._input.readline(n)) + + def readlines(self) -> t.List[bytes]: + return [self._echo(x) for x in self._input.readlines()] + + def __iter__(self) -> t.Iterator[bytes]: + return iter(self._echo(x) for x in self._input) + + def __repr__(self) -> str: + return repr(self._input) + + +@contextlib.contextmanager +def _pause_echo(stream: t.Optional[EchoingStdin]) -> t.Iterator[None]: + if stream is None: + yield + else: + stream._paused = True + yield + stream._paused = False + + +class _NamedTextIOWrapper(io.TextIOWrapper): + def __init__( + self, buffer: t.BinaryIO, name: str, mode: str, **kwargs: t.Any + ) -> None: + super().__init__(buffer, **kwargs) + self._name = name + self._mode = mode + + @property + def name(self) -> str: + return self._name + + @property + def mode(self) -> str: + return self._mode + + +def make_input_stream( + input: t.Optional[t.Union[str, bytes, t.IO]], charset: str +) -> t.BinaryIO: + # Is already an input stream. + if hasattr(input, "read"): + rv = _find_binary_reader(t.cast(t.IO, input)) + + if rv is not None: + return rv + + raise TypeError("Could not find binary reader for input stream.") + + if input is None: + input = b"" + elif isinstance(input, str): + input = input.encode(charset) + + return io.BytesIO(t.cast(bytes, input)) + + +class Result: + """Holds the captured result of an invoked CLI script.""" + + def __init__( + self, + runner: "CliRunner", + stdout_bytes: bytes, + stderr_bytes: t.Optional[bytes], + return_value: t.Any, + exit_code: int, + exception: t.Optional[BaseException], + exc_info: t.Optional[ + t.Tuple[t.Type[BaseException], BaseException, TracebackType] + ] = None, + ): + #: The runner that created the result + self.runner = runner + #: The standard output as bytes. + self.stdout_bytes = stdout_bytes + #: The standard error as bytes, or None if not available + self.stderr_bytes = stderr_bytes + #: The value returned from the invoked command. + #: + #: .. versionadded:: 8.0 + self.return_value = return_value + #: The exit code as integer. + self.exit_code = exit_code + #: The exception that happened if one did. + self.exception = exception + #: The traceback + self.exc_info = exc_info + + @property + def output(self) -> str: + """The (standard) output as unicode string.""" + return self.stdout + + @property + def stdout(self) -> str: + """The standard output as unicode string.""" + return self.stdout_bytes.decode(self.runner.charset, "replace").replace( + "\r\n", "\n" + ) + + @property + def stderr(self) -> str: + """The standard error as unicode string.""" + if self.stderr_bytes is None: + raise ValueError("stderr not separately captured") + return self.stderr_bytes.decode(self.runner.charset, "replace").replace( + "\r\n", "\n" + ) + + def __repr__(self) -> str: + exc_str = repr(self.exception) if self.exception else "okay" + return f"<{type(self).__name__} {exc_str}>" + + +class CliRunner: + """The CLI runner provides functionality to invoke a Click command line + script for unittesting purposes in a isolated environment. This only + works in single-threaded systems without any concurrency as it changes the + global interpreter state. + + :param charset: the character set for the input and output data. + :param env: a dictionary with environment variables for overriding. + :param echo_stdin: if this is set to `True`, then reading from stdin writes + to stdout. This is useful for showing examples in + some circumstances. Note that regular prompts + will automatically echo the input. + :param mix_stderr: if this is set to `False`, then stdout and stderr are + preserved as independent streams. This is useful for + Unix-philosophy apps that have predictable stdout and + noisy stderr, such that each may be measured + independently + """ + + def __init__( + self, + charset: str = "utf-8", + env: t.Optional[t.Mapping[str, t.Optional[str]]] = None, + echo_stdin: bool = False, + mix_stderr: bool = True, + ) -> None: + self.charset = charset + self.env = env or {} + self.echo_stdin = echo_stdin + self.mix_stderr = mix_stderr + + def get_default_prog_name(self, cli: "BaseCommand") -> str: + """Given a command object it will return the default program name + for it. The default is the `name` attribute or ``"root"`` if not + set. + """ + return cli.name or "root" + + def make_env( + self, overrides: t.Optional[t.Mapping[str, t.Optional[str]]] = None + ) -> t.Mapping[str, t.Optional[str]]: + """Returns the environment overrides for invoking a script.""" + rv = dict(self.env) + if overrides: + rv.update(overrides) + return rv + + @contextlib.contextmanager + def isolation( + self, + input: t.Optional[t.Union[str, bytes, t.IO]] = None, + env: t.Optional[t.Mapping[str, t.Optional[str]]] = None, + color: bool = False, + ) -> t.Iterator[t.Tuple[io.BytesIO, t.Optional[io.BytesIO]]]: + """A context manager that sets up the isolation for invoking of a + command line tool. This sets up stdin with the given input data + and `os.environ` with the overrides from the given dictionary. + This also rebinds some internals in Click to be mocked (like the + prompt functionality). + + This is automatically done in the :meth:`invoke` method. + + :param input: the input stream to put into sys.stdin. + :param env: the environment overrides as dictionary. + :param color: whether the output should contain color codes. The + application can still override this explicitly. + + .. versionchanged:: 8.0 + ``stderr`` is opened with ``errors="backslashreplace"`` + instead of the default ``"strict"``. + + .. versionchanged:: 4.0 + Added the ``color`` parameter. + """ + bytes_input = make_input_stream(input, self.charset) + echo_input = None + + old_stdin = sys.stdin + old_stdout = sys.stdout + old_stderr = sys.stderr + old_forced_width = formatting.FORCED_WIDTH + formatting.FORCED_WIDTH = 80 + + env = self.make_env(env) + + bytes_output = io.BytesIO() + + if self.echo_stdin: + bytes_input = echo_input = t.cast( + t.BinaryIO, EchoingStdin(bytes_input, bytes_output) + ) + + sys.stdin = text_input = _NamedTextIOWrapper( + bytes_input, encoding=self.charset, name="", mode="r" + ) + + if self.echo_stdin: + # Force unbuffered reads, otherwise TextIOWrapper reads a + # large chunk which is echoed early. + text_input._CHUNK_SIZE = 1 # type: ignore + + sys.stdout = _NamedTextIOWrapper( + bytes_output, encoding=self.charset, name="", mode="w" + ) + + bytes_error = None + if self.mix_stderr: + sys.stderr = sys.stdout + else: + bytes_error = io.BytesIO() + sys.stderr = _NamedTextIOWrapper( + bytes_error, + encoding=self.charset, + name="", + mode="w", + errors="backslashreplace", + ) + + @_pause_echo(echo_input) # type: ignore + def visible_input(prompt: t.Optional[str] = None) -> str: + sys.stdout.write(prompt or "") + val = text_input.readline().rstrip("\r\n") + sys.stdout.write(f"{val}\n") + sys.stdout.flush() + return val + + @_pause_echo(echo_input) # type: ignore + def hidden_input(prompt: t.Optional[str] = None) -> str: + sys.stdout.write(f"{prompt or ''}\n") + sys.stdout.flush() + return text_input.readline().rstrip("\r\n") + + @_pause_echo(echo_input) # type: ignore + def _getchar(echo: bool) -> str: + char = sys.stdin.read(1) + + if echo: + sys.stdout.write(char) + + sys.stdout.flush() + return char + + default_color = color + + def should_strip_ansi( + stream: t.Optional[t.IO] = None, color: t.Optional[bool] = None + ) -> bool: + if color is None: + return not default_color + return not color + + old_visible_prompt_func = termui.visible_prompt_func + old_hidden_prompt_func = termui.hidden_prompt_func + old__getchar_func = termui._getchar + old_should_strip_ansi = utils.should_strip_ansi # type: ignore + termui.visible_prompt_func = visible_input + termui.hidden_prompt_func = hidden_input + termui._getchar = _getchar + utils.should_strip_ansi = should_strip_ansi # type: ignore + + old_env = {} + try: + for key, value in env.items(): + old_env[key] = os.environ.get(key) + if value is None: + try: + del os.environ[key] + except Exception: + pass + else: + os.environ[key] = value + yield (bytes_output, bytes_error) + finally: + for key, value in old_env.items(): + if value is None: + try: + del os.environ[key] + except Exception: + pass + else: + os.environ[key] = value + sys.stdout = old_stdout + sys.stderr = old_stderr + sys.stdin = old_stdin + termui.visible_prompt_func = old_visible_prompt_func + termui.hidden_prompt_func = old_hidden_prompt_func + termui._getchar = old__getchar_func + utils.should_strip_ansi = old_should_strip_ansi # type: ignore + formatting.FORCED_WIDTH = old_forced_width + + def invoke( + self, + cli: "BaseCommand", + args: t.Optional[t.Union[str, t.Sequence[str]]] = None, + input: t.Optional[t.Union[str, bytes, t.IO]] = None, + env: t.Optional[t.Mapping[str, t.Optional[str]]] = None, + catch_exceptions: bool = True, + color: bool = False, + **extra: t.Any, + ) -> Result: + """Invokes a command in an isolated environment. The arguments are + forwarded directly to the command line script, the `extra` keyword + arguments are passed to the :meth:`~clickpkg.Command.main` function of + the command. + + This returns a :class:`Result` object. + + :param cli: the command to invoke + :param args: the arguments to invoke. It may be given as an iterable + or a string. When given as string it will be interpreted + as a Unix shell command. More details at + :func:`shlex.split`. + :param input: the input data for `sys.stdin`. + :param env: the environment overrides. + :param catch_exceptions: Whether to catch any other exceptions than + ``SystemExit``. + :param extra: the keyword arguments to pass to :meth:`main`. + :param color: whether the output should contain color codes. The + application can still override this explicitly. + + .. versionchanged:: 8.0 + The result object has the ``return_value`` attribute with + the value returned from the invoked command. + + .. versionchanged:: 4.0 + Added the ``color`` parameter. + + .. versionchanged:: 3.0 + Added the ``catch_exceptions`` parameter. + + .. versionchanged:: 3.0 + The result object has the ``exc_info`` attribute with the + traceback if available. + """ + exc_info = None + with self.isolation(input=input, env=env, color=color) as outstreams: + return_value = None + exception: t.Optional[BaseException] = None + exit_code = 0 + + if isinstance(args, str): + args = shlex.split(args) + + try: + prog_name = extra.pop("prog_name") + except KeyError: + prog_name = self.get_default_prog_name(cli) + + try: + return_value = cli.main(args=args or (), prog_name=prog_name, **extra) + except SystemExit as e: + exc_info = sys.exc_info() + e_code = t.cast(t.Optional[t.Union[int, t.Any]], e.code) + + if e_code is None: + e_code = 0 + + if e_code != 0: + exception = e + + if not isinstance(e_code, int): + sys.stdout.write(str(e_code)) + sys.stdout.write("\n") + e_code = 1 + + exit_code = e_code + + except Exception as e: + if not catch_exceptions: + raise + exception = e + exit_code = 1 + exc_info = sys.exc_info() + finally: + sys.stdout.flush() + stdout = outstreams[0].getvalue() + if self.mix_stderr: + stderr = None + else: + stderr = outstreams[1].getvalue() # type: ignore + + return Result( + runner=self, + stdout_bytes=stdout, + stderr_bytes=stderr, + return_value=return_value, + exit_code=exit_code, + exception=exception, + exc_info=exc_info, # type: ignore + ) + + @contextlib.contextmanager + def isolated_filesystem( + self, temp_dir: t.Optional[t.Union[str, os.PathLike]] = None + ) -> t.Iterator[str]: + """A context manager that creates a temporary directory and + changes the current working directory to it. This isolates tests + that affect the contents of the CWD to prevent them from + interfering with each other. + + :param temp_dir: Create the temporary directory under this + directory. If given, the created directory is not removed + when exiting. + + .. versionchanged:: 8.0 + Added the ``temp_dir`` parameter. + """ + cwd = os.getcwd() + t = tempfile.mkdtemp(dir=temp_dir) + os.chdir(t) + + try: + yield t + finally: + os.chdir(cwd) + + if temp_dir is None: + try: + shutil.rmtree(t) + except OSError: # noqa: B014 + pass diff --git a/venv/Lib/site-packages/click/types.py b/venv/Lib/site-packages/click/types.py new file mode 100644 index 0000000..21f0e4f --- /dev/null +++ b/venv/Lib/site-packages/click/types.py @@ -0,0 +1,1052 @@ +import os +import stat +import typing as t +from datetime import datetime +from gettext import gettext as _ +from gettext import ngettext + +from ._compat import _get_argv_encoding +from ._compat import get_filesystem_encoding +from ._compat import open_stream +from .exceptions import BadParameter +from .utils import LazyFile +from .utils import safecall + +if t.TYPE_CHECKING: + import typing_extensions as te + from .core import Context + from .core import Parameter + from .shell_completion import CompletionItem + + +class ParamType: + """Represents the type of a parameter. Validates and converts values + from the command line or Python into the correct type. + + To implement a custom type, subclass and implement at least the + following: + + - The :attr:`name` class attribute must be set. + - Calling an instance of the type with ``None`` must return + ``None``. This is already implemented by default. + - :meth:`convert` must convert string values to the correct type. + - :meth:`convert` must accept values that are already the correct + type. + - It must be able to convert a value if the ``ctx`` and ``param`` + arguments are ``None``. This can occur when converting prompt + input. + """ + + is_composite: t.ClassVar[bool] = False + arity: t.ClassVar[int] = 1 + + #: the descriptive name of this type + name: str + + #: if a list of this type is expected and the value is pulled from a + #: string environment variable, this is what splits it up. `None` + #: means any whitespace. For all parameters the general rule is that + #: whitespace splits them up. The exception are paths and files which + #: are split by ``os.path.pathsep`` by default (":" on Unix and ";" on + #: Windows). + envvar_list_splitter: t.ClassVar[t.Optional[str]] = None + + def to_info_dict(self) -> t.Dict[str, t.Any]: + """Gather information that could be useful for a tool generating + user-facing documentation. + + Use :meth:`click.Context.to_info_dict` to traverse the entire + CLI structure. + + .. versionadded:: 8.0 + """ + # The class name without the "ParamType" suffix. + param_type = type(self).__name__.partition("ParamType")[0] + param_type = param_type.partition("ParameterType")[0] + return {"param_type": param_type, "name": self.name} + + def __call__( + self, + value: t.Any, + param: t.Optional["Parameter"] = None, + ctx: t.Optional["Context"] = None, + ) -> t.Any: + if value is not None: + return self.convert(value, param, ctx) + + def get_metavar(self, param: "Parameter") -> t.Optional[str]: + """Returns the metavar default for this param if it provides one.""" + + def get_missing_message(self, param: "Parameter") -> t.Optional[str]: + """Optionally might return extra information about a missing + parameter. + + .. versionadded:: 2.0 + """ + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + """Convert the value to the correct type. This is not called if + the value is ``None`` (the missing value). + + This must accept string values from the command line, as well as + values that are already the correct type. It may also convert + other compatible types. + + The ``param`` and ``ctx`` arguments may be ``None`` in certain + situations, such as when converting prompt input. + + If the value cannot be converted, call :meth:`fail` with a + descriptive message. + + :param value: The value to convert. + :param param: The parameter that is using this type to convert + its value. May be ``None``. + :param ctx: The current context that arrived at this value. May + be ``None``. + """ + return value + + def split_envvar_value(self, rv: str) -> t.Sequence[str]: + """Given a value from an environment variable this splits it up + into small chunks depending on the defined envvar list splitter. + + If the splitter is set to `None`, which means that whitespace splits, + then leading and trailing whitespace is ignored. Otherwise, leading + and trailing splitters usually lead to empty items being included. + """ + return (rv or "").split(self.envvar_list_splitter) + + def fail( + self, + message: str, + param: t.Optional["Parameter"] = None, + ctx: t.Optional["Context"] = None, + ) -> "t.NoReturn": + """Helper method to fail with an invalid value message.""" + raise BadParameter(message, ctx=ctx, param=param) + + def shell_complete( + self, ctx: "Context", param: "Parameter", incomplete: str + ) -> t.List["CompletionItem"]: + """Return a list of + :class:`~click.shell_completion.CompletionItem` objects for the + incomplete value. Most types do not provide completions, but + some do, and this allows custom types to provide custom + completions as well. + + :param ctx: Invocation context for this command. + :param param: The parameter that is requesting completion. + :param incomplete: Value being completed. May be empty. + + .. versionadded:: 8.0 + """ + return [] + + +class CompositeParamType(ParamType): + is_composite = True + + @property + def arity(self) -> int: # type: ignore + raise NotImplementedError() + + +class FuncParamType(ParamType): + def __init__(self, func: t.Callable[[t.Any], t.Any]) -> None: + self.name = func.__name__ + self.func = func + + def to_info_dict(self) -> t.Dict[str, t.Any]: + info_dict = super().to_info_dict() + info_dict["func"] = self.func + return info_dict + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + try: + return self.func(value) + except ValueError: + try: + value = str(value) + except UnicodeError: + value = value.decode("utf-8", "replace") + + self.fail(value, param, ctx) + + +class UnprocessedParamType(ParamType): + name = "text" + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + return value + + def __repr__(self) -> str: + return "UNPROCESSED" + + +class StringParamType(ParamType): + name = "text" + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + if isinstance(value, bytes): + enc = _get_argv_encoding() + try: + value = value.decode(enc) + except UnicodeError: + fs_enc = get_filesystem_encoding() + if fs_enc != enc: + try: + value = value.decode(fs_enc) + except UnicodeError: + value = value.decode("utf-8", "replace") + else: + value = value.decode("utf-8", "replace") + return value + return str(value) + + def __repr__(self) -> str: + return "STRING" + + +class Choice(ParamType): + """The choice type allows a value to be checked against a fixed set + of supported values. All of these values have to be strings. + + You should only pass a list or tuple of choices. Other iterables + (like generators) may lead to surprising results. + + The resulting value will always be one of the originally passed choices + regardless of ``case_sensitive`` or any ``ctx.token_normalize_func`` + being specified. + + See :ref:`choice-opts` for an example. + + :param case_sensitive: Set to false to make choices case + insensitive. Defaults to true. + """ + + name = "choice" + + def __init__(self, choices: t.Sequence[str], case_sensitive: bool = True) -> None: + self.choices = choices + self.case_sensitive = case_sensitive + + def to_info_dict(self) -> t.Dict[str, t.Any]: + info_dict = super().to_info_dict() + info_dict["choices"] = self.choices + info_dict["case_sensitive"] = self.case_sensitive + return info_dict + + def get_metavar(self, param: "Parameter") -> str: + choices_str = "|".join(self.choices) + + # Use curly braces to indicate a required argument. + if param.required and param.param_type_name == "argument": + return f"{{{choices_str}}}" + + # Use square braces to indicate an option or optional argument. + return f"[{choices_str}]" + + def get_missing_message(self, param: "Parameter") -> str: + return _("Choose from:\n\t{choices}").format(choices=",\n\t".join(self.choices)) + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + # Match through normalization and case sensitivity + # first do token_normalize_func, then lowercase + # preserve original `value` to produce an accurate message in + # `self.fail` + normed_value = value + normed_choices = {choice: choice for choice in self.choices} + + if ctx is not None and ctx.token_normalize_func is not None: + normed_value = ctx.token_normalize_func(value) + normed_choices = { + ctx.token_normalize_func(normed_choice): original + for normed_choice, original in normed_choices.items() + } + + if not self.case_sensitive: + normed_value = normed_value.casefold() + normed_choices = { + normed_choice.casefold(): original + for normed_choice, original in normed_choices.items() + } + + if normed_value in normed_choices: + return normed_choices[normed_value] + + choices_str = ", ".join(map(repr, self.choices)) + self.fail( + ngettext( + "{value!r} is not {choice}.", + "{value!r} is not one of {choices}.", + len(self.choices), + ).format(value=value, choice=choices_str, choices=choices_str), + param, + ctx, + ) + + def __repr__(self) -> str: + return f"Choice({list(self.choices)})" + + def shell_complete( + self, ctx: "Context", param: "Parameter", incomplete: str + ) -> t.List["CompletionItem"]: + """Complete choices that start with the incomplete value. + + :param ctx: Invocation context for this command. + :param param: The parameter that is requesting completion. + :param incomplete: Value being completed. May be empty. + + .. versionadded:: 8.0 + """ + from click.shell_completion import CompletionItem + + str_choices = map(str, self.choices) + + if self.case_sensitive: + matched = (c for c in str_choices if c.startswith(incomplete)) + else: + incomplete = incomplete.lower() + matched = (c for c in str_choices if c.lower().startswith(incomplete)) + + return [CompletionItem(c) for c in matched] + + +class DateTime(ParamType): + """The DateTime type converts date strings into `datetime` objects. + + The format strings which are checked are configurable, but default to some + common (non-timezone aware) ISO 8601 formats. + + When specifying *DateTime* formats, you should only pass a list or a tuple. + Other iterables, like generators, may lead to surprising results. + + The format strings are processed using ``datetime.strptime``, and this + consequently defines the format strings which are allowed. + + Parsing is tried using each format, in order, and the first format which + parses successfully is used. + + :param formats: A list or tuple of date format strings, in the order in + which they should be tried. Defaults to + ``'%Y-%m-%d'``, ``'%Y-%m-%dT%H:%M:%S'``, + ``'%Y-%m-%d %H:%M:%S'``. + """ + + name = "datetime" + + def __init__(self, formats: t.Optional[t.Sequence[str]] = None): + self.formats = formats or ["%Y-%m-%d", "%Y-%m-%dT%H:%M:%S", "%Y-%m-%d %H:%M:%S"] + + def to_info_dict(self) -> t.Dict[str, t.Any]: + info_dict = super().to_info_dict() + info_dict["formats"] = self.formats + return info_dict + + def get_metavar(self, param: "Parameter") -> str: + return f"[{'|'.join(self.formats)}]" + + def _try_to_convert_date(self, value: t.Any, format: str) -> t.Optional[datetime]: + try: + return datetime.strptime(value, format) + except ValueError: + return None + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + if isinstance(value, datetime): + return value + + for format in self.formats: + converted = self._try_to_convert_date(value, format) + + if converted is not None: + return converted + + formats_str = ", ".join(map(repr, self.formats)) + self.fail( + ngettext( + "{value!r} does not match the format {format}.", + "{value!r} does not match the formats {formats}.", + len(self.formats), + ).format(value=value, format=formats_str, formats=formats_str), + param, + ctx, + ) + + def __repr__(self) -> str: + return "DateTime" + + +class _NumberParamTypeBase(ParamType): + _number_class: t.ClassVar[t.Type] + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + try: + return self._number_class(value) + except ValueError: + self.fail( + _("{value!r} is not a valid {number_type}.").format( + value=value, number_type=self.name + ), + param, + ctx, + ) + + +class _NumberRangeBase(_NumberParamTypeBase): + def __init__( + self, + min: t.Optional[float] = None, + max: t.Optional[float] = None, + min_open: bool = False, + max_open: bool = False, + clamp: bool = False, + ) -> None: + self.min = min + self.max = max + self.min_open = min_open + self.max_open = max_open + self.clamp = clamp + + def to_info_dict(self) -> t.Dict[str, t.Any]: + info_dict = super().to_info_dict() + info_dict.update( + min=self.min, + max=self.max, + min_open=self.min_open, + max_open=self.max_open, + clamp=self.clamp, + ) + return info_dict + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + import operator + + rv = super().convert(value, param, ctx) + lt_min: bool = self.min is not None and ( + operator.le if self.min_open else operator.lt + )(rv, self.min) + gt_max: bool = self.max is not None and ( + operator.ge if self.max_open else operator.gt + )(rv, self.max) + + if self.clamp: + if lt_min: + return self._clamp(self.min, 1, self.min_open) # type: ignore + + if gt_max: + return self._clamp(self.max, -1, self.max_open) # type: ignore + + if lt_min or gt_max: + self.fail( + _("{value} is not in the range {range}.").format( + value=rv, range=self._describe_range() + ), + param, + ctx, + ) + + return rv + + def _clamp(self, bound: float, dir: "te.Literal[1, -1]", open: bool) -> float: + """Find the valid value to clamp to bound in the given + direction. + + :param bound: The boundary value. + :param dir: 1 or -1 indicating the direction to move. + :param open: If true, the range does not include the bound. + """ + raise NotImplementedError + + def _describe_range(self) -> str: + """Describe the range for use in help text.""" + if self.min is None: + op = "<" if self.max_open else "<=" + return f"x{op}{self.max}" + + if self.max is None: + op = ">" if self.min_open else ">=" + return f"x{op}{self.min}" + + lop = "<" if self.min_open else "<=" + rop = "<" if self.max_open else "<=" + return f"{self.min}{lop}x{rop}{self.max}" + + def __repr__(self) -> str: + clamp = " clamped" if self.clamp else "" + return f"<{type(self).__name__} {self._describe_range()}{clamp}>" + + +class IntParamType(_NumberParamTypeBase): + name = "integer" + _number_class = int + + def __repr__(self) -> str: + return "INT" + + +class IntRange(_NumberRangeBase, IntParamType): + """Restrict an :data:`click.INT` value to a range of accepted + values. See :ref:`ranges`. + + If ``min`` or ``max`` are not passed, any value is accepted in that + direction. If ``min_open`` or ``max_open`` are enabled, the + corresponding boundary is not included in the range. + + If ``clamp`` is enabled, a value outside the range is clamped to the + boundary instead of failing. + + .. versionchanged:: 8.0 + Added the ``min_open`` and ``max_open`` parameters. + """ + + name = "integer range" + + def _clamp( # type: ignore + self, bound: int, dir: "te.Literal[1, -1]", open: bool + ) -> int: + if not open: + return bound + + return bound + dir + + +class FloatParamType(_NumberParamTypeBase): + name = "float" + _number_class = float + + def __repr__(self) -> str: + return "FLOAT" + + +class FloatRange(_NumberRangeBase, FloatParamType): + """Restrict a :data:`click.FLOAT` value to a range of accepted + values. See :ref:`ranges`. + + If ``min`` or ``max`` are not passed, any value is accepted in that + direction. If ``min_open`` or ``max_open`` are enabled, the + corresponding boundary is not included in the range. + + If ``clamp`` is enabled, a value outside the range is clamped to the + boundary instead of failing. This is not supported if either + boundary is marked ``open``. + + .. versionchanged:: 8.0 + Added the ``min_open`` and ``max_open`` parameters. + """ + + name = "float range" + + def __init__( + self, + min: t.Optional[float] = None, + max: t.Optional[float] = None, + min_open: bool = False, + max_open: bool = False, + clamp: bool = False, + ) -> None: + super().__init__( + min=min, max=max, min_open=min_open, max_open=max_open, clamp=clamp + ) + + if (min_open or max_open) and clamp: + raise TypeError("Clamping is not supported for open bounds.") + + def _clamp(self, bound: float, dir: "te.Literal[1, -1]", open: bool) -> float: + if not open: + return bound + + # Could use Python 3.9's math.nextafter here, but clamping an + # open float range doesn't seem to be particularly useful. It's + # left up to the user to write a callback to do it if needed. + raise RuntimeError("Clamping is not supported for open bounds.") + + +class BoolParamType(ParamType): + name = "boolean" + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + if value in {False, True}: + return bool(value) + + norm = value.strip().lower() + + if norm in {"1", "true", "t", "yes", "y", "on"}: + return True + + if norm in {"0", "false", "f", "no", "n", "off"}: + return False + + self.fail( + _("{value!r} is not a valid boolean.").format(value=value), param, ctx + ) + + def __repr__(self) -> str: + return "BOOL" + + +class UUIDParameterType(ParamType): + name = "uuid" + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + import uuid + + if isinstance(value, uuid.UUID): + return value + + value = value.strip() + + try: + return uuid.UUID(value) + except ValueError: + self.fail( + _("{value!r} is not a valid UUID.").format(value=value), param, ctx + ) + + def __repr__(self) -> str: + return "UUID" + + +class File(ParamType): + """Declares a parameter to be a file for reading or writing. The file + is automatically closed once the context tears down (after the command + finished working). + + Files can be opened for reading or writing. The special value ``-`` + indicates stdin or stdout depending on the mode. + + By default, the file is opened for reading text data, but it can also be + opened in binary mode or for writing. The encoding parameter can be used + to force a specific encoding. + + The `lazy` flag controls if the file should be opened immediately or upon + first IO. The default is to be non-lazy for standard input and output + streams as well as files opened for reading, `lazy` otherwise. When opening a + file lazily for reading, it is still opened temporarily for validation, but + will not be held open until first IO. lazy is mainly useful when opening + for writing to avoid creating the file until it is needed. + + Starting with Click 2.0, files can also be opened atomically in which + case all writes go into a separate file in the same folder and upon + completion the file will be moved over to the original location. This + is useful if a file regularly read by other users is modified. + + See :ref:`file-args` for more information. + """ + + name = "filename" + envvar_list_splitter = os.path.pathsep + + def __init__( + self, + mode: str = "r", + encoding: t.Optional[str] = None, + errors: t.Optional[str] = "strict", + lazy: t.Optional[bool] = None, + atomic: bool = False, + ) -> None: + self.mode = mode + self.encoding = encoding + self.errors = errors + self.lazy = lazy + self.atomic = atomic + + def to_info_dict(self) -> t.Dict[str, t.Any]: + info_dict = super().to_info_dict() + info_dict.update(mode=self.mode, encoding=self.encoding) + return info_dict + + def resolve_lazy_flag(self, value: t.Any) -> bool: + if self.lazy is not None: + return self.lazy + if value == "-": + return False + elif "w" in self.mode: + return True + return False + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + try: + if hasattr(value, "read") or hasattr(value, "write"): + return value + + lazy = self.resolve_lazy_flag(value) + + if lazy: + f: t.IO = t.cast( + t.IO, + LazyFile( + value, self.mode, self.encoding, self.errors, atomic=self.atomic + ), + ) + + if ctx is not None: + ctx.call_on_close(f.close_intelligently) # type: ignore + + return f + + f, should_close = open_stream( + value, self.mode, self.encoding, self.errors, atomic=self.atomic + ) + + # If a context is provided, we automatically close the file + # at the end of the context execution (or flush out). If a + # context does not exist, it's the caller's responsibility to + # properly close the file. This for instance happens when the + # type is used with prompts. + if ctx is not None: + if should_close: + ctx.call_on_close(safecall(f.close)) + else: + ctx.call_on_close(safecall(f.flush)) + + return f + except OSError as e: # noqa: B014 + self.fail(f"{os.fsdecode(value)!r}: {e.strerror}", param, ctx) + + def shell_complete( + self, ctx: "Context", param: "Parameter", incomplete: str + ) -> t.List["CompletionItem"]: + """Return a special completion marker that tells the completion + system to use the shell to provide file path completions. + + :param ctx: Invocation context for this command. + :param param: The parameter that is requesting completion. + :param incomplete: Value being completed. May be empty. + + .. versionadded:: 8.0 + """ + from click.shell_completion import CompletionItem + + return [CompletionItem(incomplete, type="file")] + + +class Path(ParamType): + """The path type is similar to the :class:`File` type but it performs + different checks. First of all, instead of returning an open file + handle it returns just the filename. Secondly, it can perform various + basic checks about what the file or directory should be. + + :param exists: if set to true, the file or directory needs to exist for + this value to be valid. If this is not required and a + file does indeed not exist, then all further checks are + silently skipped. + :param file_okay: controls if a file is a possible value. + :param dir_okay: controls if a directory is a possible value. + :param writable: if true, a writable check is performed. + :param readable: if true, a readable check is performed. + :param resolve_path: if this is true, then the path is fully resolved + before the value is passed onwards. This means + that it's absolute and symlinks are resolved. It + will not expand a tilde-prefix, as this is + supposed to be done by the shell only. + :param allow_dash: If this is set to `True`, a single dash to indicate + standard streams is permitted. + :param path_type: Convert the incoming path value to this type. If + ``None``, keep Python's default, which is ``str``. Useful to + convert to :class:`pathlib.Path`. + + .. versionchanged:: 8.0 + Allow passing ``type=pathlib.Path``. + + .. versionchanged:: 6.0 + Added the ``allow_dash`` parameter. + """ + + envvar_list_splitter = os.path.pathsep + + def __init__( + self, + exists: bool = False, + file_okay: bool = True, + dir_okay: bool = True, + writable: bool = False, + readable: bool = True, + resolve_path: bool = False, + allow_dash: bool = False, + path_type: t.Optional[t.Type] = None, + ): + self.exists = exists + self.file_okay = file_okay + self.dir_okay = dir_okay + self.writable = writable + self.readable = readable + self.resolve_path = resolve_path + self.allow_dash = allow_dash + self.type = path_type + + if self.file_okay and not self.dir_okay: + self.name = _("file") + elif self.dir_okay and not self.file_okay: + self.name = _("directory") + else: + self.name = _("path") + + def to_info_dict(self) -> t.Dict[str, t.Any]: + info_dict = super().to_info_dict() + info_dict.update( + exists=self.exists, + file_okay=self.file_okay, + dir_okay=self.dir_okay, + writable=self.writable, + readable=self.readable, + allow_dash=self.allow_dash, + ) + return info_dict + + def coerce_path_result(self, rv: t.Any) -> t.Any: + if self.type is not None and not isinstance(rv, self.type): + if self.type is str: + rv = os.fsdecode(rv) + elif self.type is bytes: + rv = os.fsencode(rv) + else: + rv = self.type(rv) + + return rv + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + rv = value + + is_dash = self.file_okay and self.allow_dash and rv in (b"-", "-") + + if not is_dash: + if self.resolve_path: + # realpath on Windows Python < 3.8 doesn't resolve symlinks + if os.path.islink(rv): + rv = os.readlink(rv) + + rv = os.path.realpath(rv) + + try: + st = os.stat(rv) + except OSError: + if not self.exists: + return self.coerce_path_result(rv) + self.fail( + _("{name} {filename!r} does not exist.").format( + name=self.name.title(), filename=os.fsdecode(value) + ), + param, + ctx, + ) + + if not self.file_okay and stat.S_ISREG(st.st_mode): + self.fail( + _("{name} {filename!r} is a file.").format( + name=self.name.title(), filename=os.fsdecode(value) + ), + param, + ctx, + ) + if not self.dir_okay and stat.S_ISDIR(st.st_mode): + self.fail( + _("{name} {filename!r} is a directory.").format( + name=self.name.title(), filename=os.fsdecode(value) + ), + param, + ctx, + ) + if self.writable and not os.access(value, os.W_OK): + self.fail( + _("{name} {filename!r} is not writable.").format( + name=self.name.title(), filename=os.fsdecode(value) + ), + param, + ctx, + ) + if self.readable and not os.access(value, os.R_OK): + self.fail( + _("{name} {filename!r} is not readable.").format( + name=self.name.title(), filename=os.fsdecode(value) + ), + param, + ctx, + ) + + return self.coerce_path_result(rv) + + def shell_complete( + self, ctx: "Context", param: "Parameter", incomplete: str + ) -> t.List["CompletionItem"]: + """Return a special completion marker that tells the completion + system to use the shell to provide path completions for only + directories or any paths. + + :param ctx: Invocation context for this command. + :param param: The parameter that is requesting completion. + :param incomplete: Value being completed. May be empty. + + .. versionadded:: 8.0 + """ + from click.shell_completion import CompletionItem + + type = "dir" if self.dir_okay and not self.file_okay else "file" + return [CompletionItem(incomplete, type=type)] + + +class Tuple(CompositeParamType): + """The default behavior of Click is to apply a type on a value directly. + This works well in most cases, except for when `nargs` is set to a fixed + count and different types should be used for different items. In this + case the :class:`Tuple` type can be used. This type can only be used + if `nargs` is set to a fixed number. + + For more information see :ref:`tuple-type`. + + This can be selected by using a Python tuple literal as a type. + + :param types: a list of types that should be used for the tuple items. + """ + + def __init__(self, types: t.Sequence[t.Union[t.Type, ParamType]]) -> None: + self.types = [convert_type(ty) for ty in types] + + def to_info_dict(self) -> t.Dict[str, t.Any]: + info_dict = super().to_info_dict() + info_dict["types"] = [t.to_info_dict() for t in self.types] + return info_dict + + @property + def name(self) -> str: # type: ignore + return f"<{' '.join(ty.name for ty in self.types)}>" + + @property + def arity(self) -> int: # type: ignore + return len(self.types) + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + len_type = len(self.types) + len_value = len(value) + + if len_value != len_type: + self.fail( + ngettext( + "{len_type} values are required, but {len_value} was given.", + "{len_type} values are required, but {len_value} were given.", + len_value, + ).format(len_type=len_type, len_value=len_value), + param=param, + ctx=ctx, + ) + + return tuple(ty(x, param, ctx) for ty, x in zip(self.types, value)) + + +def convert_type(ty: t.Optional[t.Any], default: t.Optional[t.Any] = None) -> ParamType: + """Find the most appropriate :class:`ParamType` for the given Python + type. If the type isn't provided, it can be inferred from a default + value. + """ + guessed_type = False + + if ty is None and default is not None: + if isinstance(default, (tuple, list)): + # If the default is empty, ty will remain None and will + # return STRING. + if default: + item = default[0] + + # A tuple of tuples needs to detect the inner types. + # Can't call convert recursively because that would + # incorrectly unwind the tuple to a single type. + if isinstance(item, (tuple, list)): + ty = tuple(map(type, item)) + else: + ty = type(item) + else: + ty = type(default) + + guessed_type = True + + if isinstance(ty, tuple): + return Tuple(ty) + + if isinstance(ty, ParamType): + return ty + + if ty is str or ty is None: + return STRING + + if ty is int: + return INT + + if ty is float: + return FLOAT + + if ty is bool: + return BOOL + + if guessed_type: + return STRING + + if __debug__: + try: + if issubclass(ty, ParamType): + raise AssertionError( + f"Attempted to use an uninstantiated parameter type ({ty})." + ) + except TypeError: + # ty is an instance (correct), so issubclass fails. + pass + + return FuncParamType(ty) + + +#: A dummy parameter type that just does nothing. From a user's +#: perspective this appears to just be the same as `STRING` but +#: internally no string conversion takes place if the input was bytes. +#: This is usually useful when working with file paths as they can +#: appear in bytes and unicode. +#: +#: For path related uses the :class:`Path` type is a better choice but +#: there are situations where an unprocessed type is useful which is why +#: it is is provided. +#: +#: .. versionadded:: 4.0 +UNPROCESSED = UnprocessedParamType() + +#: A unicode string parameter type which is the implicit default. This +#: can also be selected by using ``str`` as type. +STRING = StringParamType() + +#: An integer parameter. This can also be selected by using ``int`` as +#: type. +INT = IntParamType() + +#: A floating point value parameter. This can also be selected by using +#: ``float`` as type. +FLOAT = FloatParamType() + +#: A boolean parameter. This is the default for boolean flags. This can +#: also be selected by using ``bool`` as a type. +BOOL = BoolParamType() + +#: A UUID parameter. +UUID = UUIDParameterType() diff --git a/venv/Lib/site-packages/click/utils.py b/venv/Lib/site-packages/click/utils.py new file mode 100644 index 0000000..91a372d --- /dev/null +++ b/venv/Lib/site-packages/click/utils.py @@ -0,0 +1,579 @@ +import os +import sys +import typing as t +from functools import update_wrapper +from types import ModuleType + +from ._compat import _default_text_stderr +from ._compat import _default_text_stdout +from ._compat import _find_binary_writer +from ._compat import auto_wrap_for_ansi +from ._compat import binary_streams +from ._compat import get_filesystem_encoding +from ._compat import open_stream +from ._compat import should_strip_ansi +from ._compat import strip_ansi +from ._compat import text_streams +from ._compat import WIN +from .globals import resolve_color_default + +if t.TYPE_CHECKING: + import typing_extensions as te + +F = t.TypeVar("F", bound=t.Callable[..., t.Any]) + + +def _posixify(name: str) -> str: + return "-".join(name.split()).lower() + + +def safecall(func: F) -> F: + """Wraps a function so that it swallows exceptions.""" + + def wrapper(*args, **kwargs): # type: ignore + try: + return func(*args, **kwargs) + except Exception: + pass + + return update_wrapper(t.cast(F, wrapper), func) + + +def make_str(value: t.Any) -> str: + """Converts a value into a valid string.""" + if isinstance(value, bytes): + try: + return value.decode(get_filesystem_encoding()) + except UnicodeError: + return value.decode("utf-8", "replace") + return str(value) + + +def make_default_short_help(help: str, max_length: int = 45) -> str: + """Returns a condensed version of help string.""" + # Consider only the first paragraph. + paragraph_end = help.find("\n\n") + + if paragraph_end != -1: + help = help[:paragraph_end] + + # Collapse newlines, tabs, and spaces. + words = help.split() + + if not words: + return "" + + # The first paragraph started with a "no rewrap" marker, ignore it. + if words[0] == "\b": + words = words[1:] + + total_length = 0 + last_index = len(words) - 1 + + for i, word in enumerate(words): + total_length += len(word) + (i > 0) + + if total_length > max_length: # too long, truncate + break + + if word[-1] == ".": # sentence end, truncate without "..." + return " ".join(words[: i + 1]) + + if total_length == max_length and i != last_index: + break # not at sentence end, truncate with "..." + else: + return " ".join(words) # no truncation needed + + # Account for the length of the suffix. + total_length += len("...") + + # remove words until the length is short enough + while i > 0: + total_length -= len(words[i]) + (i > 0) + + if total_length <= max_length: + break + + i -= 1 + + return " ".join(words[:i]) + "..." + + +class LazyFile: + """A lazy file works like a regular file but it does not fully open + the file but it does perform some basic checks early to see if the + filename parameter does make sense. This is useful for safely opening + files for writing. + """ + + def __init__( + self, + filename: str, + mode: str = "r", + encoding: t.Optional[str] = None, + errors: t.Optional[str] = "strict", + atomic: bool = False, + ): + self.name = filename + self.mode = mode + self.encoding = encoding + self.errors = errors + self.atomic = atomic + self._f: t.Optional[t.IO] + + if filename == "-": + self._f, self.should_close = open_stream(filename, mode, encoding, errors) + else: + if "r" in mode: + # Open and close the file in case we're opening it for + # reading so that we can catch at least some errors in + # some cases early. + open(filename, mode).close() + self._f = None + self.should_close = True + + def __getattr__(self, name: str) -> t.Any: + return getattr(self.open(), name) + + def __repr__(self) -> str: + if self._f is not None: + return repr(self._f) + return f"" + + def open(self) -> t.IO: + """Opens the file if it's not yet open. This call might fail with + a :exc:`FileError`. Not handling this error will produce an error + that Click shows. + """ + if self._f is not None: + return self._f + try: + rv, self.should_close = open_stream( + self.name, self.mode, self.encoding, self.errors, atomic=self.atomic + ) + except OSError as e: # noqa: E402 + from .exceptions import FileError + + raise FileError(self.name, hint=e.strerror) + self._f = rv + return rv + + def close(self) -> None: + """Closes the underlying file, no matter what.""" + if self._f is not None: + self._f.close() + + def close_intelligently(self) -> None: + """This function only closes the file if it was opened by the lazy + file wrapper. For instance this will never close stdin. + """ + if self.should_close: + self.close() + + def __enter__(self) -> "LazyFile": + return self + + def __exit__(self, exc_type, exc_value, tb): # type: ignore + self.close_intelligently() + + def __iter__(self) -> t.Iterator[t.AnyStr]: + self.open() + return iter(self._f) # type: ignore + + +class KeepOpenFile: + def __init__(self, file: t.IO) -> None: + self._file = file + + def __getattr__(self, name: str) -> t.Any: + return getattr(self._file, name) + + def __enter__(self) -> "KeepOpenFile": + return self + + def __exit__(self, exc_type, exc_value, tb): # type: ignore + pass + + def __repr__(self) -> str: + return repr(self._file) + + def __iter__(self) -> t.Iterator[t.AnyStr]: + return iter(self._file) + + +def echo( + message: t.Optional[t.Any] = None, + file: t.Optional[t.IO] = None, + nl: bool = True, + err: bool = False, + color: t.Optional[bool] = None, +) -> None: + """Print a message and newline to stdout or a file. This should be + used instead of :func:`print` because it provides better support + for different data, files, and environments. + + Compared to :func:`print`, this does the following: + + - Ensures that the output encoding is not misconfigured on Linux. + - Supports Unicode in the Windows console. + - Supports writing to binary outputs, and supports writing bytes + to text outputs. + - Supports colors and styles on Windows. + - Removes ANSI color and style codes if the output does not look + like an interactive terminal. + - Always flushes the output. + + :param message: The string or bytes to output. Other objects are + converted to strings. + :param file: The file to write to. Defaults to ``stdout``. + :param err: Write to ``stderr`` instead of ``stdout``. + :param nl: Print a newline after the message. Enabled by default. + :param color: Force showing or hiding colors and other styles. By + default Click will remove color if the output does not look like + an interactive terminal. + + .. versionchanged:: 6.0 + Support Unicode output on the Windows console. Click does not + modify ``sys.stdout``, so ``sys.stdout.write()`` and ``print()`` + will still not support Unicode. + + .. versionchanged:: 4.0 + Added the ``color`` parameter. + + .. versionadded:: 3.0 + Added the ``err`` parameter. + + .. versionchanged:: 2.0 + Support colors on Windows if colorama is installed. + """ + if file is None: + if err: + file = _default_text_stderr() + else: + file = _default_text_stdout() + + # Convert non bytes/text into the native string type. + if message is not None and not isinstance(message, (str, bytes, bytearray)): + out: t.Optional[t.Union[str, bytes]] = str(message) + else: + out = message + + if nl: + out = out or "" + if isinstance(out, str): + out += "\n" + else: + out += b"\n" + + if not out: + file.flush() + return + + # If there is a message and the value looks like bytes, we manually + # need to find the binary stream and write the message in there. + # This is done separately so that most stream types will work as you + # would expect. Eg: you can write to StringIO for other cases. + if isinstance(out, (bytes, bytearray)): + binary_file = _find_binary_writer(file) + + if binary_file is not None: + file.flush() + binary_file.write(out) + binary_file.flush() + return + + # ANSI style code support. For no message or bytes, nothing happens. + # When outputting to a file instead of a terminal, strip codes. + else: + color = resolve_color_default(color) + + if should_strip_ansi(file, color): + out = strip_ansi(out) + elif WIN: + if auto_wrap_for_ansi is not None: + file = auto_wrap_for_ansi(file) # type: ignore + elif not color: + out = strip_ansi(out) + + file.write(out) # type: ignore + file.flush() + + +def get_binary_stream(name: "te.Literal['stdin', 'stdout', 'stderr']") -> t.BinaryIO: + """Returns a system stream for byte processing. + + :param name: the name of the stream to open. Valid names are ``'stdin'``, + ``'stdout'`` and ``'stderr'`` + """ + opener = binary_streams.get(name) + if opener is None: + raise TypeError(f"Unknown standard stream '{name}'") + return opener() + + +def get_text_stream( + name: "te.Literal['stdin', 'stdout', 'stderr']", + encoding: t.Optional[str] = None, + errors: t.Optional[str] = "strict", +) -> t.TextIO: + """Returns a system stream for text processing. This usually returns + a wrapped stream around a binary stream returned from + :func:`get_binary_stream` but it also can take shortcuts for already + correctly configured streams. + + :param name: the name of the stream to open. Valid names are ``'stdin'``, + ``'stdout'`` and ``'stderr'`` + :param encoding: overrides the detected default encoding. + :param errors: overrides the default error mode. + """ + opener = text_streams.get(name) + if opener is None: + raise TypeError(f"Unknown standard stream '{name}'") + return opener(encoding, errors) + + +def open_file( + filename: str, + mode: str = "r", + encoding: t.Optional[str] = None, + errors: t.Optional[str] = "strict", + lazy: bool = False, + atomic: bool = False, +) -> t.IO: + """This is similar to how the :class:`File` works but for manual + usage. Files are opened non lazy by default. This can open regular + files as well as stdin/stdout if ``'-'`` is passed. + + If stdin/stdout is returned the stream is wrapped so that the context + manager will not close the stream accidentally. This makes it possible + to always use the function like this without having to worry to + accidentally close a standard stream:: + + with open_file(filename) as f: + ... + + .. versionadded:: 3.0 + + :param filename: the name of the file to open (or ``'-'`` for stdin/stdout). + :param mode: the mode in which to open the file. + :param encoding: the encoding to use. + :param errors: the error handling for this file. + :param lazy: can be flipped to true to open the file lazily. + :param atomic: in atomic mode writes go into a temporary file and it's + moved on close. + """ + if lazy: + return t.cast(t.IO, LazyFile(filename, mode, encoding, errors, atomic=atomic)) + f, should_close = open_stream(filename, mode, encoding, errors, atomic=atomic) + if not should_close: + f = t.cast(t.IO, KeepOpenFile(f)) + return f + + +def get_os_args() -> t.Sequence[str]: + """Returns the argument part of ``sys.argv``, removing the first + value which is the name of the script. + + .. deprecated:: 8.0 + Will be removed in Click 8.1. Access ``sys.argv[1:]`` directly + instead. + """ + import warnings + + warnings.warn( + "'get_os_args' is deprecated and will be removed in Click 8.1." + " Access 'sys.argv[1:]' directly instead.", + DeprecationWarning, + stacklevel=2, + ) + return sys.argv[1:] + + +def format_filename( + filename: t.Union[str, bytes, os.PathLike], shorten: bool = False +) -> str: + """Formats a filename for user display. The main purpose of this + function is to ensure that the filename can be displayed at all. This + will decode the filename to unicode if necessary in a way that it will + not fail. Optionally, it can shorten the filename to not include the + full path to the filename. + + :param filename: formats a filename for UI display. This will also convert + the filename into unicode without failing. + :param shorten: this optionally shortens the filename to strip of the + path that leads up to it. + """ + if shorten: + filename = os.path.basename(filename) + + return os.fsdecode(filename) + + +def get_app_dir(app_name: str, roaming: bool = True, force_posix: bool = False) -> str: + r"""Returns the config folder for the application. The default behavior + is to return whatever is most appropriate for the operating system. + + To give you an idea, for an app called ``"Foo Bar"``, something like + the following folders could be returned: + + Mac OS X: + ``~/Library/Application Support/Foo Bar`` + Mac OS X (POSIX): + ``~/.foo-bar`` + Unix: + ``~/.config/foo-bar`` + Unix (POSIX): + ``~/.foo-bar`` + Windows (roaming): + ``C:\Users\\AppData\Roaming\Foo Bar`` + Windows (not roaming): + ``C:\Users\\AppData\Local\Foo Bar`` + + .. versionadded:: 2.0 + + :param app_name: the application name. This should be properly capitalized + and can contain whitespace. + :param roaming: controls if the folder should be roaming or not on Windows. + Has no affect otherwise. + :param force_posix: if this is set to `True` then on any POSIX system the + folder will be stored in the home folder with a leading + dot instead of the XDG config home or darwin's + application support folder. + """ + if WIN: + key = "APPDATA" if roaming else "LOCALAPPDATA" + folder = os.environ.get(key) + if folder is None: + folder = os.path.expanduser("~") + return os.path.join(folder, app_name) + if force_posix: + return os.path.join(os.path.expanduser(f"~/.{_posixify(app_name)}")) + if sys.platform == "darwin": + return os.path.join( + os.path.expanduser("~/Library/Application Support"), app_name + ) + return os.path.join( + os.environ.get("XDG_CONFIG_HOME", os.path.expanduser("~/.config")), + _posixify(app_name), + ) + + +class PacifyFlushWrapper: + """This wrapper is used to catch and suppress BrokenPipeErrors resulting + from ``.flush()`` being called on broken pipe during the shutdown/final-GC + of the Python interpreter. Notably ``.flush()`` is always called on + ``sys.stdout`` and ``sys.stderr``. So as to have minimal impact on any + other cleanup code, and the case where the underlying file is not a broken + pipe, all calls and attributes are proxied. + """ + + def __init__(self, wrapped: t.IO) -> None: + self.wrapped = wrapped + + def flush(self) -> None: + try: + self.wrapped.flush() + except OSError as e: + import errno + + if e.errno != errno.EPIPE: + raise + + def __getattr__(self, attr: str) -> t.Any: + return getattr(self.wrapped, attr) + + +def _detect_program_name( + path: t.Optional[str] = None, _main: ModuleType = sys.modules["__main__"] +) -> str: + """Determine the command used to run the program, for use in help + text. If a file or entry point was executed, the file name is + returned. If ``python -m`` was used to execute a module or package, + ``python -m name`` is returned. + + This doesn't try to be too precise, the goal is to give a concise + name for help text. Files are only shown as their name without the + path. ``python`` is only shown for modules, and the full path to + ``sys.executable`` is not shown. + + :param path: The Python file being executed. Python puts this in + ``sys.argv[0]``, which is used by default. + :param _main: The ``__main__`` module. This should only be passed + during internal testing. + + .. versionadded:: 8.0 + Based on command args detection in the Werkzeug reloader. + + :meta private: + """ + if not path: + path = sys.argv[0] + + # The value of __package__ indicates how Python was called. It may + # not exist if a setuptools script is installed as an egg. It may be + # set incorrectly for entry points created with pip on Windows. + if getattr(_main, "__package__", None) is None or ( + os.name == "nt" + and _main.__package__ == "" + and not os.path.exists(path) + and os.path.exists(f"{path}.exe") + ): + # Executed a file, like "python app.py". + return os.path.basename(path) + + # Executed a module, like "python -m example". + # Rewritten by Python from "-m script" to "/path/to/script.py". + # Need to look at main module to determine how it was executed. + py_module = t.cast(str, _main.__package__) + name = os.path.splitext(os.path.basename(path))[0] + + # A submodule like "example.cli". + if name != "__main__": + py_module = f"{py_module}.{name}" + + return f"python -m {py_module.lstrip('.')}" + + +def _expand_args( + args: t.Iterable[str], + *, + user: bool = True, + env: bool = True, + glob_recursive: bool = True, +) -> t.List[str]: + """Simulate Unix shell expansion with Python functions. + + See :func:`glob.glob`, :func:`os.path.expanduser`, and + :func:`os.path.expandvars`. + + This intended for use on Windows, where the shell does not do any + expansion. It may not exactly match what a Unix shell would do. + + :param args: List of command line arguments to expand. + :param user: Expand user home directory. + :param env: Expand environment variables. + :param glob_recursive: ``**`` matches directories recursively. + + .. versionadded:: 8.0 + + :meta private: + """ + from glob import glob + + out = [] + + for arg in args: + if user: + arg = os.path.expanduser(arg) + + if env: + arg = os.path.expandvars(arg) + + matches = glob(arg, recursive=glob_recursive) + + if not matches: + out.append(arg) + else: + out.extend(matches) + + return out diff --git a/venv/Lib/site-packages/colorama-0.4.4.dist-info/INSTALLER b/venv/Lib/site-packages/colorama-0.4.4.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/venv/Lib/site-packages/colorama-0.4.4.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/Lib/site-packages/colorama-0.4.4.dist-info/LICENSE.txt b/venv/Lib/site-packages/colorama-0.4.4.dist-info/LICENSE.txt new file mode 100644 index 0000000..3105888 --- /dev/null +++ b/venv/Lib/site-packages/colorama-0.4.4.dist-info/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-0.4.4.dist-info/METADATA b/venv/Lib/site-packages/colorama-0.4.4.dist-info/METADATA new file mode 100644 index 0000000..2a175c2 --- /dev/null +++ b/venv/Lib/site-packages/colorama-0.4.4.dist-info/METADATA @@ -0,0 +1,415 @@ +Metadata-Version: 2.1 +Name: colorama +Version: 0.4.4 +Summary: Cross-platform colored terminal text. +Home-page: https://github.com/tartley/colorama +Author: Jonathan Hartley +Author-email: tartley@tartley.com +Maintainer: Arnon Yaari +License: BSD +Keywords: color colour terminal text ansi windows crossplatform xplatform +Platform: UNKNOWN +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.5 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Topic :: Terminals +Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.* + +.. 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://travis-ci.org/tartley/colorama.svg?branch=master + :target: https://travis-ci.org/tartley/colorama + :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 +------------ + +.. 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.init()``. + +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 +.............. + +Applications should initialise Colorama using: + +.. code-block:: python + + from colorama import init + init() + +On Windows, calling ``init()`` will filter ANSI escape sequences out of any +text sent to ``stdout`` or ``stderr``, and replace them with equivalent Win32 +calls. + +On other platforms, calling ``init()`` has no effect (unless you request other +optional functionality; see "Init Keyword Args", below). By design, this permits +applications to call ``init()`` unconditionally on all platforms, after which +ANSI output should just work. + +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). + + +Colored Output +.............. + +Cross-platform printing of colored text can then be done using Colorama's +constant shorthand for ANSI escape sequences: + +.. 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 `_ +or the fabulous `Blessings `_. +This is highly recommended for anything more than trivial coloring: + +.. code-block:: python + + from colorama import init + from termcolor import colored + + # use Colorama to make Termcolor work on Windows too + init() + + # 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. + + +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 presumably valid ANSI sequences aren't recognised (see details below), +but to my knowledge nobody has yet complained about this. Puzzling. + +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. + + +License +------- + +Copyright Jonathan Hartley & Arnon Yaari, 2013-2020. BSD 3-Clause license; see +LICENSE file. + + +Development +----------- + +Help and fixes welcome! + +Tested on CPython 2.7, 3.5, 3.6, 3.7 and 3.8. + +No requirements other than the standard library. +Development requirements are captured in requirements-dev.txt. + +To create and populate a virtual environment:: + + ./bootstrap.ps1 # Windows + make bootstrap # Linux + +To run tests:: + + ./test.ps1 # Windows + make test # Linux + +If you use nose to run the tests, you must pass the ``-s`` flag; otherwise, +``nosetests`` applies its own proxy to ``stdout``, which confuses the unit +tests. + + +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 +------ + +* 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.4.dist-info/RECORD b/venv/Lib/site-packages/colorama-0.4.4.dist-info/RECORD new file mode 100644 index 0000000..3516c65 --- /dev/null +++ b/venv/Lib/site-packages/colorama-0.4.4.dist-info/RECORD @@ -0,0 +1,18 @@ +colorama-0.4.4.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +colorama-0.4.4.dist-info/LICENSE.txt,sha256=ysNcAmhuXQSlpxQL-zs25zrtSWZW6JEQLkKIhteTAxg,1491 +colorama-0.4.4.dist-info/METADATA,sha256=JmU7ePpEh1xcqZV0JKcrrlU7cp5o4InDlHJXbo_FTQw,14551 +colorama-0.4.4.dist-info/RECORD,, +colorama-0.4.4.dist-info/WHEEL,sha256=gxPaqcqKPLUXaSAKwmfHO7_iAOlVvmp33DewnUluBB8,116 +colorama-0.4.4.dist-info/top_level.txt,sha256=_Kx6-Cni2BT1PEATPhrSRxo0d7kSgfBbHf5o7IF1ABw,9 +colorama/__init__.py,sha256=pCdErryzLSzDW5P-rRPBlPLqbBtIRNJB6cMgoeJns5k,239 +colorama/__pycache__/__init__.cpython-39.pyc,, +colorama/__pycache__/ansi.cpython-39.pyc,, +colorama/__pycache__/ansitowin32.cpython-39.pyc,, +colorama/__pycache__/initialise.cpython-39.pyc,, +colorama/__pycache__/win32.cpython-39.pyc,, +colorama/__pycache__/winterm.cpython-39.pyc,, +colorama/ansi.py,sha256=Top4EeEuaQdBWdteKMEcGOTeKeF19Q-Wo_6_Cj5kOzQ,2522 +colorama/ansitowin32.py,sha256=yV7CEmCb19MjnJKODZEEvMH_fnbJhwnpzo4sxZuGXmA,10517 +colorama/initialise.py,sha256=PprovDNxMTrvoNHFcL2NZjpH2XzDc8BLxLxiErfUl4k,1915 +colorama/win32.py,sha256=bJ8Il9jwaBN5BJ8bmN6FoYZ1QYuMKv2j8fGrXh7TJjw,5404 +colorama/winterm.py,sha256=2y_2b7Zsv34feAsP67mLOVc-Bgq51mdYGo571VprlrM,6438 diff --git a/venv/Lib/site-packages/colorama-0.4.4.dist-info/WHEEL b/venv/Lib/site-packages/colorama-0.4.4.dist-info/WHEEL new file mode 100644 index 0000000..6d38aa0 --- /dev/null +++ b/venv/Lib/site-packages/colorama-0.4.4.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.35.1) +Root-Is-Purelib: true +Tag: py2-none-any +Tag: py3-none-any + diff --git a/venv/Lib/site-packages/colorama-0.4.4.dist-info/top_level.txt b/venv/Lib/site-packages/colorama-0.4.4.dist-info/top_level.txt new file mode 100644 index 0000000..3fcfb51 --- /dev/null +++ b/venv/Lib/site-packages/colorama-0.4.4.dist-info/top_level.txt @@ -0,0 +1 @@ +colorama diff --git a/venv/Lib/site-packages/colorama/__init__.py b/venv/Lib/site-packages/colorama/__init__.py new file mode 100644 index 0000000..b149ed7 --- /dev/null +++ b/venv/Lib/site-packages/colorama/__init__.py @@ -0,0 +1,6 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. +from .initialise import init, deinit, reinit, colorama_text +from .ansi import Fore, Back, Style, Cursor +from .ansitowin32 import AnsiToWin32 + +__version__ = '0.4.4' diff --git a/venv/Lib/site-packages/colorama/__pycache__/__init__.cpython-39.pyc b/venv/Lib/site-packages/colorama/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2a7897f1b16af5b41df7dad47a56b7556c55f69e GIT binary patch literal 406 zcmYjNO-lnY5Z&x%xBJn>YyZL?tfD=MNVOs!M1=}MSctJ3B${kWvTdz*e~f=AS5M-> zyC*Xi!GXNVo0&Y`gq39;_()$r@4kF~2l78cPsaY(Qx8qhAQ>n`kP1ntA`&S~FoJq0 zW0jCZ^+->pBvl#7e(RCURY3}W$E4rH)1XQ|ke8~k!=e?POB%k(W|PaAmfA4IXvd#B z7(3I3JAA@sPcEKzYspL2X8xH;fcF4WfDAeXbUM8PK?7go3QCvUSfN`=-vYA*qX6II$w7T>^dp~jw!WJ4 z_FOAoFL}GHFT{QAW%%~oyJipE)_+^=Pzv5C9nRM-Jyg0`NPYw!E!Y{L0qTc89~Ntb literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/colorama/__pycache__/ansi.cpython-39.pyc b/venv/Lib/site-packages/colorama/__pycache__/ansi.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f35eb9b44e6ec82548788f5059b1449ecfc43cfa GIT binary patch literal 3191 zcmc&$-EY%Y6!*2A#Bn|wN;_cMvM-HnBh{F+2SBJvnzbDvshUuq5zPKwqM>K+h^V4fJI?2lSkxGb}aIoGeQN=2!+W z&uqXMI^WhxSNDInH+KA>xI3VGJyzUiea1b(f?}oFUM+TaJnnS` zDsg`SZTFNmjwzdV&5l&{{64!pp=cHr!8&oFuZyq(*l>A8oOXvsM0U7CUbYb95md~XlniMGtoKx!eQ%e^*h{ascPiSJ z>-v3Pxb9Un4im1KX=1~@UpV%rJU-L}C*dC`&vm8kx>29PKJB_M_Pk!i;h%t*iC|;( zbCk_7%vWHkGO~!GPlX!ewTOrA0vv}rLt$pYL^5br%`F0RE2kzGfytGp7EP+jHA8Kh z!pTL`kd%V7l^c6J81N9AxQm$_U${qgDmBMlO{TiK5*4h@ufb7C`V+3H7ST7?z5>os zZ|rA2Xe@88T|i$oDp`~{t;$?#Z8g81P?noehP(@?tDUbd@l$JktFpdSn$Q^4g$ii- zh(|w zCHy9W>XQ$kI_7g=dUlTM1-PMW5i3{5*(nsIV8>*Q$;-pYBWKxdpwv;e(yNm>|#p9Y+T*x@1i z6&4ZDuMkZNF(x4z9Af!~=xgX8JVfXqJVN*t!9{qC-~lubXQx6XW7P)aTN>;2B^g^@ zuh*M0{-EAyw6>&KZEV(M;(leh-rT52v-Y6Ul(DUq)s4E0t=HT24Vh`IF0X7T5pMk# znNlQ(LL%ZRkw|70FA|M(`slBlb)<5NI#Tt4jVh6fWGU4^o|(oUz`yTd1sEiYnA5j$ z@(IWD|4(|W@C6cH2lXMAX!s2R)@k@10v2mnMp!^VmqLob5S}7zBkUmf2+t6n12lOZ z@XY9J0b?1~e~k{FKR-G+1kNvhDCBtbrf!SZ@VSEp%d-IC90Dw@SxQV7V!XPJwIq$D z)%!BuY^~p~G-OhhnOkWz&h!f1$68mf)p5=_KE>gID#t$snQ&b?=(;Y)4|a~d&Ql0U z1pL_I`1!-}<3q*=Pw*R3y*$+%P}RaQC`!6Cu|=UlAd@ntyp-{p8rWyqDXlxvYy7Sb c5`rRt8B^tGSa92hZRS(?OnwgTc%0z<4^staS^xk5 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/colorama/__pycache__/ansitowin32.cpython-39.pyc b/venv/Lib/site-packages/colorama/__pycache__/ansitowin32.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bc387421b8c6b33bf281d669218f927cd29b1815 GIT binary patch literal 7657 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/colorama/__pycache__/initialise.cpython-39.pyc b/venv/Lib/site-packages/colorama/__pycache__/initialise.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3104eda9bad58e66560d374c6761a22e854331e7 GIT binary patch literal 1673 zcma)6UymC#5VyVF&1RGA{V5bU6%_RWX{GWe@sj3O`vfP#lDToVW4vkZ#d@oDL)q%48vph*)!hD0tnEfZdH&^M%L+S?&>BO zYurc`#WMy7#!~(J)xA5zAD}ukV`qoo&rJDEH8sN{QyvY!%O4GG?#u@>HGZrn#tz3- zQGuGOkMc5ixhisNKAfFEgs1JISi+O>nERRj*qbzkjmG5X4P z!=B?B|KwNh#9^Q$>QF^A#{XpSsb;ECUU#vRv;+4Ar=es{i~`jlU+*(XlC>(DJ`W|CWn>x5<|>!zB!%ICIjVC+W*Tpi+x`K*?7`ck z$IvE~lIoDUq~dbqV~n(+C~xDsr3vCxM7-L=@kTsU5_Cd^VsD`$O65qZzxbb+feW@&C6aFv#(V3_vrWrH&_?Y^&;Cx)2RRN4JGwu%E>BAE#7d#g5 zF3xO(hc>&c+AbRZ7p^VzU4v_f%-6#O9xh*68*ovE$Hc}}BhFk_;rnPTB-~t8;Udud z5BW4)gy;N%!OcZ@D4rsmSR;bolH<@EK!4TiC^?05|ntqmdX8Iub=I}HC(O}q&nrG$RBx_>XyQn(^bdx za{AYA$vDq552lBZUMXKVS2?fKmq$%iiH{ukBmyDluU!k%Gu87T{%l(5d0{@M-1<6- Xk)5$<2N<^S62xJ0BW_?7#c|_5kNH;C literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/colorama/__pycache__/win32.cpython-39.pyc b/venv/Lib/site-packages/colorama/__pycache__/win32.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f08cfb7ad4ec268a7403ab5dc0134041d54bd7ef GIT binary patch literal 3905 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/colorama/__pycache__/winterm.cpython-39.pyc b/venv/Lib/site-packages/colorama/__pycache__/winterm.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e11022dc5b45f063f33f403a555f1443cee027f8 GIT binary patch literal 4627 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 c=#OkIaM3%I`&sgT$LACVeKs#z> 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) diff --git a/venv/Lib/site-packages/detach-1.0-py3.9.egg-info/PKG-INFO b/venv/Lib/site-packages/detach-1.0-py3.9.egg-info/PKG-INFO new file mode 100644 index 0000000..9bf914f --- /dev/null +++ b/venv/Lib/site-packages/detach-1.0-py3.9.egg-info/PKG-INFO @@ -0,0 +1,18 @@ +Metadata-Version: 2.1 +Name: detach +Version: 1.0 +Summary: Fork and detach the current processe. +Home-page: https://github.com/bluedragonx/detach +Author: Ryan Bourgeois +Author-email: bluedragonx@gmail.com +License: BSD-derived +Keywords: fork daemon detach +Platform: UNKNOWN +Classifier: Intended Audience :: Developers +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 2.7 +Classifier: License :: OSI Approved :: BSD License + +UNKNOWN + diff --git a/venv/Lib/site-packages/detach-1.0-py3.9.egg-info/SOURCES.txt b/venv/Lib/site-packages/detach-1.0-py3.9.egg-info/SOURCES.txt new file mode 100644 index 0000000..82a5f38 --- /dev/null +++ b/venv/Lib/site-packages/detach-1.0-py3.9.egg-info/SOURCES.txt @@ -0,0 +1,7 @@ +detach.py +setup.cfg +setup.py +detach.egg-info/PKG-INFO +detach.egg-info/SOURCES.txt +detach.egg-info/dependency_links.txt +detach.egg-info/top_level.txt \ No newline at end of file diff --git a/venv/Lib/site-packages/detach-1.0-py3.9.egg-info/dependency_links.txt b/venv/Lib/site-packages/detach-1.0-py3.9.egg-info/dependency_links.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/venv/Lib/site-packages/detach-1.0-py3.9.egg-info/dependency_links.txt @@ -0,0 +1 @@ + diff --git a/venv/Lib/site-packages/detach-1.0-py3.9.egg-info/installed-files.txt b/venv/Lib/site-packages/detach-1.0-py3.9.egg-info/installed-files.txt new file mode 100644 index 0000000..c44d3dd --- /dev/null +++ b/venv/Lib/site-packages/detach-1.0-py3.9.egg-info/installed-files.txt @@ -0,0 +1,6 @@ +..\__pycache__\detach.cpython-39.pyc +..\detach.py +PKG-INFO +SOURCES.txt +dependency_links.txt +top_level.txt diff --git a/venv/Lib/site-packages/detach-1.0-py3.9.egg-info/top_level.txt b/venv/Lib/site-packages/detach-1.0-py3.9.egg-info/top_level.txt new file mode 100644 index 0000000..8ddea54 --- /dev/null +++ b/venv/Lib/site-packages/detach-1.0-py3.9.egg-info/top_level.txt @@ -0,0 +1 @@ +detach diff --git a/venv/Lib/site-packages/detach.py b/venv/Lib/site-packages/detach.py new file mode 100644 index 0000000..1594b6c --- /dev/null +++ b/venv/Lib/site-packages/detach.py @@ -0,0 +1,135 @@ +"""Fork and detach the current process.""" +import errno +import os +import resource +import subprocess +import sys +import traceback +from multiprocessing import Value + +maxfd = 2048 + + +class Error(Exception): + """Raised on error.""" + + +class Detach(object): + + def __init__(self, stdout=None, stderr=None, stdin=None, close_fds=False, exclude_fds=None, + daemonize=False): + """ + Fork and detach a process. The stdio streams of the child default to /dev/null but may be + overridden with the `stdout`, `stderr`, and `stdin` parameters. If `close_fds` is True then + all open file descriptors (except those passed as overrides for stdio) are closed by the + child process. File descriptors in `exclude_fds` will not be closed. If `daemonize` is True + then the parent process exits. + """ + self.stdout = stdout + self.stderr = stderr + self.stdin = stdin + self.close_fds = close_fds + self.exclude_fds = set() + self.daemonize = daemonize + self.pid = None + self.shared_pid = Value('i', 0) + + for item in list(exclude_fds or []) + [stdout, stderr, stdin]: + if hasattr(item, 'fileno'): + item = item.fileno() + self.exclude_fds.add(item) + + def _get_max_fd(self): + """Return the maximum file descriptor value.""" + limits = resource.getrlimit(resource.RLIMIT_NOFILE) + result = limits[1] + if result == resource.RLIM_INFINITY: + result = maxfd + return result + + def _close_fd(self, fd): + """Close a file descriptor if it is open.""" + try: + os.close(fd) + except OSError, exc: + if exc.errno != errno.EBADF: + msg = "Failed to close file descriptor {}: {}".format(fd, exc) + raise Error(msg) + + def _close_open_fds(self): + """Close open file descriptors.""" + maxfd = self._get_max_fd() + for fd in reversed(range(maxfd)): + if fd not in self.exclude_fds: + self._close_fd(fd) + + def _redirect(self, stream, target): + """Redirect a system stream to the provided target.""" + if target is None: + target_fd = os.open(os.devnull, os.O_RDWR) + else: + target_fd = target.fileno() + os.dup2(target_fd, stream.fileno()) + + def __enter__(self): + """Fork and detach the process.""" + pid = os.fork() + if pid > 0: + # parent + os.waitpid(pid, 0) + self.pid = self.shared_pid.value + else: + # first child + os.setsid() + pid = os.fork() + if pid > 0: + # first child + self.shared_pid.value = pid + os._exit(0) + else: + # second child + if self.close_fds: + self._close_open_fds() + + self._redirect(sys.stdout, self.stdout) + self._redirect(sys.stderr, self.stderr) + self._redirect(sys.stdin, self.stdin) + return self + + def __exit__(self, exc_cls, exc_val, exc_tb): + """Exit processes.""" + if self.daemonize or not self.pid: + if exc_val: + traceback.print_exception(exc_cls, exc_val, exc_tb) + os._exit(0) + + +def call(args, stdout=None, stderr=None, stdin=None, daemonize=False, + preexec_fn=None, shell=False, cwd=None, env=None): + """ + Run an external command in a separate process and detach it from the current process. Excepting + `stdout`, `stderr`, and `stdin` all file descriptors are closed after forking. If `daemonize` + is True then the parent process exits. All stdio is redirected to `os.devnull` unless + specified. The `preexec_fn`, `shell`, `cwd`, and `env` parameters are the same as their `Popen` + counterparts. Return the PID of the child process if not daemonized. + """ + stream = lambda s, m: s is None and os.open(os.devnull, m) or s + stdout = stream(stdout, os.O_WRONLY) + stderr = stream(stderr, os.O_WRONLY) + stdin = stream(stdin, os.O_RDONLY) + + shared_pid = Value('i', 0) + pid = os.fork() + if pid > 0: + os.waitpid(pid, 0) + child_pid = shared_pid.value + del shared_pid + if daemonize: + sys.exit(0) + return child_pid + else: + os.setsid() + proc = subprocess.Popen(args, stdout=stdout, stderr=stderr, stdin=stdin, close_fds=True, + preexec_fn=preexec_fn, shell=shell, cwd=cwd, env=env) + shared_pid.value = proc.pid + os._exit(0) diff --git a/venv/Lib/site-packages/distutils-precedence.pth b/venv/Lib/site-packages/distutils-precedence.pth new file mode 100644 index 0000000..6de4198 --- /dev/null +++ b/venv/Lib/site-packages/distutils-precedence.pth @@ -0,0 +1 @@ +import os; var = 'SETUPTOOLS_USE_DISTUTILS'; enabled = os.environ.get(var, 'stdlib') == 'local'; enabled and __import__('_distutils_hack').add_shim(); diff --git a/venv/Lib/site-packages/flask/__init__.py b/venv/Lib/site-packages/flask/__init__.py new file mode 100644 index 0000000..c5da045 --- /dev/null +++ b/venv/Lib/site-packages/flask/__init__.py @@ -0,0 +1,46 @@ +from markupsafe import escape +from markupsafe import Markup +from werkzeug.exceptions import abort as abort +from werkzeug.utils import redirect as redirect + +from . import json as json +from .app import Flask as Flask +from .app import Request as Request +from .app import Response as Response +from .blueprints import Blueprint as Blueprint +from .config import Config as Config +from .ctx import after_this_request as after_this_request +from .ctx import copy_current_request_context as copy_current_request_context +from .ctx import has_app_context as has_app_context +from .ctx import has_request_context as has_request_context +from .globals import _app_ctx_stack as _app_ctx_stack +from .globals import _request_ctx_stack as _request_ctx_stack +from .globals import current_app as current_app +from .globals import g as g +from .globals import request as request +from .globals import session as session +from .helpers import flash as flash +from .helpers import get_flashed_messages as get_flashed_messages +from .helpers import get_template_attribute as get_template_attribute +from .helpers import make_response as make_response +from .helpers import safe_join as safe_join +from .helpers import send_file as send_file +from .helpers import send_from_directory as send_from_directory +from .helpers import stream_with_context as stream_with_context +from .helpers import url_for as url_for +from .json import jsonify as jsonify +from .signals import appcontext_popped as appcontext_popped +from .signals import appcontext_pushed as appcontext_pushed +from .signals import appcontext_tearing_down as appcontext_tearing_down +from .signals import before_render_template as before_render_template +from .signals import got_request_exception as got_request_exception +from .signals import message_flashed as message_flashed +from .signals import request_finished as request_finished +from .signals import request_started as request_started +from .signals import request_tearing_down as request_tearing_down +from .signals import signals_available as signals_available +from .signals import template_rendered as template_rendered +from .templating import render_template as render_template +from .templating import render_template_string as render_template_string + +__version__ = "2.0.1" diff --git a/venv/Lib/site-packages/flask/__main__.py b/venv/Lib/site-packages/flask/__main__.py new file mode 100644 index 0000000..4e28416 --- /dev/null +++ b/venv/Lib/site-packages/flask/__main__.py @@ -0,0 +1,3 @@ +from .cli import main + +main() diff --git a/venv/Lib/site-packages/flask/__pycache__/__init__.cpython-39.pyc b/venv/Lib/site-packages/flask/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..23d74f0baac2827441b8600e06e64000fcd47d56 GIT binary patch literal 1861 zcmbu9$!;Sz5QZh$R?FHq%lp2{i@aqfL68JN1_*M*WIQ&QXPB_!o#?s=Fz zOWwfOoCbM=oKjViY{A#a@UM?QOYCA5n{J_yP2ea0$3MUQl}RN2ror?lg@dp0-{g_J zgqKu_APLDNaSAyFDdrh96U@Ra^Q@W+(vU`;^U}%*GLT_*)O?VIEOQ1q2RY_>7Ut`Tk=Pgymx#w$$O#Zj1j5XQP)%CGtoHRa|e55rY4C`q|FF9|tD8sm>P+QW?w zaJt@?R`5W2BEWgKFAe$)@~sSFWAN>;0o?#Tr&|zaV)*p=Fkk- zT%3xu(#0UMqehMQg)pUB;EIbLc!4Gp+t&7&> zkIl~@8}K=&W@x$~^$ZHs?xnu{Acy_d^X816rJ3UM%+UJE=s7f>6564;87!s7n>V57 zN$56)Y-e)zjn1L*l*YE;`PX?G%yjL8E>Lihq(oAt38$~34(?i4$f}amNa`dDBn=dB z22#aR8@))@63H^j3dt(T8p%4z2FWJL7794yy6SDRc1U(f_DJ?g4oD73j!2p$$0R2x zkR7)G%c)PvJwtiQ32}${;9(Hr&xF3F36g)meD`_#XC2+(^}n~h`gbaU(wZLenrVA`7bs89*zF5rv3vO CaQlk@ literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/flask/__pycache__/__main__.cpython-39.pyc b/venv/Lib/site-packages/flask/__pycache__/__main__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..31b79ba694838b333bbad6cb97567ef2939c5ed9 GIT binary patch literal 192 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

    r=#5s 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%0DGNzU=)fu21{V`_IeulJ{%4e&ItaYgNxc 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/flask/__pycache__/cli.cpython-39.pyc b/venv/Lib/site-packages/flask/__pycache__/cli.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c841bfda541a7ba819123e93328ad5dae3ff81d4 GIT binary patch literal 27036 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 F@V~*ehll_G literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/flask/__pycache__/ctx.cpython-39.pyc b/venv/Lib/site-packages/flask/__pycache__/ctx.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..21a538768f1365223388654f8aec75674358fc93 GIT binary patch literal 15519 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 dHQGY_i#19CMgsa#H!2Q+NpTy`J4AKuNC3 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 B-7)|G literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/flask/__pycache__/globals.cpython-39.pyc b/venv/Lib/site-packages/flask/__pycache__/globals.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c1e0f4be82b9602aedb0b38ea8045c91162be881 GIT binary patch literal 1811 zcmcIlUvDEd5V!L`+a&F^Y5M05WFC+RI>I}u5GX2F#8IkC5xP~R#a%n;Hs1AOd(-Ar zUMPH+eg?kDiZ>*r6W@Rr7<H@TtnzrySV>U zVDSkI{Tqm{5km|MhuceHNS=t#}{?QSe4a8BX0Ul@K#w} zc=4LQ=JbYW#cjXs^rq;<>;5|Ep1;A?{70cCyE- zynBQF9k$7L+13Gqb(wFn?oA1-du$u5-IcZHtov*Sti7dmi|sn=W3~s@-2OGes6(>qwEmo_Mq2cT`hF~yNKND2XKdJJrnYb3JsRl zh-)3m9Ll!b4)EFM8hBIO$$uuXEEC~%{qarekA}-;aapuv4P;kvTIXXQ(xyv*l zWGEAz2?N2LFd1esPYktt5cn8CYB5fv@}dM}T)5~NpeE`L=!Dzh^31GIhC*w zbMDzvo)XOs$x>p8`zPvRcW?|~1j|z1>jR%7&TUrjA(ucMiKXI@amC^wwC!{Elt3xN z7{bs5gh3bhdwdPQw+yMCDVg#O{sEiHHNHkSr4hQt$Ng%)87!M72+dWXZCB=HBh$R~ zB1=pZ^FyVi%DWH3xKcr^r+IzK;jE3iE#;Mj#$2@^?=4cBF#T~b{Adh4Z+X(=uTq{I z0aN2SPv+z2(b-r>hJTPkIy&WgJh4B352r=j57GtT5>n2x)aDHew%>+bbpr&#HQaz# zbwU4cBq)1@0S%qNox>dkt`OA*U%s*OEUQ-QU{)Je*g6ZbWkXk(?Op}ucftM;^A1=- z7^}w~qCGr!a8E#S2ZyfVa=d?V*XW`$LQA}A8{*!8R6SVcWk}n{h4pX9n!zMWn62a~ zL_P$xXSag;8|7=uPPcJ+ec2bPXC2PDV0d&m{NnTDXKL3v9Dp+PpxZfbEc59iVdGK( zCa$zYqdN8*wr1A>%2KLa8EaN^-0XvvuLl855?S0wItbKb>$e=Mx2$->lGEHFHV$IR qGQmH#=c3;S;o(-P1*3*R+P74xSG-0Y-bM{taRc{o2{-<1mHz@Ur{}8x literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/flask/__pycache__/helpers.cpython-39.pyc b/venv/Lib/site-packages/flask/__pycache__/helpers.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..94f5b51186e7f4ea38e58534728d994649383881 GIT binary patch literal 27293 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 LlsoFyh$t9IjF3F!T*POPJs^pME%1PPzz1K5? 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 ah6KQm{%~007rEkB_-N|Ndy9V}z5Kt>+J=Y# literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/flask/__pycache__/typing.cpython-39.pyc b/venv/Lib/site-packages/flask/__pycache__/typing.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8d1437263c638b602e35d7a378939a52c93b09ea GIT binary patch literal 1276 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/flask/__pycache__/wrappers.cpython-39.pyc b/venv/Lib/site-packages/flask/__pycache__/wrappers.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3b731cf86d71b15f0b4831f38c50fa9675847a84 GIT binary patch literal 5001 zcmai1TW{RP6(+gdi&m?TRk^}`LJVm)N?CX2p8=|UE})RlGsyHm+ck&&jqXDn!ToL4>Zq`TfqAQZ{fYk^e0+(@hj-0^}Bl3e%obR{G*g75^v3u$JQ24CUKPTuXGH>H*+tE zBmB>MsSrGpjysv)^|F&p0$)12LCPn>NB_i?V;aAe2Hc}{PG1!&e?u=_8f(7L zS&Pj*H;;9Dwdn5@{e%(qubt~Btf>Ez>c7m(@r!Ip^rY6;Z?H+`j_2?J>7R4zCsO?r~3AX~LPcdt@;_bkjgueq_B9xXFIcvbK20 z!(A>COOAQl!WFvG8)H0=*dzwHmK!naz~_gSD@JJuGbFvX8o3EciFG)3B@IrjbYi)Q zweCT5vOf4^sFo;H8DOo=p>-6e)}afbGKP|q!1eM)vG`1-X}sq~R+8>2vCtArl12%a zN{Mw>N>QzrZ>w0-QBwxA7UYOZNTL1=ph)(~1`fH{pYn9nI}{)uFzOXt9aN%76{W^R z#0Ng(m>&dIJR~$N(#+pYC3-(BM3D*n2py9krL~5DRq3pRBoprWL*KJv4C$$^zL!m9Y~hs83G=SHc4A3)e4-O*L#VS4R!GS3!X1H*o>V#FNLlXVVGjp28A8b7;h(@Psn|OQZgB-iu z8VvfdFaY$2`BWIu8oGXfV;2i+j8hcF5+iw}NI(L>zhNvwj5y-*ykHz?FAjJ8$R!qF z9XYm&gHh21=E6b3>dG6)xRNY5Kun}a;VEbXi5qeY+LJ1-6~+vd#IkBXqzPSQT8c$k zs{}$U<5k%;Z`SA)K}vfQcW#q*TW=ZD<F!nDZ{#PE3n(4YUJ4QyLBy)U#(5NHnTG%=6(w&+cA2E!(3;sHEk3g>jj0d+M6EHEQv)~nGiajTLgZYQR5~ntD|v+AvQ&uuQV#7A7OCjRO58oUgK`nvT-?@sUaN&ub5s{lk0oCW%s zNjp9dfBR%34#wvAw;M)FH;w7)1^kl>2B=QwJxrI{=lV143tiWwA?`|JuO{xoC??bQ z^QU@p7wJnaXoSs=G3pscALF0FR9&+_T?>(q_{5vrDUqo-H{+>@tVMI7*BPJ$1%+(^1w6{g9KCtUiRBP3(^9 zAM=3Xy7<-!YU=qMXyQBgKEbVmr>3{-#(xdN{Ety<{o63je;JLAF@2@P?wlUedC7bI zcuR%lu94|nEJBn=L@uv0UhjM8MS#9a0UE8rKPzewR8Z1m_L8>g-A!Ih1ZI?J23qda*I=N;4nbDwFZX18JH zhM@Yz{R`}u`?I`6DpPxvejqM=LyG@lEcO#PrF~8wNx2MB6k4T}p@_qi92F@mc`anv zlsc<$0pIp;`#r?8;uxWtP=bnz0?GZaQlx9}uE@qzme1norpzZpNFIo=KI9ff38mze z4I&hIF4R}@)^SS5=d$_G-RDlJ@gPs4B>Irgz`d&AjuZ^%p4KgV=H|>&gZ7Vza27#@ z+)L{Wr4=DDyUxf({hvbP#CdyTHySDT{d(_?5{)2|AKE0HOk(5}07$ubg8sC+uT>T- z3#DEjesbdv)1Z=fIED>Sbx>_UVn$?0?3}tRFYqaxRVa|Fq_T}XM*-f;^sN?ni(*iT zNS<29`#yIPf651WhA^Fz{HeS>mSG?kK;ScBgt7+mM|8Mm)Fb5N_G&7I|9N&jvQApB z7e&eAcQ-ejC!6-hnSvxQQ3>8BoR_+OGOMJ^ZCI2DC;LE^Ty3~4-)<%r{8mw#k!*V@ zFMBE-x@PWkm$0?04x@6BEuDi%rOGIV=A4{3aA_wF=f{}%f^bn~OcP~HZF+U9;Ou!N zyYu4v7%Og2^8;#rh{j$nj-^u`SLJ8LO&V~Unmg3cO4&l8gU5rQ)I%xr6B?piAo-u) z<4!2yN1Cu=8YbMI@=M)ISp#7eL=%*(+iMlTRZW_x&||MuU&jsHJ)ozvno6?+YVf#1lZ#efKZrp!&v;V+e$rV903?D?PQj!^x+soC$ kMY*gh>RHY{Yu(F((9ejVikK~= (3, 8): + iscoroutinefunction = inspect.iscoroutinefunction +else: + + def iscoroutinefunction(func: t.Any) -> bool: + while inspect.ismethod(func): + func = func.__func__ + + while isinstance(func, functools.partial): + func = func.func + + return inspect.iscoroutinefunction(func) + + +def _make_timedelta(value: t.Optional[timedelta]) -> t.Optional[timedelta]: + if value is None or isinstance(value, timedelta): + return value + + return timedelta(seconds=value) + + +class Flask(Scaffold): + """The flask object implements a WSGI application and acts as the central + object. It is passed the name of the module or package of the + application. Once it is created it will act as a central registry for + the view functions, the URL rules, template configuration and much more. + + The name of the package is used to resolve resources from inside the + package or the folder the module is contained in depending on if the + package parameter resolves to an actual python package (a folder with + an :file:`__init__.py` file inside) or a standard module (just a ``.py`` file). + + For more information about resource loading, see :func:`open_resource`. + + Usually you create a :class:`Flask` instance in your main module or + in the :file:`__init__.py` file of your package like this:: + + from flask import Flask + app = Flask(__name__) + + .. admonition:: About the First Parameter + + The idea of the first parameter is to give Flask an idea of what + belongs to your application. This name is used to find resources + on the filesystem, can be used by extensions to improve debugging + information and a lot more. + + So it's important what you provide there. If you are using a single + module, `__name__` is always the correct value. If you however are + using a package, it's usually recommended to hardcode the name of + your package there. + + For example if your application is defined in :file:`yourapplication/app.py` + you should create it with one of the two versions below:: + + app = Flask('yourapplication') + app = Flask(__name__.split('.')[0]) + + Why is that? The application will work even with `__name__`, thanks + to how resources are looked up. However it will make debugging more + painful. Certain extensions can make assumptions based on the + import name of your application. For example the Flask-SQLAlchemy + extension will look for the code in your application that triggered + an SQL query in debug mode. If the import name is not properly set + up, that debugging information is lost. (For example it would only + pick up SQL queries in `yourapplication.app` and not + `yourapplication.views.frontend`) + + .. versionadded:: 0.7 + The `static_url_path`, `static_folder`, and `template_folder` + parameters were added. + + .. versionadded:: 0.8 + The `instance_path` and `instance_relative_config` parameters were + added. + + .. versionadded:: 0.11 + The `root_path` parameter was added. + + .. versionadded:: 1.0 + The ``host_matching`` and ``static_host`` parameters were added. + + .. versionadded:: 1.0 + The ``subdomain_matching`` parameter was added. Subdomain + matching needs to be enabled manually now. Setting + :data:`SERVER_NAME` does not implicitly enable it. + + :param import_name: the name of the application package + :param static_url_path: can be used to specify a different path for the + static files on the web. Defaults to the name + of the `static_folder` folder. + :param static_folder: The folder with static files that is served at + ``static_url_path``. Relative to the application ``root_path`` + or an absolute path. Defaults to ``'static'``. + :param static_host: the host to use when adding the static route. + Defaults to None. Required when using ``host_matching=True`` + with a ``static_folder`` configured. + :param host_matching: set ``url_map.host_matching`` attribute. + Defaults to False. + :param subdomain_matching: consider the subdomain relative to + :data:`SERVER_NAME` when matching routes. Defaults to False. + :param template_folder: the folder that contains the templates that should + be used by the application. Defaults to + ``'templates'`` folder in the root path of the + application. + :param instance_path: An alternative instance path for the application. + By default the folder ``'instance'`` next to the + package or module is assumed to be the instance + path. + :param instance_relative_config: if set to ``True`` relative filenames + for loading the config are assumed to + be relative to the instance path instead + of the application root. + :param root_path: The path to the root of the application files. + This should only be set manually when it can't be detected + automatically, such as for namespace packages. + """ + + #: The class that is used for request objects. See :class:`~flask.Request` + #: for more information. + request_class = Request + + #: The class that is used for response objects. See + #: :class:`~flask.Response` for more information. + response_class = Response + + #: The class that is used for the Jinja environment. + #: + #: .. versionadded:: 0.11 + jinja_environment = Environment + + #: The class that is used for the :data:`~flask.g` instance. + #: + #: Example use cases for a custom class: + #: + #: 1. Store arbitrary attributes on flask.g. + #: 2. Add a property for lazy per-request database connectors. + #: 3. Return None instead of AttributeError on unexpected attributes. + #: 4. Raise exception if an unexpected attr is set, a "controlled" flask.g. + #: + #: In Flask 0.9 this property was called `request_globals_class` but it + #: was changed in 0.10 to :attr:`app_ctx_globals_class` because the + #: flask.g object is now application context scoped. + #: + #: .. versionadded:: 0.10 + app_ctx_globals_class = _AppCtxGlobals + + #: The class that is used for the ``config`` attribute of this app. + #: Defaults to :class:`~flask.Config`. + #: + #: Example use cases for a custom class: + #: + #: 1. Default values for certain config options. + #: 2. Access to config values through attributes in addition to keys. + #: + #: .. versionadded:: 0.11 + config_class = Config + + #: The testing flag. Set this to ``True`` to enable the test mode of + #: Flask extensions (and in the future probably also Flask itself). + #: For example this might activate test helpers that have an + #: additional runtime cost which should not be enabled by default. + #: + #: If this is enabled and PROPAGATE_EXCEPTIONS is not changed from the + #: default it's implicitly enabled. + #: + #: This attribute can also be configured from the config with the + #: ``TESTING`` configuration key. Defaults to ``False``. + testing = ConfigAttribute("TESTING") + + #: If a secret key is set, cryptographic components can use this to + #: sign cookies and other things. Set this to a complex random value + #: when you want to use the secure cookie for instance. + #: + #: This attribute can also be configured from the config with the + #: :data:`SECRET_KEY` configuration key. Defaults to ``None``. + secret_key = ConfigAttribute("SECRET_KEY") + + #: The secure cookie uses this for the name of the session cookie. + #: + #: This attribute can also be configured from the config with the + #: ``SESSION_COOKIE_NAME`` configuration key. Defaults to ``'session'`` + session_cookie_name = ConfigAttribute("SESSION_COOKIE_NAME") + + #: A :class:`~datetime.timedelta` which is used to set the expiration + #: date of a permanent session. The default is 31 days which makes a + #: permanent session survive for roughly one month. + #: + #: This attribute can also be configured from the config with the + #: ``PERMANENT_SESSION_LIFETIME`` configuration key. Defaults to + #: ``timedelta(days=31)`` + permanent_session_lifetime = ConfigAttribute( + "PERMANENT_SESSION_LIFETIME", get_converter=_make_timedelta + ) + + #: A :class:`~datetime.timedelta` or number of seconds which is used + #: as the default ``max_age`` for :func:`send_file`. The default is + #: ``None``, which tells the browser to use conditional requests + #: instead of a timed cache. + #: + #: Configured with the :data:`SEND_FILE_MAX_AGE_DEFAULT` + #: configuration key. + #: + #: .. versionchanged:: 2.0 + #: Defaults to ``None`` instead of 12 hours. + send_file_max_age_default = ConfigAttribute( + "SEND_FILE_MAX_AGE_DEFAULT", get_converter=_make_timedelta + ) + + #: Enable this if you want to use the X-Sendfile feature. Keep in + #: mind that the server has to support this. This only affects files + #: sent with the :func:`send_file` method. + #: + #: .. versionadded:: 0.2 + #: + #: This attribute can also be configured from the config with the + #: ``USE_X_SENDFILE`` configuration key. Defaults to ``False``. + use_x_sendfile = ConfigAttribute("USE_X_SENDFILE") + + #: The JSON encoder class to use. Defaults to :class:`~flask.json.JSONEncoder`. + #: + #: .. versionadded:: 0.10 + json_encoder = json.JSONEncoder + + #: The JSON decoder class to use. Defaults to :class:`~flask.json.JSONDecoder`. + #: + #: .. versionadded:: 0.10 + json_decoder = json.JSONDecoder + + #: Options that are passed to the Jinja environment in + #: :meth:`create_jinja_environment`. Changing these options after + #: the environment is created (accessing :attr:`jinja_env`) will + #: have no effect. + #: + #: .. versionchanged:: 1.1.0 + #: This is a ``dict`` instead of an ``ImmutableDict`` to allow + #: easier configuration. + #: + jinja_options: dict = {} + + #: Default configuration parameters. + default_config = ImmutableDict( + { + "ENV": None, + "DEBUG": None, + "TESTING": False, + "PROPAGATE_EXCEPTIONS": None, + "PRESERVE_CONTEXT_ON_EXCEPTION": None, + "SECRET_KEY": None, + "PERMANENT_SESSION_LIFETIME": timedelta(days=31), + "USE_X_SENDFILE": False, + "SERVER_NAME": None, + "APPLICATION_ROOT": "/", + "SESSION_COOKIE_NAME": "session", + "SESSION_COOKIE_DOMAIN": None, + "SESSION_COOKIE_PATH": None, + "SESSION_COOKIE_HTTPONLY": True, + "SESSION_COOKIE_SECURE": False, + "SESSION_COOKIE_SAMESITE": None, + "SESSION_REFRESH_EACH_REQUEST": True, + "MAX_CONTENT_LENGTH": None, + "SEND_FILE_MAX_AGE_DEFAULT": None, + "TRAP_BAD_REQUEST_ERRORS": None, + "TRAP_HTTP_EXCEPTIONS": False, + "EXPLAIN_TEMPLATE_LOADING": False, + "PREFERRED_URL_SCHEME": "http", + "JSON_AS_ASCII": True, + "JSON_SORT_KEYS": True, + "JSONIFY_PRETTYPRINT_REGULAR": False, + "JSONIFY_MIMETYPE": "application/json", + "TEMPLATES_AUTO_RELOAD": None, + "MAX_COOKIE_SIZE": 4093, + } + ) + + #: The rule object to use for URL rules created. This is used by + #: :meth:`add_url_rule`. Defaults to :class:`werkzeug.routing.Rule`. + #: + #: .. versionadded:: 0.7 + url_rule_class = Rule + + #: The map object to use for storing the URL rules and routing + #: configuration parameters. Defaults to :class:`werkzeug.routing.Map`. + #: + #: .. versionadded:: 1.1.0 + url_map_class = Map + + #: the test client that is used with when `test_client` is used. + #: + #: .. versionadded:: 0.7 + test_client_class: t.Optional[t.Type["FlaskClient"]] = None + + #: The :class:`~click.testing.CliRunner` subclass, by default + #: :class:`~flask.testing.FlaskCliRunner` that is used by + #: :meth:`test_cli_runner`. Its ``__init__`` method should take a + #: Flask app object as the first argument. + #: + #: .. versionadded:: 1.0 + test_cli_runner_class: t.Optional[t.Type["FlaskCliRunner"]] = None + + #: the session interface to use. By default an instance of + #: :class:`~flask.sessions.SecureCookieSessionInterface` is used here. + #: + #: .. versionadded:: 0.8 + session_interface = SecureCookieSessionInterface() + + def __init__( + self, + import_name: str, + static_url_path: t.Optional[str] = None, + static_folder: t.Optional[str] = "static", + static_host: t.Optional[str] = None, + host_matching: bool = False, + subdomain_matching: bool = False, + template_folder: t.Optional[str] = "templates", + instance_path: t.Optional[str] = None, + instance_relative_config: bool = False, + root_path: t.Optional[str] = None, + ): + super().__init__( + import_name=import_name, + static_folder=static_folder, + static_url_path=static_url_path, + template_folder=template_folder, + root_path=root_path, + ) + + if instance_path is None: + instance_path = self.auto_find_instance_path() + elif not os.path.isabs(instance_path): + raise ValueError( + "If an instance path is provided it must be absolute." + " A relative path was given instead." + ) + + #: Holds the path to the instance folder. + #: + #: .. versionadded:: 0.8 + self.instance_path = instance_path + + #: The configuration dictionary as :class:`Config`. This behaves + #: exactly like a regular dictionary but supports additional methods + #: to load a config from files. + self.config = self.make_config(instance_relative_config) + + #: A list of functions that are called when :meth:`url_for` raises a + #: :exc:`~werkzeug.routing.BuildError`. Each function registered here + #: is called with `error`, `endpoint` and `values`. If a function + #: returns ``None`` or raises a :exc:`BuildError` the next function is + #: tried. + #: + #: .. versionadded:: 0.9 + self.url_build_error_handlers: t.List[ + t.Callable[[Exception, str, dict], str] + ] = [] + + #: A list of functions that will be called at the beginning of the + #: first request to this instance. To register a function, use the + #: :meth:`before_first_request` decorator. + #: + #: .. versionadded:: 0.8 + self.before_first_request_funcs: t.List[BeforeRequestCallable] = [] + + #: A list of functions that are called when the application context + #: is destroyed. Since the application context is also torn down + #: if the request ends this is the place to store code that disconnects + #: from databases. + #: + #: .. versionadded:: 0.9 + self.teardown_appcontext_funcs: t.List[TeardownCallable] = [] + + #: A list of shell context processor functions that should be run + #: when a shell context is created. + #: + #: .. versionadded:: 0.11 + self.shell_context_processors: t.List[t.Callable[[], t.Dict[str, t.Any]]] = [] + + #: Maps registered blueprint names to blueprint objects. The + #: dict retains the order the blueprints were registered in. + #: Blueprints can be registered multiple times, this dict does + #: not track how often they were attached. + #: + #: .. versionadded:: 0.7 + self.blueprints: t.Dict[str, "Blueprint"] = {} + + #: a place where extensions can store application specific state. For + #: example this is where an extension could store database engines and + #: similar things. + #: + #: The key must match the name of the extension module. For example in + #: case of a "Flask-Foo" extension in `flask_foo`, the key would be + #: ``'foo'``. + #: + #: .. versionadded:: 0.7 + self.extensions: dict = {} + + #: The :class:`~werkzeug.routing.Map` for this instance. You can use + #: this to change the routing converters after the class was created + #: but before any routes are connected. Example:: + #: + #: from werkzeug.routing import BaseConverter + #: + #: class ListConverter(BaseConverter): + #: def to_python(self, value): + #: return value.split(',') + #: def to_url(self, values): + #: return ','.join(super(ListConverter, self).to_url(value) + #: for value in values) + #: + #: app = Flask(__name__) + #: app.url_map.converters['list'] = ListConverter + self.url_map = self.url_map_class() + + self.url_map.host_matching = host_matching + self.subdomain_matching = subdomain_matching + + # tracks internally if the application already handled at least one + # request. + self._got_first_request = False + self._before_request_lock = Lock() + + # Add a static route using the provided static_url_path, static_host, + # and static_folder if there is a configured static_folder. + # Note we do this without checking if static_folder exists. + # For one, it might be created while the server is running (e.g. during + # development). Also, Google App Engine stores static files somewhere + if self.has_static_folder: + assert ( + bool(static_host) == host_matching + ), "Invalid static_host/host_matching combination" + # Use a weakref to avoid creating a reference cycle between the app + # and the view function (see #3761). + self_ref = weakref.ref(self) + self.add_url_rule( + f"{self.static_url_path}/", + endpoint="static", + host=static_host, + view_func=lambda **kw: self_ref().send_static_file(**kw), # type: ignore # noqa: B950 + ) + + # Set the name of the Click group in case someone wants to add + # the app's commands to another CLI tool. + self.cli.name = self.name + + def _is_setup_finished(self) -> bool: + return self.debug and self._got_first_request + + @locked_cached_property + def name(self) -> str: # type: ignore + """The name of the application. This is usually the import name + with the difference that it's guessed from the run file if the + import name is main. This name is used as a display name when + Flask needs the name of the application. It can be set and overridden + to change the value. + + .. versionadded:: 0.8 + """ + if self.import_name == "__main__": + fn = getattr(sys.modules["__main__"], "__file__", None) + if fn is None: + return "__main__" + return os.path.splitext(os.path.basename(fn))[0] + return self.import_name + + @property + def propagate_exceptions(self) -> bool: + """Returns the value of the ``PROPAGATE_EXCEPTIONS`` configuration + value in case it's set, otherwise a sensible default is returned. + + .. versionadded:: 0.7 + """ + rv = self.config["PROPAGATE_EXCEPTIONS"] + if rv is not None: + return rv + return self.testing or self.debug + + @property + def preserve_context_on_exception(self) -> bool: + """Returns the value of the ``PRESERVE_CONTEXT_ON_EXCEPTION`` + configuration value in case it's set, otherwise a sensible default + is returned. + + .. versionadded:: 0.7 + """ + rv = self.config["PRESERVE_CONTEXT_ON_EXCEPTION"] + if rv is not None: + return rv + return self.debug + + @locked_cached_property + def logger(self) -> logging.Logger: + """A standard Python :class:`~logging.Logger` for the app, with + the same name as :attr:`name`. + + In debug mode, the logger's :attr:`~logging.Logger.level` will + be set to :data:`~logging.DEBUG`. + + If there are no handlers configured, a default handler will be + added. See :doc:`/logging` for more information. + + .. versionchanged:: 1.1.0 + The logger takes the same name as :attr:`name` rather than + hard-coding ``"flask.app"``. + + .. versionchanged:: 1.0.0 + Behavior was simplified. The logger is always named + ``"flask.app"``. The level is only set during configuration, + it doesn't check ``app.debug`` each time. Only one format is + used, not different ones depending on ``app.debug``. No + handlers are removed, and a handler is only added if no + handlers are already configured. + + .. versionadded:: 0.3 + """ + return create_logger(self) + + @locked_cached_property + def jinja_env(self) -> Environment: + """The Jinja environment used to load templates. + + The environment is created the first time this property is + accessed. Changing :attr:`jinja_options` after that will have no + effect. + """ + return self.create_jinja_environment() + + @property + def got_first_request(self) -> bool: + """This attribute is set to ``True`` if the application started + handling the first request. + + .. versionadded:: 0.8 + """ + return self._got_first_request + + def make_config(self, instance_relative: bool = False) -> Config: + """Used to create the config attribute by the Flask constructor. + The `instance_relative` parameter is passed in from the constructor + of Flask (there named `instance_relative_config`) and indicates if + the config should be relative to the instance path or the root path + of the application. + + .. versionadded:: 0.8 + """ + root_path = self.root_path + if instance_relative: + root_path = self.instance_path + defaults = dict(self.default_config) + defaults["ENV"] = get_env() + defaults["DEBUG"] = get_debug_flag() + return self.config_class(root_path, defaults) + + def auto_find_instance_path(self) -> str: + """Tries to locate the instance path if it was not provided to the + constructor of the application class. It will basically calculate + the path to a folder named ``instance`` next to your main file or + the package. + + .. versionadded:: 0.8 + """ + prefix, package_path = find_package(self.import_name) + if prefix is None: + return os.path.join(package_path, "instance") + return os.path.join(prefix, "var", f"{self.name}-instance") + + def open_instance_resource(self, resource: str, mode: str = "rb") -> t.IO[t.AnyStr]: + """Opens a resource from the application's instance folder + (:attr:`instance_path`). Otherwise works like + :meth:`open_resource`. Instance resources can also be opened for + writing. + + :param resource: the name of the resource. To access resources within + subfolders use forward slashes as separator. + :param mode: resource file opening mode, default is 'rb'. + """ + return open(os.path.join(self.instance_path, resource), mode) + + @property + def templates_auto_reload(self) -> bool: + """Reload templates when they are changed. Used by + :meth:`create_jinja_environment`. + + This attribute can be configured with :data:`TEMPLATES_AUTO_RELOAD`. If + not set, it will be enabled in debug mode. + + .. versionadded:: 1.0 + This property was added but the underlying config and behavior + already existed. + """ + rv = self.config["TEMPLATES_AUTO_RELOAD"] + return rv if rv is not None else self.debug + + @templates_auto_reload.setter + def templates_auto_reload(self, value: bool) -> None: + self.config["TEMPLATES_AUTO_RELOAD"] = value + + def create_jinja_environment(self) -> Environment: + """Create the Jinja environment based on :attr:`jinja_options` + and the various Jinja-related methods of the app. Changing + :attr:`jinja_options` after this will have no effect. Also adds + Flask-related globals and filters to the environment. + + .. versionchanged:: 0.11 + ``Environment.auto_reload`` set in accordance with + ``TEMPLATES_AUTO_RELOAD`` configuration option. + + .. versionadded:: 0.5 + """ + options = dict(self.jinja_options) + + if "autoescape" not in options: + options["autoescape"] = self.select_jinja_autoescape + + if "auto_reload" not in options: + options["auto_reload"] = self.templates_auto_reload + + rv = self.jinja_environment(self, **options) + rv.globals.update( + url_for=url_for, + get_flashed_messages=get_flashed_messages, + config=self.config, + # request, session and g are normally added with the + # context processor for efficiency reasons but for imported + # templates we also want the proxies in there. + request=request, + session=session, + g=g, + ) + rv.policies["json.dumps_function"] = json.dumps + return rv + + def create_global_jinja_loader(self) -> DispatchingJinjaLoader: + """Creates the loader for the Jinja2 environment. Can be used to + override just the loader and keeping the rest unchanged. It's + discouraged to override this function. Instead one should override + the :meth:`jinja_loader` function instead. + + The global loader dispatches between the loaders of the application + and the individual blueprints. + + .. versionadded:: 0.7 + """ + return DispatchingJinjaLoader(self) + + def select_jinja_autoescape(self, filename: str) -> bool: + """Returns ``True`` if autoescaping should be active for the given + template name. If no template name is given, returns `True`. + + .. versionadded:: 0.5 + """ + if filename is None: + return True + return filename.endswith((".html", ".htm", ".xml", ".xhtml")) + + def update_template_context(self, context: dict) -> None: + """Update the template context with some commonly used variables. + This injects request, session, config and g into the template + context as well as everything template context processors want + to inject. Note that the as of Flask 0.6, the original values + in the context will not be overridden if a context processor + decides to return a value with the same key. + + :param context: the context as a dictionary that is updated in place + to add extra variables. + """ + funcs: t.Iterable[ + TemplateContextProcessorCallable + ] = self.template_context_processors[None] + reqctx = _request_ctx_stack.top + if reqctx is not None: + for bp in request.blueprints: + if bp in self.template_context_processors: + funcs = chain(funcs, self.template_context_processors[bp]) + orig_ctx = context.copy() + for func in funcs: + context.update(func()) + # make sure the original values win. This makes it possible to + # easier add new variables in context processors without breaking + # existing views. + context.update(orig_ctx) + + def make_shell_context(self) -> dict: + """Returns the shell context for an interactive shell for this + application. This runs all the registered shell context + processors. + + .. versionadded:: 0.11 + """ + rv = {"app": self, "g": g} + for processor in self.shell_context_processors: + rv.update(processor()) + return rv + + #: What environment the app is running in. Flask and extensions may + #: enable behaviors based on the environment, such as enabling debug + #: mode. This maps to the :data:`ENV` config key. This is set by the + #: :envvar:`FLASK_ENV` environment variable and may not behave as + #: expected if set in code. + #: + #: **Do not enable development when deploying in production.** + #: + #: Default: ``'production'`` + env = ConfigAttribute("ENV") + + @property + def debug(self) -> bool: + """Whether debug mode is enabled. When using ``flask run`` to start + the development server, an interactive debugger will be shown for + unhandled exceptions, and the server will be reloaded when code + changes. This maps to the :data:`DEBUG` config key. This is + enabled when :attr:`env` is ``'development'`` and is overridden + by the ``FLASK_DEBUG`` environment variable. It may not behave as + expected if set in code. + + **Do not enable debug mode when deploying in production.** + + Default: ``True`` if :attr:`env` is ``'development'``, or + ``False`` otherwise. + """ + return self.config["DEBUG"] + + @debug.setter + def debug(self, value: bool) -> None: + self.config["DEBUG"] = value + self.jinja_env.auto_reload = self.templates_auto_reload + + def run( + self, + host: t.Optional[str] = None, + port: t.Optional[int] = None, + debug: t.Optional[bool] = None, + load_dotenv: bool = True, + **options: t.Any, + ) -> None: + """Runs the application on a local development server. + + Do not use ``run()`` in a production setting. It is not intended to + meet security and performance requirements for a production server. + Instead, see :doc:`/deploying/index` for WSGI server recommendations. + + If the :attr:`debug` flag is set the server will automatically reload + for code changes and show a debugger in case an exception happened. + + If you want to run the application in debug mode, but disable the + code execution on the interactive debugger, you can pass + ``use_evalex=False`` as parameter. This will keep the debugger's + traceback screen active, but disable code execution. + + It is not recommended to use this function for development with + automatic reloading as this is badly supported. Instead you should + be using the :command:`flask` command line script's ``run`` support. + + .. admonition:: Keep in Mind + + Flask will suppress any server error with a generic error page + unless it is in debug mode. As such to enable just the + interactive debugger without the code reloading, you have to + invoke :meth:`run` with ``debug=True`` and ``use_reloader=False``. + Setting ``use_debugger`` to ``True`` without being in debug mode + won't catch any exceptions because there won't be any to + catch. + + :param host: the hostname to listen on. Set this to ``'0.0.0.0'`` to + have the server available externally as well. Defaults to + ``'127.0.0.1'`` or the host in the ``SERVER_NAME`` config variable + if present. + :param port: the port of the webserver. Defaults to ``5000`` or the + port defined in the ``SERVER_NAME`` config variable if present. + :param debug: if given, enable or disable debug mode. See + :attr:`debug`. + :param load_dotenv: Load the nearest :file:`.env` and :file:`.flaskenv` + files to set environment variables. Will also change the working + directory to the directory containing the first file found. + :param options: the options to be forwarded to the underlying Werkzeug + server. See :func:`werkzeug.serving.run_simple` for more + information. + + .. versionchanged:: 1.0 + If installed, python-dotenv will be used to load environment + variables from :file:`.env` and :file:`.flaskenv` files. + + If set, the :envvar:`FLASK_ENV` and :envvar:`FLASK_DEBUG` + environment variables will override :attr:`env` and + :attr:`debug`. + + Threaded mode is enabled by default. + + .. versionchanged:: 0.10 + The default port is now picked from the ``SERVER_NAME`` + variable. + """ + # Change this into a no-op if the server is invoked from the + # command line. Have a look at cli.py for more information. + if os.environ.get("FLASK_RUN_FROM_CLI") == "true": + from .debughelpers import explain_ignored_app_run + + explain_ignored_app_run() + return + + if get_load_dotenv(load_dotenv): + cli.load_dotenv() + + # if set, let env vars override previous values + if "FLASK_ENV" in os.environ: + self.env = get_env() + self.debug = get_debug_flag() + elif "FLASK_DEBUG" in os.environ: + self.debug = get_debug_flag() + + # debug passed to method overrides all other sources + if debug is not None: + self.debug = bool(debug) + + server_name = self.config.get("SERVER_NAME") + sn_host = sn_port = None + + if server_name: + sn_host, _, sn_port = server_name.partition(":") + + if not host: + if sn_host: + host = sn_host + else: + host = "127.0.0.1" + + if port or port == 0: + port = int(port) + elif sn_port: + port = int(sn_port) + else: + port = 5000 + + options.setdefault("use_reloader", self.debug) + options.setdefault("use_debugger", self.debug) + options.setdefault("threaded", True) + + cli.show_server_banner(self.env, self.debug, self.name, False) + + from werkzeug.serving import run_simple + + try: + run_simple(t.cast(str, host), port, self, **options) + finally: + # reset the first request information if the development server + # reset normally. This makes it possible to restart the server + # without reloader and that stuff from an interactive shell. + self._got_first_request = False + + def test_client(self, use_cookies: bool = True, **kwargs: t.Any) -> "FlaskClient": + """Creates a test client for this application. For information + about unit testing head over to :doc:`/testing`. + + Note that if you are testing for assertions or exceptions in your + application code, you must set ``app.testing = True`` in order for the + exceptions to propagate to the test client. Otherwise, the exception + will be handled by the application (not visible to the test client) and + the only indication of an AssertionError or other exception will be a + 500 status code response to the test client. See the :attr:`testing` + attribute. For example:: + + app.testing = True + client = app.test_client() + + The test client can be used in a ``with`` block to defer the closing down + of the context until the end of the ``with`` block. This is useful if + you want to access the context locals for testing:: + + with app.test_client() as c: + rv = c.get('/?vodka=42') + assert request.args['vodka'] == '42' + + Additionally, you may pass optional keyword arguments that will then + be passed to the application's :attr:`test_client_class` constructor. + For example:: + + from flask.testing import FlaskClient + + class CustomClient(FlaskClient): + def __init__(self, *args, **kwargs): + self._authentication = kwargs.pop("authentication") + super(CustomClient,self).__init__( *args, **kwargs) + + app.test_client_class = CustomClient + client = app.test_client(authentication='Basic ....') + + See :class:`~flask.testing.FlaskClient` for more information. + + .. versionchanged:: 0.4 + added support for ``with`` block usage for the client. + + .. versionadded:: 0.7 + The `use_cookies` parameter was added as well as the ability + to override the client to be used by setting the + :attr:`test_client_class` attribute. + + .. versionchanged:: 0.11 + Added `**kwargs` to support passing additional keyword arguments to + the constructor of :attr:`test_client_class`. + """ + cls = self.test_client_class + if cls is None: + from .testing import FlaskClient as cls # type: ignore + return cls( # type: ignore + self, self.response_class, use_cookies=use_cookies, **kwargs + ) + + def test_cli_runner(self, **kwargs: t.Any) -> "FlaskCliRunner": + """Create a CLI runner for testing CLI commands. + See :ref:`testing-cli`. + + Returns an instance of :attr:`test_cli_runner_class`, by default + :class:`~flask.testing.FlaskCliRunner`. The Flask app object is + passed as the first argument. + + .. versionadded:: 1.0 + """ + cls = self.test_cli_runner_class + + if cls is None: + from .testing import FlaskCliRunner as cls # type: ignore + + return cls(self, **kwargs) # type: ignore + + @setupmethod + def register_blueprint(self, blueprint: "Blueprint", **options: t.Any) -> None: + """Register a :class:`~flask.Blueprint` on the application. Keyword + arguments passed to this method will override the defaults set on the + blueprint. + + Calls the blueprint's :meth:`~flask.Blueprint.register` method after + recording the blueprint in the application's :attr:`blueprints`. + + :param blueprint: The blueprint to register. + :param url_prefix: Blueprint routes will be prefixed with this. + :param subdomain: Blueprint routes will match on this subdomain. + :param url_defaults: Blueprint routes will use these default values for + view arguments. + :param options: Additional keyword arguments are passed to + :class:`~flask.blueprints.BlueprintSetupState`. They can be + accessed in :meth:`~flask.Blueprint.record` callbacks. + + .. versionchanged:: 2.0.1 + The ``name`` option can be used to change the (pre-dotted) + name the blueprint is registered with. This allows the same + blueprint to be registered multiple times with unique names + for ``url_for``. + + .. versionadded:: 0.7 + """ + blueprint.register(self, options) + + def iter_blueprints(self) -> t.ValuesView["Blueprint"]: + """Iterates over all blueprints by the order they were registered. + + .. versionadded:: 0.11 + """ + return self.blueprints.values() + + @setupmethod + def add_url_rule( + self, + rule: str, + endpoint: t.Optional[str] = None, + view_func: t.Optional[t.Callable] = None, + provide_automatic_options: t.Optional[bool] = None, + **options: t.Any, + ) -> None: + if endpoint is None: + endpoint = _endpoint_from_view_func(view_func) # type: ignore + options["endpoint"] = endpoint + methods = options.pop("methods", None) + + # if the methods are not given and the view_func object knows its + # methods we can use that instead. If neither exists, we go with + # a tuple of only ``GET`` as default. + if methods is None: + methods = getattr(view_func, "methods", None) or ("GET",) + if isinstance(methods, str): + raise TypeError( + "Allowed methods must be a list of strings, for" + ' example: @app.route(..., methods=["POST"])' + ) + methods = {item.upper() for item in methods} + + # Methods that should always be added + required_methods = set(getattr(view_func, "required_methods", ())) + + # starting with Flask 0.8 the view_func object can disable and + # force-enable the automatic options handling. + if provide_automatic_options is None: + provide_automatic_options = getattr( + view_func, "provide_automatic_options", None + ) + + if provide_automatic_options is None: + if "OPTIONS" not in methods: + provide_automatic_options = True + required_methods.add("OPTIONS") + else: + provide_automatic_options = False + + # Add the required methods now. + methods |= required_methods + + rule = self.url_rule_class(rule, methods=methods, **options) + rule.provide_automatic_options = provide_automatic_options # type: ignore + + self.url_map.add(rule) + if view_func is not None: + old_func = self.view_functions.get(endpoint) + if old_func is not None and old_func != view_func: + raise AssertionError( + "View function mapping is overwriting an existing" + f" endpoint function: {endpoint}" + ) + self.view_functions[endpoint] = view_func + + @setupmethod + def template_filter( + self, name: t.Optional[str] = None + ) -> t.Callable[[TemplateFilterCallable], TemplateFilterCallable]: + """A decorator that is used to register custom template filter. + You can specify a name for the filter, otherwise the function + name will be used. Example:: + + @app.template_filter() + def reverse(s): + return s[::-1] + + :param name: the optional name of the filter, otherwise the + function name will be used. + """ + + def decorator(f: TemplateFilterCallable) -> TemplateFilterCallable: + self.add_template_filter(f, name=name) + return f + + return decorator + + @setupmethod + def add_template_filter( + self, f: TemplateFilterCallable, name: t.Optional[str] = None + ) -> None: + """Register a custom template filter. Works exactly like the + :meth:`template_filter` decorator. + + :param name: the optional name of the filter, otherwise the + function name will be used. + """ + self.jinja_env.filters[name or f.__name__] = f + + @setupmethod + def template_test( + self, name: t.Optional[str] = None + ) -> t.Callable[[TemplateTestCallable], TemplateTestCallable]: + """A decorator that is used to register custom template test. + You can specify a name for the test, otherwise the function + name will be used. Example:: + + @app.template_test() + def is_prime(n): + if n == 2: + return True + for i in range(2, int(math.ceil(math.sqrt(n))) + 1): + if n % i == 0: + return False + return True + + .. versionadded:: 0.10 + + :param name: the optional name of the test, otherwise the + function name will be used. + """ + + def decorator(f: TemplateTestCallable) -> TemplateTestCallable: + self.add_template_test(f, name=name) + return f + + return decorator + + @setupmethod + def add_template_test( + self, f: TemplateTestCallable, name: t.Optional[str] = None + ) -> None: + """Register a custom template test. Works exactly like the + :meth:`template_test` decorator. + + .. versionadded:: 0.10 + + :param name: the optional name of the test, otherwise the + function name will be used. + """ + self.jinja_env.tests[name or f.__name__] = f + + @setupmethod + def template_global( + self, name: t.Optional[str] = None + ) -> t.Callable[[TemplateGlobalCallable], TemplateGlobalCallable]: + """A decorator that is used to register a custom template global function. + You can specify a name for the global function, otherwise the function + name will be used. Example:: + + @app.template_global() + def double(n): + return 2 * n + + .. versionadded:: 0.10 + + :param name: the optional name of the global function, otherwise the + function name will be used. + """ + + def decorator(f: TemplateGlobalCallable) -> TemplateGlobalCallable: + self.add_template_global(f, name=name) + return f + + return decorator + + @setupmethod + def add_template_global( + self, f: TemplateGlobalCallable, name: t.Optional[str] = None + ) -> None: + """Register a custom template global function. Works exactly like the + :meth:`template_global` decorator. + + .. versionadded:: 0.10 + + :param name: the optional name of the global function, otherwise the + function name will be used. + """ + self.jinja_env.globals[name or f.__name__] = f + + @setupmethod + def before_first_request(self, f: BeforeRequestCallable) -> BeforeRequestCallable: + """Registers a function to be run before the first request to this + instance of the application. + + The function will be called without any arguments and its return + value is ignored. + + .. versionadded:: 0.8 + """ + self.before_first_request_funcs.append(f) + return f + + @setupmethod + def teardown_appcontext(self, f: TeardownCallable) -> TeardownCallable: + """Registers a function to be called when the application context + ends. These functions are typically also called when the request + context is popped. + + Example:: + + ctx = app.app_context() + ctx.push() + ... + ctx.pop() + + When ``ctx.pop()`` is executed in the above example, the teardown + functions are called just before the app context moves from the + stack of active contexts. This becomes relevant if you are using + such constructs in tests. + + Since a request context typically also manages an application + context it would also be called when you pop a request context. + + When a teardown function was called because of an unhandled exception + it will be passed an error object. If an :meth:`errorhandler` is + registered, it will handle the exception and the teardown will not + receive it. + + The return values of teardown functions are ignored. + + .. versionadded:: 0.9 + """ + self.teardown_appcontext_funcs.append(f) + return f + + @setupmethod + def shell_context_processor(self, f: t.Callable) -> t.Callable: + """Registers a shell context processor function. + + .. versionadded:: 0.11 + """ + self.shell_context_processors.append(f) + return f + + def _find_error_handler(self, e: Exception) -> t.Optional[ErrorHandlerCallable]: + """Return a registered error handler for an exception in this order: + blueprint handler for a specific code, app handler for a specific code, + blueprint handler for an exception class, app handler for an exception + class, or ``None`` if a suitable handler is not found. + """ + exc_class, code = self._get_exc_class_and_code(type(e)) + + for c in [code, None]: + for name in chain(request.blueprints, [None]): + handler_map = self.error_handler_spec[name][c] + + if not handler_map: + continue + + for cls in exc_class.__mro__: + handler = handler_map.get(cls) + + if handler is not None: + return handler + return None + + def handle_http_exception( + self, e: HTTPException + ) -> t.Union[HTTPException, ResponseReturnValue]: + """Handles an HTTP exception. By default this will invoke the + registered error handlers and fall back to returning the + exception as response. + + .. versionchanged:: 1.0.3 + ``RoutingException``, used internally for actions such as + slash redirects during routing, is not passed to error + handlers. + + .. versionchanged:: 1.0 + Exceptions are looked up by code *and* by MRO, so + ``HTTPExcpetion`` subclasses can be handled with a catch-all + handler for the base ``HTTPException``. + + .. versionadded:: 0.3 + """ + # Proxy exceptions don't have error codes. We want to always return + # those unchanged as errors + if e.code is None: + return e + + # RoutingExceptions are used internally to trigger routing + # actions, such as slash redirects raising RequestRedirect. They + # are not raised or handled in user code. + if isinstance(e, RoutingException): + return e + + handler = self._find_error_handler(e) + if handler is None: + return e + return self.ensure_sync(handler)(e) + + def trap_http_exception(self, e: Exception) -> bool: + """Checks if an HTTP exception should be trapped or not. By default + this will return ``False`` for all exceptions except for a bad request + key error if ``TRAP_BAD_REQUEST_ERRORS`` is set to ``True``. It + also returns ``True`` if ``TRAP_HTTP_EXCEPTIONS`` is set to ``True``. + + This is called for all HTTP exceptions raised by a view function. + If it returns ``True`` for any exception the error handler for this + exception is not called and it shows up as regular exception in the + traceback. This is helpful for debugging implicitly raised HTTP + exceptions. + + .. versionchanged:: 1.0 + Bad request errors are not trapped by default in debug mode. + + .. versionadded:: 0.8 + """ + if self.config["TRAP_HTTP_EXCEPTIONS"]: + return True + + trap_bad_request = self.config["TRAP_BAD_REQUEST_ERRORS"] + + # if unset, trap key errors in debug mode + if ( + trap_bad_request is None + and self.debug + and isinstance(e, BadRequestKeyError) + ): + return True + + if trap_bad_request: + return isinstance(e, BadRequest) + + return False + + def handle_user_exception( + self, e: Exception + ) -> t.Union[HTTPException, ResponseReturnValue]: + """This method is called whenever an exception occurs that + should be handled. A special case is :class:`~werkzeug + .exceptions.HTTPException` which is forwarded to the + :meth:`handle_http_exception` method. This function will either + return a response value or reraise the exception with the same + traceback. + + .. versionchanged:: 1.0 + Key errors raised from request data like ``form`` show the + bad key in debug mode rather than a generic bad request + message. + + .. versionadded:: 0.7 + """ + if isinstance(e, BadRequestKeyError) and ( + self.debug or self.config["TRAP_BAD_REQUEST_ERRORS"] + ): + e.show_exception = True + + if isinstance(e, HTTPException) and not self.trap_http_exception(e): + return self.handle_http_exception(e) + + handler = self._find_error_handler(e) + + if handler is None: + raise + + return self.ensure_sync(handler)(e) + + def handle_exception(self, e: Exception) -> Response: + """Handle an exception that did not have an error handler + associated with it, or that was raised from an error handler. + This always causes a 500 ``InternalServerError``. + + Always sends the :data:`got_request_exception` signal. + + If :attr:`propagate_exceptions` is ``True``, such as in debug + mode, the error will be re-raised so that the debugger can + display it. Otherwise, the original exception is logged, and + an :exc:`~werkzeug.exceptions.InternalServerError` is returned. + + If an error handler is registered for ``InternalServerError`` or + ``500``, it will be used. For consistency, the handler will + always receive the ``InternalServerError``. The original + unhandled exception is available as ``e.original_exception``. + + .. versionchanged:: 1.1.0 + Always passes the ``InternalServerError`` instance to the + handler, setting ``original_exception`` to the unhandled + error. + + .. versionchanged:: 1.1.0 + ``after_request`` functions and other finalization is done + even for the default 500 response when there is no handler. + + .. versionadded:: 0.3 + """ + exc_info = sys.exc_info() + got_request_exception.send(self, exception=e) + + if self.propagate_exceptions: + # Re-raise if called with an active exception, otherwise + # raise the passed in exception. + if exc_info[1] is e: + raise + + raise e + + self.log_exception(exc_info) + server_error: t.Union[InternalServerError, ResponseReturnValue] + server_error = InternalServerError(original_exception=e) + handler = self._find_error_handler(server_error) + + if handler is not None: + server_error = self.ensure_sync(handler)(server_error) + + return self.finalize_request(server_error, from_error_handler=True) + + def log_exception( + self, + exc_info: t.Union[ + t.Tuple[type, BaseException, TracebackType], t.Tuple[None, None, None] + ], + ) -> None: + """Logs an exception. This is called by :meth:`handle_exception` + if debugging is disabled and right before the handler is called. + The default implementation logs the exception as error on the + :attr:`logger`. + + .. versionadded:: 0.8 + """ + self.logger.error( + f"Exception on {request.path} [{request.method}]", exc_info=exc_info + ) + + def raise_routing_exception(self, request: Request) -> "te.NoReturn": + """Exceptions that are recording during routing are reraised with + this method. During debug we are not reraising redirect requests + for non ``GET``, ``HEAD``, or ``OPTIONS`` requests and we're raising + a different error instead to help debug situations. + + :internal: + """ + if ( + not self.debug + or not isinstance(request.routing_exception, RequestRedirect) + or request.method in ("GET", "HEAD", "OPTIONS") + ): + raise request.routing_exception # type: ignore + + from .debughelpers import FormDataRoutingRedirect + + raise FormDataRoutingRedirect(request) + + def dispatch_request(self) -> ResponseReturnValue: + """Does the request dispatching. Matches the URL and returns the + return value of the view or error handler. This does not have to + be a response object. In order to convert the return value to a + proper response object, call :func:`make_response`. + + .. versionchanged:: 0.7 + This no longer does the exception handling, this code was + moved to the new :meth:`full_dispatch_request`. + """ + req = _request_ctx_stack.top.request + if req.routing_exception is not None: + self.raise_routing_exception(req) + rule = req.url_rule + # if we provide automatic options for this URL and the + # request came with the OPTIONS method, reply automatically + if ( + getattr(rule, "provide_automatic_options", False) + and req.method == "OPTIONS" + ): + return self.make_default_options_response() + # otherwise dispatch to the handler for that endpoint + return self.ensure_sync(self.view_functions[rule.endpoint])(**req.view_args) + + def full_dispatch_request(self) -> Response: + """Dispatches the request and on top of that performs request + pre and postprocessing as well as HTTP exception catching and + error handling. + + .. versionadded:: 0.7 + """ + self.try_trigger_before_first_request_functions() + try: + request_started.send(self) + rv = self.preprocess_request() + if rv is None: + rv = self.dispatch_request() + except Exception as e: + rv = self.handle_user_exception(e) + return self.finalize_request(rv) + + def finalize_request( + self, + rv: t.Union[ResponseReturnValue, HTTPException], + from_error_handler: bool = False, + ) -> Response: + """Given the return value from a view function this finalizes + the request by converting it into a response and invoking the + postprocessing functions. This is invoked for both normal + request dispatching as well as error handlers. + + Because this means that it might be called as a result of a + failure a special safe mode is available which can be enabled + with the `from_error_handler` flag. If enabled, failures in + response processing will be logged and otherwise ignored. + + :internal: + """ + response = self.make_response(rv) + try: + response = self.process_response(response) + request_finished.send(self, response=response) + except Exception: + if not from_error_handler: + raise + self.logger.exception( + "Request finalizing failed with an error while handling an error" + ) + return response + + def try_trigger_before_first_request_functions(self) -> None: + """Called before each request and will ensure that it triggers + the :attr:`before_first_request_funcs` and only exactly once per + application instance (which means process usually). + + :internal: + """ + if self._got_first_request: + return + with self._before_request_lock: + if self._got_first_request: + return + for func in self.before_first_request_funcs: + self.ensure_sync(func)() + self._got_first_request = True + + def make_default_options_response(self) -> Response: + """This method is called to create the default ``OPTIONS`` response. + This can be changed through subclassing to change the default + behavior of ``OPTIONS`` responses. + + .. versionadded:: 0.7 + """ + adapter = _request_ctx_stack.top.url_adapter + methods = adapter.allowed_methods() + rv = self.response_class() + rv.allow.update(methods) + return rv + + def should_ignore_error(self, error: t.Optional[BaseException]) -> bool: + """This is called to figure out if an error should be ignored + or not as far as the teardown system is concerned. If this + function returns ``True`` then the teardown handlers will not be + passed the error. + + .. versionadded:: 0.10 + """ + return False + + def ensure_sync(self, func: t.Callable) -> t.Callable: + """Ensure that the function is synchronous for WSGI workers. + Plain ``def`` functions are returned as-is. ``async def`` + functions are wrapped to run and wait for the response. + + Override this method to change how the app runs async views. + + .. versionadded:: 2.0 + """ + if iscoroutinefunction(func): + return self.async_to_sync(func) + + return func + + def async_to_sync( + self, func: t.Callable[..., t.Coroutine] + ) -> t.Callable[..., t.Any]: + """Return a sync function that will run the coroutine function. + + .. code-block:: python + + result = app.async_to_sync(func)(*args, **kwargs) + + Override this method to change how the app converts async code + to be synchronously callable. + + .. versionadded:: 2.0 + """ + try: + from asgiref.sync import async_to_sync as asgiref_async_to_sync + except ImportError: + raise RuntimeError( + "Install Flask with the 'async' extra in order to use async views." + ) + + # Check that Werkzeug isn't using its fallback ContextVar class. + if ContextVar.__module__ == "werkzeug.local": + raise RuntimeError( + "Async cannot be used with this combination of Python " + "and Greenlet versions." + ) + + return asgiref_async_to_sync(func) + + def make_response(self, rv: ResponseReturnValue) -> Response: + """Convert the return value from a view function to an instance of + :attr:`response_class`. + + :param rv: the return value from the view function. The view function + must return a response. Returning ``None``, or the view ending + without returning, is not allowed. The following types are allowed + for ``view_rv``: + + ``str`` + A response object is created with the string encoded to UTF-8 + as the body. + + ``bytes`` + A response object is created with the bytes as the body. + + ``dict`` + A dictionary that will be jsonify'd before being returned. + + ``tuple`` + Either ``(body, status, headers)``, ``(body, status)``, or + ``(body, headers)``, where ``body`` is any of the other types + allowed here, ``status`` is a string or an integer, and + ``headers`` is a dictionary or a list of ``(key, value)`` + tuples. If ``body`` is a :attr:`response_class` instance, + ``status`` overwrites the exiting value and ``headers`` are + extended. + + :attr:`response_class` + The object is returned unchanged. + + other :class:`~werkzeug.wrappers.Response` class + The object is coerced to :attr:`response_class`. + + :func:`callable` + The function is called as a WSGI application. The result is + used to create a response object. + + .. versionchanged:: 0.9 + Previously a tuple was interpreted as the arguments for the + response object. + """ + + status = headers = None + + # unpack tuple returns + if isinstance(rv, tuple): + len_rv = len(rv) + + # a 3-tuple is unpacked directly + if len_rv == 3: + rv, status, headers = rv + # decide if a 2-tuple has status or headers + elif len_rv == 2: + if isinstance(rv[1], (Headers, dict, tuple, list)): + rv, headers = rv + else: + rv, status = rv + # other sized tuples are not allowed + else: + raise TypeError( + "The view function did not return a valid response tuple." + " The tuple must have the form (body, status, headers)," + " (body, status), or (body, headers)." + ) + + # the body must not be None + if rv is None: + raise TypeError( + f"The view function for {request.endpoint!r} did not" + " return a valid response. The function either returned" + " None or ended without a return statement." + ) + + # make sure the body is an instance of the response class + if not isinstance(rv, self.response_class): + if isinstance(rv, (str, bytes, bytearray)): + # let the response class set the status and headers instead of + # waiting to do it manually, so that the class can handle any + # special logic + rv = self.response_class(rv, status=status, headers=headers) + status = headers = None + elif isinstance(rv, dict): + rv = jsonify(rv) + elif isinstance(rv, BaseResponse) or callable(rv): + # evaluate a WSGI callable, or coerce a different response + # class to the correct type + try: + rv = self.response_class.force_type(rv, request.environ) # type: ignore # noqa: B950 + except TypeError as e: + raise TypeError( + f"{e}\nThe view function did not return a valid" + " response. The return type must be a string," + " dict, tuple, Response instance, or WSGI" + f" callable, but it was a {type(rv).__name__}." + ).with_traceback(sys.exc_info()[2]) + else: + raise TypeError( + "The view function did not return a valid" + " response. The return type must be a string," + " dict, tuple, Response instance, or WSGI" + f" callable, but it was a {type(rv).__name__}." + ) + + rv = t.cast(Response, rv) + # prefer the status if it was provided + if status is not None: + if isinstance(status, (str, bytes, bytearray)): + rv.status = status # type: ignore + else: + rv.status_code = status + + # extend existing headers with provided headers + if headers: + rv.headers.update(headers) + + return rv + + def create_url_adapter( + self, request: t.Optional[Request] + ) -> t.Optional[MapAdapter]: + """Creates a URL adapter for the given request. The URL adapter + is created at a point where the request context is not yet set + up so the request is passed explicitly. + + .. versionadded:: 0.6 + + .. versionchanged:: 0.9 + This can now also be called without a request object when the + URL adapter is created for the application context. + + .. versionchanged:: 1.0 + :data:`SERVER_NAME` no longer implicitly enables subdomain + matching. Use :attr:`subdomain_matching` instead. + """ + if request is not None: + # If subdomain matching is disabled (the default), use the + # default subdomain in all cases. This should be the default + # in Werkzeug but it currently does not have that feature. + if not self.subdomain_matching: + subdomain = self.url_map.default_subdomain or None + else: + subdomain = None + + return self.url_map.bind_to_environ( + request.environ, + server_name=self.config["SERVER_NAME"], + subdomain=subdomain, + ) + # We need at the very least the server name to be set for this + # to work. + if self.config["SERVER_NAME"] is not None: + return self.url_map.bind( + self.config["SERVER_NAME"], + script_name=self.config["APPLICATION_ROOT"], + url_scheme=self.config["PREFERRED_URL_SCHEME"], + ) + + return None + + def inject_url_defaults(self, endpoint: str, values: dict) -> None: + """Injects the URL defaults for the given endpoint directly into + the values dictionary passed. This is used internally and + automatically called on URL building. + + .. versionadded:: 0.7 + """ + funcs: t.Iterable[URLDefaultCallable] = self.url_default_functions[None] + + if "." in endpoint: + # This is called by url_for, which can be called outside a + # request, can't use request.blueprints. + bps = _split_blueprint_path(endpoint.rpartition(".")[0]) + bp_funcs = chain.from_iterable(self.url_default_functions[bp] for bp in bps) + funcs = chain(funcs, bp_funcs) + + for func in funcs: + func(endpoint, values) + + def handle_url_build_error( + self, error: Exception, endpoint: str, values: dict + ) -> str: + """Handle :class:`~werkzeug.routing.BuildError` on + :meth:`url_for`. + """ + for handler in self.url_build_error_handlers: + try: + rv = handler(error, endpoint, values) + except BuildError as e: + # make error available outside except block + error = e + else: + if rv is not None: + return rv + + # Re-raise if called with an active exception, otherwise raise + # the passed in exception. + if error is sys.exc_info()[1]: + raise + + raise error + + def preprocess_request(self) -> t.Optional[ResponseReturnValue]: + """Called before the request is dispatched. Calls + :attr:`url_value_preprocessors` registered with the app and the + current blueprint (if any). Then calls :attr:`before_request_funcs` + registered with the app and the blueprint. + + If any :meth:`before_request` handler returns a non-None value, the + value is handled as if it was the return value from the view, and + further request handling is stopped. + """ + + funcs: t.Iterable[URLValuePreprocessorCallable] = self.url_value_preprocessors[ + None + ] + for bp in request.blueprints: + if bp in self.url_value_preprocessors: + funcs = chain(funcs, self.url_value_preprocessors[bp]) + for func in funcs: + func(request.endpoint, request.view_args) + + funcs: t.Iterable[BeforeRequestCallable] = self.before_request_funcs[None] + for bp in request.blueprints: + if bp in self.before_request_funcs: + funcs = chain(funcs, self.before_request_funcs[bp]) + for func in funcs: + rv = self.ensure_sync(func)() + if rv is not None: + return rv + + return None + + def process_response(self, response: Response) -> Response: + """Can be overridden in order to modify the response object + before it's sent to the WSGI server. By default this will + call all the :meth:`after_request` decorated functions. + + .. versionchanged:: 0.5 + As of Flask 0.5 the functions registered for after request + execution are called in reverse order of registration. + + :param response: a :attr:`response_class` object. + :return: a new response object or the same, has to be an + instance of :attr:`response_class`. + """ + ctx = _request_ctx_stack.top + funcs: t.Iterable[AfterRequestCallable] = ctx._after_request_functions + for bp in request.blueprints: + if bp in self.after_request_funcs: + funcs = chain(funcs, reversed(self.after_request_funcs[bp])) + if None in self.after_request_funcs: + funcs = chain(funcs, reversed(self.after_request_funcs[None])) + for handler in funcs: + response = self.ensure_sync(handler)(response) + if not self.session_interface.is_null_session(ctx.session): + self.session_interface.save_session(self, ctx.session, response) + return response + + def do_teardown_request( + self, exc: t.Optional[BaseException] = _sentinel # type: ignore + ) -> None: + """Called after the request is dispatched and the response is + returned, right before the request context is popped. + + This calls all functions decorated with + :meth:`teardown_request`, and :meth:`Blueprint.teardown_request` + if a blueprint handled the request. Finally, the + :data:`request_tearing_down` signal is sent. + + This is called by + :meth:`RequestContext.pop() `, + which may be delayed during testing to maintain access to + resources. + + :param exc: An unhandled exception raised while dispatching the + request. Detected from the current exception information if + not passed. Passed to each teardown function. + + .. versionchanged:: 0.9 + Added the ``exc`` argument. + """ + if exc is _sentinel: + exc = sys.exc_info()[1] + funcs: t.Iterable[TeardownCallable] = reversed( + self.teardown_request_funcs[None] + ) + for bp in request.blueprints: + if bp in self.teardown_request_funcs: + funcs = chain(funcs, reversed(self.teardown_request_funcs[bp])) + for func in funcs: + self.ensure_sync(func)(exc) + request_tearing_down.send(self, exc=exc) + + def do_teardown_appcontext( + self, exc: t.Optional[BaseException] = _sentinel # type: ignore + ) -> None: + """Called right before the application context is popped. + + When handling a request, the application context is popped + after the request context. See :meth:`do_teardown_request`. + + This calls all functions decorated with + :meth:`teardown_appcontext`. Then the + :data:`appcontext_tearing_down` signal is sent. + + This is called by + :meth:`AppContext.pop() `. + + .. versionadded:: 0.9 + """ + if exc is _sentinel: + exc = sys.exc_info()[1] + for func in reversed(self.teardown_appcontext_funcs): + self.ensure_sync(func)(exc) + appcontext_tearing_down.send(self, exc=exc) + + def app_context(self) -> AppContext: + """Create an :class:`~flask.ctx.AppContext`. Use as a ``with`` + block to push the context, which will make :data:`current_app` + point at this application. + + An application context is automatically pushed by + :meth:`RequestContext.push() ` + when handling a request, and when running a CLI command. Use + this to manually create a context outside of these situations. + + :: + + with app.app_context(): + init_db() + + See :doc:`/appcontext`. + + .. versionadded:: 0.9 + """ + return AppContext(self) + + def request_context(self, environ: dict) -> RequestContext: + """Create a :class:`~flask.ctx.RequestContext` representing a + WSGI environment. Use a ``with`` block to push the context, + which will make :data:`request` point at this request. + + See :doc:`/reqcontext`. + + Typically you should not call this from your own code. A request + context is automatically pushed by the :meth:`wsgi_app` when + handling a request. Use :meth:`test_request_context` to create + an environment and context instead of this method. + + :param environ: a WSGI environment + """ + return RequestContext(self, environ) + + def test_request_context(self, *args: t.Any, **kwargs: t.Any) -> RequestContext: + """Create a :class:`~flask.ctx.RequestContext` for a WSGI + environment created from the given values. This is mostly useful + during testing, where you may want to run a function that uses + request data without dispatching a full request. + + See :doc:`/reqcontext`. + + Use a ``with`` block to push the context, which will make + :data:`request` point at the request for the created + environment. :: + + with test_request_context(...): + generate_report() + + When using the shell, it may be easier to push and pop the + context manually to avoid indentation. :: + + ctx = app.test_request_context(...) + ctx.push() + ... + ctx.pop() + + Takes the same arguments as Werkzeug's + :class:`~werkzeug.test.EnvironBuilder`, with some defaults from + the application. See the linked Werkzeug docs for most of the + available arguments. Flask-specific behavior is listed here. + + :param path: URL path being requested. + :param base_url: Base URL where the app is being served, which + ``path`` is relative to. If not given, built from + :data:`PREFERRED_URL_SCHEME`, ``subdomain``, + :data:`SERVER_NAME`, and :data:`APPLICATION_ROOT`. + :param subdomain: Subdomain name to append to + :data:`SERVER_NAME`. + :param url_scheme: Scheme to use instead of + :data:`PREFERRED_URL_SCHEME`. + :param data: The request body, either as a string or a dict of + form keys and values. + :param json: If given, this is serialized as JSON and passed as + ``data``. Also defaults ``content_type`` to + ``application/json``. + :param args: other positional arguments passed to + :class:`~werkzeug.test.EnvironBuilder`. + :param kwargs: other keyword arguments passed to + :class:`~werkzeug.test.EnvironBuilder`. + """ + from .testing import EnvironBuilder + + builder = EnvironBuilder(self, *args, **kwargs) + + try: + return self.request_context(builder.get_environ()) + finally: + builder.close() + + def wsgi_app(self, environ: dict, start_response: t.Callable) -> t.Any: + """The actual WSGI application. This is not implemented in + :meth:`__call__` so that middlewares can be applied without + losing a reference to the app object. Instead of doing this:: + + app = MyMiddleware(app) + + It's a better idea to do this instead:: + + app.wsgi_app = MyMiddleware(app.wsgi_app) + + Then you still have the original application object around and + can continue to call methods on it. + + .. versionchanged:: 0.7 + Teardown events for the request and app contexts are called + even if an unhandled error occurs. Other events may not be + called depending on when an error occurs during dispatch. + See :ref:`callbacks-and-errors`. + + :param environ: A WSGI environment. + :param start_response: A callable accepting a status code, + a list of headers, and an optional exception context to + start the response. + """ + ctx = self.request_context(environ) + error: t.Optional[BaseException] = None + try: + try: + ctx.push() + response = self.full_dispatch_request() + except Exception as e: + error = e + response = self.handle_exception(e) + except: # noqa: B001 + error = sys.exc_info()[1] + raise + return response(environ, start_response) + finally: + if self.should_ignore_error(error): + error = None + ctx.auto_pop(error) + + def __call__(self, environ: dict, start_response: t.Callable) -> t.Any: + """The WSGI server calls the Flask application object as the + WSGI application. This calls :meth:`wsgi_app`, which can be + wrapped to apply middleware. + """ + return self.wsgi_app(environ, start_response) diff --git a/venv/Lib/site-packages/flask/blueprints.py b/venv/Lib/site-packages/flask/blueprints.py new file mode 100644 index 0000000..f3913b3 --- /dev/null +++ b/venv/Lib/site-packages/flask/blueprints.py @@ -0,0 +1,603 @@ +import typing as t +from collections import defaultdict +from functools import update_wrapper + +from .scaffold import _endpoint_from_view_func +from .scaffold import _sentinel +from .scaffold import Scaffold +from .typing import AfterRequestCallable +from .typing import BeforeRequestCallable +from .typing import ErrorHandlerCallable +from .typing import TeardownCallable +from .typing import TemplateContextProcessorCallable +from .typing import TemplateFilterCallable +from .typing import TemplateGlobalCallable +from .typing import TemplateTestCallable +from .typing import URLDefaultCallable +from .typing import URLValuePreprocessorCallable + +if t.TYPE_CHECKING: + from .app import Flask + +DeferredSetupFunction = t.Callable[["BlueprintSetupState"], t.Callable] + + +class BlueprintSetupState: + """Temporary holder object for registering a blueprint with the + application. An instance of this class is created by the + :meth:`~flask.Blueprint.make_setup_state` method and later passed + to all register callback functions. + """ + + def __init__( + self, + blueprint: "Blueprint", + app: "Flask", + options: t.Any, + first_registration: bool, + ) -> None: + #: a reference to the current application + self.app = app + + #: a reference to the blueprint that created this setup state. + self.blueprint = blueprint + + #: a dictionary with all options that were passed to the + #: :meth:`~flask.Flask.register_blueprint` method. + self.options = options + + #: as blueprints can be registered multiple times with the + #: application and not everything wants to be registered + #: multiple times on it, this attribute can be used to figure + #: out if the blueprint was registered in the past already. + self.first_registration = first_registration + + subdomain = self.options.get("subdomain") + if subdomain is None: + subdomain = self.blueprint.subdomain + + #: The subdomain that the blueprint should be active for, ``None`` + #: otherwise. + self.subdomain = subdomain + + url_prefix = self.options.get("url_prefix") + if url_prefix is None: + url_prefix = self.blueprint.url_prefix + #: The prefix that should be used for all URLs defined on the + #: blueprint. + self.url_prefix = url_prefix + + self.name = self.options.get("name", blueprint.name) + self.name_prefix = self.options.get("name_prefix", "") + + #: A dictionary with URL defaults that is added to each and every + #: URL that was defined with the blueprint. + self.url_defaults = dict(self.blueprint.url_values_defaults) + self.url_defaults.update(self.options.get("url_defaults", ())) + + def add_url_rule( + self, + rule: str, + endpoint: t.Optional[str] = None, + view_func: t.Optional[t.Callable] = None, + **options: t.Any, + ) -> None: + """A helper method to register a rule (and optionally a view function) + to the application. The endpoint is automatically prefixed with the + blueprint's name. + """ + if self.url_prefix is not None: + if rule: + rule = "/".join((self.url_prefix.rstrip("/"), rule.lstrip("/"))) + else: + rule = self.url_prefix + options.setdefault("subdomain", self.subdomain) + if endpoint is None: + endpoint = _endpoint_from_view_func(view_func) # type: ignore + defaults = self.url_defaults + if "defaults" in options: + defaults = dict(defaults, **options.pop("defaults")) + + self.app.add_url_rule( + rule, + f"{self.name_prefix}.{self.name}.{endpoint}".lstrip("."), + view_func, + defaults=defaults, + **options, + ) + + +class Blueprint(Scaffold): + """Represents a blueprint, a collection of routes and other + app-related functions that can be registered on a real application + later. + + A blueprint is an object that allows defining application functions + without requiring an application object ahead of time. It uses the + same decorators as :class:`~flask.Flask`, but defers the need for an + application by recording them for later registration. + + Decorating a function with a blueprint creates a deferred function + that is called with :class:`~flask.blueprints.BlueprintSetupState` + when the blueprint is registered on an application. + + See :doc:`/blueprints` for more information. + + :param name: The name of the blueprint. Will be prepended to each + endpoint name. + :param import_name: The name of the blueprint package, usually + ``__name__``. This helps locate the ``root_path`` for the + blueprint. + :param static_folder: A folder with static files that should be + served by the blueprint's static route. The path is relative to + the blueprint's root path. Blueprint static files are disabled + by default. + :param static_url_path: The url to serve static files from. + Defaults to ``static_folder``. If the blueprint does not have + a ``url_prefix``, the app's static route will take precedence, + and the blueprint's static files won't be accessible. + :param template_folder: A folder with templates that should be added + to the app's template search path. The path is relative to the + blueprint's root path. Blueprint templates are disabled by + default. Blueprint templates have a lower precedence than those + in the app's templates folder. + :param url_prefix: A path to prepend to all of the blueprint's URLs, + to make them distinct from the rest of the app's routes. + :param subdomain: A subdomain that blueprint routes will match on by + default. + :param url_defaults: A dict of default values that blueprint routes + will receive by default. + :param root_path: By default, the blueprint will automatically set + this based on ``import_name``. In certain situations this + automatic detection can fail, so the path can be specified + manually instead. + + .. versionchanged:: 1.1.0 + Blueprints have a ``cli`` group to register nested CLI commands. + The ``cli_group`` parameter controls the name of the group under + the ``flask`` command. + + .. versionadded:: 0.7 + """ + + warn_on_modifications = False + _got_registered_once = False + + #: Blueprint local JSON encoder class to use. Set to ``None`` to use + #: the app's :class:`~flask.Flask.json_encoder`. + json_encoder = None + #: Blueprint local JSON decoder class to use. Set to ``None`` to use + #: the app's :class:`~flask.Flask.json_decoder`. + json_decoder = None + + def __init__( + self, + name: str, + import_name: str, + static_folder: t.Optional[str] = None, + static_url_path: t.Optional[str] = None, + template_folder: t.Optional[str] = None, + url_prefix: t.Optional[str] = None, + subdomain: t.Optional[str] = None, + url_defaults: t.Optional[dict] = None, + root_path: t.Optional[str] = None, + cli_group: t.Optional[str] = _sentinel, # type: ignore + ): + super().__init__( + import_name=import_name, + static_folder=static_folder, + static_url_path=static_url_path, + template_folder=template_folder, + root_path=root_path, + ) + + if "." in name: + raise ValueError("'name' may not contain a dot '.' character.") + + self.name = name + self.url_prefix = url_prefix + self.subdomain = subdomain + self.deferred_functions: t.List[DeferredSetupFunction] = [] + + if url_defaults is None: + url_defaults = {} + + self.url_values_defaults = url_defaults + self.cli_group = cli_group + self._blueprints: t.List[t.Tuple["Blueprint", dict]] = [] + + def _is_setup_finished(self) -> bool: + return self.warn_on_modifications and self._got_registered_once + + def record(self, func: t.Callable) -> None: + """Registers a function that is called when the blueprint is + registered on the application. This function is called with the + state as argument as returned by the :meth:`make_setup_state` + method. + """ + if self._got_registered_once and self.warn_on_modifications: + from warnings import warn + + warn( + Warning( + "The blueprint was already registered once but is" + " getting modified now. These changes will not show" + " up." + ) + ) + self.deferred_functions.append(func) + + def record_once(self, func: t.Callable) -> None: + """Works like :meth:`record` but wraps the function in another + function that will ensure the function is only called once. If the + blueprint is registered a second time on the application, the + function passed is not called. + """ + + def wrapper(state: BlueprintSetupState) -> None: + if state.first_registration: + func(state) + + return self.record(update_wrapper(wrapper, func)) + + def make_setup_state( + self, app: "Flask", options: dict, first_registration: bool = False + ) -> BlueprintSetupState: + """Creates an instance of :meth:`~flask.blueprints.BlueprintSetupState` + object that is later passed to the register callback functions. + Subclasses can override this to return a subclass of the setup state. + """ + return BlueprintSetupState(self, app, options, first_registration) + + def register_blueprint(self, blueprint: "Blueprint", **options: t.Any) -> None: + """Register a :class:`~flask.Blueprint` on this blueprint. Keyword + arguments passed to this method will override the defaults set + on the blueprint. + + .. versionchanged:: 2.0.1 + The ``name`` option can be used to change the (pre-dotted) + name the blueprint is registered with. This allows the same + blueprint to be registered multiple times with unique names + for ``url_for``. + + .. versionadded:: 2.0 + """ + if blueprint is self: + raise ValueError("Cannot register a blueprint on itself") + self._blueprints.append((blueprint, options)) + + def register(self, app: "Flask", options: dict) -> None: + """Called by :meth:`Flask.register_blueprint` to register all + views and callbacks registered on the blueprint with the + application. Creates a :class:`.BlueprintSetupState` and calls + each :meth:`record` callback with it. + + :param app: The application this blueprint is being registered + with. + :param options: Keyword arguments forwarded from + :meth:`~Flask.register_blueprint`. + + .. versionchanged:: 2.0.1 + Nested blueprints are registered with their dotted name. + This allows different blueprints with the same name to be + nested at different locations. + + .. versionchanged:: 2.0.1 + The ``name`` option can be used to change the (pre-dotted) + name the blueprint is registered with. This allows the same + blueprint to be registered multiple times with unique names + for ``url_for``. + + .. versionchanged:: 2.0.1 + Registering the same blueprint with the same name multiple + times is deprecated and will become an error in Flask 2.1. + """ + first_registration = not any(bp is self for bp in app.blueprints.values()) + name_prefix = options.get("name_prefix", "") + self_name = options.get("name", self.name) + name = f"{name_prefix}.{self_name}".lstrip(".") + + if name in app.blueprints: + existing_at = f" '{name}'" if self_name != name else "" + + if app.blueprints[name] is not self: + raise ValueError( + f"The name '{self_name}' is already registered for" + f" a different blueprint{existing_at}. Use 'name='" + " to provide a unique name." + ) + else: + import warnings + + warnings.warn( + f"The name '{self_name}' is already registered for" + f" this blueprint{existing_at}. Use 'name=' to" + " provide a unique name. This will become an error" + " in Flask 2.1.", + stacklevel=4, + ) + + app.blueprints[name] = self + self._got_registered_once = True + state = self.make_setup_state(app, options, first_registration) + + if self.has_static_folder: + state.add_url_rule( + f"{self.static_url_path}/", + view_func=self.send_static_file, + endpoint="static", + ) + + # Merge blueprint data into parent. + if first_registration: + + def extend(bp_dict, parent_dict): + for key, values in bp_dict.items(): + key = name if key is None else f"{name}.{key}" + parent_dict[key].extend(values) + + for key, value in self.error_handler_spec.items(): + key = name if key is None else f"{name}.{key}" + value = defaultdict( + dict, + { + code: { + exc_class: func for exc_class, func in code_values.items() + } + for code, code_values in value.items() + }, + ) + app.error_handler_spec[key] = value + + for endpoint, func in self.view_functions.items(): + app.view_functions[endpoint] = func + + extend(self.before_request_funcs, app.before_request_funcs) + extend(self.after_request_funcs, app.after_request_funcs) + extend( + self.teardown_request_funcs, + app.teardown_request_funcs, + ) + extend(self.url_default_functions, app.url_default_functions) + extend(self.url_value_preprocessors, app.url_value_preprocessors) + extend(self.template_context_processors, app.template_context_processors) + + for deferred in self.deferred_functions: + deferred(state) + + cli_resolved_group = options.get("cli_group", self.cli_group) + + if self.cli.commands: + if cli_resolved_group is None: + app.cli.commands.update(self.cli.commands) + elif cli_resolved_group is _sentinel: + self.cli.name = name + app.cli.add_command(self.cli) + else: + self.cli.name = cli_resolved_group + app.cli.add_command(self.cli) + + for blueprint, bp_options in self._blueprints: + bp_options = bp_options.copy() + bp_url_prefix = bp_options.get("url_prefix") + + if bp_url_prefix is None: + bp_url_prefix = blueprint.url_prefix + + if state.url_prefix is not None and bp_url_prefix is not None: + bp_options["url_prefix"] = ( + state.url_prefix.rstrip("/") + "/" + bp_url_prefix.lstrip("/") + ) + elif bp_url_prefix is not None: + bp_options["url_prefix"] = bp_url_prefix + elif state.url_prefix is not None: + bp_options["url_prefix"] = state.url_prefix + + bp_options["name_prefix"] = name + blueprint.register(app, bp_options) + + def add_url_rule( + self, + rule: str, + endpoint: t.Optional[str] = None, + view_func: t.Optional[t.Callable] = None, + provide_automatic_options: t.Optional[bool] = None, + **options: t.Any, + ) -> None: + """Like :meth:`Flask.add_url_rule` but for a blueprint. The endpoint for + the :func:`url_for` function is prefixed with the name of the blueprint. + """ + if endpoint and "." in endpoint: + raise ValueError("'endpoint' may not contain a dot '.' character.") + + if view_func and hasattr(view_func, "__name__") and "." in view_func.__name__: + raise ValueError("'view_func' name may not contain a dot '.' character.") + + self.record( + lambda s: s.add_url_rule( + rule, + endpoint, + view_func, + provide_automatic_options=provide_automatic_options, + **options, + ) + ) + + def app_template_filter( + self, name: t.Optional[str] = None + ) -> t.Callable[[TemplateFilterCallable], TemplateFilterCallable]: + """Register a custom template filter, available application wide. Like + :meth:`Flask.template_filter` but for a blueprint. + + :param name: the optional name of the filter, otherwise the + function name will be used. + """ + + def decorator(f: TemplateFilterCallable) -> TemplateFilterCallable: + self.add_app_template_filter(f, name=name) + return f + + return decorator + + def add_app_template_filter( + self, f: TemplateFilterCallable, name: t.Optional[str] = None + ) -> None: + """Register a custom template filter, available application wide. Like + :meth:`Flask.add_template_filter` but for a blueprint. Works exactly + like the :meth:`app_template_filter` decorator. + + :param name: the optional name of the filter, otherwise the + function name will be used. + """ + + def register_template(state: BlueprintSetupState) -> None: + state.app.jinja_env.filters[name or f.__name__] = f + + self.record_once(register_template) + + def app_template_test( + self, name: t.Optional[str] = None + ) -> t.Callable[[TemplateTestCallable], TemplateTestCallable]: + """Register a custom template test, available application wide. Like + :meth:`Flask.template_test` but for a blueprint. + + .. versionadded:: 0.10 + + :param name: the optional name of the test, otherwise the + function name will be used. + """ + + def decorator(f: TemplateTestCallable) -> TemplateTestCallable: + self.add_app_template_test(f, name=name) + return f + + return decorator + + def add_app_template_test( + self, f: TemplateTestCallable, name: t.Optional[str] = None + ) -> None: + """Register a custom template test, available application wide. Like + :meth:`Flask.add_template_test` but for a blueprint. Works exactly + like the :meth:`app_template_test` decorator. + + .. versionadded:: 0.10 + + :param name: the optional name of the test, otherwise the + function name will be used. + """ + + def register_template(state: BlueprintSetupState) -> None: + state.app.jinja_env.tests[name or f.__name__] = f + + self.record_once(register_template) + + def app_template_global( + self, name: t.Optional[str] = None + ) -> t.Callable[[TemplateGlobalCallable], TemplateGlobalCallable]: + """Register a custom template global, available application wide. Like + :meth:`Flask.template_global` but for a blueprint. + + .. versionadded:: 0.10 + + :param name: the optional name of the global, otherwise the + function name will be used. + """ + + def decorator(f: TemplateGlobalCallable) -> TemplateGlobalCallable: + self.add_app_template_global(f, name=name) + return f + + return decorator + + def add_app_template_global( + self, f: TemplateGlobalCallable, name: t.Optional[str] = None + ) -> None: + """Register a custom template global, available application wide. Like + :meth:`Flask.add_template_global` but for a blueprint. Works exactly + like the :meth:`app_template_global` decorator. + + .. versionadded:: 0.10 + + :param name: the optional name of the global, otherwise the + function name will be used. + """ + + def register_template(state: BlueprintSetupState) -> None: + state.app.jinja_env.globals[name or f.__name__] = f + + self.record_once(register_template) + + def before_app_request(self, f: BeforeRequestCallable) -> BeforeRequestCallable: + """Like :meth:`Flask.before_request`. Such a function is executed + before each request, even if outside of a blueprint. + """ + self.record_once( + lambda s: s.app.before_request_funcs.setdefault(None, []).append(f) + ) + return f + + def before_app_first_request( + self, f: BeforeRequestCallable + ) -> BeforeRequestCallable: + """Like :meth:`Flask.before_first_request`. Such a function is + executed before the first request to the application. + """ + self.record_once(lambda s: s.app.before_first_request_funcs.append(f)) + return f + + def after_app_request(self, f: AfterRequestCallable) -> AfterRequestCallable: + """Like :meth:`Flask.after_request` but for a blueprint. Such a function + is executed after each request, even if outside of the blueprint. + """ + self.record_once( + lambda s: s.app.after_request_funcs.setdefault(None, []).append(f) + ) + return f + + def teardown_app_request(self, f: TeardownCallable) -> TeardownCallable: + """Like :meth:`Flask.teardown_request` but for a blueprint. Such a + function is executed when tearing down each request, even if outside of + the blueprint. + """ + self.record_once( + lambda s: s.app.teardown_request_funcs.setdefault(None, []).append(f) + ) + return f + + def app_context_processor( + self, f: TemplateContextProcessorCallable + ) -> TemplateContextProcessorCallable: + """Like :meth:`Flask.context_processor` but for a blueprint. Such a + function is executed each request, even if outside of the blueprint. + """ + self.record_once( + lambda s: s.app.template_context_processors.setdefault(None, []).append(f) + ) + return f + + def app_errorhandler(self, code: t.Union[t.Type[Exception], int]) -> t.Callable: + """Like :meth:`Flask.errorhandler` but for a blueprint. This + handler is used for all requests, even if outside of the blueprint. + """ + + def decorator(f: ErrorHandlerCallable) -> ErrorHandlerCallable: + self.record_once(lambda s: s.app.errorhandler(code)(f)) + return f + + return decorator + + def app_url_value_preprocessor( + self, f: URLValuePreprocessorCallable + ) -> URLValuePreprocessorCallable: + """Same as :meth:`url_value_preprocessor` but application wide.""" + self.record_once( + lambda s: s.app.url_value_preprocessors.setdefault(None, []).append(f) + ) + return f + + def app_url_defaults(self, f: URLDefaultCallable) -> URLDefaultCallable: + """Same as :meth:`url_defaults` but application wide.""" + self.record_once( + lambda s: s.app.url_default_functions.setdefault(None, []).append(f) + ) + return f diff --git a/venv/Lib/site-packages/flask/cli.py b/venv/Lib/site-packages/flask/cli.py new file mode 100644 index 0000000..d9e810d --- /dev/null +++ b/venv/Lib/site-packages/flask/cli.py @@ -0,0 +1,994 @@ +import ast +import inspect +import os +import platform +import re +import sys +import traceback +import warnings +from functools import update_wrapper +from operator import attrgetter +from threading import Lock +from threading import Thread + +import click +from werkzeug.utils import import_string + +from .globals import current_app +from .helpers import get_debug_flag +from .helpers import get_env +from .helpers import get_load_dotenv + +try: + import dotenv +except ImportError: + dotenv = None + +try: + import ssl +except ImportError: + ssl = None # type: ignore + + +class NoAppException(click.UsageError): + """Raised if an application cannot be found or loaded.""" + + +def find_best_app(script_info, module): + """Given a module instance this tries to find the best possible + application in the module or raises an exception. + """ + from . import Flask + + # Search for the most common names first. + for attr_name in ("app", "application"): + app = getattr(module, attr_name, None) + + if isinstance(app, Flask): + return app + + # Otherwise find the only object that is a Flask instance. + matches = [v for v in module.__dict__.values() if isinstance(v, Flask)] + + if len(matches) == 1: + return matches[0] + elif len(matches) > 1: + raise NoAppException( + "Detected multiple Flask applications in module" + f" {module.__name__!r}. Use 'FLASK_APP={module.__name__}:name'" + f" to specify the correct one." + ) + + # Search for app factory functions. + for attr_name in ("create_app", "make_app"): + app_factory = getattr(module, attr_name, None) + + if inspect.isfunction(app_factory): + try: + app = call_factory(script_info, app_factory) + + if isinstance(app, Flask): + return app + except TypeError: + if not _called_with_wrong_args(app_factory): + raise + raise NoAppException( + f"Detected factory {attr_name!r} in module {module.__name__!r}," + " but could not call it without arguments. Use" + f" \"FLASK_APP='{module.__name__}:{attr_name}(args)'\"" + " to specify arguments." + ) + + raise NoAppException( + "Failed to find Flask application or factory in module" + f" {module.__name__!r}. Use 'FLASK_APP={module.__name__}:name'" + " to specify one." + ) + + +def call_factory(script_info, app_factory, args=None, kwargs=None): + """Takes an app factory, a ``script_info` object and optionally a tuple + of arguments. Checks for the existence of a script_info argument and calls + the app_factory depending on that and the arguments provided. + """ + sig = inspect.signature(app_factory) + args = [] if args is None else args + kwargs = {} if kwargs is None else kwargs + + if "script_info" in sig.parameters: + warnings.warn( + "The 'script_info' argument is deprecated and will not be" + " passed to the app factory function in Flask 2.1.", + DeprecationWarning, + ) + kwargs["script_info"] = script_info + + if ( + not args + and len(sig.parameters) == 1 + and next(iter(sig.parameters.values())).default is inspect.Parameter.empty + ): + warnings.warn( + "Script info is deprecated and will not be passed as the" + " single argument to the app factory function in Flask" + " 2.1.", + DeprecationWarning, + ) + args.append(script_info) + + return app_factory(*args, **kwargs) + + +def _called_with_wrong_args(f): + """Check whether calling a function raised a ``TypeError`` because + the call failed or because something in the factory raised the + error. + + :param f: The function that was called. + :return: ``True`` if the call failed. + """ + tb = sys.exc_info()[2] + + try: + while tb is not None: + if tb.tb_frame.f_code is f.__code__: + # In the function, it was called successfully. + return False + + tb = tb.tb_next + + # Didn't reach the function. + return True + finally: + # Delete tb to break a circular reference. + # https://docs.python.org/2/library/sys.html#sys.exc_info + del tb + + +def find_app_by_string(script_info, module, app_name): + """Check if the given string is a variable name or a function. Call + a function to get the app instance, or return the variable directly. + """ + from . import Flask + + # Parse app_name as a single expression to determine if it's a valid + # attribute name or function call. + try: + expr = ast.parse(app_name.strip(), mode="eval").body + except SyntaxError: + raise NoAppException( + f"Failed to parse {app_name!r} as an attribute name or function call." + ) + + if isinstance(expr, ast.Name): + name = expr.id + args = kwargs = None + elif isinstance(expr, ast.Call): + # Ensure the function name is an attribute name only. + if not isinstance(expr.func, ast.Name): + raise NoAppException( + f"Function reference must be a simple name: {app_name!r}." + ) + + name = expr.func.id + + # Parse the positional and keyword arguments as literals. + try: + args = [ast.literal_eval(arg) for arg in expr.args] + kwargs = {kw.arg: ast.literal_eval(kw.value) for kw in expr.keywords} + except ValueError: + # literal_eval gives cryptic error messages, show a generic + # message with the full expression instead. + raise NoAppException( + f"Failed to parse arguments as literal values: {app_name!r}." + ) + else: + raise NoAppException( + f"Failed to parse {app_name!r} as an attribute name or function call." + ) + + try: + attr = getattr(module, name) + except AttributeError: + raise NoAppException( + f"Failed to find attribute {name!r} in {module.__name__!r}." + ) + + # If the attribute is a function, call it with any args and kwargs + # to get the real application. + if inspect.isfunction(attr): + try: + app = call_factory(script_info, attr, args, kwargs) + except TypeError: + if not _called_with_wrong_args(attr): + raise + + raise NoAppException( + f"The factory {app_name!r} in module" + f" {module.__name__!r} could not be called with the" + " specified arguments." + ) + else: + app = attr + + if isinstance(app, Flask): + return app + + raise NoAppException( + "A valid Flask application was not obtained from" + f" '{module.__name__}:{app_name}'." + ) + + +def prepare_import(path): + """Given a filename this will try to calculate the python path, add it + to the search path and return the actual module name that is expected. + """ + path = os.path.realpath(path) + + fname, ext = os.path.splitext(path) + if ext == ".py": + path = fname + + if os.path.basename(path) == "__init__": + path = os.path.dirname(path) + + module_name = [] + + # move up until outside package structure (no __init__.py) + while True: + path, name = os.path.split(path) + module_name.append(name) + + if not os.path.exists(os.path.join(path, "__init__.py")): + break + + if sys.path[0] != path: + sys.path.insert(0, path) + + return ".".join(module_name[::-1]) + + +def locate_app(script_info, module_name, app_name, raise_if_not_found=True): + __traceback_hide__ = True # noqa: F841 + + try: + __import__(module_name) + except ImportError: + # Reraise the ImportError if it occurred within the imported module. + # Determine this by checking whether the trace has a depth > 1. + if sys.exc_info()[2].tb_next: + raise NoAppException( + f"While importing {module_name!r}, an ImportError was" + f" raised:\n\n{traceback.format_exc()}" + ) + elif raise_if_not_found: + raise NoAppException(f"Could not import {module_name!r}.") + else: + return + + module = sys.modules[module_name] + + if app_name is None: + return find_best_app(script_info, module) + else: + return find_app_by_string(script_info, module, app_name) + + +def get_version(ctx, param, value): + if not value or ctx.resilient_parsing: + return + + import werkzeug + from . import __version__ + + click.echo( + f"Python {platform.python_version()}\n" + f"Flask {__version__}\n" + f"Werkzeug {werkzeug.__version__}", + color=ctx.color, + ) + ctx.exit() + + +version_option = click.Option( + ["--version"], + help="Show the flask version", + expose_value=False, + callback=get_version, + is_flag=True, + is_eager=True, +) + + +class DispatchingApp: + """Special application that dispatches to a Flask application which + is imported by name in a background thread. If an error happens + it is recorded and shown as part of the WSGI handling which in case + of the Werkzeug debugger means that it shows up in the browser. + """ + + def __init__(self, loader, use_eager_loading=None): + self.loader = loader + self._app = None + self._lock = Lock() + self._bg_loading_exc_info = None + + if use_eager_loading is None: + use_eager_loading = os.environ.get("WERKZEUG_RUN_MAIN") != "true" + + if use_eager_loading: + self._load_unlocked() + else: + self._load_in_background() + + def _load_in_background(self): + def _load_app(): + __traceback_hide__ = True # noqa: F841 + with self._lock: + try: + self._load_unlocked() + except Exception: + self._bg_loading_exc_info = sys.exc_info() + + t = Thread(target=_load_app, args=()) + t.start() + + def _flush_bg_loading_exception(self): + __traceback_hide__ = True # noqa: F841 + exc_info = self._bg_loading_exc_info + if exc_info is not None: + self._bg_loading_exc_info = None + raise exc_info + + def _load_unlocked(self): + __traceback_hide__ = True # noqa: F841 + self._app = rv = self.loader() + self._bg_loading_exc_info = None + return rv + + def __call__(self, environ, start_response): + __traceback_hide__ = True # noqa: F841 + if self._app is not None: + return self._app(environ, start_response) + self._flush_bg_loading_exception() + with self._lock: + if self._app is not None: + rv = self._app + else: + rv = self._load_unlocked() + return rv(environ, start_response) + + +class ScriptInfo: + """Helper object to deal with Flask applications. This is usually not + necessary to interface with as it's used internally in the dispatching + to click. In future versions of Flask this object will most likely play + a bigger role. Typically it's created automatically by the + :class:`FlaskGroup` but you can also manually create it and pass it + onwards as click object. + """ + + def __init__(self, app_import_path=None, create_app=None, set_debug_flag=True): + #: Optionally the import path for the Flask application. + self.app_import_path = app_import_path or os.environ.get("FLASK_APP") + #: Optionally a function that is passed the script info to create + #: the instance of the application. + self.create_app = create_app + #: A dictionary with arbitrary data that can be associated with + #: this script info. + self.data = {} + self.set_debug_flag = set_debug_flag + self._loaded_app = None + + def load_app(self): + """Loads the Flask app (if not yet loaded) and returns it. Calling + this multiple times will just result in the already loaded app to + be returned. + """ + __traceback_hide__ = True # noqa: F841 + + if self._loaded_app is not None: + return self._loaded_app + + if self.create_app is not None: + app = call_factory(self, self.create_app) + else: + if self.app_import_path: + path, name = ( + re.split(r":(?![\\/])", self.app_import_path, 1) + [None] + )[:2] + import_name = prepare_import(path) + app = locate_app(self, import_name, name) + else: + for path in ("wsgi.py", "app.py"): + import_name = prepare_import(path) + app = locate_app(self, import_name, None, raise_if_not_found=False) + + if app: + break + + if not app: + raise NoAppException( + "Could not locate a Flask application. You did not provide " + 'the "FLASK_APP" environment variable, and a "wsgi.py" or ' + '"app.py" module was not found in the current directory.' + ) + + if self.set_debug_flag: + # Update the app's debug flag through the descriptor so that + # other values repopulate as well. + app.debug = get_debug_flag() + + self._loaded_app = app + return app + + +pass_script_info = click.make_pass_decorator(ScriptInfo, ensure=True) + + +def with_appcontext(f): + """Wraps a callback so that it's guaranteed to be executed with the + script's application context. If callbacks are registered directly + to the ``app.cli`` object then they are wrapped with this function + by default unless it's disabled. + """ + + @click.pass_context + def decorator(__ctx, *args, **kwargs): + with __ctx.ensure_object(ScriptInfo).load_app().app_context(): + return __ctx.invoke(f, *args, **kwargs) + + return update_wrapper(decorator, f) + + +class AppGroup(click.Group): + """This works similar to a regular click :class:`~click.Group` but it + changes the behavior of the :meth:`command` decorator so that it + automatically wraps the functions in :func:`with_appcontext`. + + Not to be confused with :class:`FlaskGroup`. + """ + + def command(self, *args, **kwargs): + """This works exactly like the method of the same name on a regular + :class:`click.Group` but it wraps callbacks in :func:`with_appcontext` + unless it's disabled by passing ``with_appcontext=False``. + """ + wrap_for_ctx = kwargs.pop("with_appcontext", True) + + def decorator(f): + if wrap_for_ctx: + f = with_appcontext(f) + return click.Group.command(self, *args, **kwargs)(f) + + return decorator + + def group(self, *args, **kwargs): + """This works exactly like the method of the same name on a regular + :class:`click.Group` but it defaults the group class to + :class:`AppGroup`. + """ + kwargs.setdefault("cls", AppGroup) + return click.Group.group(self, *args, **kwargs) + + +class FlaskGroup(AppGroup): + """Special subclass of the :class:`AppGroup` group that supports + loading more commands from the configured Flask app. Normally a + developer does not have to interface with this class but there are + some very advanced use cases for which it makes sense to create an + instance of this. see :ref:`custom-scripts`. + + :param add_default_commands: if this is True then the default run and + shell commands will be added. + :param add_version_option: adds the ``--version`` option. + :param create_app: an optional callback that is passed the script info and + returns the loaded app. + :param load_dotenv: Load the nearest :file:`.env` and :file:`.flaskenv` + files to set environment variables. Will also change the working + directory to the directory containing the first file found. + :param set_debug_flag: Set the app's debug flag based on the active + environment + + .. versionchanged:: 1.0 + If installed, python-dotenv will be used to load environment variables + from :file:`.env` and :file:`.flaskenv` files. + """ + + def __init__( + self, + add_default_commands=True, + create_app=None, + add_version_option=True, + load_dotenv=True, + set_debug_flag=True, + **extra, + ): + params = list(extra.pop("params", None) or ()) + + if add_version_option: + params.append(version_option) + + AppGroup.__init__(self, params=params, **extra) + self.create_app = create_app + self.load_dotenv = load_dotenv + self.set_debug_flag = set_debug_flag + + if add_default_commands: + self.add_command(run_command) + self.add_command(shell_command) + self.add_command(routes_command) + + self._loaded_plugin_commands = False + + def _load_plugin_commands(self): + if self._loaded_plugin_commands: + return + try: + import pkg_resources + except ImportError: + self._loaded_plugin_commands = True + return + + for ep in pkg_resources.iter_entry_points("flask.commands"): + self.add_command(ep.load(), ep.name) + self._loaded_plugin_commands = True + + def get_command(self, ctx, name): + self._load_plugin_commands() + # Look up built-in and plugin commands, which should be + # available even if the app fails to load. + rv = super().get_command(ctx, name) + + if rv is not None: + return rv + + info = ctx.ensure_object(ScriptInfo) + + # Look up commands provided by the app, showing an error and + # continuing if the app couldn't be loaded. + try: + return info.load_app().cli.get_command(ctx, name) + except NoAppException as e: + click.secho(f"Error: {e.format_message()}\n", err=True, fg="red") + + def list_commands(self, ctx): + self._load_plugin_commands() + # Start with the built-in and plugin commands. + rv = set(super().list_commands(ctx)) + info = ctx.ensure_object(ScriptInfo) + + # Add commands provided by the app, showing an error and + # continuing if the app couldn't be loaded. + try: + rv.update(info.load_app().cli.list_commands(ctx)) + except NoAppException as e: + # When an app couldn't be loaded, show the error message + # without the traceback. + click.secho(f"Error: {e.format_message()}\n", err=True, fg="red") + except Exception: + # When any other errors occurred during loading, show the + # full traceback. + click.secho(f"{traceback.format_exc()}\n", err=True, fg="red") + + return sorted(rv) + + def main(self, *args, **kwargs): + # Set a global flag that indicates that we were invoked from the + # command line interface. This is detected by Flask.run to make the + # call into a no-op. This is necessary to avoid ugly errors when the + # script that is loaded here also attempts to start a server. + os.environ["FLASK_RUN_FROM_CLI"] = "true" + + if get_load_dotenv(self.load_dotenv): + load_dotenv() + + obj = kwargs.get("obj") + + if obj is None: + obj = ScriptInfo( + create_app=self.create_app, set_debug_flag=self.set_debug_flag + ) + + kwargs["obj"] = obj + kwargs.setdefault("auto_envvar_prefix", "FLASK") + return super().main(*args, **kwargs) + + +def _path_is_ancestor(path, other): + """Take ``other`` and remove the length of ``path`` from it. Then join it + to ``path``. If it is the original value, ``path`` is an ancestor of + ``other``.""" + return os.path.join(path, other[len(path) :].lstrip(os.sep)) == other + + +def load_dotenv(path=None): + """Load "dotenv" files in order of precedence to set environment variables. + + If an env var is already set it is not overwritten, so earlier files in the + list are preferred over later files. + + This is a no-op if `python-dotenv`_ is not installed. + + .. _python-dotenv: https://github.com/theskumar/python-dotenv#readme + + :param path: Load the file at this location instead of searching. + :return: ``True`` if a file was loaded. + + .. versionchanged:: 1.1.0 + Returns ``False`` when python-dotenv is not installed, or when + the given path isn't a file. + + .. versionchanged:: 2.0 + When loading the env files, set the default encoding to UTF-8. + + .. versionadded:: 1.0 + """ + if dotenv is None: + if path or os.path.isfile(".env") or os.path.isfile(".flaskenv"): + click.secho( + " * Tip: There are .env or .flaskenv files present." + ' Do "pip install python-dotenv" to use them.', + fg="yellow", + err=True, + ) + + return False + + # if the given path specifies the actual file then return True, + # else False + if path is not None: + if os.path.isfile(path): + return dotenv.load_dotenv(path, encoding="utf-8") + + return False + + new_dir = None + + for name in (".env", ".flaskenv"): + path = dotenv.find_dotenv(name, usecwd=True) + + if not path: + continue + + if new_dir is None: + new_dir = os.path.dirname(path) + + dotenv.load_dotenv(path, encoding="utf-8") + + return new_dir is not None # at least one file was located and loaded + + +def show_server_banner(env, debug, app_import_path, eager_loading): + """Show extra startup messages the first time the server is run, + ignoring the reloader. + """ + if os.environ.get("WERKZEUG_RUN_MAIN") == "true": + return + + if app_import_path is not None: + message = f" * Serving Flask app {app_import_path!r}" + + if not eager_loading: + message += " (lazy loading)" + + click.echo(message) + + click.echo(f" * Environment: {env}") + + if env == "production": + click.secho( + " WARNING: This is a development server. Do not use it in" + " a production deployment.", + fg="red", + ) + click.secho(" Use a production WSGI server instead.", dim=True) + + if debug is not None: + click.echo(f" * Debug mode: {'on' if debug else 'off'}") + + +class CertParamType(click.ParamType): + """Click option type for the ``--cert`` option. Allows either an + existing file, the string ``'adhoc'``, or an import for a + :class:`~ssl.SSLContext` object. + """ + + name = "path" + + def __init__(self): + self.path_type = click.Path(exists=True, dir_okay=False, resolve_path=True) + + def convert(self, value, param, ctx): + if ssl is None: + raise click.BadParameter( + 'Using "--cert" requires Python to be compiled with SSL support.', + ctx, + param, + ) + + try: + return self.path_type(value, param, ctx) + except click.BadParameter: + value = click.STRING(value, param, ctx).lower() + + if value == "adhoc": + try: + import cryptography # noqa: F401 + except ImportError: + raise click.BadParameter( + "Using ad-hoc certificates requires the cryptography library.", + ctx, + param, + ) + + return value + + obj = import_string(value, silent=True) + + if isinstance(obj, ssl.SSLContext): + return obj + + raise + + +def _validate_key(ctx, param, value): + """The ``--key`` option must be specified when ``--cert`` is a file. + Modifies the ``cert`` param to be a ``(cert, key)`` pair if needed. + """ + cert = ctx.params.get("cert") + is_adhoc = cert == "adhoc" + is_context = ssl and isinstance(cert, ssl.SSLContext) + + if value is not None: + if is_adhoc: + raise click.BadParameter( + 'When "--cert" is "adhoc", "--key" is not used.', ctx, param + ) + + if is_context: + raise click.BadParameter( + 'When "--cert" is an SSLContext object, "--key is not used.', ctx, param + ) + + if not cert: + raise click.BadParameter('"--cert" must also be specified.', ctx, param) + + ctx.params["cert"] = cert, value + + else: + if cert and not (is_adhoc or is_context): + raise click.BadParameter('Required when using "--cert".', ctx, param) + + return value + + +class SeparatedPathType(click.Path): + """Click option type that accepts a list of values separated by the + OS's path separator (``:``, ``;`` on Windows). Each value is + validated as a :class:`click.Path` type. + """ + + def convert(self, value, param, ctx): + items = self.split_envvar_value(value) + super_convert = super().convert + return [super_convert(item, param, ctx) for item in items] + + +@click.command("run", short_help="Run a development server.") +@click.option("--host", "-h", default="127.0.0.1", help="The interface to bind to.") +@click.option("--port", "-p", default=5000, help="The port to bind to.") +@click.option( + "--cert", type=CertParamType(), help="Specify a certificate file to use HTTPS." +) +@click.option( + "--key", + type=click.Path(exists=True, dir_okay=False, resolve_path=True), + callback=_validate_key, + expose_value=False, + help="The key file to use when specifying a certificate.", +) +@click.option( + "--reload/--no-reload", + default=None, + help="Enable or disable the reloader. By default the reloader " + "is active if debug is enabled.", +) +@click.option( + "--debugger/--no-debugger", + default=None, + help="Enable or disable the debugger. By default the debugger " + "is active if debug is enabled.", +) +@click.option( + "--eager-loading/--lazy-loading", + default=None, + help="Enable or disable eager loading. By default eager " + "loading is enabled if the reloader is disabled.", +) +@click.option( + "--with-threads/--without-threads", + default=True, + help="Enable or disable multithreading.", +) +@click.option( + "--extra-files", + default=None, + type=SeparatedPathType(), + help=( + "Extra files that trigger a reload on change. Multiple paths" + f" are separated by {os.path.pathsep!r}." + ), +) +@pass_script_info +def run_command( + info, host, port, reload, debugger, eager_loading, with_threads, cert, extra_files +): + """Run a local development server. + + This server is for development purposes only. It does not provide + the stability, security, or performance of production WSGI servers. + + The reloader and debugger are enabled by default if + FLASK_ENV=development or FLASK_DEBUG=1. + """ + debug = get_debug_flag() + + if reload is None: + reload = debug + + if debugger is None: + debugger = debug + + show_server_banner(get_env(), debug, info.app_import_path, eager_loading) + app = DispatchingApp(info.load_app, use_eager_loading=eager_loading) + + from werkzeug.serving import run_simple + + run_simple( + host, + port, + app, + use_reloader=reload, + use_debugger=debugger, + threaded=with_threads, + ssl_context=cert, + extra_files=extra_files, + ) + + +@click.command("shell", short_help="Run a shell in the app context.") +@with_appcontext +def shell_command() -> None: + """Run an interactive Python shell in the context of a given + Flask application. The application will populate the default + namespace of this shell according to its configuration. + + This is useful for executing small snippets of management code + without having to manually configure the application. + """ + import code + from .globals import _app_ctx_stack + + app = _app_ctx_stack.top.app + banner = ( + f"Python {sys.version} on {sys.platform}\n" + f"App: {app.import_name} [{app.env}]\n" + f"Instance: {app.instance_path}" + ) + ctx: dict = {} + + # Support the regular Python interpreter startup script if someone + # is using it. + startup = os.environ.get("PYTHONSTARTUP") + if startup and os.path.isfile(startup): + with open(startup) as f: + eval(compile(f.read(), startup, "exec"), ctx) + + ctx.update(app.make_shell_context()) + + # Site, customize, or startup script can set a hook to call when + # entering interactive mode. The default one sets up readline with + # tab and history completion. + interactive_hook = getattr(sys, "__interactivehook__", None) + + if interactive_hook is not None: + try: + import readline + from rlcompleter import Completer + except ImportError: + pass + else: + # rlcompleter uses __main__.__dict__ by default, which is + # flask.__main__. Use the shell context instead. + readline.set_completer(Completer(ctx).complete) + + interactive_hook() + + code.interact(banner=banner, local=ctx) + + +@click.command("routes", short_help="Show the routes for the app.") +@click.option( + "--sort", + "-s", + type=click.Choice(("endpoint", "methods", "rule", "match")), + default="endpoint", + help=( + 'Method to sort routes by. "match" is the order that Flask will match ' + "routes when dispatching a request." + ), +) +@click.option("--all-methods", is_flag=True, help="Show HEAD and OPTIONS methods.") +@with_appcontext +def routes_command(sort: str, all_methods: bool) -> None: + """Show all registered routes with endpoints and methods.""" + + rules = list(current_app.url_map.iter_rules()) + if not rules: + click.echo("No routes were registered.") + return + + ignored_methods = set(() if all_methods else ("HEAD", "OPTIONS")) + + if sort in ("endpoint", "rule"): + rules = sorted(rules, key=attrgetter(sort)) + elif sort == "methods": + rules = sorted(rules, key=lambda rule: sorted(rule.methods)) # type: ignore + + rule_methods = [ + ", ".join(sorted(rule.methods - ignored_methods)) # type: ignore + for rule in rules + ] + + headers = ("Endpoint", "Methods", "Rule") + widths = ( + max(len(rule.endpoint) for rule in rules), + max(len(methods) for methods in rule_methods), + max(len(rule.rule) for rule in rules), + ) + widths = [max(len(h), w) for h, w in zip(headers, widths)] + row = "{{0:<{0}}} {{1:<{1}}} {{2:<{2}}}".format(*widths) + + click.echo(row.format(*headers).strip()) + click.echo(row.format(*("-" * width for width in widths))) + + for rule, methods in zip(rules, rule_methods): + click.echo(row.format(rule.endpoint, methods, rule.rule).rstrip()) + + +cli = FlaskGroup( + help="""\ +A general utility script for Flask applications. + +Provides commands from Flask, extensions, and the application. Loads the +application defined in the FLASK_APP environment variable, or from a wsgi.py +file. Setting the FLASK_ENV environment variable to 'development' will enable +debug mode. + +\b + {prefix}{cmd} FLASK_APP=hello.py + {prefix}{cmd} FLASK_ENV=development + {prefix}flask run +""".format( + cmd="export" if os.name == "posix" else "set", + prefix="$ " if os.name == "posix" else "> ", + ) +) + + +def main() -> None: + if int(click.__version__[0]) < 8: + warnings.warn( + "Using the `flask` cli with Click 7 is deprecated and" + " will not be supported starting with Flask 2.1." + " Please upgrade to Click 8 as soon as possible.", + DeprecationWarning, + ) + # TODO omit sys.argv once https://github.com/pallets/click/issues/536 is fixed + cli.main(args=sys.argv[1:]) + + +if __name__ == "__main__": + main() diff --git a/venv/Lib/site-packages/flask/config.py b/venv/Lib/site-packages/flask/config.py new file mode 100644 index 0000000..c79a558 --- /dev/null +++ b/venv/Lib/site-packages/flask/config.py @@ -0,0 +1,291 @@ +import errno +import os +import types +import typing as t + +from werkzeug.utils import import_string + + +class ConfigAttribute: + """Makes an attribute forward to the config""" + + def __init__(self, name: str, get_converter: t.Optional[t.Callable] = None) -> None: + self.__name__ = name + self.get_converter = get_converter + + def __get__(self, obj: t.Any, owner: t.Any = None) -> t.Any: + if obj is None: + return self + rv = obj.config[self.__name__] + if self.get_converter is not None: + rv = self.get_converter(rv) + return rv + + def __set__(self, obj: t.Any, value: t.Any) -> None: + obj.config[self.__name__] = value + + +class Config(dict): + """Works exactly like a dict but provides ways to fill it from files + or special dictionaries. There are two common patterns to populate the + config. + + Either you can fill the config from a config file:: + + app.config.from_pyfile('yourconfig.cfg') + + Or alternatively you can define the configuration options in the + module that calls :meth:`from_object` or provide an import path to + a module that should be loaded. It is also possible to tell it to + use the same module and with that provide the configuration values + just before the call:: + + DEBUG = True + SECRET_KEY = 'development key' + app.config.from_object(__name__) + + In both cases (loading from any Python file or loading from modules), + only uppercase keys are added to the config. This makes it possible to use + lowercase values in the config file for temporary values that are not added + to the config or to define the config keys in the same file that implements + the application. + + Probably the most interesting way to load configurations is from an + environment variable pointing to a file:: + + app.config.from_envvar('YOURAPPLICATION_SETTINGS') + + In this case before launching the application you have to set this + environment variable to the file you want to use. On Linux and OS X + use the export statement:: + + export YOURAPPLICATION_SETTINGS='/path/to/config/file' + + On windows use `set` instead. + + :param root_path: path to which files are read relative from. When the + config object is created by the application, this is + the application's :attr:`~flask.Flask.root_path`. + :param defaults: an optional dictionary of default values + """ + + def __init__(self, root_path: str, defaults: t.Optional[dict] = None) -> None: + dict.__init__(self, defaults or {}) + self.root_path = root_path + + def from_envvar(self, variable_name: str, silent: bool = False) -> bool: + """Loads a configuration from an environment variable pointing to + a configuration file. This is basically just a shortcut with nicer + error messages for this line of code:: + + app.config.from_pyfile(os.environ['YOURAPPLICATION_SETTINGS']) + + :param variable_name: name of the environment variable + :param silent: set to ``True`` if you want silent failure for missing + files. + :return: bool. ``True`` if able to load config, ``False`` otherwise. + """ + rv = os.environ.get(variable_name) + if not rv: + if silent: + return False + raise RuntimeError( + f"The environment variable {variable_name!r} is not set" + " and as such configuration could not be loaded. Set" + " this variable and make it point to a configuration" + " file" + ) + return self.from_pyfile(rv, silent=silent) + + def from_pyfile(self, filename: str, silent: bool = False) -> bool: + """Updates the values in the config from a Python file. This function + behaves as if the file was imported as module with the + :meth:`from_object` function. + + :param filename: the filename of the config. This can either be an + absolute filename or a filename relative to the + root path. + :param silent: set to ``True`` if you want silent failure for missing + files. + + .. versionadded:: 0.7 + `silent` parameter. + """ + filename = os.path.join(self.root_path, filename) + d = types.ModuleType("config") + d.__file__ = filename + try: + with open(filename, mode="rb") as config_file: + exec(compile(config_file.read(), filename, "exec"), d.__dict__) + except OSError as e: + if silent and e.errno in (errno.ENOENT, errno.EISDIR, errno.ENOTDIR): + return False + e.strerror = f"Unable to load configuration file ({e.strerror})" + raise + self.from_object(d) + return True + + def from_object(self, obj: t.Union[object, str]) -> None: + """Updates the values from the given object. An object can be of one + of the following two types: + + - a string: in this case the object with that name will be imported + - an actual object reference: that object is used directly + + Objects are usually either modules or classes. :meth:`from_object` + loads only the uppercase attributes of the module/class. A ``dict`` + object will not work with :meth:`from_object` because the keys of a + ``dict`` are not attributes of the ``dict`` class. + + Example of module-based configuration:: + + app.config.from_object('yourapplication.default_config') + from yourapplication import default_config + app.config.from_object(default_config) + + Nothing is done to the object before loading. If the object is a + class and has ``@property`` attributes, it needs to be + instantiated before being passed to this method. + + You should not use this function to load the actual configuration but + rather configuration defaults. The actual config should be loaded + with :meth:`from_pyfile` and ideally from a location not within the + package because the package might be installed system wide. + + See :ref:`config-dev-prod` for an example of class-based configuration + using :meth:`from_object`. + + :param obj: an import name or object + """ + if isinstance(obj, str): + obj = import_string(obj) + for key in dir(obj): + if key.isupper(): + self[key] = getattr(obj, key) + + def from_file( + self, + filename: str, + load: t.Callable[[t.IO[t.Any]], t.Mapping], + silent: bool = False, + ) -> bool: + """Update the values in the config from a file that is loaded + using the ``load`` parameter. The loaded data is passed to the + :meth:`from_mapping` method. + + .. code-block:: python + + import toml + app.config.from_file("config.toml", load=toml.load) + + :param filename: The path to the data file. This can be an + absolute path or relative to the config root path. + :param load: A callable that takes a file handle and returns a + mapping of loaded data from the file. + :type load: ``Callable[[Reader], Mapping]`` where ``Reader`` + implements a ``read`` method. + :param silent: Ignore the file if it doesn't exist. + + .. versionadded:: 2.0 + """ + filename = os.path.join(self.root_path, filename) + + try: + with open(filename) as f: + obj = load(f) + except OSError as e: + if silent and e.errno in (errno.ENOENT, errno.EISDIR): + return False + + e.strerror = f"Unable to load configuration file ({e.strerror})" + raise + + return self.from_mapping(obj) + + def from_json(self, filename: str, silent: bool = False) -> bool: + """Update the values in the config from a JSON file. The loaded + data is passed to the :meth:`from_mapping` method. + + :param filename: The path to the JSON file. This can be an + absolute path or relative to the config root path. + :param silent: Ignore the file if it doesn't exist. + + .. deprecated:: 2.0.0 + Will be removed in Flask 2.1. Use :meth:`from_file` instead. + This was removed early in 2.0.0, was added back in 2.0.1. + + .. versionadded:: 0.11 + """ + import warnings + from . import json + + warnings.warn( + "'from_json' is deprecated and will be removed in Flask" + " 2.1. Use 'from_file(path, json.load)' instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.from_file(filename, json.load, silent=silent) + + def from_mapping( + self, mapping: t.Optional[t.Mapping[str, t.Any]] = None, **kwargs: t.Any + ) -> bool: + """Updates the config like :meth:`update` ignoring items with non-upper + keys. + + .. versionadded:: 0.11 + """ + mappings: t.Dict[str, t.Any] = {} + if mapping is not None: + mappings.update(mapping) + mappings.update(kwargs) + for key, value in mappings.items(): + if key.isupper(): + self[key] = value + return True + + def get_namespace( + self, namespace: str, lowercase: bool = True, trim_namespace: bool = True + ) -> t.Dict[str, t.Any]: + """Returns a dictionary containing a subset of configuration options + that match the specified namespace/prefix. Example usage:: + + app.config['IMAGE_STORE_TYPE'] = 'fs' + app.config['IMAGE_STORE_PATH'] = '/var/app/images' + app.config['IMAGE_STORE_BASE_URL'] = 'http://img.website.com' + image_store_config = app.config.get_namespace('IMAGE_STORE_') + + The resulting dictionary `image_store_config` would look like:: + + { + 'type': 'fs', + 'path': '/var/app/images', + 'base_url': 'http://img.website.com' + } + + This is often useful when configuration options map directly to + keyword arguments in functions or class constructors. + + :param namespace: a configuration namespace + :param lowercase: a flag indicating if the keys of the resulting + dictionary should be lowercase + :param trim_namespace: a flag indicating if the keys of the resulting + dictionary should not include the namespace + + .. versionadded:: 0.11 + """ + rv = {} + for k, v in self.items(): + if not k.startswith(namespace): + continue + if trim_namespace: + key = k[len(namespace) :] + else: + key = k + if lowercase: + key = key.lower() + rv[key] = v + return rv + + def __repr__(self) -> str: + return f"<{type(self).__name__} {dict.__repr__(self)}>" diff --git a/venv/Lib/site-packages/flask/ctx.py b/venv/Lib/site-packages/flask/ctx.py new file mode 100644 index 0000000..5c06463 --- /dev/null +++ b/venv/Lib/site-packages/flask/ctx.py @@ -0,0 +1,480 @@ +import sys +import typing as t +from functools import update_wrapper +from types import TracebackType + +from werkzeug.exceptions import HTTPException + +from .globals import _app_ctx_stack +from .globals import _request_ctx_stack +from .signals import appcontext_popped +from .signals import appcontext_pushed +from .typing import AfterRequestCallable + +if t.TYPE_CHECKING: + from .app import Flask + from .sessions import SessionMixin + from .wrappers import Request + + +# a singleton sentinel value for parameter defaults +_sentinel = object() + + +class _AppCtxGlobals: + """A plain object. Used as a namespace for storing data during an + application context. + + Creating an app context automatically creates this object, which is + made available as the :data:`g` proxy. + + .. describe:: 'key' in g + + Check whether an attribute is present. + + .. versionadded:: 0.10 + + .. describe:: iter(g) + + Return an iterator over the attribute names. + + .. versionadded:: 0.10 + """ + + # Define attr methods to let mypy know this is a namespace object + # that has arbitrary attributes. + + def __getattr__(self, name: str) -> t.Any: + try: + return self.__dict__[name] + except KeyError: + raise AttributeError(name) from None + + def __setattr__(self, name: str, value: t.Any) -> None: + self.__dict__[name] = value + + def __delattr__(self, name: str) -> None: + try: + del self.__dict__[name] + except KeyError: + raise AttributeError(name) from None + + def get(self, name: str, default: t.Optional[t.Any] = None) -> t.Any: + """Get an attribute by name, or a default value. Like + :meth:`dict.get`. + + :param name: Name of attribute to get. + :param default: Value to return if the attribute is not present. + + .. versionadded:: 0.10 + """ + return self.__dict__.get(name, default) + + def pop(self, name: str, default: t.Any = _sentinel) -> t.Any: + """Get and remove an attribute by name. Like :meth:`dict.pop`. + + :param name: Name of attribute to pop. + :param default: Value to return if the attribute is not present, + instead of raising a ``KeyError``. + + .. versionadded:: 0.11 + """ + if default is _sentinel: + return self.__dict__.pop(name) + else: + return self.__dict__.pop(name, default) + + def setdefault(self, name: str, default: t.Any = None) -> t.Any: + """Get the value of an attribute if it is present, otherwise + set and return a default value. Like :meth:`dict.setdefault`. + + :param name: Name of attribute to get. + :param default: Value to set and return if the attribute is not + present. + + .. versionadded:: 0.11 + """ + return self.__dict__.setdefault(name, default) + + def __contains__(self, item: str) -> bool: + return item in self.__dict__ + + def __iter__(self) -> t.Iterator[str]: + return iter(self.__dict__) + + def __repr__(self) -> str: + top = _app_ctx_stack.top + if top is not None: + return f"" + return object.__repr__(self) + + +def after_this_request(f: AfterRequestCallable) -> AfterRequestCallable: + """Executes a function after this request. This is useful to modify + response objects. The function is passed the response object and has + to return the same or a new one. + + Example:: + + @app.route('/') + def index(): + @after_this_request + def add_header(response): + response.headers['X-Foo'] = 'Parachute' + return response + return 'Hello World!' + + This is more useful if a function other than the view function wants to + modify a response. For instance think of a decorator that wants to add + some headers without converting the return value into a response object. + + .. versionadded:: 0.9 + """ + _request_ctx_stack.top._after_request_functions.append(f) + return f + + +def copy_current_request_context(f: t.Callable) -> t.Callable: + """A helper function that decorates a function to retain the current + request context. This is useful when working with greenlets. The moment + the function is decorated a copy of the request context is created and + then pushed when the function is called. The current session is also + included in the copied request context. + + Example:: + + import gevent + from flask import copy_current_request_context + + @app.route('/') + def index(): + @copy_current_request_context + def do_some_work(): + # do some work here, it can access flask.request or + # flask.session like you would otherwise in the view function. + ... + gevent.spawn(do_some_work) + return 'Regular response' + + .. versionadded:: 0.10 + """ + top = _request_ctx_stack.top + if top is None: + raise RuntimeError( + "This decorator can only be used at local scopes " + "when a request context is on the stack. For instance within " + "view functions." + ) + reqctx = top.copy() + + def wrapper(*args, **kwargs): + with reqctx: + return f(*args, **kwargs) + + return update_wrapper(wrapper, f) + + +def has_request_context() -> bool: + """If you have code that wants to test if a request context is there or + not this function can be used. For instance, you may want to take advantage + of request information if the request object is available, but fail + silently if it is unavailable. + + :: + + class User(db.Model): + + def __init__(self, username, remote_addr=None): + self.username = username + if remote_addr is None and has_request_context(): + remote_addr = request.remote_addr + self.remote_addr = remote_addr + + Alternatively you can also just test any of the context bound objects + (such as :class:`request` or :class:`g`) for truthness:: + + class User(db.Model): + + def __init__(self, username, remote_addr=None): + self.username = username + if remote_addr is None and request: + remote_addr = request.remote_addr + self.remote_addr = remote_addr + + .. versionadded:: 0.7 + """ + return _request_ctx_stack.top is not None + + +def has_app_context() -> bool: + """Works like :func:`has_request_context` but for the application + context. You can also just do a boolean check on the + :data:`current_app` object instead. + + .. versionadded:: 0.9 + """ + return _app_ctx_stack.top is not None + + +class AppContext: + """The application context binds an application object implicitly + to the current thread or greenlet, similar to how the + :class:`RequestContext` binds request information. The application + context is also implicitly created if a request context is created + but the application is not on top of the individual application + context. + """ + + def __init__(self, app: "Flask") -> None: + self.app = app + self.url_adapter = app.create_url_adapter(None) + self.g = app.app_ctx_globals_class() + + # Like request context, app contexts can be pushed multiple times + # but there a basic "refcount" is enough to track them. + self._refcnt = 0 + + def push(self) -> None: + """Binds the app context to the current context.""" + self._refcnt += 1 + _app_ctx_stack.push(self) + appcontext_pushed.send(self.app) + + def pop(self, exc: t.Optional[BaseException] = _sentinel) -> None: # type: ignore + """Pops the app context.""" + try: + self._refcnt -= 1 + if self._refcnt <= 0: + if exc is _sentinel: + exc = sys.exc_info()[1] + self.app.do_teardown_appcontext(exc) + finally: + rv = _app_ctx_stack.pop() + assert rv is self, f"Popped wrong app context. ({rv!r} instead of {self!r})" + appcontext_popped.send(self.app) + + def __enter__(self) -> "AppContext": + self.push() + return self + + def __exit__( + self, exc_type: type, exc_value: BaseException, tb: TracebackType + ) -> None: + self.pop(exc_value) + + +class RequestContext: + """The request context contains all request relevant information. It is + created at the beginning of the request and pushed to the + `_request_ctx_stack` and removed at the end of it. It will create the + URL adapter and request object for the WSGI environment provided. + + Do not attempt to use this class directly, instead use + :meth:`~flask.Flask.test_request_context` and + :meth:`~flask.Flask.request_context` to create this object. + + When the request context is popped, it will evaluate all the + functions registered on the application for teardown execution + (:meth:`~flask.Flask.teardown_request`). + + The request context is automatically popped at the end of the request + for you. In debug mode the request context is kept around if + exceptions happen so that interactive debuggers have a chance to + introspect the data. With 0.4 this can also be forced for requests + that did not fail and outside of ``DEBUG`` mode. By setting + ``'flask._preserve_context'`` to ``True`` on the WSGI environment the + context will not pop itself at the end of the request. This is used by + the :meth:`~flask.Flask.test_client` for example to implement the + deferred cleanup functionality. + + You might find this helpful for unittests where you need the + information from the context local around for a little longer. Make + sure to properly :meth:`~werkzeug.LocalStack.pop` the stack yourself in + that situation, otherwise your unittests will leak memory. + """ + + def __init__( + self, + app: "Flask", + environ: dict, + request: t.Optional["Request"] = None, + session: t.Optional["SessionMixin"] = None, + ) -> None: + self.app = app + if request is None: + request = app.request_class(environ) + self.request = request + self.url_adapter = None + try: + self.url_adapter = app.create_url_adapter(self.request) + except HTTPException as e: + self.request.routing_exception = e + self.flashes = None + self.session = session + + # Request contexts can be pushed multiple times and interleaved with + # other request contexts. Now only if the last level is popped we + # get rid of them. Additionally if an application context is missing + # one is created implicitly so for each level we add this information + self._implicit_app_ctx_stack: t.List[t.Optional["AppContext"]] = [] + + # indicator if the context was preserved. Next time another context + # is pushed the preserved context is popped. + self.preserved = False + + # remembers the exception for pop if there is one in case the context + # preservation kicks in. + self._preserved_exc = None + + # Functions that should be executed after the request on the response + # object. These will be called before the regular "after_request" + # functions. + self._after_request_functions: t.List[AfterRequestCallable] = [] + + @property + def g(self) -> AppContext: + return _app_ctx_stack.top.g + + @g.setter + def g(self, value: AppContext) -> None: + _app_ctx_stack.top.g = value + + def copy(self) -> "RequestContext": + """Creates a copy of this request context with the same request object. + This can be used to move a request context to a different greenlet. + Because the actual request object is the same this cannot be used to + move a request context to a different thread unless access to the + request object is locked. + + .. versionadded:: 0.10 + + .. versionchanged:: 1.1 + The current session object is used instead of reloading the original + data. This prevents `flask.session` pointing to an out-of-date object. + """ + return self.__class__( + self.app, + environ=self.request.environ, + request=self.request, + session=self.session, + ) + + def match_request(self) -> None: + """Can be overridden by a subclass to hook into the matching + of the request. + """ + try: + result = self.url_adapter.match(return_rule=True) # type: ignore + self.request.url_rule, self.request.view_args = result # type: ignore + except HTTPException as e: + self.request.routing_exception = e + + def push(self) -> None: + """Binds the request context to the current context.""" + # If an exception occurs in debug mode or if context preservation is + # activated under exception situations exactly one context stays + # on the stack. The rationale is that you want to access that + # information under debug situations. However if someone forgets to + # pop that context again we want to make sure that on the next push + # it's invalidated, otherwise we run at risk that something leaks + # memory. This is usually only a problem in test suite since this + # functionality is not active in production environments. + top = _request_ctx_stack.top + if top is not None and top.preserved: + top.pop(top._preserved_exc) + + # Before we push the request context we have to ensure that there + # is an application context. + app_ctx = _app_ctx_stack.top + if app_ctx is None or app_ctx.app != self.app: + app_ctx = self.app.app_context() + app_ctx.push() + self._implicit_app_ctx_stack.append(app_ctx) + else: + self._implicit_app_ctx_stack.append(None) + + _request_ctx_stack.push(self) + + # Open the session at the moment that the request context is available. + # This allows a custom open_session method to use the request context. + # Only open a new session if this is the first time the request was + # pushed, otherwise stream_with_context loses the session. + if self.session is None: + session_interface = self.app.session_interface + self.session = session_interface.open_session(self.app, self.request) + + if self.session is None: + self.session = session_interface.make_null_session(self.app) + + # Match the request URL after loading the session, so that the + # session is available in custom URL converters. + if self.url_adapter is not None: + self.match_request() + + def pop(self, exc: t.Optional[BaseException] = _sentinel) -> None: # type: ignore + """Pops the request context and unbinds it by doing that. This will + also trigger the execution of functions registered by the + :meth:`~flask.Flask.teardown_request` decorator. + + .. versionchanged:: 0.9 + Added the `exc` argument. + """ + app_ctx = self._implicit_app_ctx_stack.pop() + clear_request = False + + try: + if not self._implicit_app_ctx_stack: + self.preserved = False + self._preserved_exc = None + if exc is _sentinel: + exc = sys.exc_info()[1] + self.app.do_teardown_request(exc) + + request_close = getattr(self.request, "close", None) + if request_close is not None: + request_close() + clear_request = True + finally: + rv = _request_ctx_stack.pop() + + # get rid of circular dependencies at the end of the request + # so that we don't require the GC to be active. + if clear_request: + rv.request.environ["werkzeug.request"] = None + + # Get rid of the app as well if necessary. + if app_ctx is not None: + app_ctx.pop(exc) + + assert ( + rv is self + ), f"Popped wrong request context. ({rv!r} instead of {self!r})" + + def auto_pop(self, exc: t.Optional[BaseException]) -> None: + if self.request.environ.get("flask._preserve_context") or ( + exc is not None and self.app.preserve_context_on_exception + ): + self.preserved = True + self._preserved_exc = exc # type: ignore + else: + self.pop(exc) + + def __enter__(self) -> "RequestContext": + self.push() + return self + + def __exit__( + self, exc_type: type, exc_value: BaseException, tb: TracebackType + ) -> None: + # do not pop the request stack if we are in debug mode and an + # exception happened. This will allow the debugger to still + # access the request object in the interactive shell. Furthermore + # the context can be force kept alive for the test client. + # See flask.testing for how this works. + self.auto_pop(exc_value) + + def __repr__(self) -> str: + return ( + f"<{type(self).__name__} {self.request.url!r}" + f" [{self.request.method}] of {self.app.name}>" + ) diff --git a/venv/Lib/site-packages/flask/debughelpers.py b/venv/Lib/site-packages/flask/debughelpers.py new file mode 100644 index 0000000..ce65c48 --- /dev/null +++ b/venv/Lib/site-packages/flask/debughelpers.py @@ -0,0 +1,171 @@ +import os +import typing as t +from warnings import warn + +from .app import Flask +from .blueprints import Blueprint +from .globals import _request_ctx_stack + + +class UnexpectedUnicodeError(AssertionError, UnicodeError): + """Raised in places where we want some better error reporting for + unexpected unicode or binary data. + """ + + +class DebugFilesKeyError(KeyError, AssertionError): + """Raised from request.files during debugging. The idea is that it can + provide a better error message than just a generic KeyError/BadRequest. + """ + + def __init__(self, request, key): + form_matches = request.form.getlist(key) + buf = [ + f"You tried to access the file {key!r} in the request.files" + " dictionary but it does not exist. The mimetype for the" + f" request is {request.mimetype!r} instead of" + " 'multipart/form-data' which means that no file contents" + " were transmitted. To fix this error you should provide" + ' enctype="multipart/form-data" in your form.' + ] + if form_matches: + names = ", ".join(repr(x) for x in form_matches) + buf.append( + "\n\nThe browser instead transmitted some file names. " + f"This was submitted: {names}" + ) + self.msg = "".join(buf) + + def __str__(self): + return self.msg + + +class FormDataRoutingRedirect(AssertionError): + """This exception is raised by Flask in debug mode if it detects a + redirect caused by the routing system when the request method is not + GET, HEAD or OPTIONS. Reasoning: form data will be dropped. + """ + + def __init__(self, request): + exc = request.routing_exception + buf = [ + f"A request was sent to this URL ({request.url}) but a" + " redirect was issued automatically by the routing system" + f" to {exc.new_url!r}." + ] + + # In case just a slash was appended we can be extra helpful + if f"{request.base_url}/" == exc.new_url.split("?")[0]: + buf.append( + " The URL was defined with a trailing slash so Flask" + " will automatically redirect to the URL with the" + " trailing slash if it was accessed without one." + ) + + buf.append( + " Make sure to directly send your" + f" {request.method}-request to this URL since we can't make" + " browsers or HTTP clients redirect with form data reliably" + " or without user interaction." + ) + buf.append("\n\nNote: this exception is only raised in debug mode") + AssertionError.__init__(self, "".join(buf).encode("utf-8")) + + +def attach_enctype_error_multidict(request): + """Since Flask 0.8 we're monkeypatching the files object in case a + request is detected that does not use multipart form data but the files + object is accessed. + """ + oldcls = request.files.__class__ + + class newcls(oldcls): + def __getitem__(self, key): + try: + return oldcls.__getitem__(self, key) + except KeyError: + if key not in request.form: + raise + raise DebugFilesKeyError(request, key) + + newcls.__name__ = oldcls.__name__ + newcls.__module__ = oldcls.__module__ + request.files.__class__ = newcls + + +def _dump_loader_info(loader) -> t.Generator: + yield f"class: {type(loader).__module__}.{type(loader).__name__}" + for key, value in sorted(loader.__dict__.items()): + if key.startswith("_"): + continue + if isinstance(value, (tuple, list)): + if not all(isinstance(x, str) for x in value): + continue + yield f"{key}:" + for item in value: + yield f" - {item}" + continue + elif not isinstance(value, (str, int, float, bool)): + continue + yield f"{key}: {value!r}" + + +def explain_template_loading_attempts(app: Flask, template, attempts) -> None: + """This should help developers understand what failed""" + info = [f"Locating template {template!r}:"] + total_found = 0 + blueprint = None + reqctx = _request_ctx_stack.top + if reqctx is not None and reqctx.request.blueprint is not None: + blueprint = reqctx.request.blueprint + + for idx, (loader, srcobj, triple) in enumerate(attempts): + if isinstance(srcobj, Flask): + src_info = f"application {srcobj.import_name!r}" + elif isinstance(srcobj, Blueprint): + src_info = f"blueprint {srcobj.name!r} ({srcobj.import_name})" + else: + src_info = repr(srcobj) + + info.append(f"{idx + 1:5}: trying loader of {src_info}") + + for line in _dump_loader_info(loader): + info.append(f" {line}") + + if triple is None: + detail = "no match" + else: + detail = f"found ({triple[1] or ''!r})" + total_found += 1 + info.append(f" -> {detail}") + + seems_fishy = False + if total_found == 0: + info.append("Error: the template could not be found.") + seems_fishy = True + elif total_found > 1: + info.append("Warning: multiple loaders returned a match for the template.") + seems_fishy = True + + if blueprint is not None and seems_fishy: + info.append( + " The template was looked up from an endpoint that belongs" + f" to the blueprint {blueprint!r}." + ) + info.append(" Maybe you did not place a template in the right folder?") + info.append(" See https://flask.palletsprojects.com/blueprints/#templates") + + app.logger.info("\n".join(info)) + + +def explain_ignored_app_run() -> None: + if os.environ.get("WERKZEUG_RUN_MAIN") != "true": + warn( + Warning( + "Silently ignoring app.run() because the application is" + " run from the flask command line executable. Consider" + ' putting app.run() behind an if __name__ == "__main__"' + " guard to silence this warning." + ), + stacklevel=3, + ) diff --git a/venv/Lib/site-packages/flask/globals.py b/venv/Lib/site-packages/flask/globals.py new file mode 100644 index 0000000..6d91c75 --- /dev/null +++ b/venv/Lib/site-packages/flask/globals.py @@ -0,0 +1,59 @@ +import typing as t +from functools import partial + +from werkzeug.local import LocalProxy +from werkzeug.local import LocalStack + +if t.TYPE_CHECKING: + from .app import Flask + from .ctx import _AppCtxGlobals + from .sessions import SessionMixin + from .wrappers import Request + +_request_ctx_err_msg = """\ +Working outside of request context. + +This typically means that you attempted to use functionality that needed +an active HTTP request. Consult the documentation on testing for +information about how to avoid this problem.\ +""" +_app_ctx_err_msg = """\ +Working outside of application context. + +This typically means that you attempted to use functionality that needed +to interface with the current application object in some way. To solve +this, set up an application context with app.app_context(). See the +documentation for more information.\ +""" + + +def _lookup_req_object(name): + top = _request_ctx_stack.top + if top is None: + raise RuntimeError(_request_ctx_err_msg) + return getattr(top, name) + + +def _lookup_app_object(name): + top = _app_ctx_stack.top + if top is None: + raise RuntimeError(_app_ctx_err_msg) + return getattr(top, name) + + +def _find_app(): + top = _app_ctx_stack.top + if top is None: + raise RuntimeError(_app_ctx_err_msg) + return top.app + + +# context locals +_request_ctx_stack = LocalStack() +_app_ctx_stack = LocalStack() +current_app: "Flask" = LocalProxy(_find_app) # type: ignore +request: "Request" = LocalProxy(partial(_lookup_req_object, "request")) # type: ignore +session: "SessionMixin" = LocalProxy( # type: ignore + partial(_lookup_req_object, "session") +) +g: "_AppCtxGlobals" = LocalProxy(partial(_lookup_app_object, "g")) # type: ignore diff --git a/venv/Lib/site-packages/flask/helpers.py b/venv/Lib/site-packages/flask/helpers.py new file mode 100644 index 0000000..7b8b087 --- /dev/null +++ b/venv/Lib/site-packages/flask/helpers.py @@ -0,0 +1,836 @@ +import os +import pkgutil +import socket +import sys +import typing as t +import warnings +from datetime import datetime +from datetime import timedelta +from functools import lru_cache +from functools import update_wrapper +from threading import RLock + +import werkzeug.utils +from werkzeug.exceptions import NotFound +from werkzeug.routing import BuildError +from werkzeug.urls import url_quote + +from .globals import _app_ctx_stack +from .globals import _request_ctx_stack +from .globals import current_app +from .globals import request +from .globals import session +from .signals import message_flashed + +if t.TYPE_CHECKING: + from .wrappers import Response + + +def get_env() -> str: + """Get the environment the app is running in, indicated by the + :envvar:`FLASK_ENV` environment variable. The default is + ``'production'``. + """ + return os.environ.get("FLASK_ENV") or "production" + + +def get_debug_flag() -> bool: + """Get whether debug mode should be enabled for the app, indicated + by the :envvar:`FLASK_DEBUG` environment variable. The default is + ``True`` if :func:`.get_env` returns ``'development'``, or ``False`` + otherwise. + """ + val = os.environ.get("FLASK_DEBUG") + + if not val: + return get_env() == "development" + + return val.lower() not in ("0", "false", "no") + + +def get_load_dotenv(default: bool = True) -> bool: + """Get whether the user has disabled loading dotenv files by setting + :envvar:`FLASK_SKIP_DOTENV`. The default is ``True``, load the + files. + + :param default: What to return if the env var isn't set. + """ + val = os.environ.get("FLASK_SKIP_DOTENV") + + if not val: + return default + + return val.lower() in ("0", "false", "no") + + +def stream_with_context( + generator_or_function: t.Union[ + t.Iterator[t.AnyStr], t.Callable[..., t.Iterator[t.AnyStr]] + ] +) -> t.Iterator[t.AnyStr]: + """Request contexts disappear when the response is started on the server. + This is done for efficiency reasons and to make it less likely to encounter + memory leaks with badly written WSGI middlewares. The downside is that if + you are using streamed responses, the generator cannot access request bound + information any more. + + This function however can help you keep the context around for longer:: + + from flask import stream_with_context, request, Response + + @app.route('/stream') + def streamed_response(): + @stream_with_context + def generate(): + yield 'Hello ' + yield request.args['name'] + yield '!' + return Response(generate()) + + Alternatively it can also be used around a specific generator:: + + from flask import stream_with_context, request, Response + + @app.route('/stream') + def streamed_response(): + def generate(): + yield 'Hello ' + yield request.args['name'] + yield '!' + return Response(stream_with_context(generate())) + + .. versionadded:: 0.9 + """ + try: + gen = iter(generator_or_function) # type: ignore + except TypeError: + + def decorator(*args: t.Any, **kwargs: t.Any) -> t.Any: + gen = generator_or_function(*args, **kwargs) # type: ignore + return stream_with_context(gen) + + return update_wrapper(decorator, generator_or_function) # type: ignore + + def generator() -> t.Generator: + ctx = _request_ctx_stack.top + if ctx is None: + raise RuntimeError( + "Attempted to stream with context but " + "there was no context in the first place to keep around." + ) + with ctx: + # Dummy sentinel. Has to be inside the context block or we're + # not actually keeping the context around. + yield None + + # The try/finally is here so that if someone passes a WSGI level + # iterator in we're still running the cleanup logic. Generators + # don't need that because they are closed on their destruction + # automatically. + try: + yield from gen + finally: + if hasattr(gen, "close"): + gen.close() # type: ignore + + # The trick is to start the generator. Then the code execution runs until + # the first dummy None is yielded at which point the context was already + # pushed. This item is discarded. Then when the iteration continues the + # real generator is executed. + wrapped_g = generator() + next(wrapped_g) + return wrapped_g + + +def make_response(*args: t.Any) -> "Response": + """Sometimes it is necessary to set additional headers in a view. Because + views do not have to return response objects but can return a value that + is converted into a response object by Flask itself, it becomes tricky to + add headers to it. This function can be called instead of using a return + and you will get a response object which you can use to attach headers. + + If view looked like this and you want to add a new header:: + + def index(): + return render_template('index.html', foo=42) + + You can now do something like this:: + + def index(): + response = make_response(render_template('index.html', foo=42)) + response.headers['X-Parachutes'] = 'parachutes are cool' + return response + + This function accepts the very same arguments you can return from a + view function. This for example creates a response with a 404 error + code:: + + response = make_response(render_template('not_found.html'), 404) + + The other use case of this function is to force the return value of a + view function into a response which is helpful with view + decorators:: + + response = make_response(view_function()) + response.headers['X-Parachutes'] = 'parachutes are cool' + + Internally this function does the following things: + + - if no arguments are passed, it creates a new response argument + - if one argument is passed, :meth:`flask.Flask.make_response` + is invoked with it. + - if more than one argument is passed, the arguments are passed + to the :meth:`flask.Flask.make_response` function as tuple. + + .. versionadded:: 0.6 + """ + if not args: + return current_app.response_class() + if len(args) == 1: + args = args[0] + return current_app.make_response(args) + + +def url_for(endpoint: str, **values: t.Any) -> str: + """Generates a URL to the given endpoint with the method provided. + + Variable arguments that are unknown to the target endpoint are appended + to the generated URL as query arguments. If the value of a query argument + is ``None``, the whole pair is skipped. In case blueprints are active + you can shortcut references to the same blueprint by prefixing the + local endpoint with a dot (``.``). + + This will reference the index function local to the current blueprint:: + + url_for('.index') + + See :ref:`url-building`. + + Configuration values ``APPLICATION_ROOT`` and ``SERVER_NAME`` are only used when + generating URLs outside of a request context. + + To integrate applications, :class:`Flask` has a hook to intercept URL build + errors through :attr:`Flask.url_build_error_handlers`. The `url_for` + function results in a :exc:`~werkzeug.routing.BuildError` when the current + app does not have a URL for the given endpoint and values. When it does, the + :data:`~flask.current_app` calls its :attr:`~Flask.url_build_error_handlers` if + it is not ``None``, which can return a string to use as the result of + `url_for` (instead of `url_for`'s default to raise the + :exc:`~werkzeug.routing.BuildError` exception) or re-raise the exception. + An example:: + + def external_url_handler(error, endpoint, values): + "Looks up an external URL when `url_for` cannot build a URL." + # This is an example of hooking the build_error_handler. + # Here, lookup_url is some utility function you've built + # which looks up the endpoint in some external URL registry. + url = lookup_url(endpoint, **values) + if url is None: + # External lookup did not have a URL. + # Re-raise the BuildError, in context of original traceback. + exc_type, exc_value, tb = sys.exc_info() + if exc_value is error: + raise exc_type(exc_value).with_traceback(tb) + else: + raise error + # url_for will use this result, instead of raising BuildError. + return url + + app.url_build_error_handlers.append(external_url_handler) + + Here, `error` is the instance of :exc:`~werkzeug.routing.BuildError`, and + `endpoint` and `values` are the arguments passed into `url_for`. Note + that this is for building URLs outside the current application, and not for + handling 404 NotFound errors. + + .. versionadded:: 0.10 + The `_scheme` parameter was added. + + .. versionadded:: 0.9 + The `_anchor` and `_method` parameters were added. + + .. versionadded:: 0.9 + Calls :meth:`Flask.handle_build_error` on + :exc:`~werkzeug.routing.BuildError`. + + :param endpoint: the endpoint of the URL (name of the function) + :param values: the variable arguments of the URL rule + :param _external: if set to ``True``, an absolute URL is generated. Server + address can be changed via ``SERVER_NAME`` configuration variable which + falls back to the `Host` header, then to the IP and port of the request. + :param _scheme: a string specifying the desired URL scheme. The `_external` + parameter must be set to ``True`` or a :exc:`ValueError` is raised. The default + behavior uses the same scheme as the current request, or + :data:`PREFERRED_URL_SCHEME` if no request context is available. + This also can be set to an empty string to build protocol-relative + URLs. + :param _anchor: if provided this is added as anchor to the URL. + :param _method: if provided this explicitly specifies an HTTP method. + """ + appctx = _app_ctx_stack.top + reqctx = _request_ctx_stack.top + + if appctx is None: + raise RuntimeError( + "Attempted to generate a URL without the application context being" + " pushed. This has to be executed when application context is" + " available." + ) + + # If request specific information is available we have some extra + # features that support "relative" URLs. + if reqctx is not None: + url_adapter = reqctx.url_adapter + blueprint_name = request.blueprint + + if endpoint[:1] == ".": + if blueprint_name is not None: + endpoint = f"{blueprint_name}{endpoint}" + else: + endpoint = endpoint[1:] + + external = values.pop("_external", False) + + # Otherwise go with the url adapter from the appctx and make + # the URLs external by default. + else: + url_adapter = appctx.url_adapter + + if url_adapter is None: + raise RuntimeError( + "Application was not able to create a URL adapter for request" + " independent URL generation. You might be able to fix this by" + " setting the SERVER_NAME config variable." + ) + + external = values.pop("_external", True) + + anchor = values.pop("_anchor", None) + method = values.pop("_method", None) + scheme = values.pop("_scheme", None) + appctx.app.inject_url_defaults(endpoint, values) + + # This is not the best way to deal with this but currently the + # underlying Werkzeug router does not support overriding the scheme on + # a per build call basis. + old_scheme = None + if scheme is not None: + if not external: + raise ValueError("When specifying _scheme, _external must be True") + old_scheme = url_adapter.url_scheme + url_adapter.url_scheme = scheme + + try: + try: + rv = url_adapter.build( + endpoint, values, method=method, force_external=external + ) + finally: + if old_scheme is not None: + url_adapter.url_scheme = old_scheme + except BuildError as error: + # We need to inject the values again so that the app callback can + # deal with that sort of stuff. + values["_external"] = external + values["_anchor"] = anchor + values["_method"] = method + values["_scheme"] = scheme + return appctx.app.handle_url_build_error(error, endpoint, values) + + if anchor is not None: + rv += f"#{url_quote(anchor)}" + return rv + + +def get_template_attribute(template_name: str, attribute: str) -> t.Any: + """Loads a macro (or variable) a template exports. This can be used to + invoke a macro from within Python code. If you for example have a + template named :file:`_cider.html` with the following contents: + + .. sourcecode:: html+jinja + + {% macro hello(name) %}Hello {{ name }}!{% endmacro %} + + You can access this from Python code like this:: + + hello = get_template_attribute('_cider.html', 'hello') + return hello('World') + + .. versionadded:: 0.2 + + :param template_name: the name of the template + :param attribute: the name of the variable of macro to access + """ + return getattr(current_app.jinja_env.get_template(template_name).module, attribute) + + +def flash(message: str, category: str = "message") -> None: + """Flashes a message to the next request. In order to remove the + flashed message from the session and to display it to the user, + the template has to call :func:`get_flashed_messages`. + + .. versionchanged:: 0.3 + `category` parameter added. + + :param message: the message to be flashed. + :param category: the category for the message. The following values + are recommended: ``'message'`` for any kind of message, + ``'error'`` for errors, ``'info'`` for information + messages and ``'warning'`` for warnings. However any + kind of string can be used as category. + """ + # Original implementation: + # + # session.setdefault('_flashes', []).append((category, message)) + # + # This assumed that changes made to mutable structures in the session are + # always in sync with the session object, which is not true for session + # implementations that use external storage for keeping their keys/values. + flashes = session.get("_flashes", []) + flashes.append((category, message)) + session["_flashes"] = flashes + message_flashed.send( + current_app._get_current_object(), # type: ignore + message=message, + category=category, + ) + + +def get_flashed_messages( + with_categories: bool = False, category_filter: t.Iterable[str] = () +) -> t.Union[t.List[str], t.List[t.Tuple[str, str]]]: + """Pulls all flashed messages from the session and returns them. + Further calls in the same request to the function will return + the same messages. By default just the messages are returned, + but when `with_categories` is set to ``True``, the return value will + be a list of tuples in the form ``(category, message)`` instead. + + Filter the flashed messages to one or more categories by providing those + categories in `category_filter`. This allows rendering categories in + separate html blocks. The `with_categories` and `category_filter` + arguments are distinct: + + * `with_categories` controls whether categories are returned with message + text (``True`` gives a tuple, where ``False`` gives just the message text). + * `category_filter` filters the messages down to only those matching the + provided categories. + + See :doc:`/patterns/flashing` for examples. + + .. versionchanged:: 0.3 + `with_categories` parameter added. + + .. versionchanged:: 0.9 + `category_filter` parameter added. + + :param with_categories: set to ``True`` to also receive categories. + :param category_filter: filter of categories to limit return values. Only + categories in the list will be returned. + """ + flashes = _request_ctx_stack.top.flashes + if flashes is None: + _request_ctx_stack.top.flashes = flashes = ( + session.pop("_flashes") if "_flashes" in session else [] + ) + if category_filter: + flashes = list(filter(lambda f: f[0] in category_filter, flashes)) + if not with_categories: + return [x[1] for x in flashes] + return flashes + + +def _prepare_send_file_kwargs( + download_name: t.Optional[str] = None, + attachment_filename: t.Optional[str] = None, + etag: t.Optional[t.Union[bool, str]] = None, + add_etags: t.Optional[t.Union[bool]] = None, + max_age: t.Optional[ + t.Union[int, t.Callable[[t.Optional[str]], t.Optional[int]]] + ] = None, + cache_timeout: t.Optional[int] = None, + **kwargs: t.Any, +) -> t.Dict[str, t.Any]: + if attachment_filename is not None: + warnings.warn( + "The 'attachment_filename' parameter has been renamed to" + " 'download_name'. The old name will be removed in Flask" + " 2.1.", + DeprecationWarning, + stacklevel=3, + ) + download_name = attachment_filename + + if cache_timeout is not None: + warnings.warn( + "The 'cache_timeout' parameter has been renamed to" + " 'max_age'. The old name will be removed in Flask 2.1.", + DeprecationWarning, + stacklevel=3, + ) + max_age = cache_timeout + + if add_etags is not None: + warnings.warn( + "The 'add_etags' parameter has been renamed to 'etag'. The" + " old name will be removed in Flask 2.1.", + DeprecationWarning, + stacklevel=3, + ) + etag = add_etags + + if max_age is None: + max_age = current_app.get_send_file_max_age + + kwargs.update( + environ=request.environ, + download_name=download_name, + etag=etag, + max_age=max_age, + use_x_sendfile=current_app.use_x_sendfile, + response_class=current_app.response_class, + _root_path=current_app.root_path, # type: ignore + ) + return kwargs + + +def send_file( + path_or_file: t.Union[os.PathLike, str, t.BinaryIO], + mimetype: t.Optional[str] = None, + as_attachment: bool = False, + download_name: t.Optional[str] = None, + attachment_filename: t.Optional[str] = None, + conditional: bool = True, + etag: t.Union[bool, str] = True, + add_etags: t.Optional[bool] = None, + last_modified: t.Optional[t.Union[datetime, int, float]] = None, + max_age: t.Optional[ + t.Union[int, t.Callable[[t.Optional[str]], t.Optional[int]]] + ] = None, + cache_timeout: t.Optional[int] = None, +): + """Send the contents of a file to the client. + + The first argument can be a file path or a file-like object. Paths + are preferred in most cases because Werkzeug can manage the file and + get extra information from the path. Passing a file-like object + requires that the file is opened in binary mode, and is mostly + useful when building a file in memory with :class:`io.BytesIO`. + + Never pass file paths provided by a user. The path is assumed to be + trusted, so a user could craft a path to access a file you didn't + intend. Use :func:`send_from_directory` to safely serve + user-requested paths from within a directory. + + If the WSGI server sets a ``file_wrapper`` in ``environ``, it is + used, otherwise Werkzeug's built-in wrapper is used. Alternatively, + if the HTTP server supports ``X-Sendfile``, configuring Flask with + ``USE_X_SENDFILE = True`` will tell the server to send the given + path, which is much more efficient than reading it in Python. + + :param path_or_file: The path to the file to send, relative to the + current working directory if a relative path is given. + Alternatively, a file-like object opened in binary mode. Make + sure the file pointer is seeked to the start of the data. + :param mimetype: The MIME type to send for the file. If not + provided, it will try to detect it from the file name. + :param as_attachment: Indicate to a browser that it should offer to + save the file instead of displaying it. + :param download_name: The default name browsers will use when saving + the file. Defaults to the passed file name. + :param conditional: Enable conditional and range responses based on + request headers. Requires passing a file path and ``environ``. + :param etag: Calculate an ETag for the file, which requires passing + a file path. Can also be a string to use instead. + :param last_modified: The last modified time to send for the file, + in seconds. If not provided, it will try to detect it from the + file path. + :param max_age: How long the client should cache the file, in + seconds. If set, ``Cache-Control`` will be ``public``, otherwise + it will be ``no-cache`` to prefer conditional caching. + + .. versionchanged:: 2.0 + ``download_name`` replaces the ``attachment_filename`` + parameter. If ``as_attachment=False``, it is passed with + ``Content-Disposition: inline`` instead. + + .. versionchanged:: 2.0 + ``max_age`` replaces the ``cache_timeout`` parameter. + ``conditional`` is enabled and ``max_age`` is not set by + default. + + .. versionchanged:: 2.0 + ``etag`` replaces the ``add_etags`` parameter. It can be a + string to use instead of generating one. + + .. versionchanged:: 2.0 + Passing a file-like object that inherits from + :class:`~io.TextIOBase` will raise a :exc:`ValueError` rather + than sending an empty file. + + .. versionadded:: 2.0 + Moved the implementation to Werkzeug. This is now a wrapper to + pass some Flask-specific arguments. + + .. versionchanged:: 1.1 + ``filename`` may be a :class:`~os.PathLike` object. + + .. versionchanged:: 1.1 + Passing a :class:`~io.BytesIO` object supports range requests. + + .. versionchanged:: 1.0.3 + Filenames are encoded with ASCII instead of Latin-1 for broader + compatibility with WSGI servers. + + .. versionchanged:: 1.0 + UTF-8 filenames as specified in :rfc:`2231` are supported. + + .. versionchanged:: 0.12 + The filename is no longer automatically inferred from file + objects. If you want to use automatic MIME and etag support, + pass a filename via ``filename_or_fp`` or + ``attachment_filename``. + + .. versionchanged:: 0.12 + ``attachment_filename`` is preferred over ``filename`` for MIME + detection. + + .. versionchanged:: 0.9 + ``cache_timeout`` defaults to + :meth:`Flask.get_send_file_max_age`. + + .. versionchanged:: 0.7 + MIME guessing and etag support for file-like objects was + deprecated because it was unreliable. Pass a filename if you are + able to, otherwise attach an etag yourself. + + .. versionchanged:: 0.5 + The ``add_etags``, ``cache_timeout`` and ``conditional`` + parameters were added. The default behavior is to add etags. + + .. versionadded:: 0.2 + """ + return werkzeug.utils.send_file( + **_prepare_send_file_kwargs( + path_or_file=path_or_file, + environ=request.environ, + mimetype=mimetype, + as_attachment=as_attachment, + download_name=download_name, + attachment_filename=attachment_filename, + conditional=conditional, + etag=etag, + add_etags=add_etags, + last_modified=last_modified, + max_age=max_age, + cache_timeout=cache_timeout, + ) + ) + + +def safe_join(directory: str, *pathnames: str) -> str: + """Safely join zero or more untrusted path components to a base + directory to avoid escaping the base directory. + + :param directory: The trusted base directory. + :param pathnames: The untrusted path components relative to the + base directory. + :return: A safe path, otherwise ``None``. + """ + warnings.warn( + "'flask.helpers.safe_join' is deprecated and will be removed in" + " Flask 2.1. Use 'werkzeug.utils.safe_join' instead.", + DeprecationWarning, + stacklevel=2, + ) + path = werkzeug.utils.safe_join(directory, *pathnames) + + if path is None: + raise NotFound() + + return path + + +def send_from_directory( + directory: t.Union[os.PathLike, str], + path: t.Union[os.PathLike, str], + filename: t.Optional[str] = None, + **kwargs: t.Any, +) -> "Response": + """Send a file from within a directory using :func:`send_file`. + + .. code-block:: python + + @app.route("/uploads/") + def download_file(name): + return send_from_directory( + app.config['UPLOAD_FOLDER'], name, as_attachment=True + ) + + This is a secure way to serve files from a folder, such as static + files or uploads. Uses :func:`~werkzeug.security.safe_join` to + ensure the path coming from the client is not maliciously crafted to + point outside the specified directory. + + If the final path does not point to an existing regular file, + raises a 404 :exc:`~werkzeug.exceptions.NotFound` error. + + :param directory: The directory that ``path`` must be located under. + :param path: The path to the file to send, relative to + ``directory``. + :param kwargs: Arguments to pass to :func:`send_file`. + + .. versionchanged:: 2.0 + ``path`` replaces the ``filename`` parameter. + + .. versionadded:: 2.0 + Moved the implementation to Werkzeug. This is now a wrapper to + pass some Flask-specific arguments. + + .. versionadded:: 0.5 + """ + if filename is not None: + warnings.warn( + "The 'filename' parameter has been renamed to 'path'. The" + " old name will be removed in Flask 2.1.", + DeprecationWarning, + stacklevel=2, + ) + path = filename + + return werkzeug.utils.send_from_directory( # type: ignore + directory, path, **_prepare_send_file_kwargs(**kwargs) + ) + + +def get_root_path(import_name: str) -> str: + """Find the root path of a package, or the path that contains a + module. If it cannot be found, returns the current working + directory. + + Not to be confused with the value returned by :func:`find_package`. + + :meta private: + """ + # Module already imported and has a file attribute. Use that first. + mod = sys.modules.get(import_name) + + if mod is not None and hasattr(mod, "__file__"): + return os.path.dirname(os.path.abspath(mod.__file__)) + + # Next attempt: check the loader. + loader = pkgutil.get_loader(import_name) + + # Loader does not exist or we're referring to an unloaded main + # module or a main module without path (interactive sessions), go + # with the current working directory. + if loader is None or import_name == "__main__": + return os.getcwd() + + if hasattr(loader, "get_filename"): + filepath = loader.get_filename(import_name) # type: ignore + else: + # Fall back to imports. + __import__(import_name) + mod = sys.modules[import_name] + filepath = getattr(mod, "__file__", None) + + # If we don't have a file path it might be because it is a + # namespace package. In this case pick the root path from the + # first module that is contained in the package. + if filepath is None: + raise RuntimeError( + "No root path can be found for the provided module" + f" {import_name!r}. This can happen because the module" + " came from an import hook that does not provide file" + " name information or because it's a namespace package." + " In this case the root path needs to be explicitly" + " provided." + ) + + # filepath is import_name.py for a module, or __init__.py for a package. + return os.path.dirname(os.path.abspath(filepath)) + + +class locked_cached_property(werkzeug.utils.cached_property): + """A :func:`property` that is only evaluated once. Like + :class:`werkzeug.utils.cached_property` except access uses a lock + for thread safety. + + .. versionchanged:: 2.0 + Inherits from Werkzeug's ``cached_property`` (and ``property``). + """ + + def __init__( + self, + fget: t.Callable[[t.Any], t.Any], + name: t.Optional[str] = None, + doc: t.Optional[str] = None, + ) -> None: + super().__init__(fget, name=name, doc=doc) + self.lock = RLock() + + def __get__(self, obj: object, type: type = None) -> t.Any: # type: ignore + if obj is None: + return self + + with self.lock: + return super().__get__(obj, type=type) + + def __set__(self, obj: object, value: t.Any) -> None: + with self.lock: + super().__set__(obj, value) + + def __delete__(self, obj: object) -> None: + with self.lock: + super().__delete__(obj) + + +def total_seconds(td: timedelta) -> int: + """Returns the total seconds from a timedelta object. + + :param timedelta td: the timedelta to be converted in seconds + + :returns: number of seconds + :rtype: int + + .. deprecated:: 2.0 + Will be removed in Flask 2.1. Use + :meth:`timedelta.total_seconds` instead. + """ + warnings.warn( + "'total_seconds' is deprecated and will be removed in Flask" + " 2.1. Use 'timedelta.total_seconds' instead.", + DeprecationWarning, + stacklevel=2, + ) + return td.days * 60 * 60 * 24 + td.seconds + + +def is_ip(value: str) -> bool: + """Determine if the given string is an IP address. + + :param value: value to check + :type value: str + + :return: True if string is an IP address + :rtype: bool + """ + for family in (socket.AF_INET, socket.AF_INET6): + try: + socket.inet_pton(family, value) + except OSError: + pass + else: + return True + + return False + + +@lru_cache(maxsize=None) +def _split_blueprint_path(name: str) -> t.List[str]: + out: t.List[str] = [name] + + if "." in name: + out.extend(_split_blueprint_path(name.rpartition(".")[0])) + + return out diff --git a/venv/Lib/site-packages/flask/json/__init__.py b/venv/Lib/site-packages/flask/json/__init__.py new file mode 100644 index 0000000..5780e20 --- /dev/null +++ b/venv/Lib/site-packages/flask/json/__init__.py @@ -0,0 +1,350 @@ +import io +import json as _json +import typing as t +import uuid +import warnings +from datetime import date + +from jinja2.utils import htmlsafe_json_dumps as _jinja_htmlsafe_dumps +from werkzeug.http import http_date + +from ..globals import current_app +from ..globals import request + +if t.TYPE_CHECKING: + from ..app import Flask + from ..wrappers import Response + +try: + import dataclasses +except ImportError: + # Python < 3.7 + dataclasses = None # type: ignore + + +class JSONEncoder(_json.JSONEncoder): + """The default JSON encoder. Handles extra types compared to the + built-in :class:`json.JSONEncoder`. + + - :class:`datetime.datetime` and :class:`datetime.date` are + serialized to :rfc:`822` strings. This is the same as the HTTP + date format. + - :class:`uuid.UUID` is serialized to a string. + - :class:`dataclasses.dataclass` is passed to + :func:`dataclasses.asdict`. + - :class:`~markupsafe.Markup` (or any object with a ``__html__`` + method) will call the ``__html__`` method to get a string. + + Assign a subclass of this to :attr:`flask.Flask.json_encoder` or + :attr:`flask.Blueprint.json_encoder` to override the default. + """ + + def default(self, o: t.Any) -> t.Any: + """Convert ``o`` to a JSON serializable type. See + :meth:`json.JSONEncoder.default`. Python does not support + overriding how basic types like ``str`` or ``list`` are + serialized, they are handled before this method. + """ + if isinstance(o, date): + return http_date(o) + if isinstance(o, uuid.UUID): + return str(o) + if dataclasses and dataclasses.is_dataclass(o): + return dataclasses.asdict(o) + if hasattr(o, "__html__"): + return str(o.__html__()) + return super().default(o) + + +class JSONDecoder(_json.JSONDecoder): + """The default JSON decoder. + + This does not change any behavior from the built-in + :class:`json.JSONDecoder`. + + Assign a subclass of this to :attr:`flask.Flask.json_decoder` or + :attr:`flask.Blueprint.json_decoder` to override the default. + """ + + +def _dump_arg_defaults( + kwargs: t.Dict[str, t.Any], app: t.Optional["Flask"] = None +) -> None: + """Inject default arguments for dump functions.""" + if app is None: + app = current_app + + if app: + cls = app.json_encoder + bp = app.blueprints.get(request.blueprint) if request else None # type: ignore + if bp is not None and bp.json_encoder is not None: + cls = bp.json_encoder + + kwargs.setdefault("cls", cls) + kwargs.setdefault("ensure_ascii", app.config["JSON_AS_ASCII"]) + kwargs.setdefault("sort_keys", app.config["JSON_SORT_KEYS"]) + else: + kwargs.setdefault("sort_keys", True) + kwargs.setdefault("cls", JSONEncoder) + + +def _load_arg_defaults( + kwargs: t.Dict[str, t.Any], app: t.Optional["Flask"] = None +) -> None: + """Inject default arguments for load functions.""" + if app is None: + app = current_app + + if app: + cls = app.json_decoder + bp = app.blueprints.get(request.blueprint) if request else None # type: ignore + if bp is not None and bp.json_decoder is not None: + cls = bp.json_decoder + + kwargs.setdefault("cls", cls) + else: + kwargs.setdefault("cls", JSONDecoder) + + +def dumps(obj: t.Any, app: t.Optional["Flask"] = None, **kwargs: t.Any) -> str: + """Serialize an object to a string of JSON. + + Takes the same arguments as the built-in :func:`json.dumps`, with + some defaults from application configuration. + + :param obj: Object to serialize to JSON. + :param app: Use this app's config instead of the active app context + or defaults. + :param kwargs: Extra arguments passed to :func:`json.dumps`. + + .. versionchanged:: 2.0 + ``encoding`` is deprecated and will be removed in Flask 2.1. + + .. versionchanged:: 1.0.3 + ``app`` can be passed directly, rather than requiring an app + context for configuration. + """ + _dump_arg_defaults(kwargs, app=app) + encoding = kwargs.pop("encoding", None) + rv = _json.dumps(obj, **kwargs) + + if encoding is not None: + warnings.warn( + "'encoding' is deprecated and will be removed in Flask 2.1.", + DeprecationWarning, + stacklevel=2, + ) + + if isinstance(rv, str): + return rv.encode(encoding) # type: ignore + + return rv + + +def dump( + obj: t.Any, fp: t.IO[str], app: t.Optional["Flask"] = None, **kwargs: t.Any +) -> None: + """Serialize an object to JSON written to a file object. + + Takes the same arguments as the built-in :func:`json.dump`, with + some defaults from application configuration. + + :param obj: Object to serialize to JSON. + :param fp: File object to write JSON to. + :param app: Use this app's config instead of the active app context + or defaults. + :param kwargs: Extra arguments passed to :func:`json.dump`. + + .. versionchanged:: 2.0 + Writing to a binary file, and the ``encoding`` argument, is + deprecated and will be removed in Flask 2.1. + """ + _dump_arg_defaults(kwargs, app=app) + encoding = kwargs.pop("encoding", None) + show_warning = encoding is not None + + try: + fp.write("") + except TypeError: + show_warning = True + fp = io.TextIOWrapper(fp, encoding or "utf-8") # type: ignore + + if show_warning: + warnings.warn( + "Writing to a binary file, and the 'encoding' argument, is" + " deprecated and will be removed in Flask 2.1.", + DeprecationWarning, + stacklevel=2, + ) + + _json.dump(obj, fp, **kwargs) + + +def loads(s: str, app: t.Optional["Flask"] = None, **kwargs: t.Any) -> t.Any: + """Deserialize an object from a string of JSON. + + Takes the same arguments as the built-in :func:`json.loads`, with + some defaults from application configuration. + + :param s: JSON string to deserialize. + :param app: Use this app's config instead of the active app context + or defaults. + :param kwargs: Extra arguments passed to :func:`json.loads`. + + .. versionchanged:: 2.0 + ``encoding`` is deprecated and will be removed in Flask 2.1. The + data must be a string or UTF-8 bytes. + + .. versionchanged:: 1.0.3 + ``app`` can be passed directly, rather than requiring an app + context for configuration. + """ + _load_arg_defaults(kwargs, app=app) + encoding = kwargs.pop("encoding", None) + + if encoding is not None: + warnings.warn( + "'encoding' is deprecated and will be removed in Flask 2.1." + " The data must be a string or UTF-8 bytes.", + DeprecationWarning, + stacklevel=2, + ) + + if isinstance(s, bytes): + s = s.decode(encoding) + + return _json.loads(s, **kwargs) + + +def load(fp: t.IO[str], app: t.Optional["Flask"] = None, **kwargs: t.Any) -> t.Any: + """Deserialize an object from JSON read from a file object. + + Takes the same arguments as the built-in :func:`json.load`, with + some defaults from application configuration. + + :param fp: File object to read JSON from. + :param app: Use this app's config instead of the active app context + or defaults. + :param kwargs: Extra arguments passed to :func:`json.load`. + + .. versionchanged:: 2.0 + ``encoding`` is deprecated and will be removed in Flask 2.1. The + file must be text mode, or binary mode with UTF-8 bytes. + """ + _load_arg_defaults(kwargs, app=app) + encoding = kwargs.pop("encoding", None) + + if encoding is not None: + warnings.warn( + "'encoding' is deprecated and will be removed in Flask 2.1." + " The file must be text mode, or binary mode with UTF-8" + " bytes.", + DeprecationWarning, + stacklevel=2, + ) + + if isinstance(fp.read(0), bytes): + fp = io.TextIOWrapper(fp, encoding) # type: ignore + + return _json.load(fp, **kwargs) + + +def htmlsafe_dumps(obj: t.Any, **kwargs: t.Any) -> str: + """Serialize an object to a string of JSON with :func:`dumps`, then + replace HTML-unsafe characters with Unicode escapes and mark the + result safe with :class:`~markupsafe.Markup`. + + This is available in templates as the ``|tojson`` filter. + + The returned string is safe to render in HTML documents and + ``') + # => <script> do_nasty_stuff() </script> + # sanitize_html('Click here for $100') + # => Click here for $100 + def sanitize_token(self, token): + + # accommodate filters which use token_type differently + token_type = token["type"] + if token_type in ("StartTag", "EndTag", "EmptyTag"): + name = token["name"] + namespace = token["namespace"] + if ((namespace, name) in self.allowed_elements or + (namespace is None and + (namespaces["html"], name) in self.allowed_elements)): + return self.allowed_token(token) + else: + return self.disallowed_token(token) + elif token_type == "Comment": + pass + else: + return token + + def allowed_token(self, token): + if "data" in token: + attrs = token["data"] + attr_names = set(attrs.keys()) + + # Remove forbidden attributes + for to_remove in (attr_names - self.allowed_attributes): + del token["data"][to_remove] + attr_names.remove(to_remove) + + # Remove attributes with disallowed URL values + for attr in (attr_names & self.attr_val_is_uri): + assert attr in attrs + # I don't have a clue where this regexp comes from or why it matches those + # characters, nor why we call unescape. I just know it's always been here. + # Should you be worried by this comment in a sanitizer? Yes. On the other hand, all + # this will do is remove *more* than it otherwise would. + val_unescaped = re.sub("[`\x00-\x20\x7f-\xa0\\s]+", '', + unescape(attrs[attr])).lower() + # remove replacement characters from unescaped characters + val_unescaped = val_unescaped.replace("\ufffd", "") + try: + uri = urlparse.urlparse(val_unescaped) + except ValueError: + uri = None + del attrs[attr] + if uri and uri.scheme: + if uri.scheme not in self.allowed_protocols: + del attrs[attr] + if uri.scheme == 'data': + m = data_content_type.match(uri.path) + if not m: + del attrs[attr] + elif m.group('content_type') not in self.allowed_content_types: + del attrs[attr] + + for attr in self.svg_attr_val_allows_ref: + if attr in attrs: + attrs[attr] = re.sub(r'url\s*\(\s*[^#\s][^)]+?\)', + ' ', + unescape(attrs[attr])) + if (token["name"] in self.svg_allow_local_href and + (namespaces['xlink'], 'href') in attrs and re.search(r'^\s*[^#\s].*', + attrs[(namespaces['xlink'], 'href')])): + del attrs[(namespaces['xlink'], 'href')] + if (None, 'style') in attrs: + attrs[(None, 'style')] = self.sanitize_css(attrs[(None, 'style')]) + token["data"] = attrs + return token + + def disallowed_token(self, token): + token_type = token["type"] + if token_type == "EndTag": + token["data"] = "" % token["name"] + elif token["data"]: + assert token_type in ("StartTag", "EmptyTag") + attrs = [] + for (ns, name), v in token["data"].items(): + attrs.append(' %s="%s"' % (name if ns is None else "%s:%s" % (prefixes[ns], name), escape(v))) + token["data"] = "<%s%s>" % (token["name"], ''.join(attrs)) + else: + token["data"] = "<%s>" % token["name"] + if token.get("selfClosing"): + token["data"] = token["data"][:-1] + "/>" + + token["type"] = "Characters" + + del token["name"] + return token + + def sanitize_css(self, style): + # disallow urls + style = re.compile(r'url\s*\(\s*[^\s)]+?\s*\)\s*').sub(' ', style) + + # gauntlet + if not re.match(r"""^([:,;#%.\sa-zA-Z0-9!]|\w-\w|'[\s\w]+'|"[\s\w]+"|\([\d,\s]+\))*$""", style): + return '' + if not re.match(r"^\s*([-\w]+\s*:[^:;]*(;\s*|$))*$", style): + return '' + + clean = [] + for prop, value in re.findall(r"([-\w]+)\s*:\s*([^:;]*)", style): + if not value: + continue + if prop.lower() in self.allowed_css_properties: + clean.append(prop + ': ' + value + ';') + elif prop.split('-')[0].lower() in ['background', 'border', 'margin', + 'padding']: + for keyword in value.split(): + if keyword not in self.allowed_css_keywords and \ + not re.match(r"^(#[0-9a-fA-F]+|rgb\(\d+%?,\d*%?,?\d*%?\)?|\d{0,2}\.?\d{0,2}(cm|em|ex|in|mm|pc|pt|px|%|,|\))?)$", keyword): # noqa + break + else: + clean.append(prop + ': ' + value + ';') + elif prop.lower() in self.allowed_svg_properties: + clean.append(prop + ': ' + value + ';') + + return ' '.join(clean) diff --git a/venv/Lib/site-packages/pip/_vendor/html5lib/filters/whitespace.py b/venv/Lib/site-packages/pip/_vendor/html5lib/filters/whitespace.py new file mode 100644 index 0000000..0d12584 --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/html5lib/filters/whitespace.py @@ -0,0 +1,38 @@ +from __future__ import absolute_import, division, unicode_literals + +import re + +from . import base +from ..constants import rcdataElements, spaceCharacters +spaceCharacters = "".join(spaceCharacters) + +SPACES_REGEX = re.compile("[%s]+" % spaceCharacters) + + +class Filter(base.Filter): + """Collapses whitespace except in pre, textarea, and script elements""" + spacePreserveElements = frozenset(["pre", "textarea"] + list(rcdataElements)) + + def __iter__(self): + preserve = 0 + for token in base.Filter.__iter__(self): + type = token["type"] + if type == "StartTag" \ + and (preserve or token["name"] in self.spacePreserveElements): + preserve += 1 + + elif type == "EndTag" and preserve: + preserve -= 1 + + elif not preserve and type == "SpaceCharacters" and token["data"]: + # Test on token["data"] above to not introduce spaces where there were not + token["data"] = " " + + elif not preserve and type == "Characters": + token["data"] = collapse_spaces(token["data"]) + + yield token + + +def collapse_spaces(text): + return SPACES_REGEX.sub(' ', text) diff --git a/venv/Lib/site-packages/pip/_vendor/html5lib/html5parser.py b/venv/Lib/site-packages/pip/_vendor/html5lib/html5parser.py new file mode 100644 index 0000000..d06784f --- /dev/null +++ b/venv/Lib/site-packages/pip/_vendor/html5lib/html5parser.py @@ -0,0 +1,2795 @@ +from __future__ import absolute_import, division, unicode_literals +from pip._vendor.six import with_metaclass, viewkeys + +import types + +from . import _inputstream +from . import _tokenizer + +from . import treebuilders +from .treebuilders.base import Marker + +from . import _utils +from .constants import ( + spaceCharacters, asciiUpper2Lower, + specialElements, headingElements, cdataElements, rcdataElements, + tokenTypes, tagTokenTypes, + namespaces, + htmlIntegrationPointElements, mathmlTextIntegrationPointElements, + adjustForeignAttributes as adjustForeignAttributesMap, + adjustMathMLAttributes, adjustSVGAttributes, + E, + _ReparseException +) + + +def parse(doc, treebuilder="etree", namespaceHTMLElements=True, **kwargs): + """Parse an HTML document as a string or file-like object into a tree + + :arg doc: the document to parse as a string or file-like object + + :arg treebuilder: the treebuilder to use when parsing + + :arg namespaceHTMLElements: whether or not to namespace HTML elements + + :returns: parsed tree + + Example: + + >>> from html5lib.html5parser import parse + >>> parse('

    This is a doc

    ') + + + """ + tb = treebuilders.getTreeBuilder(treebuilder) + p = HTMLParser(tb, namespaceHTMLElements=namespaceHTMLElements) + return p.parse(doc, **kwargs) + + +def parseFragment(doc, container="div", treebuilder="etree", namespaceHTMLElements=True, **kwargs): + """Parse an HTML fragment as a string or file-like object into a tree + + :arg doc: the fragment to parse as a string or file-like object + + :arg container: the container context to parse the fragment in + + :arg treebuilder: the treebuilder to use when parsing + + :arg namespaceHTMLElements: whether or not to namespace HTML elements + + :returns: parsed tree + + Example: + + >>> from html5lib.html5libparser import parseFragment + >>> parseFragment('this is a fragment') + + + """ + tb = treebuilders.getTreeBuilder(treebuilder) + p = HTMLParser(tb, namespaceHTMLElements=namespaceHTMLElements) + return p.parseFragment(doc, container=container, **kwargs) + + +def method_decorator_metaclass(function): + class Decorated(type): + def __new__(meta, classname, bases, classDict): + for attributeName, attribute in classDict.items(): + if isinstance(attribute, types.FunctionType): + attribute = function(attribute) + + classDict[attributeName] = attribute + return type.__new__(meta, classname, bases, classDict) + return Decorated + + +class HTMLParser(object): + """HTML parser + + Generates a tree structure from a stream of (possibly malformed) HTML. + + """ + + def __init__(self, tree=None, strict=False, namespaceHTMLElements=True, debug=False): + """ + :arg tree: a treebuilder class controlling the type of tree that will be + returned. Built in treebuilders can be accessed through + html5lib.treebuilders.getTreeBuilder(treeType) + + :arg strict: raise an exception when a parse error is encountered + + :arg namespaceHTMLElements: whether or not to namespace HTML elements + + :arg debug: whether or not to enable debug mode which logs things + + Example: + + >>> from html5lib.html5parser import HTMLParser + >>> parser = HTMLParser() # generates parser with etree builder + >>> parser = HTMLParser('lxml', strict=True) # generates parser with lxml builder which is strict + + """ + + # Raise an exception on the first error encountered + self.strict = strict + + if tree is None: + tree = treebuilders.getTreeBuilder("etree") + self.tree = tree(namespaceHTMLElements) + self.errors = [] + + self.phases = {name: cls(self, self.tree) for name, cls in + getPhases(debug).items()} + + def _parse(self, stream, innerHTML=False, container="div", scripting=False, **kwargs): + + self.innerHTMLMode = innerHTML + self.container = container + self.scripting = scripting + self.tokenizer = _tokenizer.HTMLTokenizer(stream, parser=self, **kwargs) + self.reset() + + try: + self.mainLoop() + except _ReparseException: + self.reset() + self.mainLoop() + + def reset(self): + self.tree.reset() + self.firstStartTag = False + self.errors = [] + self.log = [] # only used with debug mode + # "quirks" / "limited quirks" / "no quirks" + self.compatMode = "no quirks" + + if self.innerHTMLMode: + self.innerHTML = self.container.lower() + + if self.innerHTML in cdataElements: + self.tokenizer.state = self.tokenizer.rcdataState + elif self.innerHTML in rcdataElements: + self.tokenizer.state = self.tokenizer.rawtextState + elif self.innerHTML == 'plaintext': + self.tokenizer.state = self.tokenizer.plaintextState + else: + # state already is data state + # self.tokenizer.state = self.tokenizer.dataState + pass + self.phase = self.phases["beforeHtml"] + self.phase.insertHtmlElement() + self.resetInsertionMode() + else: + self.innerHTML = False # pylint:disable=redefined-variable-type + self.phase = self.phases["initial"] + + self.lastPhase = None + + self.beforeRCDataPhase = None + + self.framesetOK = True + + @property + def documentEncoding(self): + """Name of the character encoding that was used to decode the input stream, or + :obj:`None` if that is not determined yet + + """ + if not hasattr(self, 'tokenizer'): + return None + return self.tokenizer.stream.charEncoding[0].name + + def isHTMLIntegrationPoint(self, element): + if (element.name == "annotation-xml" and + element.namespace == namespaces["mathml"]): + return ("encoding" in element.attributes and + element.attributes["encoding"].translate( + asciiUpper2Lower) in + ("text/html", "application/xhtml+xml")) + else: + return (element.namespace, element.name) in htmlIntegrationPointElements + + def isMathMLTextIntegrationPoint(self, element): + return (element.namespace, element.name) in mathmlTextIntegrationPointElements + + def mainLoop(self): + CharactersToken = tokenTypes["Characters"] + SpaceCharactersToken = tokenTypes["SpaceCharacters"] + StartTagToken = tokenTypes["StartTag"] + EndTagToken = tokenTypes["EndTag"] + CommentToken = tokenTypes["Comment"] + DoctypeToken = tokenTypes["Doctype"] + ParseErrorToken = tokenTypes["ParseError"] + + for token in self.tokenizer: + prev_token = None + new_token = token + while new_token is not None: + prev_token = new_token + currentNode = self.tree.openElements[-1] if self.tree.openElements else None + currentNodeNamespace = currentNode.namespace if currentNode else None + currentNodeName = currentNode.name if currentNode else None + + type = new_token["type"] + + if type == ParseErrorToken: + self.parseError(new_token["data"], new_token.get("datavars", {})) + new_token = None + else: + if (len(self.tree.openElements) == 0 or + currentNodeNamespace == self.tree.defaultNamespace or + (self.isMathMLTextIntegrationPoint(currentNode) and + ((type == StartTagToken and + token["name"] not in frozenset(["mglyph", "malignmark"])) or + type in (CharactersToken, SpaceCharactersToken))) or + (currentNodeNamespace == namespaces["mathml"] and + currentNodeName == "annotation-xml" and + type == StartTagToken and + token["name"] == "svg") or + (self.isHTMLIntegrationPoint(currentNode) and + type in (StartTagToken, CharactersToken, SpaceCharactersToken))): + phase = self.phase + else: + phase = self.phases["inForeignContent"] + + if type == CharactersToken: + new_token = phase.processCharacters(new_token) + elif type == SpaceCharactersToken: + new_token = phase.processSpaceCharacters(new_token) + elif type == StartTagToken: + new_token = phase.processStartTag(new_token) + elif type == EndTagToken: + new_token = phase.processEndTag(new_token) + elif type == CommentToken: + new_token = phase.processComment(new_token) + elif type == DoctypeToken: + new_token = phase.processDoctype(new_token) + + if (type == StartTagToken and prev_token["selfClosing"] and + not prev_token["selfClosingAcknowledged"]): + self.parseError("non-void-element-with-trailing-solidus", + {"name": prev_token["name"]}) + + # When the loop finishes it's EOF + reprocess = True + phases = [] + while reprocess: + phases.append(self.phase) + reprocess = self.phase.processEOF() + if reprocess: + assert self.phase not in phases + + def parse(self, stream, *args, **kwargs): + """Parse a HTML document into a well-formed tree + + :arg stream: a file-like object or string containing the HTML to be parsed + + The optional encoding parameter must be a string that indicates + the encoding. If specified, that encoding will be used, + regardless of any BOM or later declaration (such as in a meta + element). + + :arg scripting: treat noscript elements as if JavaScript was turned on + + :returns: parsed tree + + Example: + + >>> from html5lib.html5parser import HTMLParser + >>> parser = HTMLParser() + >>> parser.parse('

    This is a doc

    ') + + + """ + self._parse(stream, False, None, *args, **kwargs) + return self.tree.getDocument() + + def parseFragment(self, stream, *args, **kwargs): + """Parse a HTML fragment into a well-formed tree fragment + + :arg container: name of the element we're setting the innerHTML + property if set to None, default to 'div' + + :arg stream: a file-like object or string containing the HTML to be parsed + + The optional encoding parameter must be a string that indicates + the encoding. If specified, that encoding will be used, + regardless of any BOM or later declaration (such as in a meta + element) + + :arg scripting: treat noscript elements as if JavaScript was turned on + + :returns: parsed tree + + Example: + + >>> from html5lib.html5libparser import HTMLParser + >>> parser = HTMLParser() + >>> parser.parseFragment('this is a fragment') + + + """ + self._parse(stream, True, *args, **kwargs) + return self.tree.getFragment() + + def parseError(self, errorcode="XXX-undefined-error", datavars=None): + # XXX The idea is to make errorcode mandatory. + if datavars is None: + datavars = {} + self.errors.append((self.tokenizer.stream.position(), errorcode, datavars)) + if self.strict: + raise ParseError(E[errorcode] % datavars) + + def adjustMathMLAttributes(self, token): + adjust_attributes(token, adjustMathMLAttributes) + + def adjustSVGAttributes(self, token): + adjust_attributes(token, adjustSVGAttributes) + + def adjustForeignAttributes(self, token): + adjust_attributes(token, adjustForeignAttributesMap) + + def reparseTokenNormal(self, token): + # pylint:disable=unused-argument + self.parser.phase() + + def resetInsertionMode(self): + # The name of this method is mostly historical. (It's also used in the + # specification.) + last = False + newModes = { + "select": "inSelect", + "td": "inCell", + "th": "inCell", + "tr": "inRow", + "tbody": "inTableBody", + "thead": "inTableBody", + "tfoot": "inTableBody", + "caption": "inCaption", + "colgroup": "inColumnGroup", + "table": "inTable", + "head": "inBody", + "body": "inBody", + "frameset": "inFrameset", + "html": "beforeHead" + } + for node in self.tree.openElements[::-1]: + nodeName = node.name + new_phase = None + if node == self.tree.openElements[0]: + assert self.innerHTML + last = True + nodeName = self.innerHTML + # Check for conditions that should only happen in the innerHTML + # case + if nodeName in ("select", "colgroup", "head", "html"): + assert self.innerHTML + + if not last and node.namespace != self.tree.defaultNamespace: + continue + + if nodeName in newModes: + new_phase = self.phases[newModes[nodeName]] + break + elif last: + new_phase = self.phases["inBody"] + break + + self.phase = new_phase + + def parseRCDataRawtext(self, token, contentType): + # Generic RCDATA/RAWTEXT Parsing algorithm + assert contentType in ("RAWTEXT", "RCDATA") + + self.tree.insertElement(token) + + if contentType == "RAWTEXT": + self.tokenizer.state = self.tokenizer.rawtextState + else: + self.tokenizer.state = self.tokenizer.rcdataState + + self.originalPhase = self.phase + + self.phase = self.phases["text"] + + +@_utils.memoize +def getPhases(debug): + def log(function): + """Logger that records which phase processes each token""" + type_names = {value: key for key, value in tokenTypes.items()} + + def wrapped(self, *args, **kwargs): + if function.__name__.startswith("process") and len(args) > 0: + token = args[0] + info = {"type": type_names[token['type']]} + if token['type'] in tagTokenTypes: + info["name"] = token['name'] + + self.parser.log.append((self.parser.tokenizer.state.__name__, + self.parser.phase.__class__.__name__, + self.__class__.__name__, + function.__name__, + info)) + return function(self, *args, **kwargs) + else: + return function(self, *args, **kwargs) + return wrapped + + def getMetaclass(use_metaclass, metaclass_func): + if use_metaclass: + return method_decorator_metaclass(metaclass_func) + else: + return type + + # pylint:disable=unused-argument + class Phase(with_metaclass(getMetaclass(debug, log))): + """Base class for helper object that implements each phase of processing + """ + __slots__ = ("parser", "tree", "__startTagCache", "__endTagCache") + + def __init__(self, parser, tree): + self.parser = parser + self.tree = tree + self.__startTagCache = {} + self.__endTagCache = {} + + def processEOF(self): + raise NotImplementedError + + def processComment(self, token): + # For most phases the following is correct. Where it's not it will be + # overridden. + self.tree.insertComment(token, self.tree.openElements[-1]) + + def processDoctype(self, token): + self.parser.parseError("unexpected-doctype") + + def processCharacters(self, token): + self.tree.insertText(token["data"]) + + def processSpaceCharacters(self, token): + self.tree.insertText(token["data"]) + + def processStartTag(self, token): + # Note the caching is done here rather than BoundMethodDispatcher as doing it there + # requires a circular reference to the Phase, and this ends up with a significant + # (CPython 2.7, 3.8) GC cost when parsing many short inputs + name = token["name"] + # In Py2, using `in` is quicker in general than try/except KeyError + # In Py3, `in` is quicker when there are few cache hits (typically short inputs) + if name in self.__startTagCache: + func = self.__startTagCache[name] + else: + func = self.__startTagCache[name] = self.startTagHandler[name] + # bound the cache size in case we get loads of unknown tags + while len(self.__startTagCache) > len(self.startTagHandler) * 1.1: + # this makes the eviction policy random on Py < 3.7 and FIFO >= 3.7 + self.__startTagCache.pop(next(iter(self.__startTagCache))) + return func(token) + + def startTagHtml(self, token): + if not self.parser.firstStartTag and token["name"] == "html": + self.parser.parseError("non-html-root") + # XXX Need a check here to see if the first start tag token emitted is + # this token... If it's not, invoke self.parser.parseError(). + for attr, value in token["data"].items(): + if attr not in self.tree.openElements[0].attributes: + self.tree.openElements[0].attributes[attr] = value + self.parser.firstStartTag = False + + def processEndTag(self, token): + # Note the caching is done here rather than BoundMethodDispatcher as doing it there + # requires a circular reference to the Phase, and this ends up with a significant + # (CPython 2.7, 3.8) GC cost when parsing many short inputs + name = token["name"] + # In Py2, using `in` is quicker in general than try/except KeyError + # In Py3, `in` is quicker when there are few cache hits (typically short inputs) + if name in self.__endTagCache: + func = self.__endTagCache[name] + else: + func = self.__endTagCache[name] = self.endTagHandler[name] + # bound the cache size in case we get loads of unknown tags + while len(self.__endTagCache) > len(self.endTagHandler) * 1.1: + # this makes the eviction policy random on Py < 3.7 and FIFO >= 3.7 + self.__endTagCache.pop(next(iter(self.__endTagCache))) + return func(token) + + class InitialPhase(Phase): + __slots__ = tuple() + + def processSpaceCharacters(self, token): + pass + + def processComment(self, token): + self.tree.insertComment(token, self.tree.document) + + def processDoctype(self, token): + name = token["name"] + publicId = token["publicId"] + systemId = token["systemId"] + correct = token["correct"] + + if (name != "html" or publicId is not None or + systemId is not None and systemId != "about:legacy-compat"): + self.parser.parseError("unknown-doctype") + + if publicId is None: + publicId = "" + + self.tree.insertDoctype(token) + + if publicId != "": + publicId = publicId.translate(asciiUpper2Lower) + + if (not correct or token["name"] != "html" or + publicId.startswith( + ("+//silmaril//dtd html pro v0r11 19970101//", + "-//advasoft ltd//dtd html 3.0 aswedit + extensions//", + "-//as//dtd html 3.0 aswedit + extensions//", + "-//ietf//dtd html 2.0 level 1//", + "-//ietf//dtd html 2.0 level 2//", + "-//ietf//dtd html 2.0 strict level 1//", + "-//ietf//dtd html 2.0 strict level 2//", + "-//ietf//dtd html 2.0 strict//", + "-//ietf//dtd html 2.0//", + "-//ietf//dtd html 2.1e//", + "-//ietf//dtd html 3.0//", + "-//ietf//dtd html 3.2 final//", + "-//ietf//dtd html 3.2//", + "-//ietf//dtd html 3//", + "-//ietf//dtd html level 0//", + "-//ietf//dtd html level 1//", + "-//ietf//dtd html level 2//", + "-//ietf//dtd html level 3//", + "-//ietf//dtd html strict level 0//", + "-//ietf//dtd html strict level 1//", + "-//ietf//dtd html strict level 2//", + "-//ietf//dtd html strict level 3//", + "-//ietf//dtd html strict//", + "-//ietf//dtd html//", + "-//metrius//dtd metrius presentational//", + "-//microsoft//dtd internet explorer 2.0 html strict//", + "-//microsoft//dtd internet explorer 2.0 html//", + "-//microsoft//dtd internet explorer 2.0 tables//", + "-//microsoft//dtd internet explorer 3.0 html strict//", + "-//microsoft//dtd internet explorer 3.0 html//", + "-//microsoft//dtd internet explorer 3.0 tables//", + "-//netscape comm. corp.//dtd html//", + "-//netscape comm. corp.//dtd strict html//", + "-//o'reilly and associates//dtd html 2.0//", + "-//o'reilly and associates//dtd html extended 1.0//", + "-//o'reilly and associates//dtd html extended relaxed 1.0//", + "-//softquad software//dtd hotmetal pro 6.0::19990601::extensions to html 4.0//", + "-//softquad//dtd hotmetal pro 4.0::19971010::extensions to html 4.0//", + "-//spyglass//dtd html 2.0 extended//", + "-//sq//dtd html 2.0 hotmetal + extensions//", + "-//sun microsystems corp.//dtd hotjava html//", + "-//sun microsystems corp.//dtd hotjava strict html//", + "-//w3c//dtd html 3 1995-03-24//", + "-//w3c//dtd html 3.2 draft//", + "-//w3c//dtd html 3.2 final//", + "-//w3c//dtd html 3.2//", + "-//w3c//dtd html 3.2s draft//", + "-//w3c//dtd html 4.0 frameset//", + "-//w3c//dtd html 4.0 transitional//", + "-//w3c//dtd html experimental 19960712//", + "-//w3c//dtd html experimental 970421//", + "-//w3c//dtd w3 html//", + "-//w3o//dtd w3 html 3.0//", + "-//webtechs//dtd mozilla html 2.0//", + "-//webtechs//dtd mozilla html//")) or + publicId in ("-//w3o//dtd w3 html strict 3.0//en//", + "-/w3c/dtd html 4.0 transitional/en", + "html") or + publicId.startswith( + ("-//w3c//dtd html 4.01 frameset//", + "-//w3c//dtd html 4.01 transitional//")) and + systemId is None or + systemId and systemId.lower() == "http://www.ibm.com/data/dtd/v11/ibmxhtml1-transitional.dtd"): + self.parser.compatMode = "quirks" + elif (publicId.startswith( + ("-//w3c//dtd xhtml 1.0 frameset//", + "-//w3c//dtd xhtml 1.0 transitional//")) or + publicId.startswith( + ("-//w3c//dtd html 4.01 frameset//", + "-//w3c//dtd html 4.01 transitional//")) and + systemId is not None): + self.parser.compatMode = "limited quirks" + + self.parser.phase = self.parser.phases["beforeHtml"] + + def anythingElse(self): + self.parser.compatMode = "quirks" + self.parser.phase = self.parser.phases["beforeHtml"] + + def processCharacters(self, token): + self.parser.parseError("expected-doctype-but-got-chars") + self.anythingElse() + return token + + def processStartTag(self, token): + self.parser.parseError("expected-doctype-but-got-start-tag", + {"name": token["name"]}) + self.anythingElse() + return token + + def processEndTag(self, token): + self.parser.parseError("expected-doctype-but-got-end-tag", + {"name": token["name"]}) + self.anythingElse() + return token + + def processEOF(self): + self.parser.parseError("expected-doctype-but-got-eof") + self.anythingElse() + return True + + class BeforeHtmlPhase(Phase): + __slots__ = tuple() + + # helper methods + def insertHtmlElement(self): + self.tree.insertRoot(impliedTagToken("html", "StartTag")) + self.parser.phase = self.parser.phases["beforeHead"] + + # other + def processEOF(self): + self.insertHtmlElement() + return True + + def processComment(self, token): + self.tree.insertComment(token, self.tree.document) + + def processSpaceCharacters(self, token): + pass + + def processCharacters(self, token): + self.insertHtmlElement() + return token + + def processStartTag(self, token): + if token["name"] == "html": + self.parser.firstStartTag = True + self.insertHtmlElement() + return token + + def processEndTag(self, token): + if token["name"] not in ("head", "body", "html", "br"): + self.parser.parseError("unexpected-end-tag-before-html", + {"name": token["name"]}) + else: + self.insertHtmlElement() + return token + + class BeforeHeadPhase(Phase): + __slots__ = tuple() + + def processEOF(self): + self.startTagHead(impliedTagToken("head", "StartTag")) + return True + + def processSpaceCharacters(self, token): + pass + + def processCharacters(self, token): + self.startTagHead(impliedTagToken("head", "StartTag")) + return token + + def startTagHtml(self, token): + return self.parser.phases["inBody"].processStartTag(token) + + def startTagHead(self, token): + self.tree.insertElement(token) + self.tree.headPointer = self.tree.openElements[-1] + self.parser.phase = self.parser.phases["inHead"] + + def startTagOther(self, token): + self.startTagHead(impliedTagToken("head", "StartTag")) + return token + + def endTagImplyHead(self, token): + self.startTagHead(impliedTagToken("head", "StartTag")) + return token + + def endTagOther(self, token): + self.parser.parseError("end-tag-after-implied-root", + {"name": token["name"]}) + + startTagHandler = _utils.MethodDispatcher([ + ("html", startTagHtml), + ("head", startTagHead) + ]) + startTagHandler.default = startTagOther + + endTagHandler = _utils.MethodDispatcher([ + (("head", "body", "html", "br"), endTagImplyHead) + ]) + endTagHandler.default = endTagOther + + class InHeadPhase(Phase): + __slots__ = tuple() + + # the real thing + def processEOF(self): + self.anythingElse() + return True + + def processCharacters(self, token): + self.anythingElse() + return token + + def startTagHtml(self, token): + return self.parser.phases["inBody"].processStartTag(token) + + def startTagHead(self, token): + self.parser.parseError("two-heads-are-not-better-than-one") + + def startTagBaseLinkCommand(self, token): + self.tree.insertElement(token) + self.tree.openElements.pop() + token["selfClosingAcknowledged"] = True + + def startTagMeta(self, token): + self.tree.insertElement(token) + self.tree.openElements.pop() + token["selfClosingAcknowledged"] = True + + attributes = token["data"] + if self.parser.tokenizer.stream.charEncoding[1] == "tentative": + if "charset" in attributes: + self.parser.tokenizer.stream.changeEncoding(attributes["charset"]) + elif ("content" in attributes and + "http-equiv" in attributes and + attributes["http-equiv"].lower() == "content-type"): + # Encoding it as UTF-8 here is a hack, as really we should pass + # the abstract Unicode string, and just use the + # ContentAttrParser on that, but using UTF-8 allows all chars + # to be encoded and as a ASCII-superset works. + data = _inputstream.EncodingBytes(attributes["content"].encode("utf-8")) + parser = _inputstream.ContentAttrParser(data) + codec = parser.parse() + self.parser.tokenizer.stream.changeEncoding(codec) + + def startTagTitle(self, token): + self.parser.parseRCDataRawtext(token, "RCDATA") + + def startTagNoFramesStyle(self, token): + # Need to decide whether to implement the scripting-disabled case + self.parser.parseRCDataRawtext(token, "RAWTEXT") + + def startTagNoscript(self, token): + if self.parser.scripting: + self.parser.parseRCDataRawtext(token, "RAWTEXT") + else: + self.tree.insertElement(token) + self.parser.phase = self.parser.phases["inHeadNoscript"] + + def startTagScript(self, token): + self.tree.insertElement(token) + self.parser.tokenizer.state = self.parser.tokenizer.scriptDataState + self.parser.originalPhase = self.parser.phase + self.parser.phase = self.parser.phases["text"] + + def startTagOther(self, token): + self.anythingElse() + return token + + def endTagHead(self, token): + node = self.parser.tree.openElements.pop() + assert node.name == "head", "Expected head got %s" % node.name + self.parser.phase = self.parser.phases["afterHead"] + + def endTagHtmlBodyBr(self, token): + self.anythingElse() + return token + + def endTagOther(self, token): + self.parser.parseError("unexpected-end-tag", {"name": token["name"]}) + + def anythingElse(self): + self.endTagHead(impliedTagToken("head")) + + startTagHandler = _utils.MethodDispatcher([ + ("html", startTagHtml), + ("title", startTagTitle), + (("noframes", "style"), startTagNoFramesStyle), + ("noscript", startTagNoscript), + ("script", startTagScript), + (("base", "basefont", "bgsound", "command", "link"), + startTagBaseLinkCommand), + ("meta", startTagMeta), + ("head", startTagHead) + ]) + startTagHandler.default = startTagOther + + endTagHandler = _utils.MethodDispatcher([ + ("head", endTagHead), + (("br", "html", "body"), endTagHtmlBodyBr) + ]) + endTagHandler.default = endTagOther + + class InHeadNoscriptPhase(Phase): + __slots__ = tuple() + + def processEOF(self): + self.parser.parseError("eof-in-head-noscript") + self.anythingElse() + return True + + def processComment(self, token): + return self.parser.phases["inHead"].processComment(token) + + def processCharacters(self, token): + self.parser.parseError("char-in-head-noscript") + self.anythingElse() + return token + + def processSpaceCharacters(self, token): + return self.parser.phases["inHead"].processSpaceCharacters(token) + + def startTagHtml(self, token): + return self.parser.phases["inBody"].processStartTag(token) + + def startTagBaseLinkCommand(self, token): + return self.parser.phases["inHead"].processStartTag(token) + + def startTagHeadNoscript(self, token): + self.parser.parseError("unexpected-start-tag", {"name": token["name"]}) + + def startTagOther(self, token): + self.parser.parseError("unexpected-inhead-noscript-tag", {"name": token["name"]}) + self.anythingElse() + return token + + def endTagNoscript(self, token): + node = self.parser.tree.openElements.pop() + assert node.name == "noscript", "Expected noscript got %s" % node.name + self.parser.phase = self.parser.phases["inHead"] + + def endTagBr(self, token): + self.parser.parseError("unexpected-inhead-noscript-tag", {"name": token["name"]}) + self.anythingElse() + return token + + def endTagOther(self, token): + self.parser.parseError("unexpected-end-tag", {"name": token["name"]}) + + def anythingElse(self): + # Caller must raise parse error first! + self.endTagNoscript(impliedTagToken("noscript")) + + startTagHandler = _utils.MethodDispatcher([ + ("html", startTagHtml), + (("basefont", "bgsound", "link", "meta", "noframes", "style"), startTagBaseLinkCommand), + (("head", "noscript"), startTagHeadNoscript), + ]) + startTagHandler.default = startTagOther + + endTagHandler = _utils.MethodDispatcher([ + ("noscript", endTagNoscript), + ("br", endTagBr), + ]) + endTagHandler.default = endTagOther + + class AfterHeadPhase(Phase): + __slots__ = tuple() + + def processEOF(self): + self.anythingElse() + return True + + def processCharacters(self, token): + self.anythingElse() + return token + + def startTagHtml(self, token): + return self.parser.phases["inBody"].processStartTag(token) + + def startTagBody(self, token): + self.parser.framesetOK = False + self.tree.insertElement(token) + self.parser.phase = self.parser.phases["inBody"] + + def startTagFrameset(self, token): + self.tree.insertElement(token) + self.parser.phase = self.parser.phases["inFrameset"] + + def startTagFromHead(self, token): + self.parser.parseError("unexpected-start-tag-out-of-my-head", + {"name": token["name"]}) + self.tree.openElements.append(self.tree.headPointer) + self.parser.phases["inHead"].processStartTag(token) + for node in self.tree.openElements[::-1]: + if node.name == "head": + self.tree.openElements.remove(node) + break + + def startTagHead(self, token): + self.parser.parseError("unexpected-start-tag", {"name": token["name"]}) + + def startTagOther(self, token): + self.anythingElse() + return token + + def endTagHtmlBodyBr(self, token): + self.anythingElse() + return token + + def endTagOther(self, token): + self.parser.parseError("unexpected-end-tag", {"name": token["name"]}) + + def anythingElse(self): + self.tree.insertElement(impliedTagToken("body", "StartTag")) + self.parser.phase = self.parser.phases["inBody"] + self.parser.framesetOK = True + + startTagHandler = _utils.MethodDispatcher([ + ("html", startTagHtml), + ("body", startTagBody), + ("frameset", startTagFrameset), + (("base", "basefont", "bgsound", "link", "meta", "noframes", "script", + "style", "title"), + startTagFromHead), + ("head", startTagHead) + ]) + startTagHandler.default = startTagOther + endTagHandler = _utils.MethodDispatcher([(("body", "html", "br"), + endTagHtmlBodyBr)]) + endTagHandler.default = endTagOther + + class InBodyPhase(Phase): + # http://www.whatwg.org/specs/web-apps/current-work/#parsing-main-inbody + # the really-really-really-very crazy mode + __slots__ = ("processSpaceCharacters",) + + def __init__(self, *args, **kwargs): + super(InBodyPhase, self).__init__(*args, **kwargs) + # Set this to the default handler + self.processSpaceCharacters = self.processSpaceCharactersNonPre + + def isMatchingFormattingElement(self, node1, node2): + return (node1.name == node2.name and + node1.namespace == node2.namespace and + node1.attributes == node2.attributes) + + # helper + def addFormattingElement(self, token): + self.tree.insertElement(token) + element = self.tree.openElements[-1] + + matchingElements = [] + for node in self.tree.activeFormattingElements[::-1]: + if node is Marker: + break + elif self.isMatchingFormattingElement(node, element): + matchingElements.append(node) + + assert len(matchingElements) <= 3 + if len(matchingElements) == 3: + self.tree.activeFormattingElements.remove(matchingElements[-1]) + self.tree.activeFormattingElements.append(element) + + # the real deal + def processEOF(self): + allowed_elements = frozenset(("dd", "dt", "li", "p", "tbody", "td", + "tfoot", "th", "thead", "tr", "body", + "html")) + for node in self.tree.openElements[::-1]: + if node.name not in allowed_elements: + self.parser.parseError("expected-closing-tag-but-got-eof") + break + # Stop parsing + + def processSpaceCharactersDropNewline(self, token): + # Sometimes (start of
    , , and 
    +
    +
    + The debugger caught an exception in your WSGI application. You can now + look at the traceback which led to the error. + If you enable JavaScript you can also use additional features such as code + execution (if the evalex feature is enabled), automatic pasting of the + exceptions and much more. +
    +""" + + FOOTER + + """ + +""" +) + +CONSOLE_HTML = ( + HEADER + + """\ +

    Interactive Console

    +
    +In this console you can execute Python expressions in the context of the +application. The initial namespace was created by the debugger automatically. +
    +
    The Console requires JavaScript.
    +""" + + FOOTER +) + +SUMMARY_HTML = """\ +
    + %(title)s +
      %(frames)s
    + %(description)s +
    +""" + +FRAME_HTML = """\ +
    +

    File "%(filename)s", + line %(lineno)s, + in %(function_name)s

    +
    %(lines)s
    +
    +""" + +SOURCE_LINE_HTML = """\ + + %(lineno)s + %(code)s + +""" + + +def render_console_html(secret: str, evalex_trusted: bool = True) -> str: + return CONSOLE_HTML % { + "evalex": "true", + "evalex_trusted": "true" if evalex_trusted else "false", + "console": "true", + "title": "Console", + "secret": secret, + "traceback_id": -1, + } + + +def get_current_traceback( + ignore_system_exceptions: bool = False, + show_hidden_frames: bool = False, + skip: int = 0, +) -> "Traceback": + """Get the current exception info as `Traceback` object. Per default + calling this method will reraise system exceptions such as generator exit, + system exit or others. This behavior can be disabled by passing `False` + to the function as first parameter. + """ + info = t.cast( + t.Tuple[t.Type[BaseException], BaseException, TracebackType], sys.exc_info() + ) + exc_type, exc_value, tb = info + + if ignore_system_exceptions and exc_type in { + SystemExit, + KeyboardInterrupt, + GeneratorExit, + }: + raise + for _ in range(skip): + if tb.tb_next is None: + break + tb = tb.tb_next + tb = Traceback(exc_type, exc_value, tb) + if not show_hidden_frames: + tb.filter_hidden_frames() + return tb + + +class Line: + """Helper for the source renderer.""" + + __slots__ = ("lineno", "code", "in_frame", "current") + + def __init__(self, lineno: int, code: str) -> None: + self.lineno = lineno + self.code = code + self.in_frame = False + self.current = False + + @property + def classes(self) -> t.List[str]: + rv = ["line"] + if self.in_frame: + rv.append("in-frame") + if self.current: + rv.append("current") + return rv + + def render(self) -> str: + return SOURCE_LINE_HTML % { + "classes": " ".join(self.classes), + "lineno": self.lineno, + "code": escape(self.code), + } + + +class Traceback: + """Wraps a traceback.""" + + def __init__( + self, + exc_type: t.Type[BaseException], + exc_value: BaseException, + tb: TracebackType, + ) -> None: + self.exc_type = exc_type + self.exc_value = exc_value + self.tb = tb + + exception_type = exc_type.__name__ + if exc_type.__module__ not in {"builtins", "__builtin__", "exceptions"}: + exception_type = f"{exc_type.__module__}.{exception_type}" + self.exception_type = exception_type + + self.groups = [] + memo = set() + while True: + self.groups.append(Group(exc_type, exc_value, tb)) + memo.add(id(exc_value)) + exc_value = exc_value.__cause__ or exc_value.__context__ # type: ignore + if exc_value is None or id(exc_value) in memo: + break + exc_type = type(exc_value) + tb = exc_value.__traceback__ # type: ignore + self.groups.reverse() + self.frames = [frame for group in self.groups for frame in group.frames] + + def filter_hidden_frames(self) -> None: + """Remove the frames according to the paste spec.""" + for group in self.groups: + group.filter_hidden_frames() + + self.frames[:] = [frame for group in self.groups for frame in group.frames] + + @property + def is_syntax_error(self) -> bool: + """Is it a syntax error?""" + return isinstance(self.exc_value, SyntaxError) + + @property + def exception(self) -> str: + """String representation of the final exception.""" + return self.groups[-1].exception + + def log(self, logfile: t.Optional[t.TextIO] = None) -> None: + """Log the ASCII traceback into a file object.""" + if logfile is None: + logfile = sys.stderr + tb = f"{self.plaintext.rstrip()}\n" + logfile.write(tb) + + def render_summary(self, include_title: bool = True) -> str: + """Render the traceback for the interactive console.""" + title = "" + classes = ["traceback"] + if not self.frames: + classes.append("noframe-traceback") + frames = [] + else: + library_frames = sum(frame.is_library for frame in self.frames) + mark_lib = 0 < library_frames < len(self.frames) + frames = [group.render(mark_lib=mark_lib) for group in self.groups] + + if include_title: + if self.is_syntax_error: + title = "Syntax Error" + else: + title = "Traceback (most recent call last):" + + if self.is_syntax_error: + description = f"
    {escape(self.exception)}
    " + else: + description = f"
    {escape(self.exception)}
    " + + return SUMMARY_HTML % { + "classes": " ".join(classes), + "title": f"

    {title if title else ''}

    ", + "frames": "\n".join(frames), + "description": description, + } + + def render_full( + self, + evalex: bool = False, + secret: t.Optional[str] = None, + evalex_trusted: bool = True, + ) -> str: + """Render the Full HTML page with the traceback info.""" + exc = escape(self.exception) + return PAGE_HTML % { + "evalex": "true" if evalex else "false", + "evalex_trusted": "true" if evalex_trusted else "false", + "console": "false", + "title": exc, + "exception": exc, + "exception_type": escape(self.exception_type), + "summary": self.render_summary(include_title=False), + "plaintext": escape(self.plaintext), + "plaintext_cs": re.sub("-{2,}", "-", self.plaintext), + "traceback_id": self.id, + "secret": secret, + } + + @cached_property + def plaintext(self) -> str: + return "\n".join([group.render_text() for group in self.groups]) + + @property + def id(self) -> int: + return id(self) + + +class Group: + """A group of frames for an exception in a traceback. If the + exception has a ``__cause__`` or ``__context__``, there are multiple + exception groups. + """ + + def __init__( + self, + exc_type: t.Type[BaseException], + exc_value: BaseException, + tb: TracebackType, + ) -> None: + self.exc_type = exc_type + self.exc_value = exc_value + self.info = None + if exc_value.__cause__ is not None: + self.info = ( + "The above exception was the direct cause of the following exception" + ) + elif exc_value.__context__ is not None: + self.info = ( + "During handling of the above exception, another exception occurred" + ) + + self.frames = [] + while tb is not None: + self.frames.append(Frame(exc_type, exc_value, tb)) + tb = tb.tb_next # type: ignore + + def filter_hidden_frames(self) -> None: + new_frames: t.List[Frame] = [] + hidden = False + + for frame in self.frames: + hide = frame.hide + if hide in ("before", "before_and_this"): + new_frames = [] + hidden = False + if hide == "before_and_this": + continue + elif hide in ("reset", "reset_and_this"): + hidden = False + if hide == "reset_and_this": + continue + elif hide in ("after", "after_and_this"): + hidden = True + if hide == "after_and_this": + continue + elif hide or hidden: + continue + new_frames.append(frame) + + # if we only have one frame and that frame is from the codeop + # module, remove it. + if len(new_frames) == 1 and self.frames[0].module == "codeop": + del self.frames[:] + + # if the last frame is missing something went terrible wrong :( + elif self.frames[-1] in new_frames: + self.frames[:] = new_frames + + @property + def exception(self) -> str: + """String representation of the exception.""" + buf = traceback.format_exception_only(self.exc_type, self.exc_value) + rv = "".join(buf).strip() + return _to_str(rv, "utf-8", "replace") + + def render(self, mark_lib: bool = True) -> str: + out = [] + if self.info is not None: + out.append(f'
  • {self.info}:
    ') + for frame in self.frames: + title = f' title="{escape(frame.info)}"' if frame.info else "" + out.append(f"{frame.render(mark_lib=mark_lib)}") + return "\n".join(out) + + def render_text(self) -> str: + out = [] + if self.info is not None: + out.append(f"\n{self.info}:\n") + out.append("Traceback (most recent call last):") + for frame in self.frames: + out.append(frame.render_text()) + out.append(self.exception) + return "\n".join(out) + + +class Frame: + """A single frame in a traceback.""" + + def __init__( + self, + exc_type: t.Type[BaseException], + exc_value: BaseException, + tb: TracebackType, + ) -> None: + self.lineno = tb.tb_lineno + self.function_name = tb.tb_frame.f_code.co_name + self.locals = tb.tb_frame.f_locals + self.globals = tb.tb_frame.f_globals + + fn = inspect.getsourcefile(tb) or inspect.getfile(tb) + if fn[-4:] in (".pyo", ".pyc"): + fn = fn[:-1] + # if it's a file on the file system resolve the real filename. + if os.path.isfile(fn): + fn = os.path.realpath(fn) + self.filename = _to_str(fn, get_filesystem_encoding()) + self.module = self.globals.get("__name__", self.locals.get("__name__")) + self.loader = self.globals.get("__loader__", self.locals.get("__loader__")) + self.code = tb.tb_frame.f_code + + # support for paste's traceback extensions + self.hide = self.locals.get("__traceback_hide__", False) + info = self.locals.get("__traceback_info__") + if info is not None: + info = _to_str(info, "utf-8", "replace") + self.info = info + + def render(self, mark_lib: bool = True) -> str: + """Render a single frame in a traceback.""" + return FRAME_HTML % { + "id": self.id, + "filename": escape(self.filename), + "lineno": self.lineno, + "function_name": escape(self.function_name), + "lines": self.render_line_context(), + "library": "library" if mark_lib and self.is_library else "", + } + + @cached_property + def is_library(self) -> bool: + return any( + self.filename.startswith(os.path.realpath(path)) + for path in sysconfig.get_paths().values() + ) + + def render_text(self) -> str: + return ( + f' File "{self.filename}", line {self.lineno}, in {self.function_name}\n' + f" {self.current_line.strip()}" + ) + + def render_line_context(self) -> str: + before, current, after = self.get_context_lines() + rv = [] + + def render_line(line: str, cls: str) -> None: + line = line.expandtabs().rstrip() + stripped_line = line.strip() + prefix = len(line) - len(stripped_line) + rv.append( + f'
    {" " * prefix}'
    +                f"{escape(stripped_line) if stripped_line else ' '}
    " + ) + + for line in before: + render_line(line, "before") + render_line(current, "current") + for line in after: + render_line(line, "after") + + return "\n".join(rv) + + def get_annotated_lines(self) -> t.List[Line]: + """Helper function that returns lines with extra information.""" + lines = [Line(idx + 1, x) for idx, x in enumerate(self.sourcelines)] + + # find function definition and mark lines + if hasattr(self.code, "co_firstlineno"): + lineno = self.code.co_firstlineno - 1 + while lineno > 0: + if _funcdef_re.match(lines[lineno].code): + break + lineno -= 1 + try: + offset = len(inspect.getblock([f"{x.code}\n" for x in lines[lineno:]])) + except TokenError: + offset = 0 + for line in lines[lineno : lineno + offset]: + line.in_frame = True + + # mark current line + try: + lines[self.lineno - 1].current = True + except IndexError: + pass + + return lines + + def eval(self, code: t.Union[str, CodeType], mode: str = "single") -> t.Any: + """Evaluate code in the context of the frame.""" + if isinstance(code, str): + code = compile(code, "", mode) + return eval(code, self.globals, self.locals) + + @cached_property + def sourcelines(self) -> t.List[str]: + """The sourcecode of the file as list of strings.""" + # get sourcecode from loader or file + source = None + if self.loader is not None: + try: + if hasattr(self.loader, "get_source"): + source = self.loader.get_source(self.module) + elif hasattr(self.loader, "get_source_by_code"): + source = self.loader.get_source_by_code(self.code) + except Exception: + # we munch the exception so that we don't cause troubles + # if the loader is broken. + pass + + if source is None: + try: + with open(self.filename, mode="rb") as f: + source = f.read() + except OSError: + return [] + + # already str? return right away + if isinstance(source, str): + return source.splitlines() + + charset = "utf-8" + if source.startswith(codecs.BOM_UTF8): + source = source[3:] + else: + for idx, match in enumerate(_line_re.finditer(source)): + coding_match = _coding_re.search(match.group()) + if coding_match is not None: + charset = coding_match.group(1).decode("utf-8") + break + if idx > 1: + break + + # on broken cookies we fall back to utf-8 too + charset = _to_str(charset) + try: + codecs.lookup(charset) + except LookupError: + charset = "utf-8" + + return source.decode(charset, "replace").splitlines() + + def get_context_lines( + self, context: int = 5 + ) -> t.Tuple[t.List[str], str, t.List[str]]: + before = self.sourcelines[self.lineno - context - 1 : self.lineno - 1] + past = self.sourcelines[self.lineno : self.lineno + context] + return (before, self.current_line, past) + + @property + def current_line(self) -> str: + try: + return self.sourcelines[self.lineno - 1] + except IndexError: + return "" + + @cached_property + def console(self) -> Console: + return Console(self.globals, self.locals) + + @property + def id(self) -> int: + return id(self) diff --git a/venv/Lib/site-packages/werkzeug/exceptions.py b/venv/Lib/site-packages/werkzeug/exceptions.py new file mode 100644 index 0000000..16c3964 --- /dev/null +++ b/venv/Lib/site-packages/werkzeug/exceptions.py @@ -0,0 +1,943 @@ +"""Implements a number of Python exceptions which can be raised from within +a view to trigger a standard HTTP non-200 response. + +Usage Example +------------- + +.. code-block:: python + + from werkzeug.wrappers.request import Request + from werkzeug.exceptions import HTTPException, NotFound + + def view(request): + raise NotFound() + + @Request.application + def application(request): + try: + return view(request) + except HTTPException as e: + return e + +As you can see from this example those exceptions are callable WSGI +applications. However, they are not Werkzeug response objects. You +can get a response object by calling ``get_response()`` on a HTTP +exception. + +Keep in mind that you may have to pass an environ (WSGI) or scope +(ASGI) to ``get_response()`` because some errors fetch additional +information relating to the request. + +If you want to hook in a different exception page to say, a 404 status +code, you can add a second except for a specific subclass of an error: + +.. code-block:: python + + @Request.application + def application(request): + try: + return view(request) + except NotFound as e: + return not_found(request) + except HTTPException as e: + return e + +""" +import sys +import typing as t +import warnings +from datetime import datetime +from html import escape + +from ._internal import _get_environ + +if t.TYPE_CHECKING: + import typing_extensions as te + from _typeshed.wsgi import StartResponse + from _typeshed.wsgi import WSGIEnvironment + from .datastructures import WWWAuthenticate + from .sansio.response import Response + from .wrappers.response import Response as WSGIResponse # noqa: F401 + + +class HTTPException(Exception): + """The base class for all HTTP exceptions. This exception can be called as a WSGI + application to render a default error page or you can catch the subclasses + of it independently and render nicer error messages. + """ + + code: t.Optional[int] = None + description: t.Optional[str] = None + + def __init__( + self, + description: t.Optional[str] = None, + response: t.Optional["Response"] = None, + ) -> None: + super().__init__() + if description is not None: + self.description = description + self.response = response + + @classmethod + def wrap( + cls, exception: t.Type[BaseException], name: t.Optional[str] = None + ) -> t.Type["HTTPException"]: + """Create an exception that is a subclass of the calling HTTP + exception and the ``exception`` argument. + + The first argument to the class will be passed to the + wrapped ``exception``, the rest to the HTTP exception. If + ``e.args`` is not empty and ``e.show_exception`` is ``True``, + the wrapped exception message is added to the HTTP error + description. + + .. deprecated:: 2.0 + Will be removed in Werkzeug 2.1. Create a subclass manually + instead. + + .. versionchanged:: 0.15.5 + The ``show_exception`` attribute controls whether the + description includes the wrapped exception message. + + .. versionchanged:: 0.15.0 + The description includes the wrapped exception message. + """ + warnings.warn( + "'HTTPException.wrap' is deprecated and will be removed in" + " Werkzeug 2.1. Create a subclass manually instead.", + DeprecationWarning, + stacklevel=2, + ) + + class newcls(cls, exception): # type: ignore + _description = cls.description + show_exception = False + + def __init__( + self, arg: t.Optional[t.Any] = None, *args: t.Any, **kwargs: t.Any + ) -> None: + super().__init__(*args, **kwargs) + + if arg is None: + exception.__init__(self) + else: + exception.__init__(self, arg) + + @property + def description(self) -> str: + if self.show_exception: + return ( + f"{self._description}\n" + f"{exception.__name__}: {exception.__str__(self)}" + ) + + return self._description # type: ignore + + @description.setter + def description(self, value: str) -> None: + self._description = value + + newcls.__module__ = sys._getframe(1).f_globals["__name__"] + name = name or cls.__name__ + exception.__name__ + newcls.__name__ = newcls.__qualname__ = name + return newcls + + @property + def name(self) -> str: + """The status name.""" + from .http import HTTP_STATUS_CODES + + return HTTP_STATUS_CODES.get(self.code, "Unknown Error") # type: ignore + + def get_description( + self, + environ: t.Optional["WSGIEnvironment"] = None, + scope: t.Optional[dict] = None, + ) -> str: + """Get the description.""" + if self.description is None: + description = "" + elif not isinstance(self.description, str): + description = str(self.description) + else: + description = self.description + + description = escape(description).replace("\n", "
    ") + return f"

    {description}

    " + + def get_body( + self, + environ: t.Optional["WSGIEnvironment"] = None, + scope: t.Optional[dict] = None, + ) -> str: + """Get the HTML body.""" + return ( + '\n' + f"{self.code} {escape(self.name)}\n" + f"

    {escape(self.name)}

    \n" + f"{self.get_description(environ)}\n" + ) + + def get_headers( + self, + environ: t.Optional["WSGIEnvironment"] = None, + scope: t.Optional[dict] = None, + ) -> t.List[t.Tuple[str, str]]: + """Get a list of headers.""" + return [("Content-Type", "text/html; charset=utf-8")] + + def get_response( + self, + environ: t.Optional["WSGIEnvironment"] = None, + scope: t.Optional[dict] = None, + ) -> "Response": + """Get a response object. If one was passed to the exception + it's returned directly. + + :param environ: the optional environ for the request. This + can be used to modify the response depending + on how the request looked like. + :return: a :class:`Response` object or a subclass thereof. + """ + from .wrappers.response import Response as WSGIResponse # noqa: F811 + + if self.response is not None: + return self.response + if environ is not None: + environ = _get_environ(environ) + headers = self.get_headers(environ, scope) + return WSGIResponse(self.get_body(environ, scope), self.code, headers) + + def __call__( + self, environ: "WSGIEnvironment", start_response: "StartResponse" + ) -> t.Iterable[bytes]: + """Call the exception as WSGI application. + + :param environ: the WSGI environment. + :param start_response: the response callable provided by the WSGI + server. + """ + response = t.cast("WSGIResponse", self.get_response(environ)) + return response(environ, start_response) + + def __str__(self) -> str: + code = self.code if self.code is not None else "???" + return f"{code} {self.name}: {self.description}" + + def __repr__(self) -> str: + code = self.code if self.code is not None else "???" + return f"<{type(self).__name__} '{code}: {self.name}'>" + + +class BadRequest(HTTPException): + """*400* `Bad Request` + + Raise if the browser sends something to the application the application + or server cannot handle. + """ + + code = 400 + description = ( + "The browser (or proxy) sent a request that this server could " + "not understand." + ) + + +class BadRequestKeyError(BadRequest, KeyError): + """An exception that is used to signal both a :exc:`KeyError` and a + :exc:`BadRequest`. Used by many of the datastructures. + """ + + _description = BadRequest.description + #: Show the KeyError along with the HTTP error message in the + #: response. This should be disabled in production, but can be + #: useful in a debug mode. + show_exception = False + + def __init__(self, arg: t.Optional[str] = None, *args: t.Any, **kwargs: t.Any): + super().__init__(*args, **kwargs) + + if arg is None: + KeyError.__init__(self) + else: + KeyError.__init__(self, arg) + + @property # type: ignore + def description(self) -> str: # type: ignore + if self.show_exception: + return ( + f"{self._description}\n" + f"{KeyError.__name__}: {KeyError.__str__(self)}" + ) + + return self._description + + @description.setter + def description(self, value: str) -> None: + self._description = value + + +class ClientDisconnected(BadRequest): + """Internal exception that is raised if Werkzeug detects a disconnected + client. Since the client is already gone at that point attempting to + send the error message to the client might not work and might ultimately + result in another exception in the server. Mainly this is here so that + it is silenced by default as far as Werkzeug is concerned. + + Since disconnections cannot be reliably detected and are unspecified + by WSGI to a large extent this might or might not be raised if a client + is gone. + + .. versionadded:: 0.8 + """ + + +class SecurityError(BadRequest): + """Raised if something triggers a security error. This is otherwise + exactly like a bad request error. + + .. versionadded:: 0.9 + """ + + +class BadHost(BadRequest): + """Raised if the submitted host is badly formatted. + + .. versionadded:: 0.11.2 + """ + + +class Unauthorized(HTTPException): + """*401* ``Unauthorized`` + + Raise if the user is not authorized to access a resource. + + The ``www_authenticate`` argument should be used to set the + ``WWW-Authenticate`` header. This is used for HTTP basic auth and + other schemes. Use :class:`~werkzeug.datastructures.WWWAuthenticate` + to create correctly formatted values. Strictly speaking a 401 + response is invalid if it doesn't provide at least one value for + this header, although real clients typically don't care. + + :param description: Override the default message used for the body + of the response. + :param www-authenticate: A single value, or list of values, for the + WWW-Authenticate header(s). + + .. versionchanged:: 2.0 + Serialize multiple ``www_authenticate`` items into multiple + ``WWW-Authenticate`` headers, rather than joining them + into a single value, for better interoperability. + + .. versionchanged:: 0.15.3 + If the ``www_authenticate`` argument is not set, the + ``WWW-Authenticate`` header is not set. + + .. versionchanged:: 0.15.3 + The ``response`` argument was restored. + + .. versionchanged:: 0.15.1 + ``description`` was moved back as the first argument, restoring + its previous position. + + .. versionchanged:: 0.15.0 + ``www_authenticate`` was added as the first argument, ahead of + ``description``. + """ + + code = 401 + description = ( + "The server could not verify that you are authorized to access" + " the URL requested. You either supplied the wrong credentials" + " (e.g. a bad password), or your browser doesn't understand" + " how to supply the credentials required." + ) + + def __init__( + self, + description: t.Optional[str] = None, + response: t.Optional["Response"] = None, + www_authenticate: t.Optional[ + t.Union["WWWAuthenticate", t.Iterable["WWWAuthenticate"]] + ] = None, + ) -> None: + super().__init__(description, response) + + from .datastructures import WWWAuthenticate + + if isinstance(www_authenticate, WWWAuthenticate): + www_authenticate = (www_authenticate,) + + self.www_authenticate = www_authenticate + + def get_headers( + self, + environ: t.Optional["WSGIEnvironment"] = None, + scope: t.Optional[dict] = None, + ) -> t.List[t.Tuple[str, str]]: + headers = super().get_headers(environ, scope) + if self.www_authenticate: + headers.extend(("WWW-Authenticate", str(x)) for x in self.www_authenticate) + return headers + + +class Forbidden(HTTPException): + """*403* `Forbidden` + + Raise if the user doesn't have the permission for the requested resource + but was authenticated. + """ + + code = 403 + description = ( + "You don't have the permission to access the requested" + " resource. It is either read-protected or not readable by the" + " server." + ) + + +class NotFound(HTTPException): + """*404* `Not Found` + + Raise if a resource does not exist and never existed. + """ + + code = 404 + description = ( + "The requested URL was not found on the server. If you entered" + " the URL manually please check your spelling and try again." + ) + + +class MethodNotAllowed(HTTPException): + """*405* `Method Not Allowed` + + Raise if the server used a method the resource does not handle. For + example `POST` if the resource is view only. Especially useful for REST. + + The first argument for this exception should be a list of allowed methods. + Strictly speaking the response would be invalid if you don't provide valid + methods in the header which you can do with that list. + """ + + code = 405 + description = "The method is not allowed for the requested URL." + + def __init__( + self, + valid_methods: t.Optional[t.Iterable[str]] = None, + description: t.Optional[str] = None, + response: t.Optional["Response"] = None, + ) -> None: + """Takes an optional list of valid http methods + starting with werkzeug 0.3 the list will be mandatory.""" + super().__init__(description=description, response=response) + self.valid_methods = valid_methods + + def get_headers( + self, + environ: t.Optional["WSGIEnvironment"] = None, + scope: t.Optional[dict] = None, + ) -> t.List[t.Tuple[str, str]]: + headers = super().get_headers(environ, scope) + if self.valid_methods: + headers.append(("Allow", ", ".join(self.valid_methods))) + return headers + + +class NotAcceptable(HTTPException): + """*406* `Not Acceptable` + + Raise if the server can't return any content conforming to the + `Accept` headers of the client. + """ + + code = 406 + description = ( + "The resource identified by the request is only capable of" + " generating response entities which have content" + " characteristics not acceptable according to the accept" + " headers sent in the request." + ) + + +class RequestTimeout(HTTPException): + """*408* `Request Timeout` + + Raise to signalize a timeout. + """ + + code = 408 + description = ( + "The server closed the network connection because the browser" + " didn't finish the request within the specified time." + ) + + +class Conflict(HTTPException): + """*409* `Conflict` + + Raise to signal that a request cannot be completed because it conflicts + with the current state on the server. + + .. versionadded:: 0.7 + """ + + code = 409 + description = ( + "A conflict happened while processing the request. The" + " resource might have been modified while the request was being" + " processed." + ) + + +class Gone(HTTPException): + """*410* `Gone` + + Raise if a resource existed previously and went away without new location. + """ + + code = 410 + description = ( + "The requested URL is no longer available on this server and" + " there is no forwarding address. If you followed a link from a" + " foreign page, please contact the author of this page." + ) + + +class LengthRequired(HTTPException): + """*411* `Length Required` + + Raise if the browser submitted data but no ``Content-Length`` header which + is required for the kind of processing the server does. + """ + + code = 411 + description = ( + "A request with this method requires a valid Content-" + "Length header." + ) + + +class PreconditionFailed(HTTPException): + """*412* `Precondition Failed` + + Status code used in combination with ``If-Match``, ``If-None-Match``, or + ``If-Unmodified-Since``. + """ + + code = 412 + description = ( + "The precondition on the request for the URL failed positive evaluation." + ) + + +class RequestEntityTooLarge(HTTPException): + """*413* `Request Entity Too Large` + + The status code one should return if the data submitted exceeded a given + limit. + """ + + code = 413 + description = "The data value transmitted exceeds the capacity limit." + + +class RequestURITooLarge(HTTPException): + """*414* `Request URI Too Large` + + Like *413* but for too long URLs. + """ + + code = 414 + description = ( + "The length of the requested URL exceeds the capacity limit for" + " this server. The request cannot be processed." + ) + + +class UnsupportedMediaType(HTTPException): + """*415* `Unsupported Media Type` + + The status code returned if the server is unable to handle the media type + the client transmitted. + """ + + code = 415 + description = ( + "The server does not support the media type transmitted in the request." + ) + + +class RequestedRangeNotSatisfiable(HTTPException): + """*416* `Requested Range Not Satisfiable` + + The client asked for an invalid part of the file. + + .. versionadded:: 0.7 + """ + + code = 416 + description = "The server cannot provide the requested range." + + def __init__( + self, + length: t.Optional[int] = None, + units: str = "bytes", + description: t.Optional[str] = None, + response: t.Optional["Response"] = None, + ) -> None: + """Takes an optional `Content-Range` header value based on ``length`` + parameter. + """ + super().__init__(description=description, response=response) + self.length = length + self.units = units + + def get_headers( + self, + environ: t.Optional["WSGIEnvironment"] = None, + scope: t.Optional[dict] = None, + ) -> t.List[t.Tuple[str, str]]: + headers = super().get_headers(environ, scope) + if self.length is not None: + headers.append(("Content-Range", f"{self.units} */{self.length}")) + return headers + + +class ExpectationFailed(HTTPException): + """*417* `Expectation Failed` + + The server cannot meet the requirements of the Expect request-header. + + .. versionadded:: 0.7 + """ + + code = 417 + description = "The server could not meet the requirements of the Expect header" + + +class ImATeapot(HTTPException): + """*418* `I'm a teapot` + + The server should return this if it is a teapot and someone attempted + to brew coffee with it. + + .. versionadded:: 0.7 + """ + + code = 418 + description = "This server is a teapot, not a coffee machine" + + +class UnprocessableEntity(HTTPException): + """*422* `Unprocessable Entity` + + Used if the request is well formed, but the instructions are otherwise + incorrect. + """ + + code = 422 + description = ( + "The request was well-formed but was unable to be followed due" + " to semantic errors." + ) + + +class Locked(HTTPException): + """*423* `Locked` + + Used if the resource that is being accessed is locked. + """ + + code = 423 + description = "The resource that is being accessed is locked." + + +class FailedDependency(HTTPException): + """*424* `Failed Dependency` + + Used if the method could not be performed on the resource + because the requested action depended on another action and that action failed. + """ + + code = 424 + description = ( + "The method could not be performed on the resource because the" + " requested action depended on another action and that action" + " failed." + ) + + +class PreconditionRequired(HTTPException): + """*428* `Precondition Required` + + The server requires this request to be conditional, typically to prevent + the lost update problem, which is a race condition between two or more + clients attempting to update a resource through PUT or DELETE. By requiring + each client to include a conditional header ("If-Match" or "If-Unmodified- + Since") with the proper value retained from a recent GET request, the + server ensures that each client has at least seen the previous revision of + the resource. + """ + + code = 428 + description = ( + "This request is required to be conditional; try using" + ' "If-Match" or "If-Unmodified-Since".' + ) + + +class _RetryAfter(HTTPException): + """Adds an optional ``retry_after`` parameter which will set the + ``Retry-After`` header. May be an :class:`int` number of seconds or + a :class:`~datetime.datetime`. + """ + + def __init__( + self, + description: t.Optional[str] = None, + response: t.Optional["Response"] = None, + retry_after: t.Optional[t.Union[datetime, int]] = None, + ) -> None: + super().__init__(description, response) + self.retry_after = retry_after + + def get_headers( + self, + environ: t.Optional["WSGIEnvironment"] = None, + scope: t.Optional[dict] = None, + ) -> t.List[t.Tuple[str, str]]: + headers = super().get_headers(environ, scope) + + if self.retry_after: + if isinstance(self.retry_after, datetime): + from .http import http_date + + value = http_date(self.retry_after) + else: + value = str(self.retry_after) + + headers.append(("Retry-After", value)) + + return headers + + +class TooManyRequests(_RetryAfter): + """*429* `Too Many Requests` + + The server is limiting the rate at which this user receives + responses, and this request exceeds that rate. (The server may use + any convenient method to identify users and their request rates). + The server may include a "Retry-After" header to indicate how long + the user should wait before retrying. + + :param retry_after: If given, set the ``Retry-After`` header to this + value. May be an :class:`int` number of seconds or a + :class:`~datetime.datetime`. + + .. versionchanged:: 1.0 + Added ``retry_after`` parameter. + """ + + code = 429 + description = "This user has exceeded an allotted request count. Try again later." + + +class RequestHeaderFieldsTooLarge(HTTPException): + """*431* `Request Header Fields Too Large` + + The server refuses to process the request because the header fields are too + large. One or more individual fields may be too large, or the set of all + headers is too large. + """ + + code = 431 + description = "One or more header fields exceeds the maximum size." + + +class UnavailableForLegalReasons(HTTPException): + """*451* `Unavailable For Legal Reasons` + + This status code indicates that the server is denying access to the + resource as a consequence of a legal demand. + """ + + code = 451 + description = "Unavailable for legal reasons." + + +class InternalServerError(HTTPException): + """*500* `Internal Server Error` + + Raise if an internal server error occurred. This is a good fallback if an + unknown error occurred in the dispatcher. + + .. versionchanged:: 1.0.0 + Added the :attr:`original_exception` attribute. + """ + + code = 500 + description = ( + "The server encountered an internal error and was unable to" + " complete your request. Either the server is overloaded or" + " there is an error in the application." + ) + + def __init__( + self, + description: t.Optional[str] = None, + response: t.Optional["Response"] = None, + original_exception: t.Optional[BaseException] = None, + ) -> None: + #: The original exception that caused this 500 error. Can be + #: used by frameworks to provide context when handling + #: unexpected errors. + self.original_exception = original_exception + super().__init__(description=description, response=response) + + +class NotImplemented(HTTPException): + """*501* `Not Implemented` + + Raise if the application does not support the action requested by the + browser. + """ + + code = 501 + description = "The server does not support the action requested by the browser." + + +class BadGateway(HTTPException): + """*502* `Bad Gateway` + + If you do proxying in your application you should return this status code + if you received an invalid response from the upstream server it accessed + in attempting to fulfill the request. + """ + + code = 502 + description = ( + "The proxy server received an invalid response from an upstream server." + ) + + +class ServiceUnavailable(_RetryAfter): + """*503* `Service Unavailable` + + Status code you should return if a service is temporarily + unavailable. + + :param retry_after: If given, set the ``Retry-After`` header to this + value. May be an :class:`int` number of seconds or a + :class:`~datetime.datetime`. + + .. versionchanged:: 1.0 + Added ``retry_after`` parameter. + """ + + code = 503 + description = ( + "The server is temporarily unable to service your request due" + " to maintenance downtime or capacity problems. Please try" + " again later." + ) + + +class GatewayTimeout(HTTPException): + """*504* `Gateway Timeout` + + Status code you should return if a connection to an upstream server + times out. + """ + + code = 504 + description = "The connection to an upstream server timed out." + + +class HTTPVersionNotSupported(HTTPException): + """*505* `HTTP Version Not Supported` + + The server does not support the HTTP protocol version used in the request. + """ + + code = 505 + description = ( + "The server does not support the HTTP protocol version used in the request." + ) + + +default_exceptions: t.Dict[int, t.Type[HTTPException]] = {} + + +def _find_exceptions() -> None: + for obj in globals().values(): + try: + is_http_exception = issubclass(obj, HTTPException) + except TypeError: + is_http_exception = False + if not is_http_exception or obj.code is None: + continue + old_obj = default_exceptions.get(obj.code, None) + if old_obj is not None and issubclass(obj, old_obj): + continue + default_exceptions[obj.code] = obj + + +_find_exceptions() +del _find_exceptions + + +class Aborter: + """When passed a dict of code -> exception items it can be used as + callable that raises exceptions. If the first argument to the + callable is an integer it will be looked up in the mapping, if it's + a WSGI application it will be raised in a proxy exception. + + The rest of the arguments are forwarded to the exception constructor. + """ + + def __init__( + self, + mapping: t.Optional[t.Dict[int, t.Type[HTTPException]]] = None, + extra: t.Optional[t.Dict[int, t.Type[HTTPException]]] = None, + ) -> None: + if mapping is None: + mapping = default_exceptions + self.mapping = dict(mapping) + if extra is not None: + self.mapping.update(extra) + + def __call__( + self, code: t.Union[int, "Response"], *args: t.Any, **kwargs: t.Any + ) -> "te.NoReturn": + from .sansio.response import Response + + if isinstance(code, Response): + raise HTTPException(response=code) + + if code not in self.mapping: + raise LookupError(f"no exception for {code!r}") + + raise self.mapping[code](*args, **kwargs) + + +def abort( + status: t.Union[int, "Response"], *args: t.Any, **kwargs: t.Any +) -> "te.NoReturn": + """Raises an :py:exc:`HTTPException` for the given status code or WSGI + application. + + If a status code is given, it will be looked up in the list of + exceptions and will raise that exception. If passed a WSGI application, + it will wrap it in a proxy WSGI exception and raise that:: + + abort(404) # 404 Not Found + abort(Response('Hello World')) + + """ + _aborter(status, *args, **kwargs) + + +_aborter: Aborter = Aborter() diff --git a/venv/Lib/site-packages/werkzeug/filesystem.py b/venv/Lib/site-packages/werkzeug/filesystem.py new file mode 100644 index 0000000..36a3d12 --- /dev/null +++ b/venv/Lib/site-packages/werkzeug/filesystem.py @@ -0,0 +1,55 @@ +import codecs +import sys +import typing as t +import warnings + +# We do not trust traditional unixes. +has_likely_buggy_unicode_filesystem = ( + sys.platform.startswith("linux") or "bsd" in sys.platform +) + + +def _is_ascii_encoding(encoding: t.Optional[str]) -> bool: + """Given an encoding this figures out if the encoding is actually ASCII (which + is something we don't actually want in most cases). This is necessary + because ASCII comes under many names such as ANSI_X3.4-1968. + """ + if encoding is None: + return False + try: + return codecs.lookup(encoding).name == "ascii" + except LookupError: + return False + + +class BrokenFilesystemWarning(RuntimeWarning, UnicodeWarning): + """The warning used by Werkzeug to signal a broken filesystem. Will only be + used once per runtime.""" + + +_warned_about_filesystem_encoding = False + + +def get_filesystem_encoding() -> str: + """Returns the filesystem encoding that should be used. Note that this is + different from the Python understanding of the filesystem encoding which + might be deeply flawed. Do not use this value against Python's string APIs + because it might be different. See :ref:`filesystem-encoding` for the exact + behavior. + + The concept of a filesystem encoding in generally is not something you + should rely on. As such if you ever need to use this function except for + writing wrapper code reconsider. + """ + global _warned_about_filesystem_encoding + rv = sys.getfilesystemencoding() + if has_likely_buggy_unicode_filesystem and not rv or _is_ascii_encoding(rv): + if not _warned_about_filesystem_encoding: + warnings.warn( + "Detected a misconfigured UNIX filesystem: Will use" + f" UTF-8 as filesystem encoding instead of {rv!r}", + BrokenFilesystemWarning, + ) + _warned_about_filesystem_encoding = True + return "utf-8" + return rv diff --git a/venv/Lib/site-packages/werkzeug/formparser.py b/venv/Lib/site-packages/werkzeug/formparser.py new file mode 100644 index 0000000..2dcb709 --- /dev/null +++ b/venv/Lib/site-packages/werkzeug/formparser.py @@ -0,0 +1,495 @@ +import typing as t +import warnings +from functools import update_wrapper +from io import BytesIO +from itertools import chain +from typing import Union + +from . import exceptions +from ._internal import _to_str +from .datastructures import FileStorage +from .datastructures import Headers +from .datastructures import MultiDict +from .http import parse_options_header +from .sansio.multipart import Data +from .sansio.multipart import Epilogue +from .sansio.multipart import Field +from .sansio.multipart import File +from .sansio.multipart import MultipartDecoder +from .sansio.multipart import NeedData +from .urls import url_decode_stream +from .wsgi import _make_chunk_iter +from .wsgi import get_content_length +from .wsgi import get_input_stream + +# there are some platforms where SpooledTemporaryFile is not available. +# In that case we need to provide a fallback. +try: + from tempfile import SpooledTemporaryFile +except ImportError: + from tempfile import TemporaryFile + + SpooledTemporaryFile = None # type: ignore + +if t.TYPE_CHECKING: + import typing as te + from _typeshed.wsgi import WSGIEnvironment + + t_parse_result = t.Tuple[t.BinaryIO, MultiDict, MultiDict] + + class TStreamFactory(te.Protocol): + def __call__( + self, + total_content_length: t.Optional[int], + content_type: t.Optional[str], + filename: t.Optional[str], + content_length: t.Optional[int] = None, + ) -> t.BinaryIO: + ... + + +F = t.TypeVar("F", bound=t.Callable[..., t.Any]) + + +def _exhaust(stream: t.BinaryIO) -> None: + bts = stream.read(64 * 1024) + while bts: + bts = stream.read(64 * 1024) + + +def default_stream_factory( + total_content_length: t.Optional[int], + content_type: t.Optional[str], + filename: t.Optional[str], + content_length: t.Optional[int] = None, +) -> t.BinaryIO: + max_size = 1024 * 500 + + if SpooledTemporaryFile is not None: + return t.cast(t.BinaryIO, SpooledTemporaryFile(max_size=max_size, mode="rb+")) + elif total_content_length is None or total_content_length > max_size: + return t.cast(t.BinaryIO, TemporaryFile("rb+")) + + return BytesIO() + + +def parse_form_data( + environ: "WSGIEnvironment", + stream_factory: t.Optional["TStreamFactory"] = None, + charset: str = "utf-8", + errors: str = "replace", + max_form_memory_size: t.Optional[int] = None, + max_content_length: t.Optional[int] = None, + cls: t.Optional[t.Type[MultiDict]] = None, + silent: bool = True, +) -> "t_parse_result": + """Parse the form data in the environ and return it as tuple in the form + ``(stream, form, files)``. You should only call this method if the + transport method is `POST`, `PUT`, or `PATCH`. + + If the mimetype of the data transmitted is `multipart/form-data` the + files multidict will be filled with `FileStorage` objects. If the + mimetype is unknown the input stream is wrapped and returned as first + argument, else the stream is empty. + + This is a shortcut for the common usage of :class:`FormDataParser`. + + Have a look at :doc:`/request_data` for more details. + + .. versionadded:: 0.5 + The `max_form_memory_size`, `max_content_length` and + `cls` parameters were added. + + .. versionadded:: 0.5.1 + The optional `silent` flag was added. + + :param environ: the WSGI environment to be used for parsing. + :param stream_factory: An optional callable that returns a new read and + writeable file descriptor. This callable works + the same as :meth:`Response._get_file_stream`. + :param charset: The character set for URL and url encoded form data. + :param errors: The encoding error behavior. + :param max_form_memory_size: the maximum number of bytes to be accepted for + in-memory stored form data. If the data + exceeds the value specified an + :exc:`~exceptions.RequestEntityTooLarge` + exception is raised. + :param max_content_length: If this is provided and the transmitted data + is longer than this value an + :exc:`~exceptions.RequestEntityTooLarge` + exception is raised. + :param cls: an optional dict class to use. If this is not specified + or `None` the default :class:`MultiDict` is used. + :param silent: If set to False parsing errors will not be caught. + :return: A tuple in the form ``(stream, form, files)``. + """ + return FormDataParser( + stream_factory, + charset, + errors, + max_form_memory_size, + max_content_length, + cls, + silent, + ).parse_from_environ(environ) + + +def exhaust_stream(f: F) -> F: + """Helper decorator for methods that exhausts the stream on return.""" + + def wrapper(self, stream, *args, **kwargs): # type: ignore + try: + return f(self, stream, *args, **kwargs) + finally: + exhaust = getattr(stream, "exhaust", None) + + if exhaust is not None: + exhaust() + else: + while True: + chunk = stream.read(1024 * 64) + + if not chunk: + break + + return update_wrapper(t.cast(F, wrapper), f) + + +class FormDataParser: + """This class implements parsing of form data for Werkzeug. By itself + it can parse multipart and url encoded form data. It can be subclassed + and extended but for most mimetypes it is a better idea to use the + untouched stream and expose it as separate attributes on a request + object. + + .. versionadded:: 0.8 + + :param stream_factory: An optional callable that returns a new read and + writeable file descriptor. This callable works + the same as :meth:`Response._get_file_stream`. + :param charset: The character set for URL and url encoded form data. + :param errors: The encoding error behavior. + :param max_form_memory_size: the maximum number of bytes to be accepted for + in-memory stored form data. If the data + exceeds the value specified an + :exc:`~exceptions.RequestEntityTooLarge` + exception is raised. + :param max_content_length: If this is provided and the transmitted data + is longer than this value an + :exc:`~exceptions.RequestEntityTooLarge` + exception is raised. + :param cls: an optional dict class to use. If this is not specified + or `None` the default :class:`MultiDict` is used. + :param silent: If set to False parsing errors will not be caught. + """ + + def __init__( + self, + stream_factory: t.Optional["TStreamFactory"] = None, + charset: str = "utf-8", + errors: str = "replace", + max_form_memory_size: t.Optional[int] = None, + max_content_length: t.Optional[int] = None, + cls: t.Optional[t.Type[MultiDict]] = None, + silent: bool = True, + ) -> None: + if stream_factory is None: + stream_factory = default_stream_factory + + self.stream_factory = stream_factory + self.charset = charset + self.errors = errors + self.max_form_memory_size = max_form_memory_size + self.max_content_length = max_content_length + + if cls is None: + cls = MultiDict + + self.cls = cls + self.silent = silent + + def get_parse_func( + self, mimetype: str, options: t.Dict[str, str] + ) -> t.Optional[ + t.Callable[ + ["FormDataParser", t.BinaryIO, str, t.Optional[int], t.Dict[str, str]], + "t_parse_result", + ] + ]: + return self.parse_functions.get(mimetype) + + def parse_from_environ(self, environ: "WSGIEnvironment") -> "t_parse_result": + """Parses the information from the environment as form data. + + :param environ: the WSGI environment to be used for parsing. + :return: A tuple in the form ``(stream, form, files)``. + """ + content_type = environ.get("CONTENT_TYPE", "") + content_length = get_content_length(environ) + mimetype, options = parse_options_header(content_type) + return self.parse(get_input_stream(environ), mimetype, content_length, options) + + def parse( + self, + stream: t.BinaryIO, + mimetype: str, + content_length: t.Optional[int], + options: t.Optional[t.Dict[str, str]] = None, + ) -> "t_parse_result": + """Parses the information from the given stream, mimetype, + content length and mimetype parameters. + + :param stream: an input stream + :param mimetype: the mimetype of the data + :param content_length: the content length of the incoming data + :param options: optional mimetype parameters (used for + the multipart boundary for instance) + :return: A tuple in the form ``(stream, form, files)``. + """ + if ( + self.max_content_length is not None + and content_length is not None + and content_length > self.max_content_length + ): + # if the input stream is not exhausted, firefox reports Connection Reset + _exhaust(stream) + raise exceptions.RequestEntityTooLarge() + + if options is None: + options = {} + + parse_func = self.get_parse_func(mimetype, options) + + if parse_func is not None: + try: + return parse_func(self, stream, mimetype, content_length, options) + except ValueError: + if not self.silent: + raise + + return stream, self.cls(), self.cls() + + @exhaust_stream + def _parse_multipart( + self, + stream: t.BinaryIO, + mimetype: str, + content_length: t.Optional[int], + options: t.Dict[str, str], + ) -> "t_parse_result": + parser = MultiPartParser( + self.stream_factory, + self.charset, + self.errors, + max_form_memory_size=self.max_form_memory_size, + cls=self.cls, + ) + boundary = options.get("boundary", "").encode("ascii") + + if not boundary: + raise ValueError("Missing boundary") + + form, files = parser.parse(stream, boundary, content_length) + return stream, form, files + + @exhaust_stream + def _parse_urlencoded( + self, + stream: t.BinaryIO, + mimetype: str, + content_length: t.Optional[int], + options: t.Dict[str, str], + ) -> "t_parse_result": + if ( + self.max_form_memory_size is not None + and content_length is not None + and content_length > self.max_form_memory_size + ): + # if the input stream is not exhausted, firefox reports Connection Reset + _exhaust(stream) + raise exceptions.RequestEntityTooLarge() + + form = url_decode_stream(stream, self.charset, errors=self.errors, cls=self.cls) + return stream, form, self.cls() + + #: mapping of mimetypes to parsing functions + parse_functions: t.Dict[ + str, + t.Callable[ + ["FormDataParser", t.BinaryIO, str, t.Optional[int], t.Dict[str, str]], + "t_parse_result", + ], + ] = { + "multipart/form-data": _parse_multipart, + "application/x-www-form-urlencoded": _parse_urlencoded, + "application/x-url-encoded": _parse_urlencoded, + } + + +def _line_parse(line: str) -> t.Tuple[str, bool]: + """Removes line ending characters and returns a tuple (`stripped_line`, + `is_terminated`). + """ + if line[-2:] == "\r\n": + return line[:-2], True + + elif line[-1:] in {"\r", "\n"}: + return line[:-1], True + + return line, False + + +def parse_multipart_headers(iterable: t.Iterable[bytes]) -> Headers: + """Parses multipart headers from an iterable that yields lines (including + the trailing newline symbol). The iterable has to be newline terminated. + The iterable will stop at the line where the headers ended so it can be + further consumed. + :param iterable: iterable of strings that are newline terminated + """ + warnings.warn( + "'parse_multipart_headers' is deprecated and will be removed in" + " Werkzeug 2.1.", + DeprecationWarning, + stacklevel=2, + ) + result: t.List[t.Tuple[str, str]] = [] + + for b_line in iterable: + line = _to_str(b_line) + line, line_terminated = _line_parse(line) + + if not line_terminated: + raise ValueError("unexpected end of line in multipart header") + + if not line: + break + elif line[0] in " \t" and result: + key, value = result[-1] + result[-1] = (key, f"{value}\n {line[1:]}") + else: + parts = line.split(":", 1) + + if len(parts) == 2: + result.append((parts[0].strip(), parts[1].strip())) + + # we link the list to the headers, no need to create a copy, the + # list was not shared anyways. + return Headers(result) + + +class MultiPartParser: + def __init__( + self, + stream_factory: t.Optional["TStreamFactory"] = None, + charset: str = "utf-8", + errors: str = "replace", + max_form_memory_size: t.Optional[int] = None, + cls: t.Optional[t.Type[MultiDict]] = None, + buffer_size: int = 64 * 1024, + ) -> None: + self.charset = charset + self.errors = errors + self.max_form_memory_size = max_form_memory_size + + if stream_factory is None: + stream_factory = default_stream_factory + + self.stream_factory = stream_factory + + if cls is None: + cls = MultiDict + + self.cls = cls + + self.buffer_size = buffer_size + + def fail(self, message: str) -> "te.NoReturn": + raise ValueError(message) + + def get_part_charset(self, headers: Headers) -> str: + # Figure out input charset for current part + content_type = headers.get("content-type") + + if content_type: + mimetype, ct_params = parse_options_header(content_type) + return ct_params.get("charset", self.charset) + + return self.charset + + def start_file_streaming( + self, event: File, total_content_length: t.Optional[int] + ) -> t.BinaryIO: + content_type = event.headers.get("content-type") + + try: + content_length = int(event.headers["content-length"]) + except (KeyError, ValueError): + content_length = 0 + + container = self.stream_factory( + total_content_length=total_content_length, + filename=event.filename, + content_type=content_type, + content_length=content_length, + ) + return container + + def parse( + self, stream: t.BinaryIO, boundary: bytes, content_length: t.Optional[int] + ) -> t.Tuple[MultiDict, MultiDict]: + container: t.Union[t.BinaryIO, t.List[bytes]] + _write: t.Callable[[bytes], t.Any] + + iterator = chain( + _make_chunk_iter( + stream, + limit=content_length, + buffer_size=self.buffer_size, + ), + [None], + ) + + parser = MultipartDecoder(boundary, self.max_form_memory_size) + + fields = [] + files = [] + + current_part: Union[Field, File] + for data in iterator: + parser.receive_data(data) + event = parser.next_event() + while not isinstance(event, (Epilogue, NeedData)): + if isinstance(event, Field): + current_part = event + container = [] + _write = container.append + elif isinstance(event, File): + current_part = event + container = self.start_file_streaming(event, content_length) + _write = container.write + elif isinstance(event, Data): + _write(event.data) + if not event.more_data: + if isinstance(current_part, Field): + value = b"".join(container).decode( + self.get_part_charset(current_part.headers), self.errors + ) + fields.append((current_part.name, value)) + else: + container = t.cast(t.BinaryIO, container) + container.seek(0) + files.append( + ( + current_part.name, + FileStorage( + container, + current_part.filename, + current_part.name, + headers=current_part.headers, + ), + ) + ) + + event = parser.next_event() + + return self.cls(fields), self.cls(files) diff --git a/venv/Lib/site-packages/werkzeug/http.py b/venv/Lib/site-packages/werkzeug/http.py new file mode 100644 index 0000000..ca48fe2 --- /dev/null +++ b/venv/Lib/site-packages/werkzeug/http.py @@ -0,0 +1,1388 @@ +import base64 +import email.utils +import re +import typing +import typing as t +import warnings +from datetime import date +from datetime import datetime +from datetime import time +from datetime import timedelta +from datetime import timezone +from enum import Enum +from hashlib import sha1 +from time import mktime +from time import struct_time +from urllib.parse import unquote_to_bytes as _unquote +from urllib.request import parse_http_list as _parse_list_header + +from ._internal import _cookie_parse_impl +from ._internal import _cookie_quote +from ._internal import _make_cookie_domain +from ._internal import _to_bytes +from ._internal import _to_str +from ._internal import _wsgi_decoding_dance +from werkzeug._internal import _dt_as_utc + +if t.TYPE_CHECKING: + import typing_extensions as te + from _typeshed.wsgi import WSGIEnvironment + +# for explanation of "media-range", etc. see Sections 5.3.{1,2} of RFC 7231 +_accept_re = re.compile( + r""" + ( # media-range capturing-parenthesis + [^\s;,]+ # type/subtype + (?:[ \t]*;[ \t]* # ";" + (?: # parameter non-capturing-parenthesis + [^\s;,q][^\s;,]* # token that doesn't start with "q" + | # or + q[^\s;,=][^\s;,]* # token that is more than just "q" + ) + )* # zero or more parameters + ) # end of media-range + (?:[ \t]*;[ \t]*q= # weight is a "q" parameter + (\d*(?:\.\d+)?) # qvalue capturing-parentheses + [^,]* # "extension" accept params: who cares? + )? # accept params are optional + """, + re.VERBOSE, +) +_token_chars = frozenset( + "!#$%&'*+-.0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ^_`abcdefghijklmnopqrstuvwxyz|~" +) +_etag_re = re.compile(r'([Ww]/)?(?:"(.*?)"|(.*?))(?:\s*,\s*|$)') +_option_header_piece_re = re.compile( + r""" + ;\s*,?\s* # newlines were replaced with commas + (?P + "[^"\\]*(?:\\.[^"\\]*)*" # quoted string + | + [^\s;,=*]+ # token + ) + (?:\*(?P\d+))? # *1, optional continuation index + \s* + (?: # optionally followed by =value + (?: # equals sign, possibly with encoding + \*\s*=\s* # * indicates extended notation + (?: # optional encoding + (?P[^\s]+?) + '(?P[^\s]*?)' + )? + | + =\s* # basic notation + ) + (?P + "[^"\\]*(?:\\.[^"\\]*)*" # quoted string + | + [^;,]+ # token + )? + )? + \s* + """, + flags=re.VERBOSE, +) +_option_header_start_mime_type = re.compile(r",\s*([^;,\s]+)([;,]\s*.+)?") +_entity_headers = frozenset( + [ + "allow", + "content-encoding", + "content-language", + "content-length", + "content-location", + "content-md5", + "content-range", + "content-type", + "expires", + "last-modified", + ] +) +_hop_by_hop_headers = frozenset( + [ + "connection", + "keep-alive", + "proxy-authenticate", + "proxy-authorization", + "te", + "trailer", + "transfer-encoding", + "upgrade", + ] +) +HTTP_STATUS_CODES = { + 100: "Continue", + 101: "Switching Protocols", + 102: "Processing", + 103: "Early Hints", # see RFC 8297 + 200: "OK", + 201: "Created", + 202: "Accepted", + 203: "Non Authoritative Information", + 204: "No Content", + 205: "Reset Content", + 206: "Partial Content", + 207: "Multi Status", + 208: "Already Reported", # see RFC 5842 + 226: "IM Used", # see RFC 3229 + 300: "Multiple Choices", + 301: "Moved Permanently", + 302: "Found", + 303: "See Other", + 304: "Not Modified", + 305: "Use Proxy", + 306: "Switch Proxy", # unused + 307: "Temporary Redirect", + 308: "Permanent Redirect", + 400: "Bad Request", + 401: "Unauthorized", + 402: "Payment Required", # unused + 403: "Forbidden", + 404: "Not Found", + 405: "Method Not Allowed", + 406: "Not Acceptable", + 407: "Proxy Authentication Required", + 408: "Request Timeout", + 409: "Conflict", + 410: "Gone", + 411: "Length Required", + 412: "Precondition Failed", + 413: "Request Entity Too Large", + 414: "Request URI Too Long", + 415: "Unsupported Media Type", + 416: "Requested Range Not Satisfiable", + 417: "Expectation Failed", + 418: "I'm a teapot", # see RFC 2324 + 421: "Misdirected Request", # see RFC 7540 + 422: "Unprocessable Entity", + 423: "Locked", + 424: "Failed Dependency", + 425: "Too Early", # see RFC 8470 + 426: "Upgrade Required", + 428: "Precondition Required", # see RFC 6585 + 429: "Too Many Requests", + 431: "Request Header Fields Too Large", + 449: "Retry With", # proprietary MS extension + 451: "Unavailable For Legal Reasons", + 500: "Internal Server Error", + 501: "Not Implemented", + 502: "Bad Gateway", + 503: "Service Unavailable", + 504: "Gateway Timeout", + 505: "HTTP Version Not Supported", + 506: "Variant Also Negotiates", # see RFC 2295 + 507: "Insufficient Storage", + 508: "Loop Detected", # see RFC 5842 + 510: "Not Extended", + 511: "Network Authentication Failed", +} + + +class COEP(Enum): + """Cross Origin Embedder Policies""" + + UNSAFE_NONE = "unsafe-none" + REQUIRE_CORP = "require-corp" + + +class COOP(Enum): + """Cross Origin Opener Policies""" + + UNSAFE_NONE = "unsafe-none" + SAME_ORIGIN_ALLOW_POPUPS = "same-origin-allow-popups" + SAME_ORIGIN = "same-origin" + + +def quote_header_value( + value: t.Union[str, int], extra_chars: str = "", allow_token: bool = True +) -> str: + """Quote a header value if necessary. + + .. versionadded:: 0.5 + + :param value: the value to quote. + :param extra_chars: a list of extra characters to skip quoting. + :param allow_token: if this is enabled token values are returned + unchanged. + """ + if isinstance(value, bytes): + value = value.decode("latin1") + value = str(value) + if allow_token: + token_chars = _token_chars | set(extra_chars) + if set(value).issubset(token_chars): + return value + value = value.replace("\\", "\\\\").replace('"', '\\"') + return f'"{value}"' + + +def unquote_header_value(value: str, is_filename: bool = False) -> str: + r"""Unquotes a header value. (Reversal of :func:`quote_header_value`). + This does not use the real unquoting but what browsers are actually + using for quoting. + + .. versionadded:: 0.5 + + :param value: the header value to unquote. + :param is_filename: The value represents a filename or path. + """ + if value and value[0] == value[-1] == '"': + # this is not the real unquoting, but fixing this so that the + # RFC is met will result in bugs with internet explorer and + # probably some other browsers as well. IE for example is + # uploading files with "C:\foo\bar.txt" as filename + value = value[1:-1] + + # if this is a filename and the starting characters look like + # a UNC path, then just return the value without quotes. Using the + # replace sequence below on a UNC path has the effect of turning + # the leading double slash into a single slash and then + # _fix_ie_filename() doesn't work correctly. See #458. + if not is_filename or value[:2] != "\\\\": + return value.replace("\\\\", "\\").replace('\\"', '"') + return value + + +def dump_options_header( + header: t.Optional[str], options: t.Mapping[str, t.Optional[t.Union[str, int]]] +) -> str: + """The reverse function to :func:`parse_options_header`. + + :param header: the header to dump + :param options: a dict of options to append. + """ + segments = [] + if header is not None: + segments.append(header) + for key, value in options.items(): + if value is None: + segments.append(key) + else: + segments.append(f"{key}={quote_header_value(value)}") + return "; ".join(segments) + + +def dump_header( + iterable: t.Union[t.Dict[str, t.Union[str, int]], t.Iterable[str]], + allow_token: bool = True, +) -> str: + """Dump an HTTP header again. This is the reversal of + :func:`parse_list_header`, :func:`parse_set_header` and + :func:`parse_dict_header`. This also quotes strings that include an + equals sign unless you pass it as dict of key, value pairs. + + >>> dump_header({'foo': 'bar baz'}) + 'foo="bar baz"' + >>> dump_header(('foo', 'bar baz')) + 'foo, "bar baz"' + + :param iterable: the iterable or dict of values to quote. + :param allow_token: if set to `False` tokens as values are disallowed. + See :func:`quote_header_value` for more details. + """ + if isinstance(iterable, dict): + items = [] + for key, value in iterable.items(): + if value is None: + items.append(key) + else: + items.append( + f"{key}={quote_header_value(value, allow_token=allow_token)}" + ) + else: + items = [quote_header_value(x, allow_token=allow_token) for x in iterable] + return ", ".join(items) + + +def dump_csp_header(header: "ds.ContentSecurityPolicy") -> str: + """Dump a Content Security Policy header. + + These are structured into policies such as "default-src 'self'; + script-src 'self'". + + .. versionadded:: 1.0.0 + Support for Content Security Policy headers was added. + + """ + return "; ".join(f"{key} {value}" for key, value in header.items()) + + +def parse_list_header(value: str) -> t.List[str]: + """Parse lists as described by RFC 2068 Section 2. + + In particular, parse comma-separated lists where the elements of + the list may include quoted-strings. A quoted-string could + contain a comma. A non-quoted string could have quotes in the + middle. Quotes are removed automatically after parsing. + + It basically works like :func:`parse_set_header` just that items + may appear multiple times and case sensitivity is preserved. + + The return value is a standard :class:`list`: + + >>> parse_list_header('token, "quoted value"') + ['token', 'quoted value'] + + To create a header from the :class:`list` again, use the + :func:`dump_header` function. + + :param value: a string with a list header. + :return: :class:`list` + """ + result = [] + for item in _parse_list_header(value): + if item[:1] == item[-1:] == '"': + item = unquote_header_value(item[1:-1]) + result.append(item) + return result + + +def parse_dict_header(value: str, cls: t.Type[dict] = dict) -> t.Dict[str, str]: + """Parse lists of key, value pairs as described by RFC 2068 Section 2 and + convert them into a python dict (or any other mapping object created from + the type with a dict like interface provided by the `cls` argument): + + >>> d = parse_dict_header('foo="is a fish", bar="as well"') + >>> type(d) is dict + True + >>> sorted(d.items()) + [('bar', 'as well'), ('foo', 'is a fish')] + + If there is no value for a key it will be `None`: + + >>> parse_dict_header('key_without_value') + {'key_without_value': None} + + To create a header from the :class:`dict` again, use the + :func:`dump_header` function. + + .. versionchanged:: 0.9 + Added support for `cls` argument. + + :param value: a string with a dict header. + :param cls: callable to use for storage of parsed results. + :return: an instance of `cls` + """ + result = cls() + if isinstance(value, bytes): + value = value.decode("latin1") + for item in _parse_list_header(value): + if "=" not in item: + result[item] = None + continue + name, value = item.split("=", 1) + if value[:1] == value[-1:] == '"': + value = unquote_header_value(value[1:-1]) + result[name] = value + return result + + +@typing.overload +def parse_options_header( + value: t.Optional[str], multiple: "te.Literal[False]" = False +) -> t.Tuple[str, t.Dict[str, str]]: + ... + + +@typing.overload +def parse_options_header( + value: t.Optional[str], multiple: "te.Literal[True]" +) -> t.Tuple[t.Any, ...]: + ... + + +def parse_options_header( + value: t.Optional[str], multiple: bool = False +) -> t.Union[t.Tuple[str, t.Dict[str, str]], t.Tuple[t.Any, ...]]: + """Parse a ``Content-Type`` like header into a tuple with the content + type and the options: + + >>> parse_options_header('text/html; charset=utf8') + ('text/html', {'charset': 'utf8'}) + + This should not be used to parse ``Cache-Control`` like headers that use + a slightly different format. For these headers use the + :func:`parse_dict_header` function. + + .. versionchanged:: 0.15 + :rfc:`2231` parameter continuations are handled. + + .. versionadded:: 0.5 + + :param value: the header to parse. + :param multiple: Whether try to parse and return multiple MIME types + :return: (mimetype, options) or (mimetype, options, mimetype, options, …) + if multiple=True + """ + if not value: + return "", {} + + result: t.List[t.Any] = [] + + value = "," + value.replace("\n", ",") + while value: + match = _option_header_start_mime_type.match(value) + if not match: + break + result.append(match.group(1)) # mimetype + options: t.Dict[str, str] = {} + # Parse options + rest = match.group(2) + encoding: t.Optional[str] + continued_encoding: t.Optional[str] = None + while rest: + optmatch = _option_header_piece_re.match(rest) + if not optmatch: + break + option, count, encoding, language, option_value = optmatch.groups() + # Continuations don't have to supply the encoding after the + # first line. If we're in a continuation, track the current + # encoding to use for subsequent lines. Reset it when the + # continuation ends. + if not count: + continued_encoding = None + else: + if not encoding: + encoding = continued_encoding + continued_encoding = encoding + option = unquote_header_value(option) + if option_value is not None: + option_value = unquote_header_value(option_value, option == "filename") + if encoding is not None: + option_value = _unquote(option_value).decode(encoding) + if count: + # Continuations append to the existing value. For + # simplicity, this ignores the possibility of + # out-of-order indices, which shouldn't happen anyway. + options[option] = options.get(option, "") + option_value + else: + options[option] = option_value + rest = rest[optmatch.end() :] + result.append(options) + if multiple is False: + return tuple(result) + value = rest + + return tuple(result) if result else ("", {}) + + +_TAnyAccept = t.TypeVar("_TAnyAccept", bound="ds.Accept") + + +@typing.overload +def parse_accept_header(value: t.Optional[str]) -> "ds.Accept": + ... + + +@typing.overload +def parse_accept_header( + value: t.Optional[str], cls: t.Type[_TAnyAccept] +) -> _TAnyAccept: + ... + + +def parse_accept_header( + value: t.Optional[str], cls: t.Optional[t.Type[_TAnyAccept]] = None +) -> _TAnyAccept: + """Parses an HTTP Accept-* header. This does not implement a complete + valid algorithm but one that supports at least value and quality + extraction. + + Returns a new :class:`Accept` object (basically a list of ``(value, quality)`` + tuples sorted by the quality with some additional accessor methods). + + The second parameter can be a subclass of :class:`Accept` that is created + with the parsed values and returned. + + :param value: the accept header string to be parsed. + :param cls: the wrapper class for the return value (can be + :class:`Accept` or a subclass thereof) + :return: an instance of `cls`. + """ + if cls is None: + cls = t.cast(t.Type[_TAnyAccept], ds.Accept) + + if not value: + return cls(None) + + result = [] + for match in _accept_re.finditer(value): + quality_match = match.group(2) + if not quality_match: + quality: float = 1 + else: + quality = max(min(float(quality_match), 1), 0) + result.append((match.group(1), quality)) + return cls(result) + + +_TAnyCC = t.TypeVar("_TAnyCC", bound="ds._CacheControl") +_t_cc_update = t.Optional[t.Callable[[_TAnyCC], None]] + + +@typing.overload +def parse_cache_control_header( + value: t.Optional[str], on_update: _t_cc_update, cls: None = None +) -> "ds.RequestCacheControl": + ... + + +@typing.overload +def parse_cache_control_header( + value: t.Optional[str], on_update: _t_cc_update, cls: t.Type[_TAnyCC] +) -> _TAnyCC: + ... + + +def parse_cache_control_header( + value: t.Optional[str], + on_update: _t_cc_update = None, + cls: t.Optional[t.Type[_TAnyCC]] = None, +) -> _TAnyCC: + """Parse a cache control header. The RFC differs between response and + request cache control, this method does not. It's your responsibility + to not use the wrong control statements. + + .. versionadded:: 0.5 + The `cls` was added. If not specified an immutable + :class:`~werkzeug.datastructures.RequestCacheControl` is returned. + + :param value: a cache control header to be parsed. + :param on_update: an optional callable that is called every time a value + on the :class:`~werkzeug.datastructures.CacheControl` + object is changed. + :param cls: the class for the returned object. By default + :class:`~werkzeug.datastructures.RequestCacheControl` is used. + :return: a `cls` object. + """ + if cls is None: + cls = t.cast(t.Type[_TAnyCC], ds.RequestCacheControl) + + if not value: + return cls((), on_update) + + return cls(parse_dict_header(value), on_update) + + +_TAnyCSP = t.TypeVar("_TAnyCSP", bound="ds.ContentSecurityPolicy") +_t_csp_update = t.Optional[t.Callable[[_TAnyCSP], None]] + + +@typing.overload +def parse_csp_header( + value: t.Optional[str], on_update: _t_csp_update, cls: None = None +) -> "ds.ContentSecurityPolicy": + ... + + +@typing.overload +def parse_csp_header( + value: t.Optional[str], on_update: _t_csp_update, cls: t.Type[_TAnyCSP] +) -> _TAnyCSP: + ... + + +def parse_csp_header( + value: t.Optional[str], + on_update: _t_csp_update = None, + cls: t.Optional[t.Type[_TAnyCSP]] = None, +) -> _TAnyCSP: + """Parse a Content Security Policy header. + + .. versionadded:: 1.0.0 + Support for Content Security Policy headers was added. + + :param value: a csp header to be parsed. + :param on_update: an optional callable that is called every time a value + on the object is changed. + :param cls: the class for the returned object. By default + :class:`~werkzeug.datastructures.ContentSecurityPolicy` is used. + :return: a `cls` object. + """ + if cls is None: + cls = t.cast(t.Type[_TAnyCSP], ds.ContentSecurityPolicy) + + if value is None: + return cls((), on_update) + + items = [] + + for policy in value.split(";"): + policy = policy.strip() + + # Ignore badly formatted policies (no space) + if " " in policy: + directive, value = policy.strip().split(" ", 1) + items.append((directive.strip(), value.strip())) + + return cls(items, on_update) + + +def parse_set_header( + value: t.Optional[str], + on_update: t.Optional[t.Callable[["ds.HeaderSet"], None]] = None, +) -> "ds.HeaderSet": + """Parse a set-like header and return a + :class:`~werkzeug.datastructures.HeaderSet` object: + + >>> hs = parse_set_header('token, "quoted value"') + + The return value is an object that treats the items case-insensitively + and keeps the order of the items: + + >>> 'TOKEN' in hs + True + >>> hs.index('quoted value') + 1 + >>> hs + HeaderSet(['token', 'quoted value']) + + To create a header from the :class:`HeaderSet` again, use the + :func:`dump_header` function. + + :param value: a set header to be parsed. + :param on_update: an optional callable that is called every time a + value on the :class:`~werkzeug.datastructures.HeaderSet` + object is changed. + :return: a :class:`~werkzeug.datastructures.HeaderSet` + """ + if not value: + return ds.HeaderSet(None, on_update) + return ds.HeaderSet(parse_list_header(value), on_update) + + +def parse_authorization_header( + value: t.Optional[str], +) -> t.Optional["ds.Authorization"]: + """Parse an HTTP basic/digest authorization header transmitted by the web + browser. The return value is either `None` if the header was invalid or + not given, otherwise an :class:`~werkzeug.datastructures.Authorization` + object. + + :param value: the authorization header to parse. + :return: a :class:`~werkzeug.datastructures.Authorization` object or `None`. + """ + if not value: + return None + value = _wsgi_decoding_dance(value) + try: + auth_type, auth_info = value.split(None, 1) + auth_type = auth_type.lower() + except ValueError: + return None + if auth_type == "basic": + try: + username, password = base64.b64decode(auth_info).split(b":", 1) + except Exception: + return None + try: + return ds.Authorization( + "basic", + { + "username": _to_str(username, "utf-8"), + "password": _to_str(password, "utf-8"), + }, + ) + except UnicodeDecodeError: + return None + elif auth_type == "digest": + auth_map = parse_dict_header(auth_info) + for key in "username", "realm", "nonce", "uri", "response": + if key not in auth_map: + return None + if "qop" in auth_map: + if not auth_map.get("nc") or not auth_map.get("cnonce"): + return None + return ds.Authorization("digest", auth_map) + return None + + +def parse_www_authenticate_header( + value: t.Optional[str], + on_update: t.Optional[t.Callable[["ds.WWWAuthenticate"], None]] = None, +) -> "ds.WWWAuthenticate": + """Parse an HTTP WWW-Authenticate header into a + :class:`~werkzeug.datastructures.WWWAuthenticate` object. + + :param value: a WWW-Authenticate header to parse. + :param on_update: an optional callable that is called every time a value + on the :class:`~werkzeug.datastructures.WWWAuthenticate` + object is changed. + :return: a :class:`~werkzeug.datastructures.WWWAuthenticate` object. + """ + if not value: + return ds.WWWAuthenticate(on_update=on_update) + try: + auth_type, auth_info = value.split(None, 1) + auth_type = auth_type.lower() + except (ValueError, AttributeError): + return ds.WWWAuthenticate(value.strip().lower(), on_update=on_update) + return ds.WWWAuthenticate(auth_type, parse_dict_header(auth_info), on_update) + + +def parse_if_range_header(value: t.Optional[str]) -> "ds.IfRange": + """Parses an if-range header which can be an etag or a date. Returns + a :class:`~werkzeug.datastructures.IfRange` object. + + .. versionchanged:: 2.0 + If the value represents a datetime, it is timezone-aware. + + .. versionadded:: 0.7 + """ + if not value: + return ds.IfRange() + date = parse_date(value) + if date is not None: + return ds.IfRange(date=date) + # drop weakness information + return ds.IfRange(unquote_etag(value)[0]) + + +def parse_range_header( + value: t.Optional[str], make_inclusive: bool = True +) -> t.Optional["ds.Range"]: + """Parses a range header into a :class:`~werkzeug.datastructures.Range` + object. If the header is missing or malformed `None` is returned. + `ranges` is a list of ``(start, stop)`` tuples where the ranges are + non-inclusive. + + .. versionadded:: 0.7 + """ + if not value or "=" not in value: + return None + + ranges = [] + last_end = 0 + units, rng = value.split("=", 1) + units = units.strip().lower() + + for item in rng.split(","): + item = item.strip() + if "-" not in item: + return None + if item.startswith("-"): + if last_end < 0: + return None + try: + begin = int(item) + except ValueError: + return None + end = None + last_end = -1 + elif "-" in item: + begin_str, end_str = item.split("-", 1) + begin_str = begin_str.strip() + end_str = end_str.strip() + if not begin_str.isdigit(): + return None + begin = int(begin_str) + if begin < last_end or last_end < 0: + return None + if end_str: + if not end_str.isdigit(): + return None + end = int(end_str) + 1 + if begin >= end: + return None + else: + end = None + last_end = end if end is not None else -1 + ranges.append((begin, end)) + + return ds.Range(units, ranges) + + +def parse_content_range_header( + value: t.Optional[str], + on_update: t.Optional[t.Callable[["ds.ContentRange"], None]] = None, +) -> t.Optional["ds.ContentRange"]: + """Parses a range header into a + :class:`~werkzeug.datastructures.ContentRange` object or `None` if + parsing is not possible. + + .. versionadded:: 0.7 + + :param value: a content range header to be parsed. + :param on_update: an optional callable that is called every time a value + on the :class:`~werkzeug.datastructures.ContentRange` + object is changed. + """ + if value is None: + return None + try: + units, rangedef = (value or "").strip().split(None, 1) + except ValueError: + return None + + if "/" not in rangedef: + return None + rng, length_str = rangedef.split("/", 1) + if length_str == "*": + length = None + elif length_str.isdigit(): + length = int(length_str) + else: + return None + + if rng == "*": + return ds.ContentRange(units, None, None, length, on_update=on_update) + elif "-" not in rng: + return None + + start_str, stop_str = rng.split("-", 1) + try: + start = int(start_str) + stop = int(stop_str) + 1 + except ValueError: + return None + + if is_byte_range_valid(start, stop, length): + return ds.ContentRange(units, start, stop, length, on_update=on_update) + + return None + + +def quote_etag(etag: str, weak: bool = False) -> str: + """Quote an etag. + + :param etag: the etag to quote. + :param weak: set to `True` to tag it "weak". + """ + if '"' in etag: + raise ValueError("invalid etag") + etag = f'"{etag}"' + if weak: + etag = f"W/{etag}" + return etag + + +def unquote_etag( + etag: t.Optional[str], +) -> t.Union[t.Tuple[str, bool], t.Tuple[None, None]]: + """Unquote a single etag: + + >>> unquote_etag('W/"bar"') + ('bar', True) + >>> unquote_etag('"bar"') + ('bar', False) + + :param etag: the etag identifier to unquote. + :return: a ``(etag, weak)`` tuple. + """ + if not etag: + return None, None + etag = etag.strip() + weak = False + if etag.startswith(("W/", "w/")): + weak = True + etag = etag[2:] + if etag[:1] == etag[-1:] == '"': + etag = etag[1:-1] + return etag, weak + + +def parse_etags(value: t.Optional[str]) -> "ds.ETags": + """Parse an etag header. + + :param value: the tag header to parse + :return: an :class:`~werkzeug.datastructures.ETags` object. + """ + if not value: + return ds.ETags() + strong = [] + weak = [] + end = len(value) + pos = 0 + while pos < end: + match = _etag_re.match(value, pos) + if match is None: + break + is_weak, quoted, raw = match.groups() + if raw == "*": + return ds.ETags(star_tag=True) + elif quoted: + raw = quoted + if is_weak: + weak.append(raw) + else: + strong.append(raw) + pos = match.end() + return ds.ETags(strong, weak) + + +def generate_etag(data: bytes) -> str: + """Generate an etag for some data. + + .. versionchanged:: 2.0 + Use SHA-1. MD5 may not be available in some environments. + """ + return sha1(data).hexdigest() + + +def parse_date(value: t.Optional[str]) -> t.Optional[datetime]: + """Parse an :rfc:`2822` date into a timezone-aware + :class:`datetime.datetime` object, or ``None`` if parsing fails. + + This is a wrapper for :func:`email.utils.parsedate_to_datetime`. It + returns ``None`` if parsing fails instead of raising an exception, + and always returns a timezone-aware datetime object. If the string + doesn't have timezone information, it is assumed to be UTC. + + :param value: A string with a supported date format. + + .. versionchanged:: 2.0 + Return a timezone-aware datetime object. Use + ``email.utils.parsedate_to_datetime``. + """ + if value is None: + return None + + try: + dt = email.utils.parsedate_to_datetime(value) + except (TypeError, ValueError): + return None + + if dt.tzinfo is None: + return dt.replace(tzinfo=timezone.utc) + + return dt + + +def cookie_date( + expires: t.Optional[t.Union[datetime, date, int, float, struct_time]] = None +) -> str: + """Format a datetime object or timestamp into an :rfc:`2822` date + string for ``Set-Cookie expires``. + + .. deprecated:: 2.0 + Will be removed in Werkzeug 2.1. Use :func:`http_date` instead. + """ + warnings.warn( + "'cookie_date' is deprecated and will be removed in Werkzeug" + " 2.1. Use 'http_date' instead.", + DeprecationWarning, + stacklevel=2, + ) + return http_date(expires) + + +def http_date( + timestamp: t.Optional[t.Union[datetime, date, int, float, struct_time]] = None +) -> str: + """Format a datetime object or timestamp into an :rfc:`2822` date + string. + + This is a wrapper for :func:`email.utils.format_datetime`. It + assumes naive datetime objects are in UTC instead of raising an + exception. + + :param timestamp: The datetime or timestamp to format. Defaults to + the current time. + + .. versionchanged:: 2.0 + Use ``email.utils.format_datetime``. Accept ``date`` objects. + """ + if isinstance(timestamp, date): + if not isinstance(timestamp, datetime): + # Assume plain date is midnight UTC. + timestamp = datetime.combine(timestamp, time(), tzinfo=timezone.utc) + else: + # Ensure datetime is timezone-aware. + timestamp = _dt_as_utc(timestamp) + + return email.utils.format_datetime(timestamp, usegmt=True) + + if isinstance(timestamp, struct_time): + timestamp = mktime(timestamp) + + return email.utils.formatdate(timestamp, usegmt=True) + + +def parse_age(value: t.Optional[str] = None) -> t.Optional[timedelta]: + """Parses a base-10 integer count of seconds into a timedelta. + + If parsing fails, the return value is `None`. + + :param value: a string consisting of an integer represented in base-10 + :return: a :class:`datetime.timedelta` object or `None`. + """ + if not value: + return None + try: + seconds = int(value) + except ValueError: + return None + if seconds < 0: + return None + try: + return timedelta(seconds=seconds) + except OverflowError: + return None + + +def dump_age(age: t.Optional[t.Union[timedelta, int]] = None) -> t.Optional[str]: + """Formats the duration as a base-10 integer. + + :param age: should be an integer number of seconds, + a :class:`datetime.timedelta` object, or, + if the age is unknown, `None` (default). + """ + if age is None: + return None + if isinstance(age, timedelta): + age = int(age.total_seconds()) + else: + age = int(age) + + if age < 0: + raise ValueError("age cannot be negative") + + return str(age) + + +def is_resource_modified( + environ: "WSGIEnvironment", + etag: t.Optional[str] = None, + data: t.Optional[bytes] = None, + last_modified: t.Optional[t.Union[datetime, str]] = None, + ignore_if_range: bool = True, +) -> bool: + """Convenience method for conditional requests. + + :param environ: the WSGI environment of the request to be checked. + :param etag: the etag for the response for comparison. + :param data: or alternatively the data of the response to automatically + generate an etag using :func:`generate_etag`. + :param last_modified: an optional date of the last modification. + :param ignore_if_range: If `False`, `If-Range` header will be taken into + account. + :return: `True` if the resource was modified, otherwise `False`. + + .. versionchanged:: 2.0 + SHA-1 is used to generate an etag value for the data. MD5 may + not be available in some environments. + + .. versionchanged:: 1.0.0 + The check is run for methods other than ``GET`` and ``HEAD``. + """ + if etag is None and data is not None: + etag = generate_etag(data) + elif data is not None: + raise TypeError("both data and etag given") + + unmodified = False + if isinstance(last_modified, str): + last_modified = parse_date(last_modified) + + # HTTP doesn't use microsecond, remove it to avoid false positive + # comparisons. Mark naive datetimes as UTC. + if last_modified is not None: + last_modified = _dt_as_utc(last_modified.replace(microsecond=0)) + + if_range = None + if not ignore_if_range and "HTTP_RANGE" in environ: + # https://tools.ietf.org/html/rfc7233#section-3.2 + # A server MUST ignore an If-Range header field received in a request + # that does not contain a Range header field. + if_range = parse_if_range_header(environ.get("HTTP_IF_RANGE")) + + if if_range is not None and if_range.date is not None: + modified_since: t.Optional[datetime] = if_range.date + else: + modified_since = parse_date(environ.get("HTTP_IF_MODIFIED_SINCE")) + + if modified_since and last_modified and last_modified <= modified_since: + unmodified = True + + if etag: + etag, _ = unquote_etag(etag) + etag = t.cast(str, etag) + + if if_range is not None and if_range.etag is not None: + unmodified = parse_etags(if_range.etag).contains(etag) + else: + if_none_match = parse_etags(environ.get("HTTP_IF_NONE_MATCH")) + if if_none_match: + # https://tools.ietf.org/html/rfc7232#section-3.2 + # "A recipient MUST use the weak comparison function when comparing + # entity-tags for If-None-Match" + unmodified = if_none_match.contains_weak(etag) + + # https://tools.ietf.org/html/rfc7232#section-3.1 + # "Origin server MUST use the strong comparison function when + # comparing entity-tags for If-Match" + if_match = parse_etags(environ.get("HTTP_IF_MATCH")) + if if_match: + unmodified = not if_match.is_strong(etag) + + return not unmodified + + +def remove_entity_headers( + headers: t.Union["ds.Headers", t.List[t.Tuple[str, str]]], + allowed: t.Iterable[str] = ("expires", "content-location"), +) -> None: + """Remove all entity headers from a list or :class:`Headers` object. This + operation works in-place. `Expires` and `Content-Location` headers are + by default not removed. The reason for this is :rfc:`2616` section + 10.3.5 which specifies some entity headers that should be sent. + + .. versionchanged:: 0.5 + added `allowed` parameter. + + :param headers: a list or :class:`Headers` object. + :param allowed: a list of headers that should still be allowed even though + they are entity headers. + """ + allowed = {x.lower() for x in allowed} + headers[:] = [ + (key, value) + for key, value in headers + if not is_entity_header(key) or key.lower() in allowed + ] + + +def remove_hop_by_hop_headers( + headers: t.Union["ds.Headers", t.List[t.Tuple[str, str]]] +) -> None: + """Remove all HTTP/1.1 "Hop-by-Hop" headers from a list or + :class:`Headers` object. This operation works in-place. + + .. versionadded:: 0.5 + + :param headers: a list or :class:`Headers` object. + """ + headers[:] = [ + (key, value) for key, value in headers if not is_hop_by_hop_header(key) + ] + + +def is_entity_header(header: str) -> bool: + """Check if a header is an entity header. + + .. versionadded:: 0.5 + + :param header: the header to test. + :return: `True` if it's an entity header, `False` otherwise. + """ + return header.lower() in _entity_headers + + +def is_hop_by_hop_header(header: str) -> bool: + """Check if a header is an HTTP/1.1 "Hop-by-Hop" header. + + .. versionadded:: 0.5 + + :param header: the header to test. + :return: `True` if it's an HTTP/1.1 "Hop-by-Hop" header, `False` otherwise. + """ + return header.lower() in _hop_by_hop_headers + + +def parse_cookie( + header: t.Union["WSGIEnvironment", str, bytes, None], + charset: str = "utf-8", + errors: str = "replace", + cls: t.Optional[t.Type["ds.MultiDict"]] = None, +) -> "ds.MultiDict[str, str]": + """Parse a cookie from a string or WSGI environ. + + The same key can be provided multiple times, the values are stored + in-order. The default :class:`MultiDict` will have the first value + first, and all values can be retrieved with + :meth:`MultiDict.getlist`. + + :param header: The cookie header as a string, or a WSGI environ dict + with a ``HTTP_COOKIE`` key. + :param charset: The charset for the cookie values. + :param errors: The error behavior for the charset decoding. + :param cls: A dict-like class to store the parsed cookies in. + Defaults to :class:`MultiDict`. + + .. versionchanged:: 1.0.0 + Returns a :class:`MultiDict` instead of a + ``TypeConversionDict``. + + .. versionchanged:: 0.5 + Returns a :class:`TypeConversionDict` instead of a regular dict. + The ``cls`` parameter was added. + """ + if isinstance(header, dict): + header = header.get("HTTP_COOKIE", "") + elif header is None: + header = "" + + # PEP 3333 sends headers through the environ as latin1 decoded + # strings. Encode strings back to bytes for parsing. + if isinstance(header, str): + header = header.encode("latin1", "replace") + + if cls is None: + cls = ds.MultiDict + + def _parse_pairs() -> t.Iterator[t.Tuple[str, str]]: + for key, val in _cookie_parse_impl(header): # type: ignore + key_str = _to_str(key, charset, errors, allow_none_charset=True) + + if not key_str: + continue + + val_str = _to_str(val, charset, errors, allow_none_charset=True) + yield key_str, val_str + + return cls(_parse_pairs()) + + +def dump_cookie( + key: str, + value: t.Union[bytes, str] = "", + max_age: t.Optional[t.Union[timedelta, int]] = None, + expires: t.Optional[t.Union[str, datetime, int, float]] = None, + path: t.Optional[str] = "/", + domain: t.Optional[str] = None, + secure: bool = False, + httponly: bool = False, + charset: str = "utf-8", + sync_expires: bool = True, + max_size: int = 4093, + samesite: t.Optional[str] = None, +) -> str: + """Create a Set-Cookie header without the ``Set-Cookie`` prefix. + + The return value is usually restricted to ascii as the vast majority + of values are properly escaped, but that is no guarantee. It's + tunneled through latin1 as required by :pep:`3333`. + + The return value is not ASCII safe if the key contains unicode + characters. This is technically against the specification but + happens in the wild. It's strongly recommended to not use + non-ASCII values for the keys. + + :param max_age: should be a number of seconds, or `None` (default) if + the cookie should last only as long as the client's + browser session. Additionally `timedelta` objects + are accepted, too. + :param expires: should be a `datetime` object or unix timestamp. + :param path: limits the cookie to a given path, per default it will + span the whole domain. + :param domain: Use this if you want to set a cross-domain cookie. For + example, ``domain=".example.com"`` will set a cookie + that is readable by the domain ``www.example.com``, + ``foo.example.com`` etc. Otherwise, a cookie will only + be readable by the domain that set it. + :param secure: The cookie will only be available via HTTPS + :param httponly: disallow JavaScript to access the cookie. This is an + extension to the cookie standard and probably not + supported by all browsers. + :param charset: the encoding for string values. + :param sync_expires: automatically set expires if max_age is defined + but expires not. + :param max_size: Warn if the final header value exceeds this size. The + default, 4093, should be safely `supported by most browsers + `_. Set to 0 to disable this check. + :param samesite: Limits the scope of the cookie such that it will + only be attached to requests if those requests are same-site. + + .. _`cookie`: http://browsercookielimits.squawky.net/ + + .. versionchanged:: 1.0.0 + The string ``'None'`` is accepted for ``samesite``. + """ + key = _to_bytes(key, charset) + value = _to_bytes(value, charset) + + if path is not None: + from .urls import iri_to_uri + + path = iri_to_uri(path, charset) + + domain = _make_cookie_domain(domain) + + if isinstance(max_age, timedelta): + max_age = int(max_age.total_seconds()) + + if expires is not None: + if not isinstance(expires, str): + expires = http_date(expires) + elif max_age is not None and sync_expires: + expires = http_date(datetime.now(tz=timezone.utc).timestamp() + max_age) + + if samesite is not None: + samesite = samesite.title() + + if samesite not in {"Strict", "Lax", "None"}: + raise ValueError("SameSite must be 'Strict', 'Lax', or 'None'.") + + buf = [key + b"=" + _cookie_quote(value)] + + # XXX: In theory all of these parameters that are not marked with `None` + # should be quoted. Because stdlib did not quote it before I did not + # want to introduce quoting there now. + for k, v, q in ( + (b"Domain", domain, True), + (b"Expires", expires, False), + (b"Max-Age", max_age, False), + (b"Secure", secure, None), + (b"HttpOnly", httponly, None), + (b"Path", path, False), + (b"SameSite", samesite, False), + ): + if q is None: + if v: + buf.append(k) + continue + + if v is None: + continue + + tmp = bytearray(k) + if not isinstance(v, (bytes, bytearray)): + v = _to_bytes(str(v), charset) + if q: + v = _cookie_quote(v) + tmp += b"=" + v + buf.append(bytes(tmp)) + + # The return value will be an incorrectly encoded latin1 header for + # consistency with the headers object. + rv = b"; ".join(buf) + rv = rv.decode("latin1") + + # Warn if the final value of the cookie is larger than the limit. If the + # cookie is too large, then it may be silently ignored by the browser, + # which can be quite hard to debug. + cookie_size = len(rv) + + if max_size and cookie_size > max_size: + value_size = len(value) + warnings.warn( + f"The {key.decode(charset)!r} cookie is too large: the value was" + f" {value_size} bytes but the" + f" header required {cookie_size - value_size} extra bytes. The final size" + f" was {cookie_size} bytes but the limit is {max_size} bytes. Browsers may" + f" silently ignore cookies larger than this.", + stacklevel=2, + ) + + return rv + + +def is_byte_range_valid( + start: t.Optional[int], stop: t.Optional[int], length: t.Optional[int] +) -> bool: + """Checks if a given byte content range is valid for the given length. + + .. versionadded:: 0.7 + """ + if (start is None) != (stop is None): + return False + elif start is None: + return length is None or length >= 0 + elif length is None: + return 0 <= start < stop # type: ignore + elif start >= stop: # type: ignore + return False + return 0 <= start < length + + +# circular dependencies +from . import datastructures as ds diff --git a/venv/Lib/site-packages/werkzeug/local.py b/venv/Lib/site-packages/werkzeug/local.py new file mode 100644 index 0000000..a5a7870 --- /dev/null +++ b/venv/Lib/site-packages/werkzeug/local.py @@ -0,0 +1,666 @@ +import copy +import math +import operator +import sys +import typing as t +import warnings +from functools import partial +from functools import update_wrapper + +from .wsgi import ClosingIterator + +if t.TYPE_CHECKING: + from _typeshed.wsgi import StartResponse + from _typeshed.wsgi import WSGIApplication + from _typeshed.wsgi import WSGIEnvironment + +F = t.TypeVar("F", bound=t.Callable[..., t.Any]) + +try: + from greenlet import getcurrent as _get_ident +except ImportError: + from threading import get_ident as _get_ident + + +def get_ident() -> int: + warnings.warn( + "'get_ident' is deprecated and will be removed in Werkzeug" + " 2.1. Use 'greenlet.getcurrent' or 'threading.get_ident' for" + " previous behavior.", + DeprecationWarning, + stacklevel=2, + ) + return _get_ident() # type: ignore + + +class _CannotUseContextVar(Exception): + pass + + +try: + from contextvars import ContextVar + + if "gevent" in sys.modules or "eventlet" in sys.modules: + # Both use greenlet, so first check it has patched + # ContextVars, Greenlet <0.4.17 does not. + import greenlet + + greenlet_patched = getattr(greenlet, "GREENLET_USE_CONTEXT_VARS", False) + + if not greenlet_patched: + # If Gevent is used, check it has patched ContextVars, + # <20.5 does not. + try: + from gevent.monkey import is_object_patched + except ImportError: + # Gevent isn't used, but Greenlet is and hasn't patched + raise _CannotUseContextVar() + else: + if is_object_patched("threading", "local") and not is_object_patched( + "contextvars", "ContextVar" + ): + raise _CannotUseContextVar() + + +except (ImportError, _CannotUseContextVar): + + class ContextVar: # type: ignore + """A fake ContextVar based on the previous greenlet/threading + ident function. Used on Python 3.6, eventlet, and old versions + of gevent. + """ + + def __init__(self, _name: str) -> None: + self.storage: t.Dict[int, t.Dict[str, t.Any]] = {} + + def get(self, default: t.Dict[str, t.Any]) -> t.Dict[str, t.Any]: + return self.storage.get(_get_ident(), default) + + def set(self, value: t.Dict[str, t.Any]) -> None: + self.storage[_get_ident()] = value + + +def release_local(local: t.Union["Local", "LocalStack"]) -> None: + """Releases the contents of the local for the current context. + This makes it possible to use locals without a manager. + + Example:: + + >>> loc = Local() + >>> loc.foo = 42 + >>> release_local(loc) + >>> hasattr(loc, 'foo') + False + + With this function one can release :class:`Local` objects as well + as :class:`LocalStack` objects. However it is not possible to + release data held by proxies that way, one always has to retain + a reference to the underlying local object in order to be able + to release it. + + .. versionadded:: 0.6.1 + """ + local.__release_local__() + + +class Local: + __slots__ = ("_storage",) + + def __init__(self) -> None: + object.__setattr__(self, "_storage", ContextVar("local_storage")) + + @property + def __storage__(self) -> t.Dict[str, t.Any]: + warnings.warn( + "'__storage__' is deprecated and will be removed in Werkzeug 2.1.", + DeprecationWarning, + stacklevel=2, + ) + return self._storage.get({}) # type: ignore + + @property + def __ident_func__(self) -> t.Callable[[], int]: + warnings.warn( + "'__ident_func__' is deprecated and will be removed in" + " Werkzeug 2.1. It should not be used in Python 3.7+.", + DeprecationWarning, + stacklevel=2, + ) + return _get_ident # type: ignore + + @__ident_func__.setter + def __ident_func__(self, func: t.Callable[[], int]) -> None: + warnings.warn( + "'__ident_func__' is deprecated and will be removed in" + " Werkzeug 2.1. Setting it no longer has any effect.", + DeprecationWarning, + stacklevel=2, + ) + + def __iter__(self) -> t.Iterator[t.Tuple[int, t.Any]]: + return iter(self._storage.get({}).items()) + + def __call__(self, proxy: str) -> "LocalProxy": + """Create a proxy for a name.""" + return LocalProxy(self, proxy) + + def __release_local__(self) -> None: + self._storage.set({}) + + def __getattr__(self, name: str) -> t.Any: + values = self._storage.get({}) + try: + return values[name] + except KeyError: + raise AttributeError(name) + + def __setattr__(self, name: str, value: t.Any) -> None: + values = self._storage.get({}).copy() + values[name] = value + self._storage.set(values) + + def __delattr__(self, name: str) -> None: + values = self._storage.get({}).copy() + try: + del values[name] + self._storage.set(values) + except KeyError: + raise AttributeError(name) + + +class LocalStack: + """This class works similar to a :class:`Local` but keeps a stack + of objects instead. This is best explained with an example:: + + >>> ls = LocalStack() + >>> ls.push(42) + >>> ls.top + 42 + >>> ls.push(23) + >>> ls.top + 23 + >>> ls.pop() + 23 + >>> ls.top + 42 + + They can be force released by using a :class:`LocalManager` or with + the :func:`release_local` function but the correct way is to pop the + item from the stack after using. When the stack is empty it will + no longer be bound to the current context (and as such released). + + By calling the stack without arguments it returns a proxy that resolves to + the topmost item on the stack. + + .. versionadded:: 0.6.1 + """ + + def __init__(self) -> None: + self._local = Local() + + def __release_local__(self) -> None: + self._local.__release_local__() + + @property + def __ident_func__(self) -> t.Callable[[], int]: + return self._local.__ident_func__ + + @__ident_func__.setter + def __ident_func__(self, value: t.Callable[[], int]) -> None: + object.__setattr__(self._local, "__ident_func__", value) + + def __call__(self) -> "LocalProxy": + def _lookup() -> t.Any: + rv = self.top + if rv is None: + raise RuntimeError("object unbound") + return rv + + return LocalProxy(_lookup) + + def push(self, obj: t.Any) -> t.List[t.Any]: + """Pushes a new item to the stack""" + rv = getattr(self._local, "stack", []).copy() + rv.append(obj) + self._local.stack = rv + return rv # type: ignore + + def pop(self) -> t.Any: + """Removes the topmost item from the stack, will return the + old value or `None` if the stack was already empty. + """ + stack = getattr(self._local, "stack", None) + if stack is None: + return None + elif len(stack) == 1: + release_local(self._local) + return stack[-1] + else: + return stack.pop() + + @property + def top(self) -> t.Any: + """The topmost item on the stack. If the stack is empty, + `None` is returned. + """ + try: + return self._local.stack[-1] + except (AttributeError, IndexError): + return None + + +class LocalManager: + """Local objects cannot manage themselves. For that you need a local + manager. You can pass a local manager multiple locals or add them + later y appending them to `manager.locals`. Every time the manager + cleans up, it will clean up all the data left in the locals for this + context. + + .. versionchanged:: 2.0 + ``ident_func`` is deprecated and will be removed in Werkzeug + 2.1. + + .. versionchanged:: 0.6.1 + The :func:`release_local` function can be used instead of a + manager. + + .. versionchanged:: 0.7 + The ``ident_func`` parameter was added. + """ + + def __init__( + self, + locals: t.Optional[t.Iterable[t.Union[Local, LocalStack]]] = None, + ident_func: None = None, + ) -> None: + if locals is None: + self.locals = [] + elif isinstance(locals, Local): + self.locals = [locals] + else: + self.locals = list(locals) + + if ident_func is not None: + warnings.warn( + "'ident_func' is deprecated and will be removed in" + " Werkzeug 2.1. Setting it no longer has any effect.", + DeprecationWarning, + stacklevel=2, + ) + + @property + def ident_func(self) -> t.Callable[[], int]: + warnings.warn( + "'ident_func' is deprecated and will be removed in Werkzeug 2.1.", + DeprecationWarning, + stacklevel=2, + ) + return _get_ident # type: ignore + + @ident_func.setter + def ident_func(self, func: t.Callable[[], int]) -> None: + warnings.warn( + "'ident_func' is deprecated and will be removedin Werkzeug" + " 2.1. Setting it no longer has any effect.", + DeprecationWarning, + stacklevel=2, + ) + + def get_ident(self) -> int: + """Return the context identifier the local objects use internally for + this context. You cannot override this method to change the behavior + but use it to link other context local objects (such as SQLAlchemy's + scoped sessions) to the Werkzeug locals. + + .. deprecated:: 2.0 + Will be removed in Werkzeug 2.1. + + .. versionchanged:: 0.7 + You can pass a different ident function to the local manager that + will then be propagated to all the locals passed to the + constructor. + """ + warnings.warn( + "'get_ident' is deprecated and will be removed in Werkzeug 2.1.", + DeprecationWarning, + stacklevel=2, + ) + return self.ident_func() + + def cleanup(self) -> None: + """Manually clean up the data in the locals for this context. Call + this at the end of the request or use `make_middleware()`. + """ + for local in self.locals: + release_local(local) + + def make_middleware(self, app: "WSGIApplication") -> "WSGIApplication": + """Wrap a WSGI application so that cleaning up happens after + request end. + """ + + def application( + environ: "WSGIEnvironment", start_response: "StartResponse" + ) -> t.Iterable[bytes]: + return ClosingIterator(app(environ, start_response), self.cleanup) + + return application + + def middleware(self, func: "WSGIApplication") -> "WSGIApplication": + """Like `make_middleware` but for decorating functions. + + Example usage:: + + @manager.middleware + def application(environ, start_response): + ... + + The difference to `make_middleware` is that the function passed + will have all the arguments copied from the inner application + (name, docstring, module). + """ + return update_wrapper(self.make_middleware(func), func) + + def __repr__(self) -> str: + return f"<{type(self).__name__} storages: {len(self.locals)}>" + + +class _ProxyLookup: + """Descriptor that handles proxied attribute lookup for + :class:`LocalProxy`. + + :param f: The built-in function this attribute is accessed through. + Instead of looking up the special method, the function call + is redone on the object. + :param fallback: Call this method if the proxy is unbound instead of + raising a :exc:`RuntimeError`. + :param class_value: Value to return when accessed from the class. + Used for ``__doc__`` so building docs still works. + """ + + __slots__ = ("bind_f", "fallback", "class_value", "name") + + def __init__( + self, + f: t.Optional[t.Callable] = None, + fallback: t.Optional[t.Callable] = None, + class_value: t.Optional[t.Any] = None, + ) -> None: + bind_f: t.Optional[t.Callable[["LocalProxy", t.Any], t.Callable]] + + if hasattr(f, "__get__"): + # A Python function, can be turned into a bound method. + + def bind_f(instance: "LocalProxy", obj: t.Any) -> t.Callable: + return f.__get__(obj, type(obj)) # type: ignore + + elif f is not None: + # A C function, use partial to bind the first argument. + + def bind_f(instance: "LocalProxy", obj: t.Any) -> t.Callable: + return partial(f, obj) # type: ignore + + else: + # Use getattr, which will produce a bound method. + bind_f = None + + self.bind_f = bind_f + self.fallback = fallback + self.class_value = class_value + + def __set_name__(self, owner: "LocalProxy", name: str) -> None: + self.name = name + + def __get__(self, instance: "LocalProxy", owner: t.Optional[type] = None) -> t.Any: + if instance is None: + if self.class_value is not None: + return self.class_value + + return self + + try: + obj = instance._get_current_object() + except RuntimeError: + if self.fallback is None: + raise + + return self.fallback.__get__(instance, owner) # type: ignore + + if self.bind_f is not None: + return self.bind_f(instance, obj) + + return getattr(obj, self.name) + + def __repr__(self) -> str: + return f"proxy {self.name}" + + def __call__(self, instance: "LocalProxy", *args: t.Any, **kwargs: t.Any) -> t.Any: + """Support calling unbound methods from the class. For example, + this happens with ``copy.copy``, which does + ``type(x).__copy__(x)``. ``type(x)`` can't be proxied, so it + returns the proxy type and descriptor. + """ + return self.__get__(instance, type(instance))(*args, **kwargs) + + +class _ProxyIOp(_ProxyLookup): + """Look up an augmented assignment method on a proxied object. The + method is wrapped to return the proxy instead of the object. + """ + + __slots__ = () + + def __init__( + self, f: t.Optional[t.Callable] = None, fallback: t.Optional[t.Callable] = None + ) -> None: + super().__init__(f, fallback) + + def bind_f(instance: "LocalProxy", obj: t.Any) -> t.Callable: + def i_op(self: t.Any, other: t.Any) -> "LocalProxy": + f(self, other) # type: ignore + return instance + + return i_op.__get__(obj, type(obj)) # type: ignore + + self.bind_f = bind_f + + +def _l_to_r_op(op: F) -> F: + """Swap the argument order to turn an l-op into an r-op.""" + + def r_op(obj: t.Any, other: t.Any) -> t.Any: + return op(other, obj) + + return t.cast(F, r_op) + + +class LocalProxy: + """A proxy to the object bound to a :class:`Local`. All operations + on the proxy are forwarded to the bound object. If no object is + bound, a :exc:`RuntimeError` is raised. + + .. code-block:: python + + from werkzeug.local import Local + l = Local() + + # a proxy to whatever l.user is set to + user = l("user") + + from werkzeug.local import LocalStack + _request_stack = LocalStack() + + # a proxy to _request_stack.top + request = _request_stack() + + # a proxy to the session attribute of the request proxy + session = LocalProxy(lambda: request.session) + + ``__repr__`` and ``__class__`` are forwarded, so ``repr(x)`` and + ``isinstance(x, cls)`` will look like the proxied object. Use + ``issubclass(type(x), LocalProxy)`` to check if an object is a + proxy. + + .. code-block:: python + + repr(user) # + isinstance(user, User) # True + issubclass(type(user), LocalProxy) # True + + :param local: The :class:`Local` or callable that provides the + proxied object. + :param name: The attribute name to look up on a :class:`Local`. Not + used if a callable is given. + + .. versionchanged:: 2.0 + Updated proxied attributes and methods to reflect the current + data model. + + .. versionchanged:: 0.6.1 + The class can be instantiated with a callable. + """ + + __slots__ = ("__local", "__name", "__wrapped__") + + def __init__( + self, + local: t.Union["Local", t.Callable[[], t.Any]], + name: t.Optional[str] = None, + ) -> None: + object.__setattr__(self, "_LocalProxy__local", local) + object.__setattr__(self, "_LocalProxy__name", name) + + if callable(local) and not hasattr(local, "__release_local__"): + # "local" is a callable that is not an instance of Local or + # LocalManager: mark it as a wrapped function. + object.__setattr__(self, "__wrapped__", local) + + def _get_current_object(self) -> t.Any: + """Return the current object. This is useful if you want the real + object behind the proxy at a time for performance reasons or because + you want to pass the object into a different context. + """ + if not hasattr(self.__local, "__release_local__"): # type: ignore + return self.__local() # type: ignore + + try: + return getattr(self.__local, self.__name) # type: ignore + except AttributeError: + raise RuntimeError(f"no object bound to {self.__name}") # type: ignore + + __doc__ = _ProxyLookup( # type: ignore + class_value=__doc__, fallback=lambda self: type(self).__doc__ + ) + # __del__ should only delete the proxy + __repr__ = _ProxyLookup( # type: ignore + repr, fallback=lambda self: f"<{type(self).__name__} unbound>" + ) + __str__ = _ProxyLookup(str) # type: ignore + __bytes__ = _ProxyLookup(bytes) + __format__ = _ProxyLookup() # type: ignore + __lt__ = _ProxyLookup(operator.lt) + __le__ = _ProxyLookup(operator.le) + __eq__ = _ProxyLookup(operator.eq) # type: ignore + __ne__ = _ProxyLookup(operator.ne) # type: ignore + __gt__ = _ProxyLookup(operator.gt) + __ge__ = _ProxyLookup(operator.ge) + __hash__ = _ProxyLookup(hash) # type: ignore + __bool__ = _ProxyLookup(bool, fallback=lambda self: False) + __getattr__ = _ProxyLookup(getattr) + # __getattribute__ triggered through __getattr__ + __setattr__ = _ProxyLookup(setattr) # type: ignore + __delattr__ = _ProxyLookup(delattr) # type: ignore + __dir__ = _ProxyLookup(dir, fallback=lambda self: []) # type: ignore + # __get__ (proxying descriptor not supported) + # __set__ (descriptor) + # __delete__ (descriptor) + # __set_name__ (descriptor) + # __objclass__ (descriptor) + # __slots__ used by proxy itself + # __dict__ (__getattr__) + # __weakref__ (__getattr__) + # __init_subclass__ (proxying metaclass not supported) + # __prepare__ (metaclass) + __class__ = _ProxyLookup(fallback=lambda self: type(self)) # type: ignore + __instancecheck__ = _ProxyLookup(lambda self, other: isinstance(other, self)) + __subclasscheck__ = _ProxyLookup(lambda self, other: issubclass(other, self)) + # __class_getitem__ triggered through __getitem__ + __call__ = _ProxyLookup(lambda self, *args, **kwargs: self(*args, **kwargs)) + __len__ = _ProxyLookup(len) + __length_hint__ = _ProxyLookup(operator.length_hint) + __getitem__ = _ProxyLookup(operator.getitem) + __setitem__ = _ProxyLookup(operator.setitem) + __delitem__ = _ProxyLookup(operator.delitem) + # __missing__ triggered through __getitem__ + __iter__ = _ProxyLookup(iter) + __next__ = _ProxyLookup(next) + __reversed__ = _ProxyLookup(reversed) + __contains__ = _ProxyLookup(operator.contains) + __add__ = _ProxyLookup(operator.add) + __sub__ = _ProxyLookup(operator.sub) + __mul__ = _ProxyLookup(operator.mul) + __matmul__ = _ProxyLookup(operator.matmul) + __truediv__ = _ProxyLookup(operator.truediv) + __floordiv__ = _ProxyLookup(operator.floordiv) + __mod__ = _ProxyLookup(operator.mod) + __divmod__ = _ProxyLookup(divmod) + __pow__ = _ProxyLookup(pow) + __lshift__ = _ProxyLookup(operator.lshift) + __rshift__ = _ProxyLookup(operator.rshift) + __and__ = _ProxyLookup(operator.and_) + __xor__ = _ProxyLookup(operator.xor) + __or__ = _ProxyLookup(operator.or_) + __radd__ = _ProxyLookup(_l_to_r_op(operator.add)) + __rsub__ = _ProxyLookup(_l_to_r_op(operator.sub)) + __rmul__ = _ProxyLookup(_l_to_r_op(operator.mul)) + __rmatmul__ = _ProxyLookup(_l_to_r_op(operator.matmul)) + __rtruediv__ = _ProxyLookup(_l_to_r_op(operator.truediv)) + __rfloordiv__ = _ProxyLookup(_l_to_r_op(operator.floordiv)) + __rmod__ = _ProxyLookup(_l_to_r_op(operator.mod)) + __rdivmod__ = _ProxyLookup(_l_to_r_op(divmod)) + __rpow__ = _ProxyLookup(_l_to_r_op(pow)) + __rlshift__ = _ProxyLookup(_l_to_r_op(operator.lshift)) + __rrshift__ = _ProxyLookup(_l_to_r_op(operator.rshift)) + __rand__ = _ProxyLookup(_l_to_r_op(operator.and_)) + __rxor__ = _ProxyLookup(_l_to_r_op(operator.xor)) + __ror__ = _ProxyLookup(_l_to_r_op(operator.or_)) + __iadd__ = _ProxyIOp(operator.iadd) + __isub__ = _ProxyIOp(operator.isub) + __imul__ = _ProxyIOp(operator.imul) + __imatmul__ = _ProxyIOp(operator.imatmul) + __itruediv__ = _ProxyIOp(operator.itruediv) + __ifloordiv__ = _ProxyIOp(operator.ifloordiv) + __imod__ = _ProxyIOp(operator.imod) + __ipow__ = _ProxyIOp(operator.ipow) + __ilshift__ = _ProxyIOp(operator.ilshift) + __irshift__ = _ProxyIOp(operator.irshift) + __iand__ = _ProxyIOp(operator.iand) + __ixor__ = _ProxyIOp(operator.ixor) + __ior__ = _ProxyIOp(operator.ior) + __neg__ = _ProxyLookup(operator.neg) + __pos__ = _ProxyLookup(operator.pos) + __abs__ = _ProxyLookup(abs) + __invert__ = _ProxyLookup(operator.invert) + __complex__ = _ProxyLookup(complex) + __int__ = _ProxyLookup(int) + __float__ = _ProxyLookup(float) + __index__ = _ProxyLookup(operator.index) + __round__ = _ProxyLookup(round) + __trunc__ = _ProxyLookup(math.trunc) + __floor__ = _ProxyLookup(math.floor) + __ceil__ = _ProxyLookup(math.ceil) + __enter__ = _ProxyLookup() + __exit__ = _ProxyLookup() + __await__ = _ProxyLookup() + __aiter__ = _ProxyLookup() + __anext__ = _ProxyLookup() + __aenter__ = _ProxyLookup() + __aexit__ = _ProxyLookup() + __copy__ = _ProxyLookup(copy.copy) + __deepcopy__ = _ProxyLookup(copy.deepcopy) + # __getnewargs_ex__ (pickle through proxy not supported) + # __getnewargs__ (pickle) + # __getstate__ (pickle) + # __setstate__ (pickle) + # __reduce__ (pickle) + # __reduce_ex__ (pickle) diff --git a/venv/Lib/site-packages/werkzeug/middleware/__init__.py b/venv/Lib/site-packages/werkzeug/middleware/__init__.py new file mode 100644 index 0000000..6ddcf7f --- /dev/null +++ b/venv/Lib/site-packages/werkzeug/middleware/__init__.py @@ -0,0 +1,22 @@ +""" +Middleware +========== + +A WSGI middleware is a WSGI application that wraps another application +in order to observe or change its behavior. Werkzeug provides some +middleware for common use cases. + +.. toctree:: + :maxdepth: 1 + + proxy_fix + shared_data + dispatcher + http_proxy + lint + profiler + +The :doc:`interactive debugger ` is also a middleware that can +be applied manually, although it is typically used automatically with +the :doc:`development server `. +""" diff --git a/venv/Lib/site-packages/werkzeug/middleware/__pycache__/__init__.cpython-39.pyc b/venv/Lib/site-packages/werkzeug/middleware/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f95f6bb2bdeb883e6dbec824de90e9c981434869 GIT binary patch literal 674 zcmYjPO^e$w5Owy_AlUyf*M-)hSF;T*J+!5tu!V(UP{!88QrDJ*Bs)p({W1HO_S$3V zsXg`7v9fG4ATjfvMxOU9JUGq&8Q>;IFvai|sdLld3?@Uuiljw=Z_69qO_3r*Z dt^P||NolM}QkMR3x4ayj8hcvL@9sCR;vaPp*~b6? literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/werkzeug/middleware/__pycache__/dispatcher.cpython-39.pyc b/venv/Lib/site-packages/werkzeug/middleware/__pycache__/dispatcher.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e390ff459ed139b093c55a34e52bf35de466834b GIT binary patch literal 2733 zcmZ`*TW=Fb6yBRJSsaQgs)|aLM!e8S!Js0wk_#1Spd^%ph@`5pqHf1K<78oP%#0IE zohPW&|3E9jWB<~=_6hYLptkCFX5-jgy4KFl%sF$e-#KHxxEMNk+Q0nu(^<^^q{aN> zV{sj?Uc(@qz9U@WrEcGo-UDBKft8;IS)<=@i4tJm>^JSafqARna-Ca_Xo}V|N3@=L z{kHJ7ouxLvA2?39QkH2FbCVP~yOHRUn|NQU@Y;KyFx=TsG|LhpQhCUgWU-RmNXO;UonFwphR5hy4|xsig|lf$7@GB@n|dv_Sj z+t=MNTw!sMWiVMj<{3$XRfj4uumPrQ*QRXoJ;~<$TV{t)C)v0dv3-6Z84m|MekgOn zR<>X?zMKtJ0o9n2I80cQv)lZDZ^tSr4XeE=;4w^Rsu+>V5InRTsNzt=Aq6Y(0dLNf zd^D2U8YYsdDbLZY4X zL`1+W>DV4}!@5;RkSxWLj7rlw*WqzEbgvhN3=ib8YjeQcIDyt@)~RTkk7f3=JwS&p zkV_YOIGjWfusly7+lWNp8S!W-4A*m3szQun!uqmBL6+?88)R4;$ptWx1J>|`LQkod zY;9*}tB$T>sXUPBa!6(o%xdr89E3g|(~c?L0DhfS%?4Zp%D`$`)OFTnNOX7txoLcX zV4F|LW6B;AvP_FHjvgjRC$Jz(4rCpdSS@`yq$H4~+MtHxW2@k&^~0YWsiNcedFSli>oOg zX&JskuW~k*+-h;#aAodET^8WN#s}0Q=E`!qbu&LmP<$C`J*LC+PZNvlc=a6&(&@Ws zgPw5vzUT;F1`ir|o3bVRp?A(X=Lk;(zxkpen$H?&cP-HdeL;qxEMPwrr=A7?96m$UH7DcXP>81Pax{0J(vd>2S95hok6DeZ8^T z+uc&d1E62;-YsRmR%CMbK;{R#cap)bPK>-*;trsGyn86s!zXgIcWHLNU7C7=_FRsu zb`&LfVxs5@glk-%j?-wmPd>ddz24LeLER~@B0fbv!Oj*`6UwX8C|G48bzB#!4f-2_ zpba`{1Mw>+6ZeI^`CocRzVMC$;ZOVnM{Q30iSxk4tV`dCf6h5-Jo<6szwm@Ff|uSd z_>Y?4Za#M=!E@(#|0sBLhIn5%_N)(y4J*ANJj`2@*2Mo9`b6U@PT=d}3yz8Rl>Rw1 zH~R})D?4k^`sTNHE9bR)=@p8_>7UwOy|=!#6K$?++*}GOe@_~l9^_f0Zj4ROQd=#6 zSuN6V8bj68_G;}f5b6+LTo{zu{sQ`uiIAbJf3`s9NphY>HE)#j41ZEcgdI+i93&@3 zPl}?iu+pDkaQu!Ncx^Yt&-2c>XS_3B$L+Z7|4&&hf$-HKfutvjsxXQ&+#D&*J5ls# z#M5I+D~d!BW7{=V`))~B3s0-yMiQIK*G8$1HErEU#RsXZ8iO(ZE~$^OJ7@oi6RH8i z5?%}awi|Sue>~4~KXkpnI}3r^{Q0GW)(~L{hm?CGZ#hv?GH>1_Ho2%cgZ{F&k rjzE-pUyAOb-b*Y1Z57sdBt`GDplV;OUHzJJs>%F-wY?_7hM)f*?p7q3 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/werkzeug/middleware/__pycache__/http_proxy.cpython-39.pyc b/venv/Lib/site-packages/werkzeug/middleware/__pycache__/http_proxy.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..10edb6b28f2a8ce7c54915e05e91019f70943dc9 GIT binary patch literal 6778 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 B{r~^~ literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/werkzeug/middleware/__pycache__/profiler.cpython-39.pyc b/venv/Lib/site-packages/werkzeug/middleware/__pycache__/profiler.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a2a74d1658d32ef68b7ae33954c2510166812366 GIT binary patch literal 4926 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 O&BJTw;ME%4_WuC9O^R6n literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/werkzeug/middleware/__pycache__/proxy_fix.cpython-39.pyc b/venv/Lib/site-packages/werkzeug/middleware/__pycache__/proxy_fix.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8018a4fa7932de674f81d634ceb673932b9833f5 GIT binary patch literal 6155 zcmb7I%X8bt83({ONXd^nPMo&sPTOh2RATIQI_;=&Cyp#XrirAHG>yY?2ZXpQ5f%wB zi=||`@Fh;~y(gWCkNq=x?xm+5aP2A6f1vfD>F-;-NJ@5WV7LVK@$L8gec!@eTCxpX z^*{Z)_qR6<<6rbLdzJ9=K7QdI8g6tAZgOjAb}e4w8&-HFyKiBZw=r&B_w!!PX@wvepPpq!ZtzDySyT8FE_Py0xfjn^~7o48hwhSIl z*)8v(ef@ctZSM`dkc|R9845O*!Li3h$Xqt^I3EfS6HEg{f_=Pu`z{my zu_ptc?Yp7iEb#4KZ+8D)j}3&&g`_1|57G}kQ1*OwfMtpW2h8=EJ01_czNJ)bt7F^UJlHv=ebILsD6p~*XG zD79+G-u3*xz|vH;nYH`SBse*6j|5*~(_q3za2!oy5S+ls{xqix?;_O^c#*4WRlUi}r@ zhB!in_8PoR_@P*1AMD;-{l)6~(1q&luMwHX=GPY5wi#a-yD}8c&jKUFAx( zBf@cj_2@W9VQ{1H->hpR35*e3s83{PuLfS;$1mJRBaE&IIIy_cE%6JyEb^OIc=gK~ zukrflm2L(7)dw|EzhY$nyzym;+r0UCx!d3^zJz{TG_M$Z8U5$@dGxn<1;B7Iu5VL} zKq>BzvH1-h#ag*%1~EGo#0$Y|GhBH9laXe

    el3{6|bZ=N1N3Ag}@hZxw|_-Jcy z!?|(u=1#BIdCm%OuYzLDNqFdWCUWS6{ec*X=dZgHgsNC}XK!!YxwEyq*Xyld=iQB+ z-*4Rk%pbmtP-ko^P2e-F&F#EY|1@dh2Iv!t^h!h9j!-C7*^_{!hdnsF~4nN7_ zM+wB^LQtHFCyLXBIP#R1Z6-62r12qSh^F@hP`r9Y9Ln&K^-0MgPp1N6}XO-6e| z4r`j91T5!(GzI2VggrP(fLQc=Kx?`=z=&kYFoUv8r~-m5KoO0Qcj_IpGy@Fp^-hF5 zdL$-?9S!^rwAUfZ+K7yM{mk>$5Wwz;Tyu@l_S)BH9Tl>jNOzo=H=X^^ISAw$+sxr| zK~W6D;9)k&X@6cI0@hUUYGhI;Um!Gq3%?qj#OW8vjNz)UMrK<(_aYhXtlvUtCX8m) zw4FM7>JUKfe!#`*{xIkt0k+3eH9-B4FV;yqH+7K&3ac}3L}C?yPoYcKj~D=%ygAL4 zfv<}i6bagMHS&;q>GFtD=PKze^TR}Bq61%H*K_i3<8_5;|G0jQ9>_G;ueB4FAr8uq z5ETs4sedRqM7q|wnu~K&jHN({1Lr2O*#NG*FHqn}F$#_`y^#DJY)$^!>mg}7G}fpN-glRvTS82HwHmxQ8OVpTY3E1q{DSQ8CrYcRY>jj^p{Ba-2Vac1U~~ zjjEa5k1k**U4C}5dF@hMOXDl9=-80wu&7_6rRi11f3B4SpYwq{fOX>@Np96iS~OvhmusP zD+i~?k(;Lrnq5#t2GiMe4g@yamLy+^OGDwe%kn&)X}bX z(};|(02>d8PUDIB)OuWtYLZ8_$mHgG*(g!B^=Lh+%g>@ZMy-65_H63WM%0l1h#D9z zVYKm8l@64kX-76+z5ZJ07pVL3sRSPo=raWY z3LPEB@MpBy$Eyz8rSl67=M?foPgZIru1PudxhdcIBkHlo*};>-A#Tnbl%JhjI046v z{A~N^^$e_wLRlY=;^xeOH-Di6E?$Z=C$h|U$;-b-&G)H!i<%!$!_agu>8b`cWpAO*9%r#jjiaNg%#-A~fSnIxiYW?=k#;v>e+ReCx z;Eb!2F-O%V-ymx9nK7<}FpQFyX(-Q^in4KB)MW~@00BfXZ_B``tR;;-tI0tm?y%4;2pB826josUV`()#aZmd Y_4g88{Fc-d61taPuUjpPbdJ09e+gF;Hvj+t literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/werkzeug/middleware/__pycache__/shared_data.cpython-39.pyc b/venv/Lib/site-packages/werkzeug/middleware/__pycache__/shared_data.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cc89631495143cfce0a0f32e9ee8d03ba2c7c50d GIT binary patch literal 9829 zcmbVSOKcoRdhXZE^bBYC67}-4*Dq-{;z(;J_AX;PvT507EK`Ch$JvRJndWp4*7yNRs(yVa3=uh+}niE_v`PE*nSwr1) z%Kl_;syW5=ia*_(Y0hwc0`=MEEZ3|4k=|T$j_Wo5Xzy6_7}qELrB0Jum!173Z*~ zY5&FEOU;)skLt{z{&MqWuFs} zdl0#mZwUj-bu~2|J9cBQ=Te=1hhaeV(!1NS8!cTz)yN)~Z15pod-Yol@1p%@Hz-^MuvZB3fcx`K(KR*-xQhT2)u=R4mIx^MjlOc$ z|HLtJw4=Edcfu_>np3({A{DqYu)RtTYV8;_Z&B}A_?zo=d5lhEXTd>`eC2s z&4_gm{(l`*c`fpwZ;LTjfmBcFlgtmJ#Li;wC#T1dh4-zcN&k*Sbsj6lQ=nbR!(qg z0o*hQqzyvy>Ac~1BCvbz=@!Ga)vBMdPGxV@M}&6ja{4Rlu9Zm;66#tp+TGB1Af9%+ zk)xQAW%U(STDwcI+9g@Yr zzHsAs8^U(G1K)B60y_K1rmeaiG8C3gI~+K+53^wPT*pI)+&EAQ!q^fnHVcNXAx)gs zu>;HYqYykYTfJ}qrFOt2FH3}YCtDlnf;z2<7ViZE*NQ{pi$_A}xh8`abHfBlI8u@p zZ|=dK6Z&8$od^v0)*x8N1U{xF7TpN=0(HF2RK}>Eu>jb>@}%5HgJEznFYGH=hmhsd zY+|{9s3Es+scW_Sn`Hr6l19)v z$JOBf3{_TAK=t&g-uBqaPZ22Pv&VrFi(tfo+dwkNjJMnJ9RM=hNKfu<`w;OqujcZ0 zypFBUR+0(q3YH)C$wu40)popy{a72L1au|_>y7MJP9g?yLbUsq6kfb;#}@4$0o3kv+$i!$Q&^}Z`z?1B3PJnUu{-N7?-vXa0K!bS z-v@%x8-!q_vrhW4?%GgC>?){6ux!~%76(G0Z-XPXs-!TdWbi%Uz) zBiRNrdWh#=~=CH>=~Rm;g?s>lO4Puq50`2OO_X7z&6v{3dN?iI>4Gp z?K|PXcXGY7R<}tiu-{mR^%eQ8?quoQ`L678iB1XW5$_?`3Q}7D`8#*k%>#Ys210k;q zMiWM!_6_Ui$-1QYtI!(e8spp4LmjQr6dA7z3^ogrU109U)Pm$pk%r`ewATrPQ<%aI z2qOHj9gv9=syfR$jua;2Ebg+nDtVID&Nqc626HpFg788RI! z>#|@KA{Uk&_Lx+Y_B+#}Lv$Y5p#^v<=xVlcu#*{Vh{zz=g#QbqII^8YFp&qW?BOeNVwRvt* z1IrJ?%?K81lMGet-jLxAO_}q2hF9Hg5903pk6|xIc!eQx9BeDl*Dnt2ozq zeZ<&1JpQr#e}^2-HGc0A3cO4C#$i=FVEM|ozx)S9xp(48%NNs#kn5#X$AGkyK^Dh{ z%y_2-CY#m{QBP+c5qDZd_%RScrv!TWMQ79kZXI{@uP72VQCiBHf>KQklv+|ismDg5 z#f4o>{4mk>i+7bBEz#an{&eH2wo}?M@zjh{J=7;pF`EugrObhKsGVO_ zClMUM9nsNb4-D)nyIK{bzEPLIL6NFAzR|>SJQUAR@f?bK%Zm$FFN<))?Zna5OYjvR zhCTNxqMGYhFM6w2BSh2lnG24t=F#%m15=&Fi5x9A`rE1brf>IF9sBLup#Gn@mEqYH z4yX^G5gIKV&AhnPYP{)(9XQRmQIT)f=fqLGCyr5Zf{K$U(uxPyLLp_xO%1YgX#vdj zqO^cxfbXWW^32C^Ps83gqFxA2+H7L$bi(W+5FrCWJpZT~s>OkNPDTHD>?=4?} zW4RziDAFRZ>jn-BjyTEB&*CYtRf2SjIulGa zu?pSQ;vvB-1PTT5oz^HMC5H)>==H_#pii8k2PUx04+Qa;>VvqG>Ru41B|M0{p(_QX zj>(3_@*+n6HSWknp{S;MOr8Agu<=;ow1^K^JgU1=%s&AESzDwUkxxt6JAZ|Sl)O58 z=DUbLS?rS_kr_^;9CN`xYz_&@EO@dbF-5`2&P_avM|ERdc&j#IbW)S| z@JKw50#ITUF`gJoBXQlKF)yJQzW&&iJY;76cX7Q>1etsTwXbmJqI&83OiV*oQzQPbn%9`d*FfbBhfzr=k;Y;Kg} z%1H&k6JzyqT+P!}ceGpTE$x=k)g9e2ZdP~nz`^@9dVj}=CpV_x84b`^xIV+>EcNVZ zceUGvF{)3V0iN@geyeb+*ey5(IZHw+Q(R7ukl;s&Lxf=P1=vPX4=IUptZ3*Z$TL14FO=cfBMg(4Ut3VdUMQfI(cKc8!eP2u^9~ z(hA1};$=J(WMy(_(dfD^mZ|?k6cn%$^u#CB!?J!IwZF$5QA^QH)zB*N;Z$nsBz(Kc zwd#GNsHv6v!#Dn)yKyKu)G*Zu;WyEE2;qp5pN#N5RV+(aMJSKX6aEu*udt6uRa#Nj zfzlxzFt_`Mb%1bwOB+Xi-B=bMftN>6LJ}Q8ogC&aY<3LDAA&jyUR=c39OVY(x}@+A zTj_tNL1d-HWfVXW?Xp;*f_7{SO7qlw4+ZNG$F$;8>iZ)U^+H-hP{3gff41PcQZq|v zi`1YDC5Ih#Rs1o%Hrn!Ep%oEVhqv6oh8qau@~y8Qk-lBWNtL}fgBdn`b8UcZXIde< zCe6&FS0-jKWD;6*Q+w<3`9Ae?raAsJKeSYgDi_>ZtuK?ud>c z3I($OdoV@x=IH{xD~+l5ApUP#UeK{f-5~`#Zr;60BjE_)k~x{0Sw)aLb0E88RC7e zL;W}`!7)`T6pgLJ=#hHVixRG*{SpjP1}-5LP$Rbn6XfAX3i<&4wBG-~9ZjPk?2)l4 zY6cDx$F$*3o+#mwJTtUCZ0vy!8wABF6$CdyYAhTSFo3+<%nr>jJhzp6ni?IH@vQu!h#P~{a^hD#b@Rjm)k@4UYq#pe2!OwJM zSlk*$PH+C6I*$!|8U1Ntl|F-}MNYQ5g6wZvK8V+e84P7nZK5uGDtc60L(w#mT<&Z} zgPs)MXiMqSCbSvfM_X=euieMkh~i-dCqqLu9vB8rgzE5xCx~JIO-(E$MF+;4 z6D>w!a1vwz36Ufz?Bk0moXW+d;HYmv*4PU@K`0<75ke`O0(L?99A8ZzdLN(puO-(I zGE%4(eTg(oAt`~<-=J1R3J75&g16GXNxew#loQhdmHt11!-}KB-IWt#SGlV1RCXqI zs*Vv)>>@B*Q+H|@QN?Hc%lNJBOmc!}BB_FozB8575NZ~aDIlTvwGMZNud&G_T4idj zX;?!op2GZ7$%Iq+LjQ#ty`EI|r|%*hOz@>PsqJbil2dS@wfz~V8c)QtyGk;7oSYk{ ziGm70sZq)ZwIj|XC6Jt{crK~Z7-u@3NG7P(MX1hHXC&45DCVu~lC7FuzNen$e9^GL z3B=Su_Qh=)7XX=i>I?U3i}dkgei0wfMCcFAo_%8;pNrlanrE#3U=?4_>T0uw|4K5`~eT@yEeh_onQ9af1hz7Wh){$x0rw=TNDTD(52 zT?EDo#*A`6D5ysGvZ?(GT&}>_^l=t^rDV1N=JDl94vmr=?~_G$f{ijBIm) z4Nm5OCnDDtoppJ>VnavT+pID#F5+!0zR65-HC4M}r0DS892rJwDa(1M2C4JI%8q;x za$|n0h5sI-L=?Wr0Adoy^Qw#+DPmMluvwo)UqzimNzaOiCn>O)yk9I#V?6AG_7t40 zp%qcD{8oEllq%{IGe>e>xVXBH1m;b!k|YQ`d;&Q#?$8UimvwbWN0Xb&J3NQ?je zt9p96gg<@u;+Zu01WwvZ s7eQ-(oM4gbl>m4f;8db>D2$2;q{+W3{1E>k*ckiMo>9*Lo!q1U4;vyf&j0`b literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/werkzeug/middleware/dispatcher.py b/venv/Lib/site-packages/werkzeug/middleware/dispatcher.py new file mode 100644 index 0000000..ace1c75 --- /dev/null +++ b/venv/Lib/site-packages/werkzeug/middleware/dispatcher.py @@ -0,0 +1,78 @@ +""" +Application Dispatcher +====================== + +This middleware creates a single WSGI application that dispatches to +multiple other WSGI applications mounted at different URL paths. + +A common example is writing a Single Page Application, where you have a +backend API and a frontend written in JavaScript that does the routing +in the browser rather than requesting different pages from the server. +The frontend is a single HTML and JS file that should be served for any +path besides "/api". + +This example dispatches to an API app under "/api", an admin app +under "/admin", and an app that serves frontend files for all other +requests:: + + app = DispatcherMiddleware(serve_frontend, { + '/api': api_app, + '/admin': admin_app, + }) + +In production, you might instead handle this at the HTTP server level, +serving files or proxying to application servers based on location. The +API and admin apps would each be deployed with a separate WSGI server, +and the static files would be served directly by the HTTP server. + +.. autoclass:: DispatcherMiddleware + +:copyright: 2007 Pallets +:license: BSD-3-Clause +""" +import typing as t + +if t.TYPE_CHECKING: + from _typeshed.wsgi import StartResponse + from _typeshed.wsgi import WSGIApplication + from _typeshed.wsgi import WSGIEnvironment + + +class DispatcherMiddleware: + """Combine multiple applications as a single WSGI application. + Requests are dispatched to an application based on the path it is + mounted under. + + :param app: The WSGI application to dispatch to if the request + doesn't match a mounted path. + :param mounts: Maps path prefixes to applications for dispatching. + """ + + def __init__( + self, + app: "WSGIApplication", + mounts: t.Optional[t.Dict[str, "WSGIApplication"]] = None, + ) -> None: + self.app = app + self.mounts = mounts or {} + + def __call__( + self, environ: "WSGIEnvironment", start_response: "StartResponse" + ) -> t.Iterable[bytes]: + script = environ.get("PATH_INFO", "") + path_info = "" + + while "/" in script: + if script in self.mounts: + app = self.mounts[script] + break + + script, last_item = script.rsplit("/", 1) + path_info = f"/{last_item}{path_info}" + else: + app = self.mounts.get(script, self.app) + + original_script_name = environ.get("SCRIPT_NAME", "") + environ["SCRIPT_NAME"] = original_script_name + script + environ["PATH_INFO"] = path_info + return app(environ, start_response) diff --git a/venv/Lib/site-packages/werkzeug/middleware/http_proxy.py b/venv/Lib/site-packages/werkzeug/middleware/http_proxy.py new file mode 100644 index 0000000..1cde458 --- /dev/null +++ b/venv/Lib/site-packages/werkzeug/middleware/http_proxy.py @@ -0,0 +1,230 @@ +""" +Basic HTTP Proxy +================ + +.. autoclass:: ProxyMiddleware + +:copyright: 2007 Pallets +:license: BSD-3-Clause +""" +import typing as t +from http import client + +from ..datastructures import EnvironHeaders +from ..http import is_hop_by_hop_header +from ..urls import url_parse +from ..urls import url_quote +from ..wsgi import get_input_stream + +if t.TYPE_CHECKING: + from _typeshed.wsgi import StartResponse + from _typeshed.wsgi import WSGIApplication + from _typeshed.wsgi import WSGIEnvironment + + +class ProxyMiddleware: + """Proxy requests under a path to an external server, routing other + requests to the app. + + This middleware can only proxy HTTP requests, as HTTP is the only + protocol handled by the WSGI server. Other protocols, such as + WebSocket requests, cannot be proxied at this layer. This should + only be used for development, in production a real proxy server + should be used. + + The middleware takes a dict mapping a path prefix to a dict + describing the host to be proxied to:: + + app = ProxyMiddleware(app, { + "/static/": { + "target": "http://127.0.0.1:5001/", + } + }) + + Each host has the following options: + + ``target``: + The target URL to dispatch to. This is required. + ``remove_prefix``: + Whether to remove the prefix from the URL before dispatching it + to the target. The default is ``False``. + ``host``: + ``""`` (default): + The host header is automatically rewritten to the URL of the + target. + ``None``: + The host header is unmodified from the client request. + Any other value: + The host header is overwritten with the value. + ``headers``: + A dictionary of headers to be sent with the request to the + target. The default is ``{}``. + ``ssl_context``: + A :class:`ssl.SSLContext` defining how to verify requests if the + target is HTTPS. The default is ``None``. + + In the example above, everything under ``"/static/"`` is proxied to + the server on port 5001. The host header is rewritten to the target, + and the ``"/static/"`` prefix is removed from the URLs. + + :param app: The WSGI application to wrap. + :param targets: Proxy target configurations. See description above. + :param chunk_size: Size of chunks to read from input stream and + write to target. + :param timeout: Seconds before an operation to a target fails. + + .. versionadded:: 0.14 + """ + + def __init__( + self, + app: "WSGIApplication", + targets: t.Mapping[str, t.Dict[str, t.Any]], + chunk_size: int = 2 << 13, + timeout: int = 10, + ) -> None: + def _set_defaults(opts: t.Dict[str, t.Any]) -> t.Dict[str, t.Any]: + opts.setdefault("remove_prefix", False) + opts.setdefault("host", "") + opts.setdefault("headers", {}) + opts.setdefault("ssl_context", None) + return opts + + self.app = app + self.targets = { + f"/{k.strip('/')}/": _set_defaults(v) for k, v in targets.items() + } + self.chunk_size = chunk_size + self.timeout = timeout + + def proxy_to( + self, opts: t.Dict[str, t.Any], path: str, prefix: str + ) -> "WSGIApplication": + target = url_parse(opts["target"]) + host = t.cast(str, target.ascii_host) + + def application( + environ: "WSGIEnvironment", start_response: "StartResponse" + ) -> t.Iterable[bytes]: + headers = list(EnvironHeaders(environ).items()) + headers[:] = [ + (k, v) + for k, v in headers + if not is_hop_by_hop_header(k) + and k.lower() not in ("content-length", "host") + ] + headers.append(("Connection", "close")) + + if opts["host"] == "": + headers.append(("Host", host)) + elif opts["host"] is None: + headers.append(("Host", environ["HTTP_HOST"])) + else: + headers.append(("Host", opts["host"])) + + headers.extend(opts["headers"].items()) + remote_path = path + + if opts["remove_prefix"]: + remote_path = remote_path[len(prefix) :].lstrip("/") + remote_path = f"{target.path.rstrip('/')}/{remote_path}" + + content_length = environ.get("CONTENT_LENGTH") + chunked = False + + if content_length not in ("", None): + headers.append(("Content-Length", content_length)) # type: ignore + elif content_length is not None: + headers.append(("Transfer-Encoding", "chunked")) + chunked = True + + try: + if target.scheme == "http": + con = client.HTTPConnection( + host, target.port or 80, timeout=self.timeout + ) + elif target.scheme == "https": + con = client.HTTPSConnection( + host, + target.port or 443, + timeout=self.timeout, + context=opts["ssl_context"], + ) + else: + raise RuntimeError( + "Target scheme must be 'http' or 'https', got" + f" {target.scheme!r}." + ) + + con.connect() + remote_url = url_quote(remote_path) + querystring = environ["QUERY_STRING"] + + if querystring: + remote_url = f"{remote_url}?{querystring}" + + con.putrequest(environ["REQUEST_METHOD"], remote_url, skip_host=True) + + for k, v in headers: + if k.lower() == "connection": + v = "close" + + con.putheader(k, v) + + con.endheaders() + stream = get_input_stream(environ) + + while True: + data = stream.read(self.chunk_size) + + if not data: + break + + if chunked: + con.send(b"%x\r\n%s\r\n" % (len(data), data)) + else: + con.send(data) + + resp = con.getresponse() + except OSError: + from ..exceptions import BadGateway + + return BadGateway()(environ, start_response) + + start_response( + f"{resp.status} {resp.reason}", + [ + (k.title(), v) + for k, v in resp.getheaders() + if not is_hop_by_hop_header(k) + ], + ) + + def read() -> t.Iterator[bytes]: + while True: + try: + data = resp.read(self.chunk_size) + except OSError: + break + + if not data: + break + + yield data + + return read() + + return application + + def __call__( + self, environ: "WSGIEnvironment", start_response: "StartResponse" + ) -> t.Iterable[bytes]: + path = environ["PATH_INFO"] + app = self.app + + for prefix, opts in self.targets.items(): + if path.startswith(prefix): + app = self.proxy_to(opts, path, prefix) + break + + return app(environ, start_response) diff --git a/venv/Lib/site-packages/werkzeug/middleware/lint.py b/venv/Lib/site-packages/werkzeug/middleware/lint.py new file mode 100644 index 0000000..80c423d --- /dev/null +++ b/venv/Lib/site-packages/werkzeug/middleware/lint.py @@ -0,0 +1,420 @@ +""" +WSGI Protocol Linter +==================== + +This module provides a middleware that performs sanity checks on the +behavior of the WSGI server and application. It checks that the +:pep:`3333` WSGI spec is properly implemented. It also warns on some +common HTTP errors such as non-empty responses for 304 status codes. + +.. autoclass:: LintMiddleware + +:copyright: 2007 Pallets +:license: BSD-3-Clause +""" +import typing as t +from types import TracebackType +from urllib.parse import urlparse +from warnings import warn + +from ..datastructures import Headers +from ..http import is_entity_header +from ..wsgi import FileWrapper + +if t.TYPE_CHECKING: + from _typeshed.wsgi import StartResponse + from _typeshed.wsgi import WSGIApplication + from _typeshed.wsgi import WSGIEnvironment + + +class WSGIWarning(Warning): + """Warning class for WSGI warnings.""" + + +class HTTPWarning(Warning): + """Warning class for HTTP warnings.""" + + +def check_type(context: str, obj: object, need: t.Type = str) -> None: + if type(obj) is not need: + warn( + f"{context!r} requires {need.__name__!r}, got {type(obj).__name__!r}.", + WSGIWarning, + stacklevel=3, + ) + + +class InputStream: + def __init__(self, stream: t.BinaryIO) -> None: + self._stream = stream + + def read(self, *args: t.Any) -> bytes: + if len(args) == 0: + warn( + "WSGI does not guarantee an EOF marker on the input stream, thus making" + " calls to 'wsgi.input.read()' unsafe. Conforming servers may never" + " return from this call.", + WSGIWarning, + stacklevel=2, + ) + elif len(args) != 1: + warn( + "Too many parameters passed to 'wsgi.input.read()'.", + WSGIWarning, + stacklevel=2, + ) + return self._stream.read(*args) + + def readline(self, *args: t.Any) -> bytes: + if len(args) == 0: + warn( + "Calls to 'wsgi.input.readline()' without arguments are unsafe. Use" + " 'wsgi.input.read()' instead.", + WSGIWarning, + stacklevel=2, + ) + elif len(args) == 1: + warn( + "'wsgi.input.readline()' was called with a size hint. WSGI does not" + " support this, although it's available on all major servers.", + WSGIWarning, + stacklevel=2, + ) + else: + raise TypeError("Too many arguments passed to 'wsgi.input.readline()'.") + return self._stream.readline(*args) + + def __iter__(self) -> t.Iterator[bytes]: + try: + return iter(self._stream) + except TypeError: + warn("'wsgi.input' is not iterable.", WSGIWarning, stacklevel=2) + return iter(()) + + def close(self) -> None: + warn("The application closed the input stream!", WSGIWarning, stacklevel=2) + self._stream.close() + + +class ErrorStream: + def __init__(self, stream: t.TextIO) -> None: + self._stream = stream + + def write(self, s: str) -> None: + check_type("wsgi.error.write()", s, str) + self._stream.write(s) + + def flush(self) -> None: + self._stream.flush() + + def writelines(self, seq: t.Iterable[str]) -> None: + for line in seq: + self.write(line) + + def close(self) -> None: + warn("The application closed the error stream!", WSGIWarning, stacklevel=2) + self._stream.close() + + +class GuardedWrite: + def __init__(self, write: t.Callable[[bytes], None], chunks: t.List[int]) -> None: + self._write = write + self._chunks = chunks + + def __call__(self, s: bytes) -> None: + check_type("write()", s, bytes) + self._write(s) + self._chunks.append(len(s)) + + +class GuardedIterator: + def __init__( + self, + iterator: t.Iterable[bytes], + headers_set: t.Tuple[int, Headers], + chunks: t.List[int], + ) -> None: + self._iterator = iterator + self._next = iter(iterator).__next__ + self.closed = False + self.headers_set = headers_set + self.chunks = chunks + + def __iter__(self) -> "GuardedIterator": + return self + + def __next__(self) -> bytes: + if self.closed: + warn("Iterated over closed 'app_iter'.", WSGIWarning, stacklevel=2) + + rv = self._next() + + if not self.headers_set: + warn( + "The application returned before it started the response.", + WSGIWarning, + stacklevel=2, + ) + + check_type("application iterator items", rv, bytes) + self.chunks.append(len(rv)) + return rv + + def close(self) -> None: + self.closed = True + + if hasattr(self._iterator, "close"): + self._iterator.close() # type: ignore + + if self.headers_set: + status_code, headers = self.headers_set + bytes_sent = sum(self.chunks) + content_length = headers.get("content-length", type=int) + + if status_code == 304: + for key, _value in headers: + key = key.lower() + if key not in ("expires", "content-location") and is_entity_header( + key + ): + warn( + f"Entity header {key!r} found in 304 response.", HTTPWarning + ) + if bytes_sent: + warn("304 responses must not have a body.", HTTPWarning) + elif 100 <= status_code < 200 or status_code == 204: + if content_length != 0: + warn( + f"{status_code} responses must have an empty content length.", + HTTPWarning, + ) + if bytes_sent: + warn(f"{status_code} responses must not have a body.", HTTPWarning) + elif content_length is not None and content_length != bytes_sent: + warn( + "Content-Length and the number of bytes sent to the" + " client do not match.", + WSGIWarning, + ) + + def __del__(self) -> None: + if not self.closed: + try: + warn( + "Iterator was garbage collected before it was closed.", WSGIWarning + ) + except Exception: + pass + + +class LintMiddleware: + """Warns about common errors in the WSGI and HTTP behavior of the + server and wrapped application. Some of the issues it checks are: + + - invalid status codes + - non-bytes sent to the WSGI server + - strings returned from the WSGI application + - non-empty conditional responses + - unquoted etags + - relative URLs in the Location header + - unsafe calls to wsgi.input + - unclosed iterators + + Error information is emitted using the :mod:`warnings` module. + + :param app: The WSGI application to wrap. + + .. code-block:: python + + from werkzeug.middleware.lint import LintMiddleware + app = LintMiddleware(app) + """ + + def __init__(self, app: "WSGIApplication") -> None: + self.app = app + + def check_environ(self, environ: "WSGIEnvironment") -> None: + if type(environ) is not dict: + warn( + "WSGI environment is not a standard Python dict.", + WSGIWarning, + stacklevel=4, + ) + for key in ( + "REQUEST_METHOD", + "SERVER_NAME", + "SERVER_PORT", + "wsgi.version", + "wsgi.input", + "wsgi.errors", + "wsgi.multithread", + "wsgi.multiprocess", + "wsgi.run_once", + ): + if key not in environ: + warn( + f"Required environment key {key!r} not found", + WSGIWarning, + stacklevel=3, + ) + if environ["wsgi.version"] != (1, 0): + warn("Environ is not a WSGI 1.0 environ.", WSGIWarning, stacklevel=3) + + script_name = environ.get("SCRIPT_NAME", "") + path_info = environ.get("PATH_INFO", "") + + if script_name and script_name[0] != "/": + warn( + f"'SCRIPT_NAME' does not start with a slash: {script_name!r}", + WSGIWarning, + stacklevel=3, + ) + + if path_info and path_info[0] != "/": + warn( + f"'PATH_INFO' does not start with a slash: {path_info!r}", + WSGIWarning, + stacklevel=3, + ) + + def check_start_response( + self, + status: str, + headers: t.List[t.Tuple[str, str]], + exc_info: t.Optional[ + t.Tuple[t.Type[BaseException], BaseException, TracebackType] + ], + ) -> t.Tuple[int, Headers]: + check_type("status", status, str) + status_code_str = status.split(None, 1)[0] + + if len(status_code_str) != 3 or not status_code_str.isdigit(): + warn("Status code must be three digits.", WSGIWarning, stacklevel=3) + + if len(status) < 4 or status[3] != " ": + warn( + f"Invalid value for status {status!r}. Valid status strings are three" + " digits, a space and a status explanation.", + WSGIWarning, + stacklevel=3, + ) + + status_code = int(status_code_str) + + if status_code < 100: + warn("Status code < 100 detected.", WSGIWarning, stacklevel=3) + + if type(headers) is not list: + warn("Header list is not a list.", WSGIWarning, stacklevel=3) + + for item in headers: + if type(item) is not tuple or len(item) != 2: + warn("Header items must be 2-item tuples.", WSGIWarning, stacklevel=3) + name, value = item + if type(name) is not str or type(value) is not str: + warn( + "Header keys and values must be strings.", WSGIWarning, stacklevel=3 + ) + if name.lower() == "status": + warn( + "The status header is not supported due to" + " conflicts with the CGI spec.", + WSGIWarning, + stacklevel=3, + ) + + if exc_info is not None and not isinstance(exc_info, tuple): + warn("Invalid value for exc_info.", WSGIWarning, stacklevel=3) + + headers = Headers(headers) + self.check_headers(headers) + + return status_code, headers + + def check_headers(self, headers: Headers) -> None: + etag = headers.get("etag") + + if etag is not None: + if etag.startswith(("W/", "w/")): + if etag.startswith("w/"): + warn( + "Weak etag indicator should be upper case.", + HTTPWarning, + stacklevel=4, + ) + + etag = etag[2:] + + if not (etag[:1] == etag[-1:] == '"'): + warn("Unquoted etag emitted.", HTTPWarning, stacklevel=4) + + location = headers.get("location") + + if location is not None: + if not urlparse(location).netloc: + warn( + "Absolute URLs required for location header.", + HTTPWarning, + stacklevel=4, + ) + + def check_iterator(self, app_iter: t.Iterable[bytes]) -> None: + if isinstance(app_iter, bytes): + warn( + "The application returned a bytestring. The response will send one" + " character at a time to the client, which will kill performance." + " Return a list or iterable instead.", + WSGIWarning, + stacklevel=3, + ) + + def __call__(self, *args: t.Any, **kwargs: t.Any) -> t.Iterable[bytes]: + if len(args) != 2: + warn("A WSGI app takes two arguments.", WSGIWarning, stacklevel=2) + + if kwargs: + warn( + "A WSGI app does not take keyword arguments.", WSGIWarning, stacklevel=2 + ) + + environ: "WSGIEnvironment" = args[0] + start_response: "StartResponse" = args[1] + + self.check_environ(environ) + environ["wsgi.input"] = InputStream(environ["wsgi.input"]) + environ["wsgi.errors"] = ErrorStream(environ["wsgi.errors"]) + + # Hook our own file wrapper in so that applications will always + # iterate to the end and we can check the content length. + environ["wsgi.file_wrapper"] = FileWrapper + + headers_set: t.List[t.Any] = [] + chunks: t.List[int] = [] + + def checking_start_response( + *args: t.Any, **kwargs: t.Any + ) -> t.Callable[[bytes], None]: + if len(args) not in {2, 3}: + warn( + f"Invalid number of arguments: {len(args)}, expected 2 or 3.", + WSGIWarning, + stacklevel=2, + ) + + if kwargs: + warn("'start_response' does not take keyword arguments.", WSGIWarning) + + status: str = args[0] + headers: t.List[t.Tuple[str, str]] = args[1] + exc_info: t.Optional[ + t.Tuple[t.Type[BaseException], BaseException, TracebackType] + ] = (args[2] if len(args) == 3 else None) + + headers_set[:] = self.check_start_response(status, headers, exc_info) + return GuardedWrite(start_response(status, headers, exc_info), chunks) + + app_iter = self.app(environ, t.cast("StartResponse", checking_start_response)) + self.check_iterator(app_iter) + return GuardedIterator( + app_iter, t.cast(t.Tuple[int, Headers], headers_set), chunks + ) diff --git a/venv/Lib/site-packages/werkzeug/middleware/profiler.py b/venv/Lib/site-packages/werkzeug/middleware/profiler.py new file mode 100644 index 0000000..0992f8f --- /dev/null +++ b/venv/Lib/site-packages/werkzeug/middleware/profiler.py @@ -0,0 +1,139 @@ +""" +Application Profiler +==================== + +This module provides a middleware that profiles each request with the +:mod:`cProfile` module. This can help identify bottlenecks in your code +that may be slowing down your application. + +.. autoclass:: ProfilerMiddleware + +:copyright: 2007 Pallets +:license: BSD-3-Clause +""" +import os.path +import sys +import time +import typing as t +from pstats import Stats + +try: + from cProfile import Profile +except ImportError: + from profile import Profile # type: ignore + +if t.TYPE_CHECKING: + from _typeshed.wsgi import StartResponse + from _typeshed.wsgi import WSGIApplication + from _typeshed.wsgi import WSGIEnvironment + + +class ProfilerMiddleware: + """Wrap a WSGI application and profile the execution of each + request. Responses are buffered so that timings are more exact. + + If ``stream`` is given, :class:`pstats.Stats` are written to it + after each request. If ``profile_dir`` is given, :mod:`cProfile` + data files are saved to that directory, one file per request. + + The filename can be customized by passing ``filename_format``. If + it is a string, it will be formatted using :meth:`str.format` with + the following fields available: + + - ``{method}`` - The request method; GET, POST, etc. + - ``{path}`` - The request path or 'root' should one not exist. + - ``{elapsed}`` - The elapsed time of the request. + - ``{time}`` - The time of the request. + + If it is a callable, it will be called with the WSGI ``environ`` + dict and should return a filename. + + :param app: The WSGI application to wrap. + :param stream: Write stats to this stream. Disable with ``None``. + :param sort_by: A tuple of columns to sort stats by. See + :meth:`pstats.Stats.sort_stats`. + :param restrictions: A tuple of restrictions to filter stats by. See + :meth:`pstats.Stats.print_stats`. + :param profile_dir: Save profile data files to this directory. + :param filename_format: Format string for profile data file names, + or a callable returning a name. See explanation above. + + .. code-block:: python + + from werkzeug.middleware.profiler import ProfilerMiddleware + app = ProfilerMiddleware(app) + + .. versionchanged:: 0.15 + Stats are written even if ``profile_dir`` is given, and can be + disable by passing ``stream=None``. + + .. versionadded:: 0.15 + Added ``filename_format``. + + .. versionadded:: 0.9 + Added ``restrictions`` and ``profile_dir``. + """ + + def __init__( + self, + app: "WSGIApplication", + stream: t.TextIO = sys.stdout, + sort_by: t.Iterable[str] = ("time", "calls"), + restrictions: t.Iterable[t.Union[str, int, float]] = (), + profile_dir: t.Optional[str] = None, + filename_format: str = "{method}.{path}.{elapsed:.0f}ms.{time:.0f}.prof", + ) -> None: + self._app = app + self._stream = stream + self._sort_by = sort_by + self._restrictions = restrictions + self._profile_dir = profile_dir + self._filename_format = filename_format + + def __call__( + self, environ: "WSGIEnvironment", start_response: "StartResponse" + ) -> t.Iterable[bytes]: + response_body: t.List[bytes] = [] + + def catching_start_response(status, headers, exc_info=None): # type: ignore + start_response(status, headers, exc_info) + return response_body.append + + def runapp() -> None: + app_iter = self._app( + environ, t.cast("StartResponse", catching_start_response) + ) + response_body.extend(app_iter) + + if hasattr(app_iter, "close"): + app_iter.close() # type: ignore + + profile = Profile() + start = time.time() + profile.runcall(runapp) + body = b"".join(response_body) + elapsed = time.time() - start + + if self._profile_dir is not None: + if callable(self._filename_format): + filename = self._filename_format(environ) + else: + filename = self._filename_format.format( + method=environ["REQUEST_METHOD"], + path=environ["PATH_INFO"].strip("/").replace("/", ".") or "root", + elapsed=elapsed * 1000.0, + time=time.time(), + ) + filename = os.path.join(self._profile_dir, filename) + profile.dump_stats(filename) + + if self._stream is not None: + stats = Stats(profile, stream=self._stream) + stats.sort_stats(*self._sort_by) + print("-" * 80, file=self._stream) + path_info = environ.get("PATH_INFO", "") + print(f"PATH: {path_info!r}", file=self._stream) + stats.print_stats(*self._restrictions) + print(f"{'-' * 80}\n", file=self._stream) + + return [body] diff --git a/venv/Lib/site-packages/werkzeug/middleware/proxy_fix.py b/venv/Lib/site-packages/werkzeug/middleware/proxy_fix.py new file mode 100644 index 0000000..e90b1b3 --- /dev/null +++ b/venv/Lib/site-packages/werkzeug/middleware/proxy_fix.py @@ -0,0 +1,187 @@ +""" +X-Forwarded-For Proxy Fix +========================= + +This module provides a middleware that adjusts the WSGI environ based on +``X-Forwarded-`` headers that proxies in front of an application may +set. + +When an application is running behind a proxy server, WSGI may see the +request as coming from that server rather than the real client. Proxies +set various headers to track where the request actually came from. + +This middleware should only be used if the application is actually +behind such a proxy, and should be configured with the number of proxies +that are chained in front of it. Not all proxies set all the headers. +Since incoming headers can be faked, you must set how many proxies are +setting each header so the middleware knows what to trust. + +.. autoclass:: ProxyFix + +:copyright: 2007 Pallets +:license: BSD-3-Clause +""" +import typing as t + +from ..http import parse_list_header + +if t.TYPE_CHECKING: + from _typeshed.wsgi import StartResponse + from _typeshed.wsgi import WSGIApplication + from _typeshed.wsgi import WSGIEnvironment + + +class ProxyFix: + """Adjust the WSGI environ based on ``X-Forwarded-`` that proxies in + front of the application may set. + + - ``X-Forwarded-For`` sets ``REMOTE_ADDR``. + - ``X-Forwarded-Proto`` sets ``wsgi.url_scheme``. + - ``X-Forwarded-Host`` sets ``HTTP_HOST``, ``SERVER_NAME``, and + ``SERVER_PORT``. + - ``X-Forwarded-Port`` sets ``HTTP_HOST`` and ``SERVER_PORT``. + - ``X-Forwarded-Prefix`` sets ``SCRIPT_NAME``. + + You must tell the middleware how many proxies set each header so it + knows what values to trust. It is a security issue to trust values + that came from the client rather than a proxy. + + The original values of the headers are stored in the WSGI + environ as ``werkzeug.proxy_fix.orig``, a dict. + + :param app: The WSGI application to wrap. + :param x_for: Number of values to trust for ``X-Forwarded-For``. + :param x_proto: Number of values to trust for ``X-Forwarded-Proto``. + :param x_host: Number of values to trust for ``X-Forwarded-Host``. + :param x_port: Number of values to trust for ``X-Forwarded-Port``. + :param x_prefix: Number of values to trust for + ``X-Forwarded-Prefix``. + + .. code-block:: python + + from werkzeug.middleware.proxy_fix import ProxyFix + # App is behind one proxy that sets the -For and -Host headers. + app = ProxyFix(app, x_for=1, x_host=1) + + .. versionchanged:: 1.0 + Deprecated code has been removed: + + * The ``num_proxies`` argument and attribute. + * The ``get_remote_addr`` method. + * The environ keys ``orig_remote_addr``, + ``orig_wsgi_url_scheme``, and ``orig_http_host``. + + .. versionchanged:: 0.15 + All headers support multiple values. The ``num_proxies`` + argument is deprecated. Each header is configured with a + separate number of trusted proxies. + + .. versionchanged:: 0.15 + Original WSGI environ values are stored in the + ``werkzeug.proxy_fix.orig`` dict. ``orig_remote_addr``, + ``orig_wsgi_url_scheme``, and ``orig_http_host`` are deprecated + and will be removed in 1.0. + + .. versionchanged:: 0.15 + Support ``X-Forwarded-Port`` and ``X-Forwarded-Prefix``. + + .. versionchanged:: 0.15 + ``X-Forwarded-Host`` and ``X-Forwarded-Port`` modify + ``SERVER_NAME`` and ``SERVER_PORT``. + """ + + def __init__( + self, + app: "WSGIApplication", + x_for: int = 1, + x_proto: int = 1, + x_host: int = 0, + x_port: int = 0, + x_prefix: int = 0, + ) -> None: + self.app = app + self.x_for = x_for + self.x_proto = x_proto + self.x_host = x_host + self.x_port = x_port + self.x_prefix = x_prefix + + def _get_real_value(self, trusted: int, value: t.Optional[str]) -> t.Optional[str]: + """Get the real value from a list header based on the configured + number of trusted proxies. + + :param trusted: Number of values to trust in the header. + :param value: Comma separated list header value to parse. + :return: The real value, or ``None`` if there are fewer values + than the number of trusted proxies. + + .. versionchanged:: 1.0 + Renamed from ``_get_trusted_comma``. + + .. versionadded:: 0.15 + """ + if not (trusted and value): + return None + values = parse_list_header(value) + if len(values) >= trusted: + return values[-trusted] + return None + + def __call__( + self, environ: "WSGIEnvironment", start_response: "StartResponse" + ) -> t.Iterable[bytes]: + """Modify the WSGI environ based on the various ``Forwarded`` + headers before calling the wrapped application. Store the + original environ values in ``werkzeug.proxy_fix.orig_{key}``. + """ + environ_get = environ.get + orig_remote_addr = environ_get("REMOTE_ADDR") + orig_wsgi_url_scheme = environ_get("wsgi.url_scheme") + orig_http_host = environ_get("HTTP_HOST") + environ.update( + { + "werkzeug.proxy_fix.orig": { + "REMOTE_ADDR": orig_remote_addr, + "wsgi.url_scheme": orig_wsgi_url_scheme, + "HTTP_HOST": orig_http_host, + "SERVER_NAME": environ_get("SERVER_NAME"), + "SERVER_PORT": environ_get("SERVER_PORT"), + "SCRIPT_NAME": environ_get("SCRIPT_NAME"), + } + } + ) + + x_for = self._get_real_value(self.x_for, environ_get("HTTP_X_FORWARDED_FOR")) + if x_for: + environ["REMOTE_ADDR"] = x_for + + x_proto = self._get_real_value( + self.x_proto, environ_get("HTTP_X_FORWARDED_PROTO") + ) + if x_proto: + environ["wsgi.url_scheme"] = x_proto + + x_host = self._get_real_value(self.x_host, environ_get("HTTP_X_FORWARDED_HOST")) + if x_host: + environ["HTTP_HOST"] = x_host + parts = x_host.split(":", 1) + environ["SERVER_NAME"] = parts[0] + if len(parts) == 2: + environ["SERVER_PORT"] = parts[1] + + x_port = self._get_real_value(self.x_port, environ_get("HTTP_X_FORWARDED_PORT")) + if x_port: + host = environ.get("HTTP_HOST") + if host: + parts = host.split(":", 1) + host = parts[0] if len(parts) == 2 else host + environ["HTTP_HOST"] = f"{host}:{x_port}" + environ["SERVER_PORT"] = x_port + + x_prefix = self._get_real_value( + self.x_prefix, environ_get("HTTP_X_FORWARDED_PREFIX") + ) + if x_prefix: + environ["SCRIPT_NAME"] = x_prefix + + return self.app(environ, start_response) diff --git a/venv/Lib/site-packages/werkzeug/middleware/shared_data.py b/venv/Lib/site-packages/werkzeug/middleware/shared_data.py new file mode 100644 index 0000000..f11b43a --- /dev/null +++ b/venv/Lib/site-packages/werkzeug/middleware/shared_data.py @@ -0,0 +1,320 @@ +""" +Serve Shared Static Files +========================= + +.. autoclass:: SharedDataMiddleware + :members: is_allowed + +:copyright: 2007 Pallets +:license: BSD-3-Clause +""" +import mimetypes +import os +import pkgutil +import posixpath +import typing as t +from datetime import datetime +from datetime import timezone +from io import BytesIO +from time import time +from zlib import adler32 + +from ..filesystem import get_filesystem_encoding +from ..http import http_date +from ..http import is_resource_modified +from ..security import safe_join +from ..utils import get_content_type +from ..wsgi import get_path_info +from ..wsgi import wrap_file + +_TOpener = t.Callable[[], t.Tuple[t.BinaryIO, datetime, int]] +_TLoader = t.Callable[[t.Optional[str]], t.Tuple[t.Optional[str], t.Optional[_TOpener]]] + +if t.TYPE_CHECKING: + from _typeshed.wsgi import StartResponse + from _typeshed.wsgi import WSGIApplication + from _typeshed.wsgi import WSGIEnvironment + + +class SharedDataMiddleware: + + """A WSGI middleware which provides static content for development + environments or simple server setups. Its usage is quite simple:: + + import os + from werkzeug.middleware.shared_data import SharedDataMiddleware + + app = SharedDataMiddleware(app, { + '/shared': os.path.join(os.path.dirname(__file__), 'shared') + }) + + The contents of the folder ``./shared`` will now be available on + ``http://example.com/shared/``. This is pretty useful during development + because a standalone media server is not required. Files can also be + mounted on the root folder and still continue to use the application because + the shared data middleware forwards all unhandled requests to the + application, even if the requests are below one of the shared folders. + + If `pkg_resources` is available you can also tell the middleware to serve + files from package data:: + + app = SharedDataMiddleware(app, { + '/static': ('myapplication', 'static') + }) + + This will then serve the ``static`` folder in the `myapplication` + Python package. + + The optional `disallow` parameter can be a list of :func:`~fnmatch.fnmatch` + rules for files that are not accessible from the web. If `cache` is set to + `False` no caching headers are sent. + + Currently the middleware does not support non-ASCII filenames. If the + encoding on the file system happens to match the encoding of the URI it may + work but this could also be by accident. We strongly suggest using ASCII + only file names for static files. + + The middleware will guess the mimetype using the Python `mimetype` + module. If it's unable to figure out the charset it will fall back + to `fallback_mimetype`. + + :param app: the application to wrap. If you don't want to wrap an + application you can pass it :exc:`NotFound`. + :param exports: a list or dict of exported files and folders. + :param disallow: a list of :func:`~fnmatch.fnmatch` rules. + :param cache: enable or disable caching headers. + :param cache_timeout: the cache timeout in seconds for the headers. + :param fallback_mimetype: The fallback mimetype for unknown files. + + .. versionchanged:: 1.0 + The default ``fallback_mimetype`` is + ``application/octet-stream``. If a filename looks like a text + mimetype, the ``utf-8`` charset is added to it. + + .. versionadded:: 0.6 + Added ``fallback_mimetype``. + + .. versionchanged:: 0.5 + Added ``cache_timeout``. + """ + + def __init__( + self, + app: "WSGIApplication", + exports: t.Union[ + t.Dict[str, t.Union[str, t.Tuple[str, str]]], + t.Iterable[t.Tuple[str, t.Union[str, t.Tuple[str, str]]]], + ], + disallow: None = None, + cache: bool = True, + cache_timeout: int = 60 * 60 * 12, + fallback_mimetype: str = "application/octet-stream", + ) -> None: + self.app = app + self.exports: t.List[t.Tuple[str, _TLoader]] = [] + self.cache = cache + self.cache_timeout = cache_timeout + + if isinstance(exports, dict): + exports = exports.items() + + for key, value in exports: + if isinstance(value, tuple): + loader = self.get_package_loader(*value) + elif isinstance(value, str): + if os.path.isfile(value): + loader = self.get_file_loader(value) + else: + loader = self.get_directory_loader(value) + else: + raise TypeError(f"unknown def {value!r}") + + self.exports.append((key, loader)) + + if disallow is not None: + from fnmatch import fnmatch + + self.is_allowed = lambda x: not fnmatch(x, disallow) + + self.fallback_mimetype = fallback_mimetype + + def is_allowed(self, filename: str) -> bool: + """Subclasses can override this method to disallow the access to + certain files. However by providing `disallow` in the constructor + this method is overwritten. + """ + return True + + def _opener(self, filename: str) -> _TOpener: + return lambda: ( + open(filename, "rb"), + datetime.fromtimestamp(os.path.getmtime(filename), tz=timezone.utc), + int(os.path.getsize(filename)), + ) + + def get_file_loader(self, filename: str) -> _TLoader: + return lambda x: (os.path.basename(filename), self._opener(filename)) + + def get_package_loader(self, package: str, package_path: str) -> _TLoader: + load_time = datetime.now(timezone.utc) + provider = pkgutil.get_loader(package) + + if hasattr(provider, "get_resource_reader"): + # Python 3 + reader = provider.get_resource_reader(package) # type: ignore + + def loader( + path: t.Optional[str], + ) -> t.Tuple[t.Optional[str], t.Optional[_TOpener]]: + if path is None: + return None, None + + path = safe_join(package_path, path) + + if path is None: + return None, None + + basename = posixpath.basename(path) + + try: + resource = reader.open_resource(path) + except OSError: + return None, None + + if isinstance(resource, BytesIO): + return ( + basename, + lambda: (resource, load_time, len(resource.getvalue())), + ) + + return ( + basename, + lambda: ( + resource, + datetime.fromtimestamp( + os.path.getmtime(resource.name), tz=timezone.utc + ), + os.path.getsize(resource.name), + ), + ) + + else: + # Python 3.6 + package_filename = provider.get_filename(package) # type: ignore + is_filesystem = os.path.exists(package_filename) + root = os.path.join(os.path.dirname(package_filename), package_path) + + def loader( + path: t.Optional[str], + ) -> t.Tuple[t.Optional[str], t.Optional[_TOpener]]: + if path is None: + return None, None + + path = safe_join(root, path) + + if path is None: + return None, None + + basename = posixpath.basename(path) + + if is_filesystem: + if not os.path.isfile(path): + return None, None + + return basename, self._opener(path) + + try: + data = provider.get_data(path) # type: ignore + except OSError: + return None, None + + return basename, lambda: (BytesIO(data), load_time, len(data)) + + return loader + + def get_directory_loader(self, directory: str) -> _TLoader: + def loader( + path: t.Optional[str], + ) -> t.Tuple[t.Optional[str], t.Optional[_TOpener]]: + if path is not None: + path = safe_join(directory, path) + + if path is None: + return None, None + else: + path = directory + + if os.path.isfile(path): + return os.path.basename(path), self._opener(path) + + return None, None + + return loader + + def generate_etag(self, mtime: datetime, file_size: int, real_filename: str) -> str: + if not isinstance(real_filename, bytes): + real_filename = real_filename.encode( # type: ignore + get_filesystem_encoding() + ) + + timestamp = mtime.timestamp() + checksum = adler32(real_filename) & 0xFFFFFFFF # type: ignore + return f"wzsdm-{timestamp}-{file_size}-{checksum}" + + def __call__( + self, environ: "WSGIEnvironment", start_response: "StartResponse" + ) -> t.Iterable[bytes]: + path = get_path_info(environ) + file_loader = None + + for search_path, loader in self.exports: + if search_path == path: + real_filename, file_loader = loader(None) + + if file_loader is not None: + break + + if not search_path.endswith("/"): + search_path += "/" + + if path.startswith(search_path): + real_filename, file_loader = loader(path[len(search_path) :]) + + if file_loader is not None: + break + + if file_loader is None or not self.is_allowed(real_filename): # type: ignore + return self.app(environ, start_response) + + guessed_type = mimetypes.guess_type(real_filename) # type: ignore + mime_type = get_content_type(guessed_type[0] or self.fallback_mimetype, "utf-8") + f, mtime, file_size = file_loader() + + headers = [("Date", http_date())] + + if self.cache: + timeout = self.cache_timeout + etag = self.generate_etag(mtime, file_size, real_filename) # type: ignore + headers += [ + ("Etag", f'"{etag}"'), + ("Cache-Control", f"max-age={timeout}, public"), + ] + + if not is_resource_modified(environ, etag, last_modified=mtime): + f.close() + start_response("304 Not Modified", headers) + return [] + + headers.append(("Expires", http_date(time() + timeout))) + else: + headers.append(("Cache-Control", "public")) + + headers.extend( + ( + ("Content-Type", mime_type), + ("Content-Length", str(file_size)), + ("Last-Modified", http_date(mtime)), + ) + ) + start_response("200 OK", headers) + return wrap_file(environ, f) diff --git a/venv/Lib/site-packages/werkzeug/py.typed b/venv/Lib/site-packages/werkzeug/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/venv/Lib/site-packages/werkzeug/routing.py b/venv/Lib/site-packages/werkzeug/routing.py new file mode 100644 index 0000000..1043875 --- /dev/null +++ b/venv/Lib/site-packages/werkzeug/routing.py @@ -0,0 +1,2332 @@ +"""When it comes to combining multiple controller or view functions +(however you want to call them) you need a dispatcher. A simple way +would be applying regular expression tests on the ``PATH_INFO`` and +calling registered callback functions that return the value then. + +This module implements a much more powerful system than simple regular +expression matching because it can also convert values in the URLs and +build URLs. + +Here a simple example that creates a URL map for an application with +two subdomains (www and kb) and some URL rules: + +.. code-block:: python + + m = Map([ + # Static URLs + Rule('/', endpoint='static/index'), + Rule('/about', endpoint='static/about'), + Rule('/help', endpoint='static/help'), + # Knowledge Base + Subdomain('kb', [ + Rule('/', endpoint='kb/index'), + Rule('/browse/', endpoint='kb/browse'), + Rule('/browse//', endpoint='kb/browse'), + Rule('/browse//', endpoint='kb/browse') + ]) + ], default_subdomain='www') + +If the application doesn't use subdomains it's perfectly fine to not set +the default subdomain and not use the `Subdomain` rule factory. The +endpoint in the rules can be anything, for example import paths or +unique identifiers. The WSGI application can use those endpoints to get the +handler for that URL. It doesn't have to be a string at all but it's +recommended. + +Now it's possible to create a URL adapter for one of the subdomains and +build URLs: + +.. code-block:: python + + c = m.bind('example.com') + + c.build("kb/browse", dict(id=42)) + 'http://kb.example.com/browse/42/' + + c.build("kb/browse", dict()) + 'http://kb.example.com/browse/' + + c.build("kb/browse", dict(id=42, page=3)) + 'http://kb.example.com/browse/42/3' + + c.build("static/about") + '/about' + + c.build("static/index", force_external=True) + 'http://www.example.com/' + + c = m.bind('example.com', subdomain='kb') + + c.build("static/about") + 'http://www.example.com/about' + +The first argument to bind is the server name *without* the subdomain. +Per default it will assume that the script is mounted on the root, but +often that's not the case so you can provide the real mount point as +second argument: + +.. code-block:: python + + c = m.bind('example.com', '/applications/example') + +The third argument can be the subdomain, if not given the default +subdomain is used. For more details about binding have a look at the +documentation of the `MapAdapter`. + +And here is how you can match URLs: + +.. code-block:: python + + c = m.bind('example.com') + + c.match("/") + ('static/index', {}) + + c.match("/about") + ('static/about', {}) + + c = m.bind('example.com', '/', 'kb') + + c.match("/") + ('kb/index', {}) + + c.match("/browse/42/23") + ('kb/browse', {'id': 42, 'page': 23}) + +If matching fails you get a ``NotFound`` exception, if the rule thinks +it's a good idea to redirect (for example because the URL was defined +to have a slash at the end but the request was missing that slash) it +will raise a ``RequestRedirect`` exception. Both are subclasses of +``HTTPException`` so you can use those errors as responses in the +application. + +If matching succeeded but the URL rule was incompatible to the given +method (for example there were only rules for ``GET`` and ``HEAD`` but +routing tried to match a ``POST`` request) a ``MethodNotAllowed`` +exception is raised. +""" +import ast +import difflib +import posixpath +import re +import typing +import typing as t +import uuid +import warnings +from pprint import pformat +from string import Template +from threading import Lock +from types import CodeType + +from ._internal import _encode_idna +from ._internal import _get_environ +from ._internal import _to_bytes +from ._internal import _to_str +from ._internal import _wsgi_decoding_dance +from .datastructures import ImmutableDict +from .datastructures import MultiDict +from .exceptions import BadHost +from .exceptions import BadRequest +from .exceptions import HTTPException +from .exceptions import MethodNotAllowed +from .exceptions import NotFound +from .urls import _fast_url_quote +from .urls import url_encode +from .urls import url_join +from .urls import url_quote +from .utils import cached_property +from .utils import redirect +from .wsgi import get_host + +if t.TYPE_CHECKING: + import typing_extensions as te + from _typeshed.wsgi import WSGIApplication + from _typeshed.wsgi import WSGIEnvironment + from .wrappers.response import Response + +_rule_re = re.compile( + r""" + (?P[^<]*) # static rule data + < + (?: + (?P[a-zA-Z_][a-zA-Z0-9_]*) # converter name + (?:\((?P.*?)\))? # converter arguments + \: # variable delimiter + )? + (?P[a-zA-Z_][a-zA-Z0-9_]*) # variable name + > + """, + re.VERBOSE, +) +_simple_rule_re = re.compile(r"<([^>]+)>") +_converter_args_re = re.compile( + r""" + ((?P\w+)\s*=\s*)? + (?P + True|False| + \d+.\d+| + \d+.| + \d+| + [\w\d_.]+| + [urUR]?(?P"[^"]*?"|'[^']*') + )\s*, + """, + re.VERBOSE, +) + + +_PYTHON_CONSTANTS = {"None": None, "True": True, "False": False} + + +def _pythonize(value: str) -> t.Union[None, bool, int, float, str]: + if value in _PYTHON_CONSTANTS: + return _PYTHON_CONSTANTS[value] + for convert in int, float: + try: + return convert(value) # type: ignore + except ValueError: + pass + if value[:1] == value[-1:] and value[0] in "\"'": + value = value[1:-1] + return str(value) + + +def parse_converter_args(argstr: str) -> t.Tuple[t.Tuple, t.Dict[str, t.Any]]: + argstr += "," + args = [] + kwargs = {} + + for item in _converter_args_re.finditer(argstr): + value = item.group("stringval") + if value is None: + value = item.group("value") + value = _pythonize(value) + if not item.group("name"): + args.append(value) + else: + name = item.group("name") + kwargs[name] = value + + return tuple(args), kwargs + + +def parse_rule(rule: str) -> t.Iterator[t.Tuple[t.Optional[str], t.Optional[str], str]]: + """Parse a rule and return it as generator. Each iteration yields tuples + in the form ``(converter, arguments, variable)``. If the converter is + `None` it's a static url part, otherwise it's a dynamic one. + + :internal: + """ + pos = 0 + end = len(rule) + do_match = _rule_re.match + used_names = set() + while pos < end: + m = do_match(rule, pos) + if m is None: + break + data = m.groupdict() + if data["static"]: + yield None, None, data["static"] + variable = data["variable"] + converter = data["converter"] or "default" + if variable in used_names: + raise ValueError(f"variable name {variable!r} used twice.") + used_names.add(variable) + yield converter, data["args"] or None, variable + pos = m.end() + if pos < end: + remaining = rule[pos:] + if ">" in remaining or "<" in remaining: + raise ValueError(f"malformed url rule: {rule!r}") + yield None, None, remaining + + +class RoutingException(Exception): + """Special exceptions that require the application to redirect, notifying + about missing urls, etc. + + :internal: + """ + + +class RequestRedirect(HTTPException, RoutingException): + """Raise if the map requests a redirect. This is for example the case if + `strict_slashes` are activated and an url that requires a trailing slash. + + The attribute `new_url` contains the absolute destination url. + """ + + code = 308 + + def __init__(self, new_url: str) -> None: + super().__init__(new_url) + self.new_url = new_url + + def get_response( + self, + environ: t.Optional["WSGIEnvironment"] = None, + scope: t.Optional[dict] = None, + ) -> "Response": + return redirect(self.new_url, self.code) + + +class RequestPath(RoutingException): + """Internal exception.""" + + __slots__ = ("path_info",) + + def __init__(self, path_info: str) -> None: + super().__init__() + self.path_info = path_info + + +class RequestAliasRedirect(RoutingException): # noqa: B903 + """This rule is an alias and wants to redirect to the canonical URL.""" + + def __init__(self, matched_values: t.Mapping[str, t.Any]) -> None: + super().__init__() + self.matched_values = matched_values + + +class BuildError(RoutingException, LookupError): + """Raised if the build system cannot find a URL for an endpoint with the + values provided. + """ + + def __init__( + self, + endpoint: str, + values: t.Mapping[str, t.Any], + method: t.Optional[str], + adapter: t.Optional["MapAdapter"] = None, + ) -> None: + super().__init__(endpoint, values, method) + self.endpoint = endpoint + self.values = values + self.method = method + self.adapter = adapter + + @cached_property + def suggested(self) -> t.Optional["Rule"]: + return self.closest_rule(self.adapter) + + def closest_rule(self, adapter: t.Optional["MapAdapter"]) -> t.Optional["Rule"]: + def _score_rule(rule: "Rule") -> float: + return sum( + [ + 0.98 + * difflib.SequenceMatcher( + None, rule.endpoint, self.endpoint + ).ratio(), + 0.01 * bool(set(self.values or ()).issubset(rule.arguments)), + 0.01 * bool(rule.methods and self.method in rule.methods), + ] + ) + + if adapter and adapter.map._rules: + return max(adapter.map._rules, key=_score_rule) + + return None + + def __str__(self) -> str: + message = [f"Could not build url for endpoint {self.endpoint!r}"] + if self.method: + message.append(f" ({self.method!r})") + if self.values: + message.append(f" with values {sorted(self.values)!r}") + message.append(".") + if self.suggested: + if self.endpoint == self.suggested.endpoint: + if ( + self.method + and self.suggested.methods is not None + and self.method not in self.suggested.methods + ): + message.append( + " Did you mean to use methods" + f" {sorted(self.suggested.methods)!r}?" + ) + missing_values = self.suggested.arguments.union( + set(self.suggested.defaults or ()) + ) - set(self.values.keys()) + if missing_values: + message.append( + f" Did you forget to specify values {sorted(missing_values)!r}?" + ) + else: + message.append(f" Did you mean {self.suggested.endpoint!r} instead?") + return "".join(message) + + +class WebsocketMismatch(BadRequest): + """The only matched rule is either a WebSocket and the request is + HTTP, or the rule is HTTP and the request is a WebSocket. + """ + + +class ValidationError(ValueError): + """Validation error. If a rule converter raises this exception the rule + does not match the current URL and the next URL is tried. + """ + + +class RuleFactory: + """As soon as you have more complex URL setups it's a good idea to use rule + factories to avoid repetitive tasks. Some of them are builtin, others can + be added by subclassing `RuleFactory` and overriding `get_rules`. + """ + + def get_rules(self, map: "Map") -> t.Iterable["Rule"]: + """Subclasses of `RuleFactory` have to override this method and return + an iterable of rules.""" + raise NotImplementedError() + + +class Subdomain(RuleFactory): + """All URLs provided by this factory have the subdomain set to a + specific domain. For example if you want to use the subdomain for + the current language this can be a good setup:: + + url_map = Map([ + Rule('/', endpoint='#select_language'), + Subdomain('', [ + Rule('/', endpoint='index'), + Rule('/about', endpoint='about'), + Rule('/help', endpoint='help') + ]) + ]) + + All the rules except for the ``'#select_language'`` endpoint will now + listen on a two letter long subdomain that holds the language code + for the current request. + """ + + def __init__(self, subdomain: str, rules: t.Iterable["Rule"]) -> None: + self.subdomain = subdomain + self.rules = rules + + def get_rules(self, map: "Map") -> t.Iterator["Rule"]: + for rulefactory in self.rules: + for rule in rulefactory.get_rules(map): + rule = rule.empty() + rule.subdomain = self.subdomain + yield rule + + +class Submount(RuleFactory): + """Like `Subdomain` but prefixes the URL rule with a given string:: + + url_map = Map([ + Rule('/', endpoint='index'), + Submount('/blog', [ + Rule('/', endpoint='blog/index'), + Rule('/entry/', endpoint='blog/show') + ]) + ]) + + Now the rule ``'blog/show'`` matches ``/blog/entry/``. + """ + + def __init__(self, path: str, rules: t.Iterable["Rule"]) -> None: + self.path = path.rstrip("/") + self.rules = rules + + def get_rules(self, map: "Map") -> t.Iterator["Rule"]: + for rulefactory in self.rules: + for rule in rulefactory.get_rules(map): + rule = rule.empty() + rule.rule = self.path + rule.rule + yield rule + + +class EndpointPrefix(RuleFactory): + """Prefixes all endpoints (which must be strings for this factory) with + another string. This can be useful for sub applications:: + + url_map = Map([ + Rule('/', endpoint='index'), + EndpointPrefix('blog/', [Submount('/blog', [ + Rule('/', endpoint='index'), + Rule('/entry/', endpoint='show') + ])]) + ]) + """ + + def __init__(self, prefix: str, rules: t.Iterable["Rule"]) -> None: + self.prefix = prefix + self.rules = rules + + def get_rules(self, map: "Map") -> t.Iterator["Rule"]: + for rulefactory in self.rules: + for rule in rulefactory.get_rules(map): + rule = rule.empty() + rule.endpoint = self.prefix + rule.endpoint + yield rule + + +class RuleTemplate: + """Returns copies of the rules wrapped and expands string templates in + the endpoint, rule, defaults or subdomain sections. + + Here a small example for such a rule template:: + + from werkzeug.routing import Map, Rule, RuleTemplate + + resource = RuleTemplate([ + Rule('/$name/', endpoint='$name.list'), + Rule('/$name/', endpoint='$name.show') + ]) + + url_map = Map([resource(name='user'), resource(name='page')]) + + When a rule template is called the keyword arguments are used to + replace the placeholders in all the string parameters. + """ + + def __init__(self, rules: t.Iterable["Rule"]) -> None: + self.rules = list(rules) + + def __call__(self, *args: t.Any, **kwargs: t.Any) -> "RuleTemplateFactory": + return RuleTemplateFactory(self.rules, dict(*args, **kwargs)) + + +class RuleTemplateFactory(RuleFactory): + """A factory that fills in template variables into rules. Used by + `RuleTemplate` internally. + + :internal: + """ + + def __init__(self, rules: t.Iterable["Rule"], context: t.Dict[str, t.Any]) -> None: + self.rules = rules + self.context = context + + def get_rules(self, map: "Map") -> t.Iterator["Rule"]: + for rulefactory in self.rules: + for rule in rulefactory.get_rules(map): + new_defaults = subdomain = None + if rule.defaults: + new_defaults = {} + for key, value in rule.defaults.items(): + if isinstance(value, str): + value = Template(value).substitute(self.context) + new_defaults[key] = value + if rule.subdomain is not None: + subdomain = Template(rule.subdomain).substitute(self.context) + new_endpoint = rule.endpoint + if isinstance(new_endpoint, str): + new_endpoint = Template(new_endpoint).substitute(self.context) + yield Rule( + Template(rule.rule).substitute(self.context), + new_defaults, + subdomain, + rule.methods, + rule.build_only, + new_endpoint, + rule.strict_slashes, + ) + + +def _prefix_names(src: str) -> ast.stmt: + """ast parse and prefix names with `.` to avoid collision with user vars""" + tree = ast.parse(src).body[0] + if isinstance(tree, ast.Expr): + tree = tree.value # type: ignore + for node in ast.walk(tree): + if isinstance(node, ast.Name): + node.id = f".{node.id}" + return tree + + +_CALL_CONVERTER_CODE_FMT = "self._converters[{elem!r}].to_url()" +_IF_KWARGS_URL_ENCODE_CODE = """\ +if kwargs: + q = '?' + params = self._encode_query_vars(kwargs) +else: + q = params = '' +""" +_IF_KWARGS_URL_ENCODE_AST = _prefix_names(_IF_KWARGS_URL_ENCODE_CODE) +_URL_ENCODE_AST_NAMES = (_prefix_names("q"), _prefix_names("params")) + + +class Rule(RuleFactory): + """A Rule represents one URL pattern. There are some options for `Rule` + that change the way it behaves and are passed to the `Rule` constructor. + Note that besides the rule-string all arguments *must* be keyword arguments + in order to not break the application on Werkzeug upgrades. + + `string` + Rule strings basically are just normal URL paths with placeholders in + the format ```` where the converter and the + arguments are optional. If no converter is defined the `default` + converter is used which means `string` in the normal configuration. + + URL rules that end with a slash are branch URLs, others are leaves. + If you have `strict_slashes` enabled (which is the default), all + branch URLs that are matched without a trailing slash will trigger a + redirect to the same URL with the missing slash appended. + + The converters are defined on the `Map`. + + `endpoint` + The endpoint for this rule. This can be anything. A reference to a + function, a string, a number etc. The preferred way is using a string + because the endpoint is used for URL generation. + + `defaults` + An optional dict with defaults for other rules with the same endpoint. + This is a bit tricky but useful if you want to have unique URLs:: + + url_map = Map([ + Rule('/all/', defaults={'page': 1}, endpoint='all_entries'), + Rule('/all/page/', endpoint='all_entries') + ]) + + If a user now visits ``http://example.com/all/page/1`` he will be + redirected to ``http://example.com/all/``. If `redirect_defaults` is + disabled on the `Map` instance this will only affect the URL + generation. + + `subdomain` + The subdomain rule string for this rule. If not specified the rule + only matches for the `default_subdomain` of the map. If the map is + not bound to a subdomain this feature is disabled. + + Can be useful if you want to have user profiles on different subdomains + and all subdomains are forwarded to your application:: + + url_map = Map([ + Rule('/', subdomain='', endpoint='user/homepage'), + Rule('/stats', subdomain='', endpoint='user/stats') + ]) + + `methods` + A sequence of http methods this rule applies to. If not specified, all + methods are allowed. For example this can be useful if you want different + endpoints for `POST` and `GET`. If methods are defined and the path + matches but the method matched against is not in this list or in the + list of another rule for that path the error raised is of the type + `MethodNotAllowed` rather than `NotFound`. If `GET` is present in the + list of methods and `HEAD` is not, `HEAD` is added automatically. + + `strict_slashes` + Override the `Map` setting for `strict_slashes` only for this rule. If + not specified the `Map` setting is used. + + `merge_slashes` + Override :attr:`Map.merge_slashes` for this rule. + + `build_only` + Set this to True and the rule will never match but will create a URL + that can be build. This is useful if you have resources on a subdomain + or folder that are not handled by the WSGI application (like static data) + + `redirect_to` + If given this must be either a string or callable. In case of a + callable it's called with the url adapter that triggered the match and + the values of the URL as keyword arguments and has to return the target + for the redirect, otherwise it has to be a string with placeholders in + rule syntax:: + + def foo_with_slug(adapter, id): + # ask the database for the slug for the old id. this of + # course has nothing to do with werkzeug. + return f'foo/{Foo.get_slug_for_id(id)}' + + url_map = Map([ + Rule('/foo/', endpoint='foo'), + Rule('/some/old/url/', redirect_to='foo/'), + Rule('/other/old/url/', redirect_to=foo_with_slug) + ]) + + When the rule is matched the routing system will raise a + `RequestRedirect` exception with the target for the redirect. + + Keep in mind that the URL will be joined against the URL root of the + script so don't use a leading slash on the target URL unless you + really mean root of that domain. + + `alias` + If enabled this rule serves as an alias for another rule with the same + endpoint and arguments. + + `host` + If provided and the URL map has host matching enabled this can be + used to provide a match rule for the whole host. This also means + that the subdomain feature is disabled. + + `websocket` + If ``True``, this rule is only matches for WebSocket (``ws://``, + ``wss://``) requests. By default, rules will only match for HTTP + requests. + + .. versionadded:: 1.0 + Added ``websocket``. + + .. versionadded:: 1.0 + Added ``merge_slashes``. + + .. versionadded:: 0.7 + Added ``alias`` and ``host``. + + .. versionchanged:: 0.6.1 + ``HEAD`` is added to ``methods`` if ``GET`` is present. + """ + + def __init__( + self, + string: str, + defaults: t.Optional[t.Mapping[str, t.Any]] = None, + subdomain: t.Optional[str] = None, + methods: t.Optional[t.Iterable[str]] = None, + build_only: bool = False, + endpoint: t.Optional[str] = None, + strict_slashes: t.Optional[bool] = None, + merge_slashes: t.Optional[bool] = None, + redirect_to: t.Optional[t.Union[str, t.Callable[..., str]]] = None, + alias: bool = False, + host: t.Optional[str] = None, + websocket: bool = False, + ) -> None: + if not string.startswith("/"): + raise ValueError("urls must start with a leading slash") + self.rule = string + self.is_leaf = not string.endswith("/") + + self.map: "Map" = None # type: ignore + self.strict_slashes = strict_slashes + self.merge_slashes = merge_slashes + self.subdomain = subdomain + self.host = host + self.defaults = defaults + self.build_only = build_only + self.alias = alias + self.websocket = websocket + + if methods is not None: + if isinstance(methods, str): + raise TypeError("'methods' should be a list of strings.") + + methods = {x.upper() for x in methods} + + if "HEAD" not in methods and "GET" in methods: + methods.add("HEAD") + + if websocket and methods - {"GET", "HEAD", "OPTIONS"}: + raise ValueError( + "WebSocket rules can only use 'GET', 'HEAD', and 'OPTIONS' methods." + ) + + self.methods = methods + self.endpoint: str = endpoint # type: ignore + self.redirect_to = redirect_to + + if defaults: + self.arguments = set(map(str, defaults)) + else: + self.arguments = set() + + self._trace: t.List[t.Tuple[bool, str]] = [] + + def empty(self) -> "Rule": + """ + Return an unbound copy of this rule. + + This can be useful if want to reuse an already bound URL for another + map. See ``get_empty_kwargs`` to override what keyword arguments are + provided to the new copy. + """ + return type(self)(self.rule, **self.get_empty_kwargs()) + + def get_empty_kwargs(self) -> t.Mapping[str, t.Any]: + """ + Provides kwargs for instantiating empty copy with empty() + + Use this method to provide custom keyword arguments to the subclass of + ``Rule`` when calling ``some_rule.empty()``. Helpful when the subclass + has custom keyword arguments that are needed at instantiation. + + Must return a ``dict`` that will be provided as kwargs to the new + instance of ``Rule``, following the initial ``self.rule`` value which + is always provided as the first, required positional argument. + """ + defaults = None + if self.defaults: + defaults = dict(self.defaults) + return dict( + defaults=defaults, + subdomain=self.subdomain, + methods=self.methods, + build_only=self.build_only, + endpoint=self.endpoint, + strict_slashes=self.strict_slashes, + redirect_to=self.redirect_to, + alias=self.alias, + host=self.host, + ) + + def get_rules(self, map: "Map") -> t.Iterator["Rule"]: + yield self + + def refresh(self) -> None: + """Rebinds and refreshes the URL. Call this if you modified the + rule in place. + + :internal: + """ + self.bind(self.map, rebind=True) + + def bind(self, map: "Map", rebind: bool = False) -> None: + """Bind the url to a map and create a regular expression based on + the information from the rule itself and the defaults from the map. + + :internal: + """ + if self.map is not None and not rebind: + raise RuntimeError(f"url rule {self!r} already bound to map {self.map!r}") + self.map = map + if self.strict_slashes is None: + self.strict_slashes = map.strict_slashes + if self.merge_slashes is None: + self.merge_slashes = map.merge_slashes + if self.subdomain is None: + self.subdomain = map.default_subdomain + self.compile() + + def get_converter( + self, + variable_name: str, + converter_name: str, + args: t.Tuple, + kwargs: t.Mapping[str, t.Any], + ) -> "BaseConverter": + """Looks up the converter for the given parameter. + + .. versionadded:: 0.9 + """ + if converter_name not in self.map.converters: + raise LookupError(f"the converter {converter_name!r} does not exist") + return self.map.converters[converter_name](self.map, *args, **kwargs) + + def _encode_query_vars(self, query_vars: t.Mapping[str, t.Any]) -> str: + return url_encode( + query_vars, + charset=self.map.charset, + sort=self.map.sort_parameters, + key=self.map.sort_key, + ) + + def compile(self) -> None: + """Compiles the regular expression and stores it.""" + assert self.map is not None, "rule not bound" + + if self.map.host_matching: + domain_rule = self.host or "" + else: + domain_rule = self.subdomain or "" + + self._trace = [] + self._converters: t.Dict[str, "BaseConverter"] = {} + self._static_weights: t.List[t.Tuple[int, int]] = [] + self._argument_weights: t.List[int] = [] + regex_parts = [] + + def _build_regex(rule: str) -> None: + index = 0 + for converter, arguments, variable in parse_rule(rule): + if converter is None: + for match in re.finditer(r"/+|[^/]+", variable): + part = match.group(0) + if part.startswith("/"): + if self.merge_slashes: + regex_parts.append(r"/+?") + self._trace.append((False, "/")) + else: + regex_parts.append(part) + self._trace.append((False, part)) + continue + self._trace.append((False, part)) + regex_parts.append(re.escape(part)) + if part: + self._static_weights.append((index, -len(part))) + else: + if arguments: + c_args, c_kwargs = parse_converter_args(arguments) + else: + c_args = () + c_kwargs = {} + convobj = self.get_converter(variable, converter, c_args, c_kwargs) + regex_parts.append(f"(?P<{variable}>{convobj.regex})") + self._converters[variable] = convobj + self._trace.append((True, variable)) + self._argument_weights.append(convobj.weight) + self.arguments.add(str(variable)) + index = index + 1 + + _build_regex(domain_rule) + regex_parts.append("\\|") + self._trace.append((False, "|")) + _build_regex(self.rule if self.is_leaf else self.rule.rstrip("/")) + if not self.is_leaf: + self._trace.append((False, "/")) + + self._build: t.Callable[..., t.Tuple[str, str]] + self._build = self._compile_builder(False).__get__(self, None) # type: ignore + self._build_unknown: t.Callable[..., t.Tuple[str, str]] + self._build_unknown = self._compile_builder(True).__get__( # type: ignore + self, None + ) + + if self.build_only: + return + + if not (self.is_leaf and self.strict_slashes): + reps = "*" if self.merge_slashes else "?" + tail = f"(?/{reps})" + else: + tail = "" + + regex = f"^{''.join(regex_parts)}{tail}$" + self._regex = re.compile(regex) + + def match( + self, path: str, method: t.Optional[str] = None + ) -> t.Optional[t.MutableMapping[str, t.Any]]: + """Check if the rule matches a given path. Path is a string in the + form ``"subdomain|/path"`` and is assembled by the map. If + the map is doing host matching the subdomain part will be the host + instead. + + If the rule matches a dict with the converted values is returned, + otherwise the return value is `None`. + + :internal: + """ + if not self.build_only: + require_redirect = False + + m = self._regex.search(path) + if m is not None: + groups = m.groupdict() + # we have a folder like part of the url without a trailing + # slash and strict slashes enabled. raise an exception that + # tells the map to redirect to the same url but with a + # trailing slash + if ( + self.strict_slashes + and not self.is_leaf + and not groups.pop("__suffix__") + and ( + method is None or self.methods is None or method in self.methods + ) + ): + path += "/" + require_redirect = True + # if we are not in strict slashes mode we have to remove + # a __suffix__ + elif not self.strict_slashes: + del groups["__suffix__"] + + result = {} + for name, value in groups.items(): + try: + value = self._converters[name].to_python(value) + except ValidationError: + return None + result[str(name)] = value + if self.defaults: + result.update(self.defaults) + + if self.merge_slashes: + new_path = "|".join(self.build(result, False)) # type: ignore + if path.endswith("/") and not new_path.endswith("/"): + new_path += "/" + if new_path.count("/") < path.count("/"): + path = new_path + require_redirect = True + + if require_redirect: + path = path.split("|", 1)[1] + raise RequestPath(path) + + if self.alias and self.map.redirect_defaults: + raise RequestAliasRedirect(result) + + return result + + return None + + @staticmethod + def _get_func_code(code: CodeType, name: str) -> t.Callable[..., t.Tuple[str, str]]: + globs: t.Dict[str, t.Any] = {} + locs: t.Dict[str, t.Any] = {} + exec(code, globs, locs) + return locs[name] # type: ignore + + def _compile_builder( + self, append_unknown: bool = True + ) -> t.Callable[..., t.Tuple[str, str]]: + defaults = self.defaults or {} + dom_ops: t.List[t.Tuple[bool, str]] = [] + url_ops: t.List[t.Tuple[bool, str]] = [] + + opl = dom_ops + for is_dynamic, data in self._trace: + if data == "|" and opl is dom_ops: + opl = url_ops + continue + # this seems like a silly case to ever come up but: + # if a default is given for a value that appears in the rule, + # resolve it to a constant ahead of time + if is_dynamic and data in defaults: + data = self._converters[data].to_url(defaults[data]) + opl.append((False, data)) + elif not is_dynamic: + opl.append( + (False, url_quote(_to_bytes(data, self.map.charset), safe="/:|+")) + ) + else: + opl.append((True, data)) + + def _convert(elem: str) -> ast.stmt: + ret = _prefix_names(_CALL_CONVERTER_CODE_FMT.format(elem=elem)) + ret.args = [ast.Name(str(elem), ast.Load())] # type: ignore # str for py2 + return ret + + def _parts(ops: t.List[t.Tuple[bool, str]]) -> t.List[ast.AST]: + parts = [ + _convert(elem) if is_dynamic else ast.Str(s=elem) + for is_dynamic, elem in ops + ] + parts = parts or [ast.Str("")] + # constant fold + ret = [parts[0]] + for p in parts[1:]: + if isinstance(p, ast.Str) and isinstance(ret[-1], ast.Str): + ret[-1] = ast.Str(ret[-1].s + p.s) + else: + ret.append(p) + return ret + + dom_parts = _parts(dom_ops) + url_parts = _parts(url_ops) + if not append_unknown: + body = [] + else: + body = [_IF_KWARGS_URL_ENCODE_AST] + url_parts.extend(_URL_ENCODE_AST_NAMES) + + def _join(parts: t.List[ast.AST]) -> ast.AST: + if len(parts) == 1: # shortcut + return parts[0] + return ast.JoinedStr(parts) + + body.append( + ast.Return(ast.Tuple([_join(dom_parts), _join(url_parts)], ast.Load())) + ) + + pargs = [ + elem + for is_dynamic, elem in dom_ops + url_ops + if is_dynamic and elem not in defaults + ] + kargs = [str(k) for k in defaults] + + func_ast: ast.FunctionDef = _prefix_names("def _(): pass") # type: ignore + func_ast.name = f"" + func_ast.args.args.append(ast.arg(".self", None)) + for arg in pargs + kargs: + func_ast.args.args.append(ast.arg(arg, None)) + func_ast.args.kwarg = ast.arg(".kwargs", None) + for _ in kargs: + func_ast.args.defaults.append(ast.Str("")) + func_ast.body = body + + # use `ast.parse` instead of `ast.Module` for better portability + # Python 3.8 changes the signature of `ast.Module` + module = ast.parse("") + module.body = [func_ast] + + # mark everything as on line 1, offset 0 + # less error-prone than `ast.fix_missing_locations` + # bad line numbers cause an assert to fail in debug builds + for node in ast.walk(module): + if "lineno" in node._attributes: + node.lineno = 1 + if "col_offset" in node._attributes: + node.col_offset = 0 + + code = compile(module, "", "exec") + return self._get_func_code(code, func_ast.name) + + def build( + self, values: t.Mapping[str, t.Any], append_unknown: bool = True + ) -> t.Optional[t.Tuple[str, str]]: + """Assembles the relative url for that rule and the subdomain. + If building doesn't work for some reasons `None` is returned. + + :internal: + """ + try: + if append_unknown: + return self._build_unknown(**values) + else: + return self._build(**values) + except ValidationError: + return None + + def provides_defaults_for(self, rule: "Rule") -> bool: + """Check if this rule has defaults for a given rule. + + :internal: + """ + return bool( + not self.build_only + and self.defaults + and self.endpoint == rule.endpoint + and self != rule + and self.arguments == rule.arguments + ) + + def suitable_for( + self, values: t.Mapping[str, t.Any], method: t.Optional[str] = None + ) -> bool: + """Check if the dict of values has enough data for url generation. + + :internal: + """ + # if a method was given explicitly and that method is not supported + # by this rule, this rule is not suitable. + if ( + method is not None + and self.methods is not None + and method not in self.methods + ): + return False + + defaults = self.defaults or () + + # all arguments required must be either in the defaults dict or + # the value dictionary otherwise it's not suitable + for key in self.arguments: + if key not in defaults and key not in values: + return False + + # in case defaults are given we ensure that either the value was + # skipped or the value is the same as the default value. + if defaults: + for key, value in defaults.items(): + if key in values and value != values[key]: + return False + + return True + + def match_compare_key( + self, + ) -> t.Tuple[bool, int, t.Iterable[t.Tuple[int, int]], int, t.Iterable[int]]: + """The match compare key for sorting. + + Current implementation: + + 1. rules without any arguments come first for performance + reasons only as we expect them to match faster and some + common ones usually don't have any arguments (index pages etc.) + 2. rules with more static parts come first so the second argument + is the negative length of the number of the static weights. + 3. we order by static weights, which is a combination of index + and length + 4. The more complex rules come first so the next argument is the + negative length of the number of argument weights. + 5. lastly we order by the actual argument weights. + + :internal: + """ + return ( + bool(self.arguments), + -len(self._static_weights), + self._static_weights, + -len(self._argument_weights), + self._argument_weights, + ) + + def build_compare_key(self) -> t.Tuple[int, int, int]: + """The build compare key for sorting. + + :internal: + """ + return (1 if self.alias else 0, -len(self.arguments), -len(self.defaults or ())) + + def __eq__(self, other: object) -> bool: + return isinstance(other, type(self)) and self._trace == other._trace + + __hash__ = None # type: ignore + + def __str__(self) -> str: + return self.rule + + def __repr__(self) -> str: + if self.map is None: + return f"<{type(self).__name__} (unbound)>" + parts = [] + for is_dynamic, data in self._trace: + if is_dynamic: + parts.append(f"<{data}>") + else: + parts.append(data) + parts = "".join(parts).lstrip("|") + methods = f" ({', '.join(self.methods)})" if self.methods is not None else "" + return f"<{type(self).__name__} {parts!r}{methods} -> {self.endpoint}>" + + +class BaseConverter: + """Base class for all converters.""" + + regex = "[^/]+" + weight = 100 + + def __init__(self, map: "Map", *args: t.Any, **kwargs: t.Any) -> None: + self.map = map + + def to_python(self, value: str) -> t.Any: + return value + + def to_url(self, value: t.Any) -> str: + if isinstance(value, (bytes, bytearray)): + return _fast_url_quote(value) + return _fast_url_quote(str(value).encode(self.map.charset)) + + +class UnicodeConverter(BaseConverter): + """This converter is the default converter and accepts any string but + only one path segment. Thus the string can not include a slash. + + This is the default validator. + + Example:: + + Rule('/pages/'), + Rule('/') + + :param map: the :class:`Map`. + :param minlength: the minimum length of the string. Must be greater + or equal 1. + :param maxlength: the maximum length of the string. + :param length: the exact length of the string. + """ + + def __init__( + self, + map: "Map", + minlength: int = 1, + maxlength: t.Optional[int] = None, + length: t.Optional[int] = None, + ) -> None: + super().__init__(map) + if length is not None: + length_regex = f"{{{int(length)}}}" + else: + if maxlength is None: + maxlength_value = "" + else: + maxlength_value = str(int(maxlength)) + length_regex = f"{{{int(minlength)},{maxlength_value}}}" + self.regex = f"[^/]{length_regex}" + + +class AnyConverter(BaseConverter): + """Matches one of the items provided. Items can either be Python + identifiers or strings:: + + Rule('/') + + :param map: the :class:`Map`. + :param items: this function accepts the possible items as positional + arguments. + """ + + def __init__(self, map: "Map", *items: str) -> None: + super().__init__(map) + self.regex = f"(?:{'|'.join([re.escape(x) for x in items])})" + + +class PathConverter(BaseConverter): + """Like the default :class:`UnicodeConverter`, but it also matches + slashes. This is useful for wikis and similar applications:: + + Rule('/') + Rule('//edit') + + :param map: the :class:`Map`. + """ + + regex = "[^/].*?" + weight = 200 + + +class NumberConverter(BaseConverter): + """Baseclass for `IntegerConverter` and `FloatConverter`. + + :internal: + """ + + weight = 50 + num_convert: t.Callable = int + + def __init__( + self, + map: "Map", + fixed_digits: int = 0, + min: t.Optional[int] = None, + max: t.Optional[int] = None, + signed: bool = False, + ) -> None: + if signed: + self.regex = self.signed_regex + super().__init__(map) + self.fixed_digits = fixed_digits + self.min = min + self.max = max + self.signed = signed + + def to_python(self, value: str) -> t.Any: + if self.fixed_digits and len(value) != self.fixed_digits: + raise ValidationError() + value = self.num_convert(value) + if (self.min is not None and value < self.min) or ( + self.max is not None and value > self.max + ): + raise ValidationError() + return value + + def to_url(self, value: t.Any) -> str: + value = str(self.num_convert(value)) + if self.fixed_digits: + value = value.zfill(self.fixed_digits) + return value + + @property + def signed_regex(self) -> str: + return f"-?{self.regex}" + + +class IntegerConverter(NumberConverter): + """This converter only accepts integer values:: + + Rule("/page/") + + By default it only accepts unsigned, positive values. The ``signed`` + parameter will enable signed, negative values. :: + + Rule("/page/") + + :param map: The :class:`Map`. + :param fixed_digits: The number of fixed digits in the URL. If you + set this to ``4`` for example, the rule will only match if the + URL looks like ``/0001/``. The default is variable length. + :param min: The minimal value. + :param max: The maximal value. + :param signed: Allow signed (negative) values. + + .. versionadded:: 0.15 + The ``signed`` parameter. + """ + + regex = r"\d+" + + +class FloatConverter(NumberConverter): + """This converter only accepts floating point values:: + + Rule("/probability/") + + By default it only accepts unsigned, positive values. The ``signed`` + parameter will enable signed, negative values. :: + + Rule("/offset/") + + :param map: The :class:`Map`. + :param min: The minimal value. + :param max: The maximal value. + :param signed: Allow signed (negative) values. + + .. versionadded:: 0.15 + The ``signed`` parameter. + """ + + regex = r"\d+\.\d+" + num_convert = float + + def __init__( + self, + map: "Map", + min: t.Optional[float] = None, + max: t.Optional[float] = None, + signed: bool = False, + ) -> None: + super().__init__(map, min=min, max=max, signed=signed) # type: ignore + + +class UUIDConverter(BaseConverter): + """This converter only accepts UUID strings:: + + Rule('/object/') + + .. versionadded:: 0.10 + + :param map: the :class:`Map`. + """ + + regex = ( + r"[A-Fa-f0-9]{8}-[A-Fa-f0-9]{4}-" + r"[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{12}" + ) + + def to_python(self, value: str) -> uuid.UUID: + return uuid.UUID(value) + + def to_url(self, value: uuid.UUID) -> str: + return str(value) + + +#: the default converter mapping for the map. +DEFAULT_CONVERTERS: t.Mapping[str, t.Type[BaseConverter]] = { + "default": UnicodeConverter, + "string": UnicodeConverter, + "any": AnyConverter, + "path": PathConverter, + "int": IntegerConverter, + "float": FloatConverter, + "uuid": UUIDConverter, +} + + +class Map: + """The map class stores all the URL rules and some configuration + parameters. Some of the configuration values are only stored on the + `Map` instance since those affect all rules, others are just defaults + and can be overridden for each rule. Note that you have to specify all + arguments besides the `rules` as keyword arguments! + + :param rules: sequence of url rules for this map. + :param default_subdomain: The default subdomain for rules without a + subdomain defined. + :param charset: charset of the url. defaults to ``"utf-8"`` + :param strict_slashes: If a rule ends with a slash but the matched + URL does not, redirect to the URL with a trailing slash. + :param merge_slashes: Merge consecutive slashes when matching or + building URLs. Matches will redirect to the normalized URL. + Slashes in variable parts are not merged. + :param redirect_defaults: This will redirect to the default rule if it + wasn't visited that way. This helps creating + unique URLs. + :param converters: A dict of converters that adds additional converters + to the list of converters. If you redefine one + converter this will override the original one. + :param sort_parameters: If set to `True` the url parameters are sorted. + See `url_encode` for more details. + :param sort_key: The sort key function for `url_encode`. + :param encoding_errors: the error method to use for decoding + :param host_matching: if set to `True` it enables the host matching + feature and disables the subdomain one. If + enabled the `host` parameter to rules is used + instead of the `subdomain` one. + + .. versionchanged:: 1.0 + If ``url_scheme`` is ``ws`` or ``wss``, only WebSocket rules + will match. + + .. versionchanged:: 1.0 + Added ``merge_slashes``. + + .. versionchanged:: 0.7 + Added ``encoding_errors`` and ``host_matching``. + + .. versionchanged:: 0.5 + Added ``sort_parameters`` and ``sort_key``. + """ + + #: A dict of default converters to be used. + default_converters = ImmutableDict(DEFAULT_CONVERTERS) + + #: The type of lock to use when updating. + #: + #: .. versionadded:: 1.0 + lock_class = Lock + + def __init__( + self, + rules: t.Optional[t.Iterable[RuleFactory]] = None, + default_subdomain: str = "", + charset: str = "utf-8", + strict_slashes: bool = True, + merge_slashes: bool = True, + redirect_defaults: bool = True, + converters: t.Optional[t.Mapping[str, t.Type[BaseConverter]]] = None, + sort_parameters: bool = False, + sort_key: t.Optional[t.Callable[[t.Any], t.Any]] = None, + encoding_errors: str = "replace", + host_matching: bool = False, + ) -> None: + self._rules: t.List[Rule] = [] + self._rules_by_endpoint: t.Dict[str, t.List[Rule]] = {} + self._remap = True + self._remap_lock = self.lock_class() + + self.default_subdomain = default_subdomain + self.charset = charset + self.encoding_errors = encoding_errors + self.strict_slashes = strict_slashes + self.merge_slashes = merge_slashes + self.redirect_defaults = redirect_defaults + self.host_matching = host_matching + + self.converters = self.default_converters.copy() + if converters: + self.converters.update(converters) + + self.sort_parameters = sort_parameters + self.sort_key = sort_key + + for rulefactory in rules or (): + self.add(rulefactory) + + def is_endpoint_expecting(self, endpoint: str, *arguments: str) -> bool: + """Iterate over all rules and check if the endpoint expects + the arguments provided. This is for example useful if you have + some URLs that expect a language code and others that do not and + you want to wrap the builder a bit so that the current language + code is automatically added if not provided but endpoints expect + it. + + :param endpoint: the endpoint to check. + :param arguments: this function accepts one or more arguments + as positional arguments. Each one of them is + checked. + """ + self.update() + arguments = set(arguments) + for rule in self._rules_by_endpoint[endpoint]: + if arguments.issubset(rule.arguments): + return True + return False + + def iter_rules(self, endpoint: t.Optional[str] = None) -> t.Iterator[Rule]: + """Iterate over all rules or the rules of an endpoint. + + :param endpoint: if provided only the rules for that endpoint + are returned. + :return: an iterator + """ + self.update() + if endpoint is not None: + return iter(self._rules_by_endpoint[endpoint]) + return iter(self._rules) + + def add(self, rulefactory: RuleFactory) -> None: + """Add a new rule or factory to the map and bind it. Requires that the + rule is not bound to another map. + + :param rulefactory: a :class:`Rule` or :class:`RuleFactory` + """ + for rule in rulefactory.get_rules(self): + rule.bind(self) + self._rules.append(rule) + self._rules_by_endpoint.setdefault(rule.endpoint, []).append(rule) + self._remap = True + + def bind( + self, + server_name: str, + script_name: t.Optional[str] = None, + subdomain: t.Optional[str] = None, + url_scheme: str = "http", + default_method: str = "GET", + path_info: t.Optional[str] = None, + query_args: t.Optional[t.Union[t.Mapping[str, t.Any], str]] = None, + ) -> "MapAdapter": + """Return a new :class:`MapAdapter` with the details specified to the + call. Note that `script_name` will default to ``'/'`` if not further + specified or `None`. The `server_name` at least is a requirement + because the HTTP RFC requires absolute URLs for redirects and so all + redirect exceptions raised by Werkzeug will contain the full canonical + URL. + + If no path_info is passed to :meth:`match` it will use the default path + info passed to bind. While this doesn't really make sense for + manual bind calls, it's useful if you bind a map to a WSGI + environment which already contains the path info. + + `subdomain` will default to the `default_subdomain` for this map if + no defined. If there is no `default_subdomain` you cannot use the + subdomain feature. + + .. versionchanged:: 1.0 + If ``url_scheme`` is ``ws`` or ``wss``, only WebSocket rules + will match. + + .. versionchanged:: 0.15 + ``path_info`` defaults to ``'/'`` if ``None``. + + .. versionchanged:: 0.8 + ``query_args`` can be a string. + + .. versionchanged:: 0.7 + Added ``query_args``. + """ + server_name = server_name.lower() + if self.host_matching: + if subdomain is not None: + raise RuntimeError("host matching enabled and a subdomain was provided") + elif subdomain is None: + subdomain = self.default_subdomain + if script_name is None: + script_name = "/" + if path_info is None: + path_info = "/" + try: + server_name = _encode_idna(server_name) # type: ignore + except UnicodeError: + raise BadHost() + return MapAdapter( + self, + server_name, + script_name, + subdomain, + url_scheme, + path_info, + default_method, + query_args, + ) + + def bind_to_environ( + self, + environ: "WSGIEnvironment", + server_name: t.Optional[str] = None, + subdomain: t.Optional[str] = None, + ) -> "MapAdapter": + """Like :meth:`bind` but you can pass it an WSGI environment and it + will fetch the information from that dictionary. Note that because of + limitations in the protocol there is no way to get the current + subdomain and real `server_name` from the environment. If you don't + provide it, Werkzeug will use `SERVER_NAME` and `SERVER_PORT` (or + `HTTP_HOST` if provided) as used `server_name` with disabled subdomain + feature. + + If `subdomain` is `None` but an environment and a server name is + provided it will calculate the current subdomain automatically. + Example: `server_name` is ``'example.com'`` and the `SERVER_NAME` + in the wsgi `environ` is ``'staging.dev.example.com'`` the calculated + subdomain will be ``'staging.dev'``. + + If the object passed as environ has an environ attribute, the value of + this attribute is used instead. This allows you to pass request + objects. Additionally `PATH_INFO` added as a default of the + :class:`MapAdapter` so that you don't have to pass the path info to + the match method. + + .. versionchanged:: 1.0.0 + If the passed server name specifies port 443, it will match + if the incoming scheme is ``https`` without a port. + + .. versionchanged:: 1.0.0 + A warning is shown when the passed server name does not + match the incoming WSGI server name. + + .. versionchanged:: 0.8 + This will no longer raise a ValueError when an unexpected server + name was passed. + + .. versionchanged:: 0.5 + previously this method accepted a bogus `calculate_subdomain` + parameter that did not have any effect. It was removed because + of that. + + :param environ: a WSGI environment. + :param server_name: an optional server name hint (see above). + :param subdomain: optionally the current subdomain (see above). + """ + environ = _get_environ(environ) + wsgi_server_name = get_host(environ).lower() + scheme = environ["wsgi.url_scheme"] + + if ( + environ.get("HTTP_CONNECTION", "").lower() == "upgrade" + and environ.get("HTTP_UPGRADE", "").lower() == "websocket" + ): + scheme = "wss" if scheme == "https" else "ws" + + if server_name is None: + server_name = wsgi_server_name + else: + server_name = server_name.lower() + + # strip standard port to match get_host() + if scheme in {"http", "ws"} and server_name.endswith(":80"): + server_name = server_name[:-3] + elif scheme in {"https", "wss"} and server_name.endswith(":443"): + server_name = server_name[:-4] + + if subdomain is None and not self.host_matching: + cur_server_name = wsgi_server_name.split(".") + real_server_name = server_name.split(".") + offset = -len(real_server_name) + + if cur_server_name[offset:] != real_server_name: + # This can happen even with valid configs if the server was + # accessed directly by IP address under some situations. + # Instead of raising an exception like in Werkzeug 0.7 or + # earlier we go by an invalid subdomain which will result + # in a 404 error on matching. + warnings.warn( + f"Current server name {wsgi_server_name!r} doesn't match configured" + f" server name {server_name!r}", + stacklevel=2, + ) + subdomain = "" + else: + subdomain = ".".join(filter(None, cur_server_name[:offset])) + + def _get_wsgi_string(name: str) -> t.Optional[str]: + val = environ.get(name) + if val is not None: + return _wsgi_decoding_dance(val, self.charset) + return None + + script_name = _get_wsgi_string("SCRIPT_NAME") + path_info = _get_wsgi_string("PATH_INFO") + query_args = _get_wsgi_string("QUERY_STRING") + return Map.bind( + self, + server_name, + script_name, + subdomain, + scheme, + environ["REQUEST_METHOD"], + path_info, + query_args=query_args, + ) + + def update(self) -> None: + """Called before matching and building to keep the compiled rules + in the correct order after things changed. + """ + if not self._remap: + return + + with self._remap_lock: + if not self._remap: + return + + self._rules.sort(key=lambda x: x.match_compare_key()) + for rules in self._rules_by_endpoint.values(): + rules.sort(key=lambda x: x.build_compare_key()) + self._remap = False + + def __repr__(self) -> str: + rules = self.iter_rules() + return f"{type(self).__name__}({pformat(list(rules))})" + + +class MapAdapter: + + """Returned by :meth:`Map.bind` or :meth:`Map.bind_to_environ` and does + the URL matching and building based on runtime information. + """ + + def __init__( + self, + map: Map, + server_name: str, + script_name: str, + subdomain: t.Optional[str], + url_scheme: str, + path_info: str, + default_method: str, + query_args: t.Optional[t.Union[t.Mapping[str, t.Any], str]] = None, + ): + self.map = map + self.server_name = _to_str(server_name) + script_name = _to_str(script_name) + if not script_name.endswith("/"): + script_name += "/" + self.script_name = script_name + self.subdomain = _to_str(subdomain) + self.url_scheme = _to_str(url_scheme) + self.path_info = _to_str(path_info) + self.default_method = _to_str(default_method) + self.query_args = query_args + self.websocket = self.url_scheme in {"ws", "wss"} + + def dispatch( + self, + view_func: t.Callable[[str, t.Mapping[str, t.Any]], "WSGIApplication"], + path_info: t.Optional[str] = None, + method: t.Optional[str] = None, + catch_http_exceptions: bool = False, + ) -> "WSGIApplication": + """Does the complete dispatching process. `view_func` is called with + the endpoint and a dict with the values for the view. It should + look up the view function, call it, and return a response object + or WSGI application. http exceptions are not caught by default + so that applications can display nicer error messages by just + catching them by hand. If you want to stick with the default + error messages you can pass it ``catch_http_exceptions=True`` and + it will catch the http exceptions. + + Here a small example for the dispatch usage:: + + from werkzeug.wrappers import Request, Response + from werkzeug.wsgi import responder + from werkzeug.routing import Map, Rule + + def on_index(request): + return Response('Hello from the index') + + url_map = Map([Rule('/', endpoint='index')]) + views = {'index': on_index} + + @responder + def application(environ, start_response): + request = Request(environ) + urls = url_map.bind_to_environ(environ) + return urls.dispatch(lambda e, v: views[e](request, **v), + catch_http_exceptions=True) + + Keep in mind that this method might return exception objects, too, so + use :class:`Response.force_type` to get a response object. + + :param view_func: a function that is called with the endpoint as + first argument and the value dict as second. Has + to dispatch to the actual view function with this + information. (see above) + :param path_info: the path info to use for matching. Overrides the + path info specified on binding. + :param method: the HTTP method used for matching. Overrides the + method specified on binding. + :param catch_http_exceptions: set to `True` to catch any of the + werkzeug :class:`HTTPException`\\s. + """ + try: + try: + endpoint, args = self.match(path_info, method) + except RequestRedirect as e: + return e + return view_func(endpoint, args) + except HTTPException as e: + if catch_http_exceptions: + return e + raise + + @typing.overload + def match( # type: ignore + self, + path_info: t.Optional[str] = None, + method: t.Optional[str] = None, + return_rule: "te.Literal[False]" = False, + query_args: t.Optional[t.Union[t.Mapping[str, t.Any], str]] = None, + websocket: t.Optional[bool] = None, + ) -> t.Tuple[str, t.Mapping[str, t.Any]]: + ... + + @typing.overload + def match( + self, + path_info: t.Optional[str] = None, + method: t.Optional[str] = None, + return_rule: "te.Literal[True]" = True, + query_args: t.Optional[t.Union[t.Mapping[str, t.Any], str]] = None, + websocket: t.Optional[bool] = None, + ) -> t.Tuple[Rule, t.Mapping[str, t.Any]]: + ... + + def match( + self, + path_info: t.Optional[str] = None, + method: t.Optional[str] = None, + return_rule: bool = False, + query_args: t.Optional[t.Union[t.Mapping[str, t.Any], str]] = None, + websocket: t.Optional[bool] = None, + ) -> t.Tuple[t.Union[str, Rule], t.Mapping[str, t.Any]]: + """The usage is simple: you just pass the match method the current + path info as well as the method (which defaults to `GET`). The + following things can then happen: + + - you receive a `NotFound` exception that indicates that no URL is + matching. A `NotFound` exception is also a WSGI application you + can call to get a default page not found page (happens to be the + same object as `werkzeug.exceptions.NotFound`) + + - you receive a `MethodNotAllowed` exception that indicates that there + is a match for this URL but not for the current request method. + This is useful for RESTful applications. + + - you receive a `RequestRedirect` exception with a `new_url` + attribute. This exception is used to notify you about a request + Werkzeug requests from your WSGI application. This is for example the + case if you request ``/foo`` although the correct URL is ``/foo/`` + You can use the `RequestRedirect` instance as response-like object + similar to all other subclasses of `HTTPException`. + + - you receive a ``WebsocketMismatch`` exception if the only + match is a WebSocket rule but the bind is an HTTP request, or + if the match is an HTTP rule but the bind is a WebSocket + request. + + - you get a tuple in the form ``(endpoint, arguments)`` if there is + a match (unless `return_rule` is True, in which case you get a tuple + in the form ``(rule, arguments)``) + + If the path info is not passed to the match method the default path + info of the map is used (defaults to the root URL if not defined + explicitly). + + All of the exceptions raised are subclasses of `HTTPException` so they + can be used as WSGI responses. They will all render generic error or + redirect pages. + + Here is a small example for matching: + + >>> m = Map([ + ... Rule('/', endpoint='index'), + ... Rule('/downloads/', endpoint='downloads/index'), + ... Rule('/downloads/', endpoint='downloads/show') + ... ]) + >>> urls = m.bind("example.com", "/") + >>> urls.match("/", "GET") + ('index', {}) + >>> urls.match("/downloads/42") + ('downloads/show', {'id': 42}) + + And here is what happens on redirect and missing URLs: + + >>> urls.match("/downloads") + Traceback (most recent call last): + ... + RequestRedirect: http://example.com/downloads/ + >>> urls.match("/missing") + Traceback (most recent call last): + ... + NotFound: 404 Not Found + + :param path_info: the path info to use for matching. Overrides the + path info specified on binding. + :param method: the HTTP method used for matching. Overrides the + method specified on binding. + :param return_rule: return the rule that matched instead of just the + endpoint (defaults to `False`). + :param query_args: optional query arguments that are used for + automatic redirects as string or dictionary. It's + currently not possible to use the query arguments + for URL matching. + :param websocket: Match WebSocket instead of HTTP requests. A + websocket request has a ``ws`` or ``wss`` + :attr:`url_scheme`. This overrides that detection. + + .. versionadded:: 1.0 + Added ``websocket``. + + .. versionchanged:: 0.8 + ``query_args`` can be a string. + + .. versionadded:: 0.7 + Added ``query_args``. + + .. versionadded:: 0.6 + Added ``return_rule``. + """ + self.map.update() + if path_info is None: + path_info = self.path_info + else: + path_info = _to_str(path_info, self.map.charset) + if query_args is None: + query_args = self.query_args or {} + method = (method or self.default_method).upper() + + if websocket is None: + websocket = self.websocket + + require_redirect = False + + domain_part = self.server_name if self.map.host_matching else self.subdomain + path_part = f"/{path_info.lstrip('/')}" if path_info else "" + path = f"{domain_part}|{path_part}" + + have_match_for = set() + websocket_mismatch = False + + for rule in self.map._rules: + try: + rv = rule.match(path, method) + except RequestPath as e: + raise RequestRedirect( + self.make_redirect_url( + url_quote(e.path_info, self.map.charset, safe="/:|+"), + query_args, + ) + ) + except RequestAliasRedirect as e: + raise RequestRedirect( + self.make_alias_redirect_url( + path, rule.endpoint, e.matched_values, method, query_args + ) + ) + if rv is None: + continue + if rule.methods is not None and method not in rule.methods: + have_match_for.update(rule.methods) + continue + + if rule.websocket != websocket: + websocket_mismatch = True + continue + + if self.map.redirect_defaults: + redirect_url = self.get_default_redirect(rule, method, rv, query_args) + if redirect_url is not None: + raise RequestRedirect(redirect_url) + + if rule.redirect_to is not None: + if isinstance(rule.redirect_to, str): + + def _handle_match(match: t.Match[str]) -> str: + value = rv[match.group(1)] # type: ignore + return rule._converters[match.group(1)].to_url(value) + + redirect_url = _simple_rule_re.sub(_handle_match, rule.redirect_to) + else: + redirect_url = rule.redirect_to(self, **rv) + + if self.subdomain: + netloc = f"{self.subdomain}.{self.server_name}" + else: + netloc = self.server_name + + raise RequestRedirect( + url_join( + f"{self.url_scheme or 'http'}://{netloc}{self.script_name}", + redirect_url, + ) + ) + + if require_redirect: + raise RequestRedirect( + self.make_redirect_url( + url_quote(path_info, self.map.charset, safe="/:|+"), query_args + ) + ) + + if return_rule: + return rule, rv + else: + return rule.endpoint, rv + + if have_match_for: + raise MethodNotAllowed(valid_methods=list(have_match_for)) + + if websocket_mismatch: + raise WebsocketMismatch() + + raise NotFound() + + def test( + self, path_info: t.Optional[str] = None, method: t.Optional[str] = None + ) -> bool: + """Test if a rule would match. Works like `match` but returns `True` + if the URL matches, or `False` if it does not exist. + + :param path_info: the path info to use for matching. Overrides the + path info specified on binding. + :param method: the HTTP method used for matching. Overrides the + method specified on binding. + """ + try: + self.match(path_info, method) + except RequestRedirect: + pass + except HTTPException: + return False + return True + + def allowed_methods(self, path_info: t.Optional[str] = None) -> t.Iterable[str]: + """Returns the valid methods that match for a given path. + + .. versionadded:: 0.7 + """ + try: + self.match(path_info, method="--") + except MethodNotAllowed as e: + return e.valid_methods # type: ignore + except HTTPException: + pass + return [] + + def get_host(self, domain_part: t.Optional[str]) -> str: + """Figures out the full host name for the given domain part. The + domain part is a subdomain in case host matching is disabled or + a full host name. + """ + if self.map.host_matching: + if domain_part is None: + return self.server_name + return _to_str(domain_part, "ascii") + subdomain = domain_part + if subdomain is None: + subdomain = self.subdomain + else: + subdomain = _to_str(subdomain, "ascii") + + if subdomain: + return f"{subdomain}.{self.server_name}" + else: + return self.server_name + + def get_default_redirect( + self, + rule: Rule, + method: str, + values: t.MutableMapping[str, t.Any], + query_args: t.Union[t.Mapping[str, t.Any], str], + ) -> t.Optional[str]: + """A helper that returns the URL to redirect to if it finds one. + This is used for default redirecting only. + + :internal: + """ + assert self.map.redirect_defaults + for r in self.map._rules_by_endpoint[rule.endpoint]: + # every rule that comes after this one, including ourself + # has a lower priority for the defaults. We order the ones + # with the highest priority up for building. + if r is rule: + break + if r.provides_defaults_for(rule) and r.suitable_for(values, method): + values.update(r.defaults) # type: ignore + domain_part, path = r.build(values) # type: ignore + return self.make_redirect_url(path, query_args, domain_part=domain_part) + return None + + def encode_query_args(self, query_args: t.Union[t.Mapping[str, t.Any], str]) -> str: + if not isinstance(query_args, str): + return url_encode(query_args, self.map.charset) + return query_args + + def make_redirect_url( + self, + path_info: str, + query_args: t.Optional[t.Union[t.Mapping[str, t.Any], str]] = None, + domain_part: t.Optional[str] = None, + ) -> str: + """Creates a redirect URL. + + :internal: + """ + if query_args: + suffix = f"?{self.encode_query_args(query_args)}" + else: + suffix = "" + + scheme = self.url_scheme or "http" + host = self.get_host(domain_part) + path = posixpath.join(self.script_name.strip("/"), path_info.lstrip("/")) + return f"{scheme}://{host}/{path}{suffix}" + + def make_alias_redirect_url( + self, + path: str, + endpoint: str, + values: t.Mapping[str, t.Any], + method: str, + query_args: t.Union[t.Mapping[str, t.Any], str], + ) -> str: + """Internally called to make an alias redirect URL.""" + url = self.build( + endpoint, values, method, append_unknown=False, force_external=True + ) + if query_args: + url += f"?{self.encode_query_args(query_args)}" + assert url != path, "detected invalid alias setting. No canonical URL found" + return url + + def _partial_build( + self, + endpoint: str, + values: t.Mapping[str, t.Any], + method: t.Optional[str], + append_unknown: bool, + ) -> t.Optional[t.Tuple[str, str, bool]]: + """Helper for :meth:`build`. Returns subdomain and path for the + rule that accepts this endpoint, values and method. + + :internal: + """ + # in case the method is none, try with the default method first + if method is None: + rv = self._partial_build( + endpoint, values, self.default_method, append_unknown + ) + if rv is not None: + return rv + + # Default method did not match or a specific method is passed. + # Check all for first match with matching host. If no matching + # host is found, go with first result. + first_match = None + + for rule in self.map._rules_by_endpoint.get(endpoint, ()): + if rule.suitable_for(values, method): + build_rv = rule.build(values, append_unknown) + + if build_rv is not None: + rv = (build_rv[0], build_rv[1], rule.websocket) + if self.map.host_matching: + if rv[0] == self.server_name: + return rv + elif first_match is None: + first_match = rv + else: + return rv + + return first_match + + def build( + self, + endpoint: str, + values: t.Optional[t.Mapping[str, t.Any]] = None, + method: t.Optional[str] = None, + force_external: bool = False, + append_unknown: bool = True, + url_scheme: t.Optional[str] = None, + ) -> str: + """Building URLs works pretty much the other way round. Instead of + `match` you call `build` and pass it the endpoint and a dict of + arguments for the placeholders. + + The `build` function also accepts an argument called `force_external` + which, if you set it to `True` will force external URLs. Per default + external URLs (include the server name) will only be used if the + target URL is on a different subdomain. + + >>> m = Map([ + ... Rule('/', endpoint='index'), + ... Rule('/downloads/', endpoint='downloads/index'), + ... Rule('/downloads/', endpoint='downloads/show') + ... ]) + >>> urls = m.bind("example.com", "/") + >>> urls.build("index", {}) + '/' + >>> urls.build("downloads/show", {'id': 42}) + '/downloads/42' + >>> urls.build("downloads/show", {'id': 42}, force_external=True) + 'http://example.com/downloads/42' + + Because URLs cannot contain non ASCII data you will always get + bytes back. Non ASCII characters are urlencoded with the + charset defined on the map instance. + + Additional values are converted to strings and appended to the URL as + URL querystring parameters: + + >>> urls.build("index", {'q': 'My Searchstring'}) + '/?q=My+Searchstring' + + When processing those additional values, lists are furthermore + interpreted as multiple values (as per + :py:class:`werkzeug.datastructures.MultiDict`): + + >>> urls.build("index", {'q': ['a', 'b', 'c']}) + '/?q=a&q=b&q=c' + + Passing a ``MultiDict`` will also add multiple values: + + >>> urls.build("index", MultiDict((('p', 'z'), ('q', 'a'), ('q', 'b')))) + '/?p=z&q=a&q=b' + + If a rule does not exist when building a `BuildError` exception is + raised. + + The build method accepts an argument called `method` which allows you + to specify the method you want to have an URL built for if you have + different methods for the same endpoint specified. + + :param endpoint: the endpoint of the URL to build. + :param values: the values for the URL to build. Unhandled values are + appended to the URL as query parameters. + :param method: the HTTP method for the rule if there are different + URLs for different methods on the same endpoint. + :param force_external: enforce full canonical external URLs. If the URL + scheme is not provided, this will generate + a protocol-relative URL. + :param append_unknown: unknown parameters are appended to the generated + URL as query string argument. Disable this + if you want the builder to ignore those. + :param url_scheme: Scheme to use in place of the bound + :attr:`url_scheme`. + + .. versionchanged:: 2.0 + Added the ``url_scheme`` parameter. + + .. versionadded:: 0.6 + Added the ``append_unknown`` parameter. + """ + self.map.update() + + if values: + temp_values: t.Dict[str, t.Union[t.List[t.Any], t.Any]] = {} + always_list = isinstance(values, MultiDict) + key: str + value: t.Optional[t.Union[t.List[t.Any], t.Any]] + + # For MultiDict, dict.items(values) is like values.lists() + # without the call or list coercion overhead. + for key, value in dict.items(values): # type: ignore + if value is None: + continue + + if always_list or isinstance(value, (list, tuple)): + value = [v for v in value if v is not None] + + if not value: + continue + + if len(value) == 1: + value = value[0] + + temp_values[key] = value + + values = temp_values + else: + values = {} + + rv = self._partial_build(endpoint, values, method, append_unknown) + if rv is None: + raise BuildError(endpoint, values, method, self) + + domain_part, path, websocket = rv + host = self.get_host(domain_part) + + if url_scheme is None: + url_scheme = self.url_scheme + + # Always build WebSocket routes with the scheme (browsers + # require full URLs). If bound to a WebSocket, ensure that HTTP + # routes are built with an HTTP scheme. + secure = url_scheme in {"https", "wss"} + + if websocket: + force_external = True + url_scheme = "wss" if secure else "ws" + elif url_scheme: + url_scheme = "https" if secure else "http" + + # shortcut this. + if not force_external and ( + (self.map.host_matching and host == self.server_name) + or (not self.map.host_matching and domain_part == self.subdomain) + ): + return f"{self.script_name.rstrip('/')}/{path.lstrip('/')}" + + scheme = f"{url_scheme}:" if url_scheme else "" + return f"{scheme}//{host}{self.script_name[:-1]}/{path.lstrip('/')}" diff --git a/venv/Lib/site-packages/werkzeug/sansio/__init__.py b/venv/Lib/site-packages/werkzeug/sansio/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/venv/Lib/site-packages/werkzeug/sansio/__pycache__/__init__.cpython-39.pyc b/venv/Lib/site-packages/werkzeug/sansio/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6f13abbc876128d7ea8a8645b3dbeeb9164da986 GIT binary patch literal 162 zcmYe~<>g`k0*=<#2_X70h(HF6K#l_t7qb9~6oz01O-8?!3`HPe1o6w=*(xTWC_gJT zxuiJ8zaTZwBR@AarYtqDEXF4@DW*8HBvrQ{F*!RiJ+(NdJhdphDz!9Szc?|kI5S^A fK0Y%qvm`!Vub}c4hfQvNN@-529ms;uK+FIDIp`?p literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/werkzeug/sansio/__pycache__/multipart.cpython-39.pyc b/venv/Lib/site-packages/werkzeug/sansio/__pycache__/multipart.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2eb3b6a3e6a37513d440f7347b7e631faae39698 GIT binary patch literal 6513 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/werkzeug/sansio/__pycache__/response.cpython-39.pyc b/venv/Lib/site-packages/werkzeug/sansio/__pycache__/response.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..676fe6002794b837cb131d85ce357d11a9ed2292 GIT binary patch literal 21087 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/werkzeug/sansio/multipart.py b/venv/Lib/site-packages/werkzeug/sansio/multipart.py new file mode 100644 index 0000000..bb8ab34 --- /dev/null +++ b/venv/Lib/site-packages/werkzeug/sansio/multipart.py @@ -0,0 +1,260 @@ +import re +from dataclasses import dataclass +from enum import auto +from enum import Enum +from typing import cast +from typing import List +from typing import Optional +from typing import Tuple + +from .._internal import _to_bytes +from .._internal import _to_str +from ..datastructures import Headers +from ..exceptions import RequestEntityTooLarge +from ..http import parse_options_header + + +class Event: + pass + + +@dataclass(frozen=True) +class Preamble(Event): + data: bytes + + +@dataclass(frozen=True) +class Field(Event): + name: str + headers: Headers + + +@dataclass(frozen=True) +class File(Event): + name: str + filename: str + headers: Headers + + +@dataclass(frozen=True) +class Data(Event): + data: bytes + more_data: bool + + +@dataclass(frozen=True) +class Epilogue(Event): + data: bytes + + +class NeedData(Event): + pass + + +NEED_DATA = NeedData() + + +class State(Enum): + PREAMBLE = auto() + PART = auto() + DATA = auto() + EPILOGUE = auto() + COMPLETE = auto() + + +# Multipart line breaks MUST be CRLF (\r\n) by RFC-7578, except that +# many implementations break this and either use CR or LF alone. +LINE_BREAK = b"(?:\r\n|\n|\r)" +BLANK_LINE_RE = re.compile(b"(?:\r\n\r\n|\r\r|\n\n)", re.MULTILINE) +LINE_BREAK_RE = re.compile(LINE_BREAK, re.MULTILINE) +# Header values can be continued via a space or tab after the linebreak, as +# per RFC2231 +HEADER_CONTINUATION_RE = re.compile(b"%s[ \t]" % LINE_BREAK, re.MULTILINE) + + +class MultipartDecoder: + """Decodes a multipart message as bytes into Python events. + + The part data is returned as available to allow the caller to save + the data from memory to disk, if desired. + """ + + def __init__( + self, + boundary: bytes, + max_form_memory_size: Optional[int] = None, + ) -> None: + self.buffer = bytearray() + self.complete = False + self.max_form_memory_size = max_form_memory_size + self.state = State.PREAMBLE + self.boundary = boundary + + # Note in the below \h i.e. horizontal whitespace is used + # as [^\S\n\r] as \h isn't supported in python. + + # The preamble must end with a boundary where the boundary is + # prefixed by a line break, RFC2046. Except that many + # implementations including Werkzeug's tests omit the line + # break prefix. In addition the first boundary could be the + # epilogue boundary (for empty form-data) hence the matching + # group to understand if it is an epilogue boundary. + self.preamble_re = re.compile( + br"%s?--%s(--[^\S\n\r]*%s?|[^\S\n\r]*%s)" + % (LINE_BREAK, re.escape(boundary), LINE_BREAK, LINE_BREAK), + re.MULTILINE, + ) + # A boundary must include a line break prefix and suffix, and + # may include trailing whitespace. In addition the boundary + # could be the epilogue boundary hence the matching group to + # understand if it is an epilogue boundary. + self.boundary_re = re.compile( + br"%s--%s(--[^\S\n\r]*%s?|[^\S\n\r]*%s)" + % (LINE_BREAK, re.escape(boundary), LINE_BREAK, LINE_BREAK), + re.MULTILINE, + ) + + def last_newline(self) -> int: + try: + last_nl = self.buffer.rindex(b"\n") + except ValueError: + last_nl = len(self.buffer) + try: + last_cr = self.buffer.rindex(b"\r") + except ValueError: + last_cr = len(self.buffer) + + return min(last_nl, last_cr) + + def receive_data(self, data: Optional[bytes]) -> None: + if data is None: + self.complete = True + elif ( + self.max_form_memory_size is not None + and len(self.buffer) + len(data) > self.max_form_memory_size + ): + raise RequestEntityTooLarge() + else: + self.buffer.extend(data) + + def next_event(self) -> Event: + event: Event = NEED_DATA + + if self.state == State.PREAMBLE: + match = self.preamble_re.search(self.buffer) + if match is not None: + if match.group(1).startswith(b"--"): + self.state = State.EPILOGUE + else: + self.state = State.PART + data = bytes(self.buffer[: match.start()]) + del self.buffer[: match.end()] + event = Preamble(data=data) + + elif self.state == State.PART: + match = BLANK_LINE_RE.search(self.buffer) + if match is not None: + headers = self._parse_headers(self.buffer[: match.start()]) + del self.buffer[: match.end()] + + if "content-disposition" not in headers: + raise ValueError("Missing Content-Disposition header") + + disposition, extra = parse_options_header( + headers["content-disposition"] + ) + name = cast(str, extra.get("name")) + filename = extra.get("filename") + if filename is not None: + event = File( + filename=filename, + headers=headers, + name=name, + ) + else: + event = Field( + headers=headers, + name=name, + ) + self.state = State.DATA + + elif self.state == State.DATA: + if self.buffer.find(b"--" + self.boundary) == -1: + # No complete boundary in the buffer, but there may be + # a partial boundary at the end. As the boundary + # starts with either a nl or cr find the earliest and + # return up to that as data. + data_length = del_index = self.last_newline() + more_data = True + else: + match = self.boundary_re.search(self.buffer) + if match is not None: + if match.group(1).startswith(b"--"): + self.state = State.EPILOGUE + else: + self.state = State.PART + data_length = match.start() + del_index = match.end() + else: + data_length = del_index = self.last_newline() + more_data = match is None + + data = bytes(self.buffer[:data_length]) + del self.buffer[:del_index] + if data or not more_data: + event = Data(data=data, more_data=more_data) + + elif self.state == State.EPILOGUE and self.complete: + event = Epilogue(data=bytes(self.buffer)) + del self.buffer[:] + self.state = State.COMPLETE + + if self.complete and isinstance(event, NeedData): + raise ValueError(f"Invalid form-data cannot parse beyond {self.state}") + + return event + + def _parse_headers(self, data: bytes) -> Headers: + headers: List[Tuple[str, str]] = [] + # Merge the continued headers into one line + data = HEADER_CONTINUATION_RE.sub(b" ", data) + # Now there is one header per line + for line in data.splitlines(): + if line.strip() != b"": + name, value = _to_str(line).strip().split(":", 1) + headers.append((name.strip(), value.strip())) + return Headers(headers) + + +class MultipartEncoder: + def __init__(self, boundary: bytes) -> None: + self.boundary = boundary + self.state = State.PREAMBLE + + def send_event(self, event: Event) -> bytes: + if isinstance(event, Preamble) and self.state == State.PREAMBLE: + self.state = State.PART + return event.data + elif isinstance(event, (Field, File)) and self.state in { + State.PREAMBLE, + State.PART, + State.DATA, + }: + self.state = State.DATA + data = b"\r\n--" + self.boundary + b"\r\n" + data += b'Content-Disposition: form-data; name="%s"' % _to_bytes(event.name) + if isinstance(event, File): + data += b'; filename="%s"' % _to_bytes(event.filename) + data += b"\r\n" + for name, value in cast(Field, event).headers: + if name.lower() != "content-disposition": + data += _to_bytes(f"{name}: {value}\r\n") + data += b"\r\n" + return data + elif isinstance(event, Data) and self.state == State.DATA: + return event.data + elif isinstance(event, Epilogue): + self.state = State.COMPLETE + return b"\r\n--" + self.boundary + b"--\r\n" + event.data + else: + raise ValueError(f"Cannot generate {event} in state: {self.state}") diff --git a/venv/Lib/site-packages/werkzeug/sansio/request.py b/venv/Lib/site-packages/werkzeug/sansio/request.py new file mode 100644 index 0000000..2c21a21 --- /dev/null +++ b/venv/Lib/site-packages/werkzeug/sansio/request.py @@ -0,0 +1,548 @@ +import typing as t +from datetime import datetime + +from .._internal import _to_str +from ..datastructures import Accept +from ..datastructures import Authorization +from ..datastructures import CharsetAccept +from ..datastructures import ETags +from ..datastructures import Headers +from ..datastructures import HeaderSet +from ..datastructures import IfRange +from ..datastructures import ImmutableList +from ..datastructures import ImmutableMultiDict +from ..datastructures import LanguageAccept +from ..datastructures import MIMEAccept +from ..datastructures import MultiDict +from ..datastructures import Range +from ..datastructures import RequestCacheControl +from ..http import parse_accept_header +from ..http import parse_authorization_header +from ..http import parse_cache_control_header +from ..http import parse_cookie +from ..http import parse_date +from ..http import parse_etags +from ..http import parse_if_range_header +from ..http import parse_list_header +from ..http import parse_options_header +from ..http import parse_range_header +from ..http import parse_set_header +from ..urls import url_decode +from ..user_agent import UserAgent +from ..useragents import _UserAgent as _DeprecatedUserAgent +from ..utils import cached_property +from ..utils import header_property +from .utils import get_current_url +from .utils import get_host + + +class Request: + """Represents the non-IO parts of a HTTP request, including the + method, URL info, and headers. + + This class is not meant for general use. It should only be used when + implementing WSGI, ASGI, or another HTTP application spec. Werkzeug + provides a WSGI implementation at :cls:`werkzeug.wrappers.Request`. + + :param method: The method the request was made with, such as + ``GET``. + :param scheme: The URL scheme of the protocol the request used, such + as ``https`` or ``wss``. + :param server: The address of the server. ``(host, port)``, + ``(path, None)`` for unix sockets, or ``None`` if not known. + :param root_path: The prefix that the application is mounted under. + This is prepended to generated URLs, but is not part of route + matching. + :param path: The path part of the URL after ``root_path``. + :param query_string: The part of the URL after the "?". + :param headers: The headers received with the request. + :param remote_addr: The address of the client sending the request. + + .. versionadded:: 2.0 + """ + + #: The charset used to decode most data in the request. + charset = "utf-8" + + #: the error handling procedure for errors, defaults to 'replace' + encoding_errors = "replace" + + #: the class to use for `args` and `form`. The default is an + #: :class:`~werkzeug.datastructures.ImmutableMultiDict` which supports + #: multiple values per key. alternatively it makes sense to use an + #: :class:`~werkzeug.datastructures.ImmutableOrderedMultiDict` which + #: preserves order or a :class:`~werkzeug.datastructures.ImmutableDict` + #: which is the fastest but only remembers the last key. It is also + #: possible to use mutable structures, but this is not recommended. + #: + #: .. versionadded:: 0.6 + parameter_storage_class: t.Type[MultiDict] = ImmutableMultiDict + + #: The type to be used for dict values from the incoming WSGI + #: environment. (For example for :attr:`cookies`.) By default an + #: :class:`~werkzeug.datastructures.ImmutableMultiDict` is used. + #: + #: .. versionchanged:: 1.0.0 + #: Changed to ``ImmutableMultiDict`` to support multiple values. + #: + #: .. versionadded:: 0.6 + dict_storage_class: t.Type[MultiDict] = ImmutableMultiDict + + #: the type to be used for list values from the incoming WSGI environment. + #: By default an :class:`~werkzeug.datastructures.ImmutableList` is used + #: (for example for :attr:`access_list`). + #: + #: .. versionadded:: 0.6 + list_storage_class: t.Type[t.List] = ImmutableList + + user_agent_class = _DeprecatedUserAgent + """The class used and returned by the :attr:`user_agent` property to + parse the header. Defaults to + :class:`~werkzeug.user_agent.UserAgent`, which does no parsing. An + extension can provide a subclass that uses a parser to provide other + data. + + .. versionadded:: 2.0 + """ + + #: Valid host names when handling requests. By default all hosts are + #: trusted, which means that whatever the client says the host is + #: will be accepted. + #: + #: Because ``Host`` and ``X-Forwarded-Host`` headers can be set to + #: any value by a malicious client, it is recommended to either set + #: this property or implement similar validation in the proxy (if + #: the application is being run behind one). + #: + #: .. versionadded:: 0.9 + trusted_hosts: t.Optional[t.List[str]] = None + + def __init__( + self, + method: str, + scheme: str, + server: t.Optional[t.Tuple[str, t.Optional[int]]], + root_path: str, + path: str, + query_string: bytes, + headers: Headers, + remote_addr: t.Optional[str], + ) -> None: + #: The method the request was made with, such as ``GET``. + self.method = method.upper() + #: The URL scheme of the protocol the request used, such as + #: ``https`` or ``wss``. + self.scheme = scheme + #: The address of the server. ``(host, port)``, ``(path, None)`` + #: for unix sockets, or ``None`` if not known. + self.server = server + #: The prefix that the application is mounted under, without a + #: trailing slash. :attr:`path` comes after this. + self.root_path = root_path.rstrip("/") + #: The path part of the URL after :attr:`root_path`. This is the + #: path used for routing within the application. + self.path = "/" + path.lstrip("/") + #: The part of the URL after the "?". This is the raw value, use + #: :attr:`args` for the parsed values. + self.query_string = query_string + #: The headers received with the request. + self.headers = headers + #: The address of the client sending the request. + self.remote_addr = remote_addr + + def __repr__(self) -> str: + try: + url = self.url + except Exception as e: + url = f"(invalid URL: {e})" + + return f"<{type(self).__name__} {url!r} [{self.method}]>" + + @property + def url_charset(self) -> str: + """The charset that is assumed for URLs. Defaults to the value + of :attr:`charset`. + + .. versionadded:: 0.6 + """ + return self.charset + + @cached_property + def args(self) -> "MultiDict[str, str]": + """The parsed URL parameters (the part in the URL after the question + mark). + + By default an + :class:`~werkzeug.datastructures.ImmutableMultiDict` + is returned from this function. This can be changed by setting + :attr:`parameter_storage_class` to a different type. This might + be necessary if the order of the form data is important. + """ + return url_decode( + self.query_string, + self.url_charset, + errors=self.encoding_errors, + cls=self.parameter_storage_class, + ) + + @cached_property + def access_route(self) -> t.List[str]: + """If a forwarded header exists this is a list of all ip addresses + from the client ip to the last proxy server. + """ + if "X-Forwarded-For" in self.headers: + return self.list_storage_class( + parse_list_header(self.headers["X-Forwarded-For"]) + ) + elif self.remote_addr is not None: + return self.list_storage_class([self.remote_addr]) + return self.list_storage_class() + + @cached_property + def full_path(self) -> str: + """Requested path, including the query string.""" + return f"{self.path}?{_to_str(self.query_string, self.url_charset)}" + + @property + def is_secure(self) -> bool: + """``True`` if the request was made with a secure protocol + (HTTPS or WSS). + """ + return self.scheme in {"https", "wss"} + + @cached_property + def url(self) -> str: + """The full request URL with the scheme, host, root path, path, + and query string.""" + return get_current_url( + self.scheme, self.host, self.root_path, self.path, self.query_string + ) + + @cached_property + def base_url(self) -> str: + """Like :attr:`url` but without the query string.""" + return get_current_url(self.scheme, self.host, self.root_path, self.path) + + @cached_property + def root_url(self) -> str: + """The request URL scheme, host, and root path. This is the root + that the application is accessed from. + """ + return get_current_url(self.scheme, self.host, self.root_path) + + @cached_property + def host_url(self) -> str: + """The request URL scheme and host only.""" + return get_current_url(self.scheme, self.host) + + @cached_property + def host(self) -> str: + """The host name the request was made to, including the port if + it's non-standard. Validated with :attr:`trusted_hosts`. + """ + return get_host( + self.scheme, self.headers.get("host"), self.server, self.trusted_hosts + ) + + @cached_property + def cookies(self) -> "ImmutableMultiDict[str, str]": + """A :class:`dict` with the contents of all cookies transmitted with + the request.""" + wsgi_combined_cookie = ";".join(self.headers.getlist("Cookie")) + return parse_cookie( # type: ignore + wsgi_combined_cookie, + self.charset, + self.encoding_errors, + cls=self.dict_storage_class, + ) + + # Common Descriptors + + content_type = header_property[str]( + "Content-Type", + doc="""The Content-Type entity-header field indicates the media + type of the entity-body sent to the recipient or, in the case of + the HEAD method, the media type that would have been sent had + the request been a GET.""", + read_only=True, + ) + + @cached_property + def content_length(self) -> t.Optional[int]: + """The Content-Length entity-header field indicates the size of the + entity-body in bytes or, in the case of the HEAD method, the size of + the entity-body that would have been sent had the request been a + GET. + """ + if self.headers.get("Transfer-Encoding", "") == "chunked": + return None + + content_length = self.headers.get("Content-Length") + if content_length is not None: + try: + return max(0, int(content_length)) + except (ValueError, TypeError): + pass + + return None + + content_encoding = header_property[str]( + "Content-Encoding", + doc="""The Content-Encoding entity-header field is used as a + modifier to the media-type. When present, its value indicates + what additional content codings have been applied to the + entity-body, and thus what decoding mechanisms must be applied + in order to obtain the media-type referenced by the Content-Type + header field. + + .. versionadded:: 0.9""", + read_only=True, + ) + content_md5 = header_property[str]( + "Content-MD5", + doc="""The Content-MD5 entity-header field, as defined in + RFC 1864, is an MD5 digest of the entity-body for the purpose of + providing an end-to-end message integrity check (MIC) of the + entity-body. (Note: a MIC is good for detecting accidental + modification of the entity-body in transit, but is not proof + against malicious attacks.) + + .. versionadded:: 0.9""", + read_only=True, + ) + referrer = header_property[str]( + "Referer", + doc="""The Referer[sic] request-header field allows the client + to specify, for the server's benefit, the address (URI) of the + resource from which the Request-URI was obtained (the + "referrer", although the header field is misspelled).""", + read_only=True, + ) + date = header_property( + "Date", + None, + parse_date, + doc="""The Date general-header field represents the date and + time at which the message was originated, having the same + semantics as orig-date in RFC 822. + + .. versionchanged:: 2.0 + The datetime object is timezone-aware. + """, + read_only=True, + ) + max_forwards = header_property( + "Max-Forwards", + None, + int, + doc="""The Max-Forwards request-header field provides a + mechanism with the TRACE and OPTIONS methods to limit the number + of proxies or gateways that can forward the request to the next + inbound server.""", + read_only=True, + ) + + def _parse_content_type(self) -> None: + if not hasattr(self, "_parsed_content_type"): + self._parsed_content_type = parse_options_header( + self.headers.get("Content-Type", "") + ) + + @property + def mimetype(self) -> str: + """Like :attr:`content_type`, but without parameters (eg, without + charset, type etc.) and always lowercase. For example if the content + type is ``text/HTML; charset=utf-8`` the mimetype would be + ``'text/html'``. + """ + self._parse_content_type() + return self._parsed_content_type[0].lower() + + @property + def mimetype_params(self) -> t.Dict[str, str]: + """The mimetype parameters as dict. For example if the content + type is ``text/html; charset=utf-8`` the params would be + ``{'charset': 'utf-8'}``. + """ + self._parse_content_type() + return self._parsed_content_type[1] + + @cached_property + def pragma(self) -> HeaderSet: + """The Pragma general-header field is used to include + implementation-specific directives that might apply to any recipient + along the request/response chain. All pragma directives specify + optional behavior from the viewpoint of the protocol; however, some + systems MAY require that behavior be consistent with the directives. + """ + return parse_set_header(self.headers.get("Pragma", "")) + + # Accept + + @cached_property + def accept_mimetypes(self) -> MIMEAccept: + """List of mimetypes this client supports as + :class:`~werkzeug.datastructures.MIMEAccept` object. + """ + return parse_accept_header(self.headers.get("Accept"), MIMEAccept) + + @cached_property + def accept_charsets(self) -> CharsetAccept: + """List of charsets this client supports as + :class:`~werkzeug.datastructures.CharsetAccept` object. + """ + return parse_accept_header(self.headers.get("Accept-Charset"), CharsetAccept) + + @cached_property + def accept_encodings(self) -> Accept: + """List of encodings this client accepts. Encodings in a HTTP term + are compression encodings such as gzip. For charsets have a look at + :attr:`accept_charset`. + """ + return parse_accept_header(self.headers.get("Accept-Encoding")) + + @cached_property + def accept_languages(self) -> LanguageAccept: + """List of languages this client accepts as + :class:`~werkzeug.datastructures.LanguageAccept` object. + + .. versionchanged 0.5 + In previous versions this was a regular + :class:`~werkzeug.datastructures.Accept` object. + """ + return parse_accept_header(self.headers.get("Accept-Language"), LanguageAccept) + + # ETag + + @cached_property + def cache_control(self) -> RequestCacheControl: + """A :class:`~werkzeug.datastructures.RequestCacheControl` object + for the incoming cache control headers. + """ + cache_control = self.headers.get("Cache-Control") + return parse_cache_control_header(cache_control, None, RequestCacheControl) + + @cached_property + def if_match(self) -> ETags: + """An object containing all the etags in the `If-Match` header. + + :rtype: :class:`~werkzeug.datastructures.ETags` + """ + return parse_etags(self.headers.get("If-Match")) + + @cached_property + def if_none_match(self) -> ETags: + """An object containing all the etags in the `If-None-Match` header. + + :rtype: :class:`~werkzeug.datastructures.ETags` + """ + return parse_etags(self.headers.get("If-None-Match")) + + @cached_property + def if_modified_since(self) -> t.Optional[datetime]: + """The parsed `If-Modified-Since` header as a datetime object. + + .. versionchanged:: 2.0 + The datetime object is timezone-aware. + """ + return parse_date(self.headers.get("If-Modified-Since")) + + @cached_property + def if_unmodified_since(self) -> t.Optional[datetime]: + """The parsed `If-Unmodified-Since` header as a datetime object. + + .. versionchanged:: 2.0 + The datetime object is timezone-aware. + """ + return parse_date(self.headers.get("If-Unmodified-Since")) + + @cached_property + def if_range(self) -> IfRange: + """The parsed ``If-Range`` header. + + .. versionchanged:: 2.0 + ``IfRange.date`` is timezone-aware. + + .. versionadded:: 0.7 + """ + return parse_if_range_header(self.headers.get("If-Range")) + + @cached_property + def range(self) -> t.Optional[Range]: + """The parsed `Range` header. + + .. versionadded:: 0.7 + + :rtype: :class:`~werkzeug.datastructures.Range` + """ + return parse_range_header(self.headers.get("Range")) + + # User Agent + + @cached_property + def user_agent(self) -> UserAgent: + """The user agent. Use ``user_agent.string`` to get the header + value. Set :attr:`user_agent_class` to a subclass of + :class:`~werkzeug.user_agent.UserAgent` to provide parsing for + the other properties or other extended data. + + .. versionchanged:: 2.0 + The built in parser is deprecated and will be removed in + Werkzeug 2.1. A ``UserAgent`` subclass must be set to parse + data from the string. + """ + return self.user_agent_class(self.headers.get("User-Agent", "")) + + # Authorization + + @cached_property + def authorization(self) -> t.Optional[Authorization]: + """The `Authorization` object in parsed form.""" + return parse_authorization_header(self.headers.get("Authorization")) + + # CORS + + origin = header_property[str]( + "Origin", + doc=( + "The host that the request originated from. Set" + " :attr:`~CORSResponseMixin.access_control_allow_origin` on" + " the response to indicate which origins are allowed." + ), + read_only=True, + ) + + access_control_request_headers = header_property( + "Access-Control-Request-Headers", + load_func=parse_set_header, + doc=( + "Sent with a preflight request to indicate which headers" + " will be sent with the cross origin request. Set" + " :attr:`~CORSResponseMixin.access_control_allow_headers`" + " on the response to indicate which headers are allowed." + ), + read_only=True, + ) + + access_control_request_method = header_property[str]( + "Access-Control-Request-Method", + doc=( + "Sent with a preflight request to indicate which method" + " will be used for the cross origin request. Set" + " :attr:`~CORSResponseMixin.access_control_allow_methods`" + " on the response to indicate which methods are allowed." + ), + read_only=True, + ) + + @property + def is_json(self) -> bool: + """Check if the mimetype indicates JSON data, either + :mimetype:`application/json` or :mimetype:`application/*+json`. + """ + mt = self.mimetype + return ( + mt == "application/json" + or mt.startswith("application/") + and mt.endswith("+json") + ) diff --git a/venv/Lib/site-packages/werkzeug/sansio/response.py b/venv/Lib/site-packages/werkzeug/sansio/response.py new file mode 100644 index 0000000..aedfcb0 --- /dev/null +++ b/venv/Lib/site-packages/werkzeug/sansio/response.py @@ -0,0 +1,656 @@ +import typing +import typing as t +from datetime import datetime +from datetime import timedelta +from datetime import timezone +from http import HTTPStatus + +from .._internal import _to_str +from ..datastructures import Headers +from ..datastructures import HeaderSet +from ..http import dump_cookie +from ..http import HTTP_STATUS_CODES +from ..utils import get_content_type +from werkzeug.datastructures import CallbackDict +from werkzeug.datastructures import ContentRange +from werkzeug.datastructures import ResponseCacheControl +from werkzeug.datastructures import WWWAuthenticate +from werkzeug.http import COEP +from werkzeug.http import COOP +from werkzeug.http import dump_age +from werkzeug.http import dump_csp_header +from werkzeug.http import dump_header +from werkzeug.http import dump_options_header +from werkzeug.http import http_date +from werkzeug.http import parse_age +from werkzeug.http import parse_cache_control_header +from werkzeug.http import parse_content_range_header +from werkzeug.http import parse_csp_header +from werkzeug.http import parse_date +from werkzeug.http import parse_options_header +from werkzeug.http import parse_set_header +from werkzeug.http import parse_www_authenticate_header +from werkzeug.http import quote_etag +from werkzeug.http import unquote_etag +from werkzeug.utils import header_property + + +def _set_property(name: str, doc: t.Optional[str] = None) -> property: + def fget(self: "Response") -> HeaderSet: + def on_update(header_set: HeaderSet) -> None: + if not header_set and name in self.headers: + del self.headers[name] + elif header_set: + self.headers[name] = header_set.to_header() + + return parse_set_header(self.headers.get(name), on_update) + + def fset( + self: "Response", + value: t.Optional[ + t.Union[str, t.Dict[str, t.Union[str, int]], t.Iterable[str]] + ], + ) -> None: + if not value: + del self.headers[name] + elif isinstance(value, str): + self.headers[name] = value + else: + self.headers[name] = dump_header(value) + + return property(fget, fset, doc=doc) + + +class Response: + """Represents the non-IO parts of an HTTP response, specifically the + status and headers but not the body. + + This class is not meant for general use. It should only be used when + implementing WSGI, ASGI, or another HTTP application spec. Werkzeug + provides a WSGI implementation at :cls:`werkzeug.wrappers.Response`. + + :param status: The status code for the response. Either an int, in + which case the default status message is added, or a string in + the form ``{code} {message}``, like ``404 Not Found``. Defaults + to 200. + :param headers: A :class:`~werkzeug.datastructures.Headers` object, + or a list of ``(key, value)`` tuples that will be converted to a + ``Headers`` object. + :param mimetype: The mime type (content type without charset or + other parameters) of the response. If the value starts with + ``text/`` (or matches some other special cases), the charset + will be added to create the ``content_type``. + :param content_type: The full content type of the response. + Overrides building the value from ``mimetype``. + + .. versionadded:: 2.0 + """ + + #: the charset of the response. + charset = "utf-8" + + #: the default status if none is provided. + default_status = 200 + + #: the default mimetype if none is provided. + default_mimetype = "text/plain" + + #: Warn if a cookie header exceeds this size. The default, 4093, should be + #: safely `supported by most browsers `_. A cookie larger than + #: this size will still be sent, but it may be ignored or handled + #: incorrectly by some browsers. Set to 0 to disable this check. + #: + #: .. versionadded:: 0.13 + #: + #: .. _`cookie`: http://browsercookielimits.squawky.net/ + max_cookie_size = 4093 + + # A :class:`Headers` object representing the response headers. + headers: Headers + + def __init__( + self, + status: t.Optional[t.Union[int, str, HTTPStatus]] = None, + headers: t.Optional[ + t.Union[ + t.Mapping[str, t.Union[str, int, t.Iterable[t.Union[str, int]]]], + t.Iterable[t.Tuple[str, t.Union[str, int]]], + ] + ] = None, + mimetype: t.Optional[str] = None, + content_type: t.Optional[str] = None, + ) -> None: + if isinstance(headers, Headers): + self.headers = headers + elif not headers: + self.headers = Headers() + else: + self.headers = Headers(headers) + + if content_type is None: + if mimetype is None and "content-type" not in self.headers: + mimetype = self.default_mimetype + if mimetype is not None: + mimetype = get_content_type(mimetype, self.charset) + content_type = mimetype + if content_type is not None: + self.headers["Content-Type"] = content_type + if status is None: + status = self.default_status + self.status = status # type: ignore + + def __repr__(self) -> str: + return f"<{type(self).__name__} [{self.status}]>" + + @property + def status_code(self) -> int: + """The HTTP status code as a number.""" + return self._status_code + + @status_code.setter + def status_code(self, code: int) -> None: + self.status = code # type: ignore + + @property + def status(self) -> str: + """The HTTP status code as a string.""" + return self._status + + @status.setter + def status(self, value: t.Union[str, int, HTTPStatus]) -> None: + if not isinstance(value, (str, bytes, int, HTTPStatus)): + raise TypeError("Invalid status argument") + + self._status, self._status_code = self._clean_status(value) + + def _clean_status(self, value: t.Union[str, int, HTTPStatus]) -> t.Tuple[str, int]: + if isinstance(value, HTTPStatus): + value = int(value) + status = _to_str(value, self.charset) + split_status = status.split(None, 1) + + if len(split_status) == 0: + raise ValueError("Empty status argument") + + if len(split_status) > 1: + if split_status[0].isdigit(): + # code and message + return status, int(split_status[0]) + + # multi-word message + return f"0 {status}", 0 + + if split_status[0].isdigit(): + # code only + status_code = int(split_status[0]) + + try: + status = f"{status_code} {HTTP_STATUS_CODES[status_code].upper()}" + except KeyError: + status = f"{status_code} UNKNOWN" + + return status, status_code + + # one-word message + return f"0 {status}", 0 + + def set_cookie( + self, + key: str, + value: str = "", + max_age: t.Optional[t.Union[timedelta, int]] = None, + expires: t.Optional[t.Union[str, datetime, int, float]] = None, + path: t.Optional[str] = "/", + domain: t.Optional[str] = None, + secure: bool = False, + httponly: bool = False, + samesite: t.Optional[str] = None, + ) -> None: + """Sets a cookie. + + A warning is raised if the size of the cookie header exceeds + :attr:`max_cookie_size`, but the header will still be set. + + :param key: the key (name) of the cookie to be set. + :param value: the value of the cookie. + :param max_age: should be a number of seconds, or `None` (default) if + the cookie should last only as long as the client's + browser session. + :param expires: should be a `datetime` object or UNIX timestamp. + :param path: limits the cookie to a given path, per default it will + span the whole domain. + :param domain: if you want to set a cross-domain cookie. For example, + ``domain=".example.com"`` will set a cookie that is + readable by the domain ``www.example.com``, + ``foo.example.com`` etc. Otherwise, a cookie will only + be readable by the domain that set it. + :param secure: If ``True``, the cookie will only be available + via HTTPS. + :param httponly: Disallow JavaScript access to the cookie. + :param samesite: Limit the scope of the cookie to only be + attached to requests that are "same-site". + """ + self.headers.add( + "Set-Cookie", + dump_cookie( + key, + value=value, + max_age=max_age, + expires=expires, + path=path, + domain=domain, + secure=secure, + httponly=httponly, + charset=self.charset, + max_size=self.max_cookie_size, + samesite=samesite, + ), + ) + + def delete_cookie( + self, + key: str, + path: str = "/", + domain: t.Optional[str] = None, + secure: bool = False, + httponly: bool = False, + samesite: t.Optional[str] = None, + ) -> None: + """Delete a cookie. Fails silently if key doesn't exist. + + :param key: the key (name) of the cookie to be deleted. + :param path: if the cookie that should be deleted was limited to a + path, the path has to be defined here. + :param domain: if the cookie that should be deleted was limited to a + domain, that domain has to be defined here. + :param secure: If ``True``, the cookie will only be available + via HTTPS. + :param httponly: Disallow JavaScript access to the cookie. + :param samesite: Limit the scope of the cookie to only be + attached to requests that are "same-site". + """ + self.set_cookie( + key, + expires=0, + max_age=0, + path=path, + domain=domain, + secure=secure, + httponly=httponly, + samesite=samesite, + ) + + @property + def is_json(self) -> bool: + """Check if the mimetype indicates JSON data, either + :mimetype:`application/json` or :mimetype:`application/*+json`. + """ + mt = self.mimetype + return mt is not None and ( + mt == "application/json" + or mt.startswith("application/") + and mt.endswith("+json") + ) + + # Common Descriptors + + @property + def mimetype(self) -> t.Optional[str]: + """The mimetype (content type without charset etc.)""" + ct = self.headers.get("content-type") + + if ct: + return ct.split(";")[0].strip() + else: + return None + + @mimetype.setter + def mimetype(self, value: str) -> None: + self.headers["Content-Type"] = get_content_type(value, self.charset) + + @property + def mimetype_params(self) -> t.Dict[str, str]: + """The mimetype parameters as dict. For example if the + content type is ``text/html; charset=utf-8`` the params would be + ``{'charset': 'utf-8'}``. + + .. versionadded:: 0.5 + """ + + def on_update(d: t.Dict[str, str]) -> None: + self.headers["Content-Type"] = dump_options_header(self.mimetype, d) + + d = parse_options_header(self.headers.get("content-type", ""))[1] + return CallbackDict(d, on_update) + + location = header_property[str]( + "Location", + doc="""The Location response-header field is used to redirect + the recipient to a location other than the Request-URI for + completion of the request or identification of a new + resource.""", + ) + age = header_property( + "Age", + None, + parse_age, + dump_age, # type: ignore + doc="""The Age response-header field conveys the sender's + estimate of the amount of time since the response (or its + revalidation) was generated at the origin server. + + Age values are non-negative decimal integers, representing time + in seconds.""", + ) + content_type = header_property[str]( + "Content-Type", + doc="""The Content-Type entity-header field indicates the media + type of the entity-body sent to the recipient or, in the case of + the HEAD method, the media type that would have been sent had + the request been a GET.""", + ) + content_length = header_property( + "Content-Length", + None, + int, + str, + doc="""The Content-Length entity-header field indicates the size + of the entity-body, in decimal number of OCTETs, sent to the + recipient or, in the case of the HEAD method, the size of the + entity-body that would have been sent had the request been a + GET.""", + ) + content_location = header_property[str]( + "Content-Location", + doc="""The Content-Location entity-header field MAY be used to + supply the resource location for the entity enclosed in the + message when that entity is accessible from a location separate + from the requested resource's URI.""", + ) + content_encoding = header_property[str]( + "Content-Encoding", + doc="""The Content-Encoding entity-header field is used as a + modifier to the media-type. When present, its value indicates + what additional content codings have been applied to the + entity-body, and thus what decoding mechanisms must be applied + in order to obtain the media-type referenced by the Content-Type + header field.""", + ) + content_md5 = header_property[str]( + "Content-MD5", + doc="""The Content-MD5 entity-header field, as defined in + RFC 1864, is an MD5 digest of the entity-body for the purpose of + providing an end-to-end message integrity check (MIC) of the + entity-body. (Note: a MIC is good for detecting accidental + modification of the entity-body in transit, but is not proof + against malicious attacks.)""", + ) + date = header_property( + "Date", + None, + parse_date, + http_date, + doc="""The Date general-header field represents the date and + time at which the message was originated, having the same + semantics as orig-date in RFC 822. + + .. versionchanged:: 2.0 + The datetime object is timezone-aware. + """, + ) + expires = header_property( + "Expires", + None, + parse_date, + http_date, + doc="""The Expires entity-header field gives the date/time after + which the response is considered stale. A stale cache entry may + not normally be returned by a cache. + + .. versionchanged:: 2.0 + The datetime object is timezone-aware. + """, + ) + last_modified = header_property( + "Last-Modified", + None, + parse_date, + http_date, + doc="""The Last-Modified entity-header field indicates the date + and time at which the origin server believes the variant was + last modified. + + .. versionchanged:: 2.0 + The datetime object is timezone-aware. + """, + ) + + @property + def retry_after(self) -> t.Optional[datetime]: + """The Retry-After response-header field can be used with a + 503 (Service Unavailable) response to indicate how long the + service is expected to be unavailable to the requesting client. + + Time in seconds until expiration or date. + + .. versionchanged:: 2.0 + The datetime object is timezone-aware. + """ + value = self.headers.get("retry-after") + if value is None: + return None + elif value.isdigit(): + return datetime.now(timezone.utc) + timedelta(seconds=int(value)) + return parse_date(value) + + @retry_after.setter + def retry_after(self, value: t.Optional[t.Union[datetime, int, str]]) -> None: + if value is None: + if "retry-after" in self.headers: + del self.headers["retry-after"] + return + elif isinstance(value, datetime): + value = http_date(value) + else: + value = str(value) + self.headers["Retry-After"] = value + + vary = _set_property( + "Vary", + doc="""The Vary field value indicates the set of request-header + fields that fully determines, while the response is fresh, + whether a cache is permitted to use the response to reply to a + subsequent request without revalidation.""", + ) + content_language = _set_property( + "Content-Language", + doc="""The Content-Language entity-header field describes the + natural language(s) of the intended audience for the enclosed + entity. Note that this might not be equivalent to all the + languages used within the entity-body.""", + ) + allow = _set_property( + "Allow", + doc="""The Allow entity-header field lists the set of methods + supported by the resource identified by the Request-URI. The + purpose of this field is strictly to inform the recipient of + valid methods associated with the resource. An Allow header + field MUST be present in a 405 (Method Not Allowed) + response.""", + ) + + # ETag + + @property + def cache_control(self) -> ResponseCacheControl: + """The Cache-Control general-header field is used to specify + directives that MUST be obeyed by all caching mechanisms along the + request/response chain. + """ + + def on_update(cache_control: ResponseCacheControl) -> None: + if not cache_control and "cache-control" in self.headers: + del self.headers["cache-control"] + elif cache_control: + self.headers["Cache-Control"] = cache_control.to_header() + + return parse_cache_control_header( + self.headers.get("cache-control"), on_update, ResponseCacheControl + ) + + def set_etag(self, etag: str, weak: bool = False) -> None: + """Set the etag, and override the old one if there was one.""" + self.headers["ETag"] = quote_etag(etag, weak) + + def get_etag(self) -> t.Union[t.Tuple[str, bool], t.Tuple[None, None]]: + """Return a tuple in the form ``(etag, is_weak)``. If there is no + ETag the return value is ``(None, None)``. + """ + return unquote_etag(self.headers.get("ETag")) + + accept_ranges = header_property[str]( + "Accept-Ranges", + doc="""The `Accept-Ranges` header. Even though the name would + indicate that multiple values are supported, it must be one + string token only. + + The values ``'bytes'`` and ``'none'`` are common. + + .. versionadded:: 0.7""", + ) + + @property + def content_range(self) -> ContentRange: + """The ``Content-Range`` header as a + :class:`~werkzeug.datastructures.ContentRange` object. Available + even if the header is not set. + + .. versionadded:: 0.7 + """ + + def on_update(rng: ContentRange) -> None: + if not rng: + del self.headers["content-range"] + else: + self.headers["Content-Range"] = rng.to_header() + + rv = parse_content_range_header(self.headers.get("content-range"), on_update) + # always provide a content range object to make the descriptor + # more user friendly. It provides an unset() method that can be + # used to remove the header quickly. + if rv is None: + rv = ContentRange(None, None, None, on_update=on_update) + return rv + + @content_range.setter + def content_range(self, value: t.Optional[t.Union[ContentRange, str]]) -> None: + if not value: + del self.headers["content-range"] + elif isinstance(value, str): + self.headers["Content-Range"] = value + else: + self.headers["Content-Range"] = value.to_header() + + # Authorization + + @property + def www_authenticate(self) -> WWWAuthenticate: + """The ``WWW-Authenticate`` header in a parsed form.""" + + def on_update(www_auth: WWWAuthenticate) -> None: + if not www_auth and "www-authenticate" in self.headers: + del self.headers["www-authenticate"] + elif www_auth: + self.headers["WWW-Authenticate"] = www_auth.to_header() + + header = self.headers.get("www-authenticate") + return parse_www_authenticate_header(header, on_update) + + # CSP + + content_security_policy = header_property( + "Content-Security-Policy", + None, + parse_csp_header, # type: ignore + dump_csp_header, + doc="""The Content-Security-Policy header adds an additional layer of + security to help detect and mitigate certain types of attacks.""", + ) + content_security_policy_report_only = header_property( + "Content-Security-Policy-Report-Only", + None, + parse_csp_header, # type: ignore + dump_csp_header, + doc="""The Content-Security-Policy-Report-Only header adds a csp policy + that is not enforced but is reported thereby helping detect + certain types of attacks.""", + ) + + # CORS + + @property + def access_control_allow_credentials(self) -> bool: + """Whether credentials can be shared by the browser to + JavaScript code. As part of the preflight request it indicates + whether credentials can be used on the cross origin request. + """ + return "Access-Control-Allow-Credentials" in self.headers + + @access_control_allow_credentials.setter + def access_control_allow_credentials(self, value: t.Optional[bool]) -> None: + if value is True: + self.headers["Access-Control-Allow-Credentials"] = "true" + else: + self.headers.pop("Access-Control-Allow-Credentials", None) + + access_control_allow_headers = header_property( + "Access-Control-Allow-Headers", + load_func=parse_set_header, + dump_func=dump_header, + doc="Which headers can be sent with the cross origin request.", + ) + + access_control_allow_methods = header_property( + "Access-Control-Allow-Methods", + load_func=parse_set_header, + dump_func=dump_header, + doc="Which methods can be used for the cross origin request.", + ) + + access_control_allow_origin = header_property[str]( + "Access-Control-Allow-Origin", + doc="The origin or '*' for any origin that may make cross origin requests.", + ) + + access_control_expose_headers = header_property( + "Access-Control-Expose-Headers", + load_func=parse_set_header, + dump_func=dump_header, + doc="Which headers can be shared by the browser to JavaScript code.", + ) + + access_control_max_age = header_property( + "Access-Control-Max-Age", + load_func=int, + dump_func=str, + doc="The maximum age in seconds the access control settings can be cached for.", + ) + + cross_origin_opener_policy = header_property[COOP]( + "Cross-Origin-Opener-Policy", + load_func=lambda value: COOP(value), + dump_func=lambda value: value.value, + default=COOP.UNSAFE_NONE, + doc="""Allows control over sharing of browsing context group with cross-origin + documents. Values must be a member of the :class:`werkzeug.http.COOP` enum.""", + ) + + cross_origin_embedder_policy = header_property[COEP]( + "Cross-Origin-Embedder-Policy", + load_func=lambda value: COEP(value), + dump_func=lambda value: value.value, + default=COEP.UNSAFE_NONE, + doc="""Prevents a document from loading any cross-origin resources that do not + explicitly grant the document permission. Values must be a member of the + :class:`werkzeug.http.COEP` enum.""", + ) diff --git a/venv/Lib/site-packages/werkzeug/sansio/utils.py b/venv/Lib/site-packages/werkzeug/sansio/utils.py new file mode 100644 index 0000000..1b4d892 --- /dev/null +++ b/venv/Lib/site-packages/werkzeug/sansio/utils.py @@ -0,0 +1,142 @@ +import typing as t + +from .._internal import _encode_idna +from ..exceptions import SecurityError +from ..urls import uri_to_iri +from ..urls import url_quote + + +def host_is_trusted(hostname: str, trusted_list: t.Iterable[str]) -> bool: + """Check if a host matches a list of trusted names. + + :param hostname: The name to check. + :param trusted_list: A list of valid names to match. If a name + starts with a dot it will match all subdomains. + + .. versionadded:: 0.9 + """ + if not hostname: + return False + + if isinstance(trusted_list, str): + trusted_list = [trusted_list] + + def _normalize(hostname: str) -> bytes: + if ":" in hostname: + hostname = hostname.rsplit(":", 1)[0] + + return _encode_idna(hostname) + + try: + hostname_bytes = _normalize(hostname) + except UnicodeError: + return False + + for ref in trusted_list: + if ref.startswith("."): + ref = ref[1:] + suffix_match = True + else: + suffix_match = False + + try: + ref_bytes = _normalize(ref) + except UnicodeError: + return False + + if ref_bytes == hostname_bytes: + return True + + if suffix_match and hostname_bytes.endswith(b"." + ref_bytes): + return True + + return False + + +def get_host( + scheme: str, + host_header: t.Optional[str], + server: t.Optional[t.Tuple[str, t.Optional[int]]] = None, + trusted_hosts: t.Optional[t.Iterable[str]] = None, +) -> str: + """Return the host for the given parameters. + + This first checks the ``host_header``. If it's not present, then + ``server`` is used. The host will only contain the port if it is + different than the standard port for the protocol. + + Optionally, verify that the host is trusted using + :func:`host_is_trusted` and raise a + :exc:`~werkzeug.exceptions.SecurityError` if it is not. + + :param scheme: The protocol the request used, like ``"https"``. + :param host_header: The ``Host`` header value. + :param server: Address of the server. ``(host, port)``, or + ``(path, None)`` for unix sockets. + :param trusted_hosts: A list of trusted host names. + + :return: Host, with port if necessary. + :raise ~werkzeug.exceptions.SecurityError: If the host is not + trusted. + """ + host = "" + + if host_header is not None: + host = host_header + elif server is not None: + host = server[0] + + if server[1] is not None: + host = f"{host}:{server[1]}" + + if scheme in {"http", "ws"} and host.endswith(":80"): + host = host[:-3] + elif scheme in {"https", "wss"} and host.endswith(":443"): + host = host[:-4] + + if trusted_hosts is not None: + if not host_is_trusted(host, trusted_hosts): + raise SecurityError(f"Host {host!r} is not trusted.") + + return host + + +def get_current_url( + scheme: str, + host: str, + root_path: t.Optional[str] = None, + path: t.Optional[str] = None, + query_string: t.Optional[bytes] = None, +) -> str: + """Recreate the URL for a request. If an optional part isn't + provided, it and subsequent parts are not included in the URL. + + The URL is an IRI, not a URI, so it may contain Unicode characters. + Use :func:`~werkzeug.urls.iri_to_uri` to convert it to ASCII. + + :param scheme: The protocol the request used, like ``"https"``. + :param host: The host the request was made to. See :func:`get_host`. + :param root_path: Prefix that the application is mounted under. This + is prepended to ``path``. + :param path: The path part of the URL after ``root_path``. + :param query_string: The portion of the URL after the "?". + """ + url = [scheme, "://", host] + + if root_path is None: + url.append("/") + return uri_to_iri("".join(url)) + + url.append(url_quote(root_path.rstrip("/"))) + url.append("/") + + if path is None: + return uri_to_iri("".join(url)) + + url.append(url_quote(path.lstrip("/"))) + + if query_string: + url.append("?") + url.append(url_quote(query_string, safe=":&%=+$!*'(),")) + + return uri_to_iri("".join(url)) diff --git a/venv/Lib/site-packages/werkzeug/security.py b/venv/Lib/site-packages/werkzeug/security.py new file mode 100644 index 0000000..e23040a --- /dev/null +++ b/venv/Lib/site-packages/werkzeug/security.py @@ -0,0 +1,247 @@ +import hashlib +import hmac +import os +import posixpath +import secrets +import typing as t +import warnings + +if t.TYPE_CHECKING: + pass + +SALT_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" +DEFAULT_PBKDF2_ITERATIONS = 260000 + +_os_alt_seps: t.List[str] = list( + sep for sep in [os.path.sep, os.path.altsep] if sep is not None and sep != "/" +) + + +def pbkdf2_hex( + data: t.Union[str, bytes], + salt: t.Union[str, bytes], + iterations: int = DEFAULT_PBKDF2_ITERATIONS, + keylen: t.Optional[int] = None, + hashfunc: t.Optional[t.Union[str, t.Callable]] = None, +) -> str: + """Like :func:`pbkdf2_bin`, but returns a hex-encoded string. + + :param data: the data to derive. + :param salt: the salt for the derivation. + :param iterations: the number of iterations. + :param keylen: the length of the resulting key. If not provided, + the digest size will be used. + :param hashfunc: the hash function to use. This can either be the + string name of a known hash function, or a function + from the hashlib module. Defaults to sha256. + + .. deprecated:: 2.0 + Will be removed in Werkzeug 2.1. Use :func:`hashlib.pbkdf2_hmac` + instead. + + .. versionadded:: 0.9 + """ + warnings.warn( + "'pbkdf2_hex' is deprecated and will be removed in Werkzeug" + " 2.1. Use 'hashlib.pbkdf2_hmac().hex()' instead.", + DeprecationWarning, + stacklevel=2, + ) + return pbkdf2_bin(data, salt, iterations, keylen, hashfunc).hex() + + +def pbkdf2_bin( + data: t.Union[str, bytes], + salt: t.Union[str, bytes], + iterations: int = DEFAULT_PBKDF2_ITERATIONS, + keylen: t.Optional[int] = None, + hashfunc: t.Optional[t.Union[str, t.Callable]] = None, +) -> bytes: + """Returns a binary digest for the PBKDF2 hash algorithm of `data` + with the given `salt`. It iterates `iterations` times and produces a + key of `keylen` bytes. By default, SHA-256 is used as hash function; + a different hashlib `hashfunc` can be provided. + + :param data: the data to derive. + :param salt: the salt for the derivation. + :param iterations: the number of iterations. + :param keylen: the length of the resulting key. If not provided + the digest size will be used. + :param hashfunc: the hash function to use. This can either be the + string name of a known hash function or a function + from the hashlib module. Defaults to sha256. + + .. deprecated:: 2.0 + Will be removed in Werkzeug 2.1. Use :func:`hashlib.pbkdf2_hmac` + instead. + + .. versionadded:: 0.9 + """ + warnings.warn( + "'pbkdf2_bin' is deprecated and will be removed in Werkzeug" + " 2.1. Use 'hashlib.pbkdf2_hmac()' instead.", + DeprecationWarning, + stacklevel=2, + ) + + if isinstance(data, str): + data = data.encode("utf8") + + if isinstance(salt, str): + salt = salt.encode("utf8") + + if not hashfunc: + hash_name = "sha256" + elif callable(hashfunc): + hash_name = hashfunc().name + else: + hash_name = hashfunc + + return hashlib.pbkdf2_hmac(hash_name, data, salt, iterations, keylen) + + +def safe_str_cmp(a: str, b: str) -> bool: + """This function compares strings in somewhat constant time. This + requires that the length of at least one string is known in advance. + + Returns `True` if the two strings are equal, or `False` if they are not. + + .. deprecated:: 2.0 + Will be removed in Werkzeug 2.1. Use + :func:`hmac.compare_digest` instead. + + .. versionadded:: 0.7 + """ + warnings.warn( + "'safe_str_cmp' is deprecated and will be removed in Werkzeug" + " 2.1. Use 'hmac.compare_digest' instead.", + DeprecationWarning, + stacklevel=2, + ) + + if isinstance(a, str): + a = a.encode("utf-8") # type: ignore + + if isinstance(b, str): + b = b.encode("utf-8") # type: ignore + + return hmac.compare_digest(a, b) + + +def gen_salt(length: int) -> str: + """Generate a random string of SALT_CHARS with specified ``length``.""" + if length <= 0: + raise ValueError("Salt length must be positive") + + return "".join(secrets.choice(SALT_CHARS) for _ in range(length)) + + +def _hash_internal(method: str, salt: str, password: str) -> t.Tuple[str, str]: + """Internal password hash helper. Supports plaintext without salt, + unsalted and salted passwords. In case salted passwords are used + hmac is used. + """ + if method == "plain": + return password, method + + salt = salt.encode("utf-8") + password = password.encode("utf-8") + + if method.startswith("pbkdf2:"): + if not salt: + raise ValueError("Salt is required for PBKDF2") + + args = method[7:].split(":") + + if len(args) not in (1, 2): + raise ValueError("Invalid number of arguments for PBKDF2") + + method = args.pop(0) + iterations = int(args[0] or 0) if args else DEFAULT_PBKDF2_ITERATIONS + return ( + hashlib.pbkdf2_hmac(method, password, salt, iterations).hex(), + f"pbkdf2:{method}:{iterations}", + ) + + if salt: + return hmac.new(salt, password, method).hexdigest(), method + + return hashlib.new(method, password).hexdigest(), method + + +def generate_password_hash( + password: str, method: str = "pbkdf2:sha256", salt_length: int = 16 +) -> str: + """Hash a password with the given method and salt with a string of + the given length. The format of the string returned includes the method + that was used so that :func:`check_password_hash` can check the hash. + + The format for the hashed string looks like this:: + + method$salt$hash + + This method can **not** generate unsalted passwords but it is possible + to set param method='plain' in order to enforce plaintext passwords. + If a salt is used, hmac is used internally to salt the password. + + If PBKDF2 is wanted it can be enabled by setting the method to + ``pbkdf2:method:iterations`` where iterations is optional:: + + pbkdf2:sha256:80000$salt$hash + pbkdf2:sha256$salt$hash + + :param password: the password to hash. + :param method: the hash method to use (one that hashlib supports). Can + optionally be in the format ``pbkdf2:method:iterations`` + to enable PBKDF2. + :param salt_length: the length of the salt in letters. + """ + salt = gen_salt(salt_length) if method != "plain" else "" + h, actual_method = _hash_internal(method, salt, password) + return f"{actual_method}${salt}${h}" + + +def check_password_hash(pwhash: str, password: str) -> bool: + """Check a password against a given salted and hashed password value. + In order to support unsalted legacy passwords this method supports + plain text passwords, md5 and sha1 hashes (both salted and unsalted). + + Returns `True` if the password matched, `False` otherwise. + + :param pwhash: a hashed string like returned by + :func:`generate_password_hash`. + :param password: the plaintext password to compare against the hash. + """ + if pwhash.count("$") < 2: + return False + + method, salt, hashval = pwhash.split("$", 2) + return hmac.compare_digest(_hash_internal(method, salt, password)[0], hashval) + + +def safe_join(directory: str, *pathnames: str) -> t.Optional[str]: + """Safely join zero or more untrusted path components to a base + directory to avoid escaping the base directory. + + :param directory: The trusted base directory. + :param pathnames: The untrusted path components relative to the + base directory. + :return: A safe path, otherwise ``None``. + """ + parts = [directory] + + for filename in pathnames: + if filename != "": + filename = posixpath.normpath(filename) + + if ( + any(sep in filename for sep in _os_alt_seps) + or os.path.isabs(filename) + or filename == ".." + or filename.startswith("../") + ): + return None + + parts.append(filename) + + return posixpath.join(*parts) diff --git a/venv/Lib/site-packages/werkzeug/serving.py b/venv/Lib/site-packages/werkzeug/serving.py new file mode 100644 index 0000000..1be9949 --- /dev/null +++ b/venv/Lib/site-packages/werkzeug/serving.py @@ -0,0 +1,1079 @@ +"""A WSGI and HTTP server for use **during development only**. This +server is convenient to use, but is not designed to be particularly +stable, secure, or efficient. Use a dedicate WSGI server and HTTP +server when deploying to production. + +It provides features like interactive debugging and code reloading. Use +``run_simple`` to start the server. Put this in a ``run.py`` script: + +.. code-block:: python + + from myapp import create_app + from werkzeug import run_simple +""" +import io +import os +import platform +import signal +import socket +import socketserver +import sys +import typing as t +import warnings +from datetime import datetime as dt +from datetime import timedelta +from datetime import timezone +from http.server import BaseHTTPRequestHandler +from http.server import HTTPServer + +from ._internal import _log +from ._internal import _wsgi_encoding_dance +from .exceptions import InternalServerError +from .urls import uri_to_iri +from .urls import url_parse +from .urls import url_unquote + +try: + import ssl +except ImportError: + + class _SslDummy: + def __getattr__(self, name: str) -> t.Any: + raise RuntimeError("SSL support unavailable") + + ssl = _SslDummy() # type: ignore + +_log_add_style = True + +if os.name == "nt": + try: + __import__("colorama") + except ImportError: + _log_add_style = False + +can_fork = hasattr(os, "fork") + +if can_fork: + ForkingMixIn = socketserver.ForkingMixIn +else: + + class ForkingMixIn: # type: ignore + pass + + +try: + af_unix = socket.AF_UNIX +except AttributeError: + af_unix = None # type: ignore + +LISTEN_QUEUE = 128 +can_open_by_fd = not platform.system() == "Windows" and hasattr(socket, "fromfd") + +_TSSLContextArg = t.Optional[ + t.Union["ssl.SSLContext", t.Tuple[str, t.Optional[str]], "te.Literal['adhoc']"] +] + +if t.TYPE_CHECKING: + import typing_extensions as te # noqa: F401 + from _typeshed.wsgi import WSGIApplication + from _typeshed.wsgi import WSGIEnvironment + from cryptography.hazmat.primitives.asymmetric.rsa import ( + RSAPrivateKeyWithSerialization, + ) + from cryptography.x509 import Certificate + + +class DechunkedInput(io.RawIOBase): + """An input stream that handles Transfer-Encoding 'chunked'""" + + def __init__(self, rfile: t.BinaryIO) -> None: + self._rfile = rfile + self._done = False + self._len = 0 + + def readable(self) -> bool: + return True + + def read_chunk_len(self) -> int: + try: + line = self._rfile.readline().decode("latin1") + _len = int(line.strip(), 16) + except ValueError: + raise OSError("Invalid chunk header") + if _len < 0: + raise OSError("Negative chunk length not allowed") + return _len + + def readinto(self, buf: bytearray) -> int: # type: ignore + read = 0 + while not self._done and read < len(buf): + if self._len == 0: + # This is the first chunk or we fully consumed the previous + # one. Read the next length of the next chunk + self._len = self.read_chunk_len() + + if self._len == 0: + # Found the final chunk of size 0. The stream is now exhausted, + # but there is still a final newline that should be consumed + self._done = True + + if self._len > 0: + # There is data (left) in this chunk, so append it to the + # buffer. If this operation fully consumes the chunk, this will + # reset self._len to 0. + n = min(len(buf), self._len) + + # If (read + chunk size) becomes more than len(buf), buf will + # grow beyond the original size and read more data than + # required. So only read as much data as can fit in buf. + if read + n > len(buf): + buf[read:] = self._rfile.read(len(buf) - read) + self._len -= len(buf) - read + read = len(buf) + else: + buf[read : read + n] = self._rfile.read(n) + self._len -= n + read += n + + if self._len == 0: + # Skip the terminating newline of a chunk that has been fully + # consumed. This also applies to the 0-sized final chunk + terminator = self._rfile.readline() + if terminator not in (b"\n", b"\r\n", b"\r"): + raise OSError("Missing chunk terminating newline") + + return read + + +class WSGIRequestHandler(BaseHTTPRequestHandler): + """A request handler that implements WSGI dispatching.""" + + server: "BaseWSGIServer" + + @property + def server_version(self) -> str: # type: ignore + from . import __version__ + + return f"Werkzeug/{__version__}" + + def make_environ(self) -> "WSGIEnvironment": + request_url = url_parse(self.path) + + def shutdown_server() -> None: + warnings.warn( + "The 'environ['werkzeug.server.shutdown']' function is" + " deprecated and will be removed in Werkzeug 2.1.", + stacklevel=2, + ) + self.server.shutdown_signal = True + + url_scheme = "http" if self.server.ssl_context is None else "https" + + if not self.client_address: + self.client_address = ("", 0) + elif isinstance(self.client_address, str): + self.client_address = (self.client_address, 0) + + # If there was no scheme but the path started with two slashes, + # the first segment may have been incorrectly parsed as the + # netloc, prepend it to the path again. + if not request_url.scheme and request_url.netloc: + path_info = f"/{request_url.netloc}{request_url.path}" + else: + path_info = request_url.path + + path_info = url_unquote(path_info) + + environ: "WSGIEnvironment" = { + "wsgi.version": (1, 0), + "wsgi.url_scheme": url_scheme, + "wsgi.input": self.rfile, + "wsgi.errors": sys.stderr, + "wsgi.multithread": self.server.multithread, + "wsgi.multiprocess": self.server.multiprocess, + "wsgi.run_once": False, + "werkzeug.server.shutdown": shutdown_server, + "werkzeug.socket": self.connection, + "SERVER_SOFTWARE": self.server_version, + "REQUEST_METHOD": self.command, + "SCRIPT_NAME": "", + "PATH_INFO": _wsgi_encoding_dance(path_info), + "QUERY_STRING": _wsgi_encoding_dance(request_url.query), + # Non-standard, added by mod_wsgi, uWSGI + "REQUEST_URI": _wsgi_encoding_dance(self.path), + # Non-standard, added by gunicorn + "RAW_URI": _wsgi_encoding_dance(self.path), + "REMOTE_ADDR": self.address_string(), + "REMOTE_PORT": self.port_integer(), + "SERVER_NAME": self.server.server_address[0], + "SERVER_PORT": str(self.server.server_address[1]), + "SERVER_PROTOCOL": self.request_version, + } + + for key, value in self.headers.items(): + key = key.upper().replace("-", "_") + value = value.replace("\r\n", "") + if key not in ("CONTENT_TYPE", "CONTENT_LENGTH"): + key = f"HTTP_{key}" + if key in environ: + value = f"{environ[key]},{value}" + environ[key] = value + + if environ.get("HTTP_TRANSFER_ENCODING", "").strip().lower() == "chunked": + environ["wsgi.input_terminated"] = True + environ["wsgi.input"] = DechunkedInput(environ["wsgi.input"]) + + # Per RFC 2616, if the URL is absolute, use that as the host. + # We're using "has a scheme" to indicate an absolute URL. + if request_url.scheme and request_url.netloc: + environ["HTTP_HOST"] = request_url.netloc + + try: + # binary_form=False gives nicer information, but wouldn't be compatible with + # what Nginx or Apache could return. + peer_cert = self.connection.getpeercert(binary_form=True) + if peer_cert is not None: + # Nginx and Apache use PEM format. + environ["SSL_CLIENT_CERT"] = ssl.DER_cert_to_PEM_cert(peer_cert) + except ValueError: + # SSL handshake hasn't finished. + self.server.log("error", "Cannot fetch SSL peer certificate info") + except AttributeError: + # Not using TLS, the socket will not have getpeercert(). + pass + + return environ + + def run_wsgi(self) -> None: + if self.headers.get("Expect", "").lower().strip() == "100-continue": + self.wfile.write(b"HTTP/1.1 100 Continue\r\n\r\n") + + self.environ = environ = self.make_environ() + status_set: t.Optional[str] = None + headers_set: t.Optional[t.List[t.Tuple[str, str]]] = None + status_sent: t.Optional[str] = None + headers_sent: t.Optional[t.List[t.Tuple[str, str]]] = None + + def write(data: bytes) -> None: + nonlocal status_sent, headers_sent + assert status_set is not None, "write() before start_response" + assert headers_set is not None, "write() before start_response" + if status_sent is None: + status_sent = status_set + headers_sent = headers_set + try: + code_str, msg = status_sent.split(None, 1) + except ValueError: + code_str, msg = status_sent, "" + code = int(code_str) + self.send_response(code, msg) + header_keys = set() + for key, value in headers_sent: + self.send_header(key, value) + key = key.lower() + header_keys.add(key) + if not ( + "content-length" in header_keys + or environ["REQUEST_METHOD"] == "HEAD" + or code < 200 + or code in (204, 304) + ): + self.close_connection = True + self.send_header("Connection", "close") + if "server" not in header_keys: + self.send_header("Server", self.version_string()) + if "date" not in header_keys: + self.send_header("Date", self.date_time_string()) + self.end_headers() + + assert isinstance(data, bytes), "applications must write bytes" + self.wfile.write(data) + self.wfile.flush() + + def start_response(status, headers, exc_info=None): # type: ignore + nonlocal status_set, headers_set + if exc_info: + try: + if headers_sent: + raise exc_info[1].with_traceback(exc_info[2]) + finally: + exc_info = None + elif headers_set: + raise AssertionError("Headers already set") + status_set = status + headers_set = headers + return write + + def execute(app: "WSGIApplication") -> None: + application_iter = app(environ, start_response) + try: + for data in application_iter: + write(data) + if not headers_sent: + write(b"") + finally: + if hasattr(application_iter, "close"): + application_iter.close() # type: ignore + + try: + execute(self.server.app) + except (ConnectionError, socket.timeout) as e: + self.connection_dropped(e, environ) + except Exception: + if self.server.passthrough_errors: + raise + from .debug.tbtools import get_current_traceback + + traceback = get_current_traceback(ignore_system_exceptions=True) + try: + # if we haven't yet sent the headers but they are set + # we roll back to be able to set them again. + if status_sent is None: + status_set = None + headers_set = None + execute(InternalServerError()) + except Exception: + pass + self.server.log("error", "Error on request:\n%s", traceback.plaintext) + + def handle(self) -> None: + """Handles a request ignoring dropped connections.""" + try: + BaseHTTPRequestHandler.handle(self) + except (ConnectionError, socket.timeout) as e: + self.connection_dropped(e) + except Exception as e: + if self.server.ssl_context is not None and is_ssl_error(e): + self.log_error("SSL error occurred: %s", e) + else: + raise + if self.server.shutdown_signal: + self.initiate_shutdown() + + def initiate_shutdown(self) -> None: + if is_running_from_reloader(): + # Windows does not provide SIGKILL, go with SIGTERM then. + sig = getattr(signal, "SIGKILL", signal.SIGTERM) + os.kill(os.getpid(), sig) + + self.server._BaseServer__shutdown_request = True # type: ignore + + def connection_dropped( + self, error: BaseException, environ: t.Optional["WSGIEnvironment"] = None + ) -> None: + """Called if the connection was closed by the client. By default + nothing happens. + """ + + def handle_one_request(self) -> None: + """Handle a single HTTP request.""" + self.raw_requestline = self.rfile.readline() + if not self.raw_requestline: + self.close_connection = True + elif self.parse_request(): + self.run_wsgi() + + def send_response(self, code: int, message: t.Optional[str] = None) -> None: + """Send the response header and log the response code.""" + self.log_request(code) + if message is None: + message = self.responses[code][0] if code in self.responses else "" + if self.request_version != "HTTP/0.9": + hdr = f"{self.protocol_version} {code} {message}\r\n" + self.wfile.write(hdr.encode("ascii")) + + def version_string(self) -> str: + return super().version_string().strip() + + def address_string(self) -> str: + if getattr(self, "environ", None): + return self.environ["REMOTE_ADDR"] # type: ignore + + if not self.client_address: + return "" + + return self.client_address[0] + + def port_integer(self) -> int: + return self.client_address[1] + + def log_request( + self, code: t.Union[int, str] = "-", size: t.Union[int, str] = "-" + ) -> None: + try: + path = uri_to_iri(self.path) + msg = f"{self.command} {path} {self.request_version}" + except AttributeError: + # path isn't set if the requestline was bad + msg = self.requestline + + code = str(code) + + if _log_add_style: + if code[0] == "1": # 1xx - Informational + msg = _ansi_style(msg, "bold") + elif code == "200": # 2xx - Success + pass + elif code == "304": # 304 - Resource Not Modified + msg = _ansi_style(msg, "cyan") + elif code[0] == "3": # 3xx - Redirection + msg = _ansi_style(msg, "green") + elif code == "404": # 404 - Resource Not Found + msg = _ansi_style(msg, "yellow") + elif code[0] == "4": # 4xx - Client Error + msg = _ansi_style(msg, "bold", "red") + else: # 5xx, or any other response + msg = _ansi_style(msg, "bold", "magenta") + + self.log("info", '"%s" %s %s', msg, code, size) + + def log_error(self, format: str, *args: t.Any) -> None: + self.log("error", format, *args) + + def log_message(self, format: str, *args: t.Any) -> None: + self.log("info", format, *args) + + def log(self, type: str, message: str, *args: t.Any) -> None: + _log( + type, + f"{self.address_string()} - - [{self.log_date_time_string()}] {message}\n", + *args, + ) + + +def _ansi_style(value: str, *styles: str) -> str: + codes = { + "bold": 1, + "red": 31, + "green": 32, + "yellow": 33, + "magenta": 35, + "cyan": 36, + } + + for style in styles: + value = f"\x1b[{codes[style]}m{value}" + + return f"{value}\x1b[0m" + + +def generate_adhoc_ssl_pair( + cn: t.Optional[str] = None, +) -> t.Tuple["Certificate", "RSAPrivateKeyWithSerialization"]: + try: + from cryptography import x509 + from cryptography.x509.oid import NameOID + from cryptography.hazmat.backends import default_backend + from cryptography.hazmat.primitives import hashes + from cryptography.hazmat.primitives.asymmetric import rsa + except ImportError: + raise TypeError("Using ad-hoc certificates requires the cryptography library.") + + backend = default_backend() + pkey = rsa.generate_private_key( + public_exponent=65537, key_size=2048, backend=backend + ) + + # pretty damn sure that this is not actually accepted by anyone + if cn is None: + cn = "*" + + subject = x509.Name( + [ + x509.NameAttribute(NameOID.ORGANIZATION_NAME, "Dummy Certificate"), + x509.NameAttribute(NameOID.COMMON_NAME, cn), + ] + ) + + backend = default_backend() + cert = ( + x509.CertificateBuilder() + .subject_name(subject) + .issuer_name(subject) + .public_key(pkey.public_key()) + .serial_number(x509.random_serial_number()) + .not_valid_before(dt.now(timezone.utc)) + .not_valid_after(dt.now(timezone.utc) + timedelta(days=365)) + .add_extension(x509.ExtendedKeyUsage([x509.OID_SERVER_AUTH]), critical=False) + .add_extension(x509.SubjectAlternativeName([x509.DNSName("*")]), critical=False) + .sign(pkey, hashes.SHA256(), backend) + ) + return cert, pkey + + +def make_ssl_devcert( + base_path: str, host: t.Optional[str] = None, cn: t.Optional[str] = None +) -> t.Tuple[str, str]: + """Creates an SSL key for development. This should be used instead of + the ``'adhoc'`` key which generates a new cert on each server start. + It accepts a path for where it should store the key and cert and + either a host or CN. If a host is given it will use the CN + ``*.host/CN=host``. + + For more information see :func:`run_simple`. + + .. versionadded:: 0.9 + + :param base_path: the path to the certificate and key. The extension + ``.crt`` is added for the certificate, ``.key`` is + added for the key. + :param host: the name of the host. This can be used as an alternative + for the `cn`. + :param cn: the `CN` to use. + """ + + if host is not None: + cn = f"*.{host}/CN={host}" + cert, pkey = generate_adhoc_ssl_pair(cn=cn) + + from cryptography.hazmat.primitives import serialization + + cert_file = f"{base_path}.crt" + pkey_file = f"{base_path}.key" + + with open(cert_file, "wb") as f: + f.write(cert.public_bytes(serialization.Encoding.PEM)) + with open(pkey_file, "wb") as f: + f.write( + pkey.private_bytes( + encoding=serialization.Encoding.PEM, + format=serialization.PrivateFormat.TraditionalOpenSSL, + encryption_algorithm=serialization.NoEncryption(), + ) + ) + + return cert_file, pkey_file + + +def generate_adhoc_ssl_context() -> "ssl.SSLContext": + """Generates an adhoc SSL context for the development server.""" + import tempfile + import atexit + + cert, pkey = generate_adhoc_ssl_pair() + + from cryptography.hazmat.primitives import serialization + + cert_handle, cert_file = tempfile.mkstemp() + pkey_handle, pkey_file = tempfile.mkstemp() + atexit.register(os.remove, pkey_file) + atexit.register(os.remove, cert_file) + + os.write(cert_handle, cert.public_bytes(serialization.Encoding.PEM)) + os.write( + pkey_handle, + pkey.private_bytes( + encoding=serialization.Encoding.PEM, + format=serialization.PrivateFormat.TraditionalOpenSSL, + encryption_algorithm=serialization.NoEncryption(), + ), + ) + + os.close(cert_handle) + os.close(pkey_handle) + ctx = load_ssl_context(cert_file, pkey_file) + return ctx + + +def load_ssl_context( + cert_file: str, pkey_file: t.Optional[str] = None, protocol: t.Optional[int] = None +) -> "ssl.SSLContext": + """Loads SSL context from cert/private key files and optional protocol. + Many parameters are directly taken from the API of + :py:class:`ssl.SSLContext`. + + :param cert_file: Path of the certificate to use. + :param pkey_file: Path of the private key to use. If not given, the key + will be obtained from the certificate file. + :param protocol: A ``PROTOCOL`` constant from the :mod:`ssl` module. + Defaults to :data:`ssl.PROTOCOL_TLS_SERVER`. + """ + if protocol is None: + protocol = ssl.PROTOCOL_TLS_SERVER + + ctx = ssl.SSLContext(protocol) + ctx.load_cert_chain(cert_file, pkey_file) + return ctx + + +def is_ssl_error(error: t.Optional[Exception] = None) -> bool: + """Checks if the given error (or the current one) is an SSL error.""" + if error is None: + error = t.cast(Exception, sys.exc_info()[1]) + return isinstance(error, ssl.SSLError) + + +def select_address_family(host: str, port: int) -> socket.AddressFamily: + """Return ``AF_INET4``, ``AF_INET6``, or ``AF_UNIX`` depending on + the host and port.""" + if host.startswith("unix://"): + return socket.AF_UNIX + elif ":" in host and hasattr(socket, "AF_INET6"): + return socket.AF_INET6 + return socket.AF_INET + + +def get_sockaddr( + host: str, port: int, family: socket.AddressFamily +) -> t.Union[t.Tuple[str, int], str]: + """Return a fully qualified socket address that can be passed to + :func:`socket.bind`.""" + if family == af_unix: + return host.split("://", 1)[1] + try: + res = socket.getaddrinfo( + host, port, family, socket.SOCK_STREAM, socket.IPPROTO_TCP + ) + except socket.gaierror: + return host, port + return res[0][4] # type: ignore + + +def get_interface_ip(family: socket.AddressFamily) -> str: + """Get the IP address of an external interface. Used when binding to + 0.0.0.0 or ::1 to show a more useful URL. + + :meta private: + """ + # arbitrary private address + host = "fd31:f903:5ab5:1::1" if family == socket.AF_INET6 else "10.253.155.219" + + with socket.socket(family, socket.SOCK_DGRAM) as s: + try: + s.connect((host, 58162)) + except OSError: + return "::1" if family == socket.AF_INET6 else "127.0.0.1" + + return s.getsockname()[0] # type: ignore + + +class BaseWSGIServer(HTTPServer): + + """Simple single-threaded, single-process WSGI server.""" + + multithread = False + multiprocess = False + request_queue_size = LISTEN_QUEUE + + def __init__( + self, + host: str, + port: int, + app: "WSGIApplication", + handler: t.Optional[t.Type[WSGIRequestHandler]] = None, + passthrough_errors: bool = False, + ssl_context: t.Optional[_TSSLContextArg] = None, + fd: t.Optional[int] = None, + ) -> None: + if handler is None: + handler = WSGIRequestHandler + + self.address_family = select_address_family(host, port) + + if fd is not None: + real_sock = socket.fromfd(fd, self.address_family, socket.SOCK_STREAM) + port = 0 + + server_address = get_sockaddr(host, int(port), self.address_family) + + # remove socket file if it already exists + if self.address_family == af_unix: + server_address = t.cast(str, server_address) + + if os.path.exists(server_address): + os.unlink(server_address) + + super().__init__(server_address, handler) # type: ignore + + self.app = app + self.passthrough_errors = passthrough_errors + self.shutdown_signal = False + self.host = host + self.port = self.socket.getsockname()[1] + + # Patch in the original socket. + if fd is not None: + self.socket.close() + self.socket = real_sock + self.server_address = self.socket.getsockname() + + if ssl_context is not None: + if isinstance(ssl_context, tuple): + ssl_context = load_ssl_context(*ssl_context) + if ssl_context == "adhoc": + ssl_context = generate_adhoc_ssl_context() + + self.socket = ssl_context.wrap_socket(self.socket, server_side=True) + self.ssl_context: t.Optional["ssl.SSLContext"] = ssl_context + else: + self.ssl_context = None + + def log(self, type: str, message: str, *args: t.Any) -> None: + _log(type, message, *args) + + def serve_forever(self, poll_interval: float = 0.5) -> None: + self.shutdown_signal = False + try: + super().serve_forever(poll_interval=poll_interval) + except KeyboardInterrupt: + pass + finally: + self.server_close() + + def handle_error(self, request: t.Any, client_address: t.Tuple[str, int]) -> None: + if self.passthrough_errors: + raise + + return super().handle_error(request, client_address) + + +class ThreadedWSGIServer(socketserver.ThreadingMixIn, BaseWSGIServer): + + """A WSGI server that does threading.""" + + multithread = True + daemon_threads = True + + +class ForkingWSGIServer(ForkingMixIn, BaseWSGIServer): + + """A WSGI server that does forking.""" + + multiprocess = True + + def __init__( + self, + host: str, + port: int, + app: "WSGIApplication", + processes: int = 40, + handler: t.Optional[t.Type[WSGIRequestHandler]] = None, + passthrough_errors: bool = False, + ssl_context: t.Optional[_TSSLContextArg] = None, + fd: t.Optional[int] = None, + ) -> None: + if not can_fork: + raise ValueError("Your platform does not support forking.") + BaseWSGIServer.__init__( + self, host, port, app, handler, passthrough_errors, ssl_context, fd + ) + self.max_children = processes + + +def make_server( + host: str, + port: int, + app: "WSGIApplication", + threaded: bool = False, + processes: int = 1, + request_handler: t.Optional[t.Type[WSGIRequestHandler]] = None, + passthrough_errors: bool = False, + ssl_context: t.Optional[_TSSLContextArg] = None, + fd: t.Optional[int] = None, +) -> BaseWSGIServer: + """Create a new server instance that is either threaded, or forks + or just processes one request after another. + """ + if threaded and processes > 1: + raise ValueError("cannot have a multithreaded and multi process server.") + elif threaded: + return ThreadedWSGIServer( + host, port, app, request_handler, passthrough_errors, ssl_context, fd=fd + ) + elif processes > 1: + return ForkingWSGIServer( + host, + port, + app, + processes, + request_handler, + passthrough_errors, + ssl_context, + fd=fd, + ) + else: + return BaseWSGIServer( + host, port, app, request_handler, passthrough_errors, ssl_context, fd=fd + ) + + +def is_running_from_reloader() -> bool: + """Checks if the application is running from within the Werkzeug + reloader subprocess. + + .. versionadded:: 0.10 + """ + return os.environ.get("WERKZEUG_RUN_MAIN") == "true" + + +def run_simple( + hostname: str, + port: int, + application: "WSGIApplication", + use_reloader: bool = False, + use_debugger: bool = False, + use_evalex: bool = True, + extra_files: t.Optional[t.Iterable[str]] = None, + exclude_patterns: t.Optional[t.Iterable[str]] = None, + reloader_interval: int = 1, + reloader_type: str = "auto", + threaded: bool = False, + processes: int = 1, + request_handler: t.Optional[t.Type[WSGIRequestHandler]] = None, + static_files: t.Optional[t.Dict[str, t.Union[str, t.Tuple[str, str]]]] = None, + passthrough_errors: bool = False, + ssl_context: t.Optional[_TSSLContextArg] = None, +) -> None: + """Start a WSGI application. Optional features include a reloader, + multithreading and fork support. + + This function has a command-line interface too:: + + python -m werkzeug.serving --help + + .. versionchanged:: 2.0 + Added ``exclude_patterns`` parameter. + + .. versionadded:: 0.5 + `static_files` was added to simplify serving of static files as well + as `passthrough_errors`. + + .. versionadded:: 0.6 + support for SSL was added. + + .. versionadded:: 0.8 + Added support for automatically loading a SSL context from certificate + file and private key. + + .. versionadded:: 0.9 + Added command-line interface. + + .. versionadded:: 0.10 + Improved the reloader and added support for changing the backend + through the `reloader_type` parameter. See :ref:`reloader` + for more information. + + .. versionchanged:: 0.15 + Bind to a Unix socket by passing a path that starts with + ``unix://`` as the ``hostname``. + + :param hostname: The host to bind to, for example ``'localhost'``. + If the value is a path that starts with ``unix://`` it will bind + to a Unix socket instead of a TCP socket.. + :param port: The port for the server. eg: ``8080`` + :param application: the WSGI application to execute + :param use_reloader: should the server automatically restart the python + process if modules were changed? + :param use_debugger: should the werkzeug debugging system be used? + :param use_evalex: should the exception evaluation feature be enabled? + :param extra_files: a list of files the reloader should watch + additionally to the modules. For example configuration + files. + :param exclude_patterns: List of :mod:`fnmatch` patterns to ignore + when running the reloader. For example, ignore cache files that + shouldn't reload when updated. + :param reloader_interval: the interval for the reloader in seconds. + :param reloader_type: the type of reloader to use. The default is + auto detection. Valid values are ``'stat'`` and + ``'watchdog'``. See :ref:`reloader` for more + information. + :param threaded: should the process handle each request in a separate + thread? + :param processes: if greater than 1 then handle each request in a new process + up to this maximum number of concurrent processes. + :param request_handler: optional parameter that can be used to replace + the default one. You can use this to replace it + with a different + :class:`~BaseHTTPServer.BaseHTTPRequestHandler` + subclass. + :param static_files: a list or dict of paths for static files. This works + exactly like :class:`SharedDataMiddleware`, it's actually + just wrapping the application in that middleware before + serving. + :param passthrough_errors: set this to `True` to disable the error catching. + This means that the server will die on errors but + it can be useful to hook debuggers in (pdb etc.) + :param ssl_context: an SSL context for the connection. Either an + :class:`ssl.SSLContext`, a tuple in the form + ``(cert_file, pkey_file)``, the string ``'adhoc'`` if + the server should automatically create one, or ``None`` + to disable SSL (which is the default). + """ + if not isinstance(port, int): + raise TypeError("port must be an integer") + if use_debugger: + from .debug import DebuggedApplication + + application = DebuggedApplication(application, use_evalex) + if static_files: + from .middleware.shared_data import SharedDataMiddleware + + application = SharedDataMiddleware(application, static_files) + + def log_startup(sock: socket.socket) -> None: + all_addresses_message = ( + " * Running on all addresses.\n" + " WARNING: This is a development server. Do not use it in" + " a production deployment." + ) + + if sock.family == af_unix: + _log("info", " * Running on %s (Press CTRL+C to quit)", hostname) + else: + if hostname == "0.0.0.0": + _log("warning", all_addresses_message) + display_hostname = get_interface_ip(socket.AF_INET) + elif hostname == "::": + _log("warning", all_addresses_message) + display_hostname = get_interface_ip(socket.AF_INET6) + else: + display_hostname = hostname + + if ":" in display_hostname: + display_hostname = f"[{display_hostname}]" + + _log( + "info", + " * Running on %s://%s:%d/ (Press CTRL+C to quit)", + "http" if ssl_context is None else "https", + display_hostname, + sock.getsockname()[1], + ) + + def inner() -> None: + try: + fd: t.Optional[int] = int(os.environ["WERKZEUG_SERVER_FD"]) + except (LookupError, ValueError): + fd = None + srv = make_server( + hostname, + port, + application, + threaded, + processes, + request_handler, + passthrough_errors, + ssl_context, + fd=fd, + ) + if fd is None: + log_startup(srv.socket) + srv.serve_forever() + + if use_reloader: + # If we're not running already in the subprocess that is the + # reloader we want to open up a socket early to make sure the + # port is actually available. + if not is_running_from_reloader(): + if port == 0 and not can_open_by_fd: + raise ValueError( + "Cannot bind to a random port with enabled " + "reloader if the Python interpreter does " + "not support socket opening by fd." + ) + + # Create and destroy a socket so that any exceptions are + # raised before we spawn a separate Python interpreter and + # lose this ability. + address_family = select_address_family(hostname, port) + server_address = get_sockaddr(hostname, port, address_family) + s = socket.socket(address_family, socket.SOCK_STREAM) + s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + s.bind(server_address) + s.set_inheritable(True) + + # If we can open the socket by file descriptor, then we can just + # reuse this one and our socket will survive the restarts. + if can_open_by_fd: + os.environ["WERKZEUG_SERVER_FD"] = str(s.fileno()) + s.listen(LISTEN_QUEUE) + log_startup(s) + else: + s.close() + if address_family == af_unix: + server_address = t.cast(str, server_address) + _log("info", "Unlinking %s", server_address) + os.unlink(server_address) + + from ._reloader import run_with_reloader as _rwr + + _rwr( + inner, + extra_files=extra_files, + exclude_patterns=exclude_patterns, + interval=reloader_interval, + reloader_type=reloader_type, + ) + else: + inner() + + +def run_with_reloader(*args: t.Any, **kwargs: t.Any) -> None: + """Run a process with the reloader. This is not a public API, do + not use this function. + + .. deprecated:: 2.0 + Will be removed in Werkzeug 2.1. + """ + from ._reloader import run_with_reloader as _rwr + + warnings.warn( + ( + "'run_with_reloader' is a private API, it will no longer be" + " accessible in Werkzeug 2.1. Use 'run_simple' instead." + ), + DeprecationWarning, + stacklevel=2, + ) + _rwr(*args, **kwargs) + + +def main() -> None: + """A simple command-line interface for :py:func:`run_simple`.""" + import argparse + from .utils import import_string + + _log("warning", "This CLI is deprecated and will be removed in version 2.1.") + + parser = argparse.ArgumentParser( + description="Run the given WSGI application with the development server.", + allow_abbrev=False, + ) + parser.add_argument( + "-b", + "--bind", + dest="address", + help="The hostname:port the app should listen on.", + ) + parser.add_argument( + "-d", + "--debug", + action="store_true", + help="Show the interactive debugger for unhandled exceptions.", + ) + parser.add_argument( + "-r", + "--reload", + action="store_true", + help="Reload the process if modules change.", + ) + parser.add_argument( + "application", help="Application to import and serve, in the form module:app." + ) + args = parser.parse_args() + hostname, port = None, None + + if args.address: + hostname, _, port = args.address.partition(":") + + run_simple( + hostname=hostname or "127.0.0.1", + port=int(port or 5000), + application=import_string(args.application), + use_reloader=args.reload, + use_debugger=args.debug, + ) + + +if __name__ == "__main__": + main() diff --git a/venv/Lib/site-packages/werkzeug/test.py b/venv/Lib/site-packages/werkzeug/test.py new file mode 100644 index 0000000..9301c02 --- /dev/null +++ b/venv/Lib/site-packages/werkzeug/test.py @@ -0,0 +1,1324 @@ +import mimetypes +import sys +import typing as t +import warnings +from collections import defaultdict +from datetime import datetime +from datetime import timedelta +from http.cookiejar import CookieJar +from io import BytesIO +from itertools import chain +from random import random +from tempfile import TemporaryFile +from time import time +from urllib.request import Request as _UrllibRequest + +from ._internal import _get_environ +from ._internal import _make_encode_wrapper +from ._internal import _wsgi_decoding_dance +from ._internal import _wsgi_encoding_dance +from .datastructures import Authorization +from .datastructures import CallbackDict +from .datastructures import CombinedMultiDict +from .datastructures import EnvironHeaders +from .datastructures import FileMultiDict +from .datastructures import Headers +from .datastructures import MultiDict +from .http import dump_cookie +from .http import dump_options_header +from .http import parse_options_header +from .sansio.multipart import Data +from .sansio.multipart import Epilogue +from .sansio.multipart import Field +from .sansio.multipart import File +from .sansio.multipart import MultipartEncoder +from .sansio.multipart import Preamble +from .urls import iri_to_uri +from .urls import url_encode +from .urls import url_fix +from .urls import url_parse +from .urls import url_unparse +from .urls import url_unquote +from .utils import get_content_type +from .wrappers.request import Request +from .wrappers.response import Response +from .wsgi import ClosingIterator +from .wsgi import get_current_url + +if t.TYPE_CHECKING: + from _typeshed.wsgi import WSGIApplication + from _typeshed.wsgi import WSGIEnvironment + + +def stream_encode_multipart( + data: t.Mapping[str, t.Any], + use_tempfile: bool = True, + threshold: int = 1024 * 500, + boundary: t.Optional[str] = None, + charset: str = "utf-8", +) -> t.Tuple[t.BinaryIO, int, str]: + """Encode a dict of values (either strings or file descriptors or + :class:`FileStorage` objects.) into a multipart encoded string stored + in a file descriptor. + """ + if boundary is None: + boundary = f"---------------WerkzeugFormPart_{time()}{random()}" + + stream: t.BinaryIO = BytesIO() + total_length = 0 + on_disk = False + + if use_tempfile: + + def write_binary(s: bytes) -> int: + nonlocal stream, total_length, on_disk + + if on_disk: + return stream.write(s) + else: + length = len(s) + + if length + total_length <= threshold: + stream.write(s) + else: + new_stream = t.cast(t.BinaryIO, TemporaryFile("wb+")) + new_stream.write(stream.getvalue()) # type: ignore + new_stream.write(s) + stream = new_stream + on_disk = True + + total_length += length + return length + + else: + write_binary = stream.write + + encoder = MultipartEncoder(boundary.encode()) + write_binary(encoder.send_event(Preamble(data=b""))) + for key, value in _iter_data(data): + reader = getattr(value, "read", None) + if reader is not None: + filename = getattr(value, "filename", getattr(value, "name", None)) + content_type = getattr(value, "content_type", None) + if content_type is None: + content_type = ( + filename + and mimetypes.guess_type(filename)[0] + or "application/octet-stream" + ) + headers = Headers([("Content-Type", content_type)]) + if filename is None: + write_binary(encoder.send_event(Field(name=key, headers=headers))) + else: + write_binary( + encoder.send_event( + File(name=key, filename=filename, headers=headers) + ) + ) + while True: + chunk = reader(16384) + + if not chunk: + break + + write_binary(encoder.send_event(Data(data=chunk, more_data=True))) + else: + if not isinstance(value, str): + value = str(value) + write_binary(encoder.send_event(Field(name=key, headers=Headers()))) + write_binary( + encoder.send_event(Data(data=value.encode(charset), more_data=False)) + ) + + write_binary(encoder.send_event(Epilogue(data=b""))) + + length = stream.tell() + stream.seek(0) + return stream, length, boundary + + +def encode_multipart( + values: t.Mapping[str, t.Any], + boundary: t.Optional[str] = None, + charset: str = "utf-8", +) -> t.Tuple[str, bytes]: + """Like `stream_encode_multipart` but returns a tuple in the form + (``boundary``, ``data``) where data is bytes. + """ + stream, length, boundary = stream_encode_multipart( + values, use_tempfile=False, boundary=boundary, charset=charset + ) + return boundary, stream.read() + + +class _TestCookieHeaders: + """A headers adapter for cookielib""" + + def __init__(self, headers: t.Union[Headers, t.List[t.Tuple[str, str]]]) -> None: + self.headers = headers + + def getheaders(self, name: str) -> t.Iterable[str]: + headers = [] + name = name.lower() + for k, v in self.headers: + if k.lower() == name: + headers.append(v) + return headers + + def get_all( + self, name: str, default: t.Optional[t.Iterable[str]] = None + ) -> t.Iterable[str]: + headers = self.getheaders(name) + + if not headers: + return default # type: ignore + + return headers + + +class _TestCookieResponse: + """Something that looks like a httplib.HTTPResponse, but is actually just an + adapter for our test responses to make them available for cookielib. + """ + + def __init__(self, headers: t.Union[Headers, t.List[t.Tuple[str, str]]]) -> None: + self.headers = _TestCookieHeaders(headers) + + def info(self) -> _TestCookieHeaders: + return self.headers + + +class _TestCookieJar(CookieJar): + """A cookielib.CookieJar modified to inject and read cookie headers from + and to wsgi environments, and wsgi application responses. + """ + + def inject_wsgi(self, environ: "WSGIEnvironment") -> None: + """Inject the cookies as client headers into the server's wsgi + environment. + """ + cvals = [f"{c.name}={c.value}" for c in self] + + if cvals: + environ["HTTP_COOKIE"] = "; ".join(cvals) + else: + environ.pop("HTTP_COOKIE", None) + + def extract_wsgi( + self, + environ: "WSGIEnvironment", + headers: t.Union[Headers, t.List[t.Tuple[str, str]]], + ) -> None: + """Extract the server's set-cookie headers as cookies into the + cookie jar. + """ + self.extract_cookies( + _TestCookieResponse(headers), # type: ignore + _UrllibRequest(get_current_url(environ)), + ) + + +def _iter_data(data: t.Mapping[str, t.Any]) -> t.Iterator[t.Tuple[str, t.Any]]: + """Iterate over a mapping that might have a list of values, yielding + all key, value pairs. Almost like iter_multi_items but only allows + lists, not tuples, of values so tuples can be used for files. + """ + if isinstance(data, MultiDict): + yield from data.items(multi=True) + else: + for key, value in data.items(): + if isinstance(value, list): + for v in value: + yield key, v + else: + yield key, value + + +_TAnyMultiDict = t.TypeVar("_TAnyMultiDict", bound=MultiDict) + + +class EnvironBuilder: + """This class can be used to conveniently create a WSGI environment + for testing purposes. It can be used to quickly create WSGI environments + or request objects from arbitrary data. + + The signature of this class is also used in some other places as of + Werkzeug 0.5 (:func:`create_environ`, :meth:`Response.from_values`, + :meth:`Client.open`). Because of this most of the functionality is + available through the constructor alone. + + Files and regular form data can be manipulated independently of each + other with the :attr:`form` and :attr:`files` attributes, but are + passed with the same argument to the constructor: `data`. + + `data` can be any of these values: + + - a `str` or `bytes` object: The object is converted into an + :attr:`input_stream`, the :attr:`content_length` is set and you have to + provide a :attr:`content_type`. + - a `dict` or :class:`MultiDict`: The keys have to be strings. The values + have to be either any of the following objects, or a list of any of the + following objects: + + - a :class:`file`-like object: These are converted into + :class:`FileStorage` objects automatically. + - a `tuple`: The :meth:`~FileMultiDict.add_file` method is called + with the key and the unpacked `tuple` items as positional + arguments. + - a `str`: The string is set as form data for the associated key. + - a file-like object: The object content is loaded in memory and then + handled like a regular `str` or a `bytes`. + + :param path: the path of the request. In the WSGI environment this will + end up as `PATH_INFO`. If the `query_string` is not defined + and there is a question mark in the `path` everything after + it is used as query string. + :param base_url: the base URL is a URL that is used to extract the WSGI + URL scheme, host (server name + server port) and the + script root (`SCRIPT_NAME`). + :param query_string: an optional string or dict with URL parameters. + :param method: the HTTP method to use, defaults to `GET`. + :param input_stream: an optional input stream. Do not specify this and + `data`. As soon as an input stream is set you can't + modify :attr:`args` and :attr:`files` unless you + set the :attr:`input_stream` to `None` again. + :param content_type: The content type for the request. As of 0.5 you + don't have to provide this when specifying files + and form data via `data`. + :param content_length: The content length for the request. You don't + have to specify this when providing data via + `data`. + :param errors_stream: an optional error stream that is used for + `wsgi.errors`. Defaults to :data:`stderr`. + :param multithread: controls `wsgi.multithread`. Defaults to `False`. + :param multiprocess: controls `wsgi.multiprocess`. Defaults to `False`. + :param run_once: controls `wsgi.run_once`. Defaults to `False`. + :param headers: an optional list or :class:`Headers` object of headers. + :param data: a string or dict of form data or a file-object. + See explanation above. + :param json: An object to be serialized and assigned to ``data``. + Defaults the content type to ``"application/json"``. + Serialized with the function assigned to :attr:`json_dumps`. + :param environ_base: an optional dict of environment defaults. + :param environ_overrides: an optional dict of environment overrides. + :param charset: the charset used to encode string data. + :param auth: An authorization object to use for the + ``Authorization`` header value. A ``(username, password)`` tuple + is a shortcut for ``Basic`` authorization. + + .. versionchanged:: 2.0 + ``REQUEST_URI`` and ``RAW_URI`` is the full raw URI including + the query string, not only the path. + + .. versionchanged:: 2.0 + The default :attr:`request_class` is ``Request`` instead of + ``BaseRequest``. + + .. versionadded:: 2.0 + Added the ``auth`` parameter. + + .. versionadded:: 0.15 + The ``json`` param and :meth:`json_dumps` method. + + .. versionadded:: 0.15 + The environ has keys ``REQUEST_URI`` and ``RAW_URI`` containing + the path before perecent-decoding. This is not part of the WSGI + PEP, but many WSGI servers include it. + + .. versionchanged:: 0.6 + ``path`` and ``base_url`` can now be unicode strings that are + encoded with :func:`iri_to_uri`. + """ + + #: the server protocol to use. defaults to HTTP/1.1 + server_protocol = "HTTP/1.1" + + #: the wsgi version to use. defaults to (1, 0) + wsgi_version = (1, 0) + + #: The default request class used by :meth:`get_request`. + request_class = Request + + import json + + #: The serialization function used when ``json`` is passed. + json_dumps = staticmethod(json.dumps) + del json + + _args: t.Optional[MultiDict] + _query_string: t.Optional[str] + _input_stream: t.Optional[t.BinaryIO] + _form: t.Optional[MultiDict] + _files: t.Optional[FileMultiDict] + + def __init__( + self, + path: str = "/", + base_url: t.Optional[str] = None, + query_string: t.Optional[t.Union[t.Mapping[str, str], str]] = None, + method: str = "GET", + input_stream: t.Optional[t.BinaryIO] = None, + content_type: t.Optional[str] = None, + content_length: t.Optional[int] = None, + errors_stream: t.Optional[t.TextIO] = None, + multithread: bool = False, + multiprocess: bool = False, + run_once: bool = False, + headers: t.Optional[t.Union[Headers, t.Iterable[t.Tuple[str, str]]]] = None, + data: t.Optional[t.Union[t.BinaryIO, str, bytes, t.Mapping[str, t.Any]]] = None, + environ_base: t.Optional[t.Mapping[str, t.Any]] = None, + environ_overrides: t.Optional[t.Mapping[str, t.Any]] = None, + charset: str = "utf-8", + mimetype: t.Optional[str] = None, + json: t.Optional[t.Mapping[str, t.Any]] = None, + auth: t.Optional[t.Union[Authorization, t.Tuple[str, str]]] = None, + ) -> None: + path_s = _make_encode_wrapper(path) + if query_string is not None and path_s("?") in path: + raise ValueError("Query string is defined in the path and as an argument") + request_uri = url_parse(path) + if query_string is None and path_s("?") in path: + query_string = request_uri.query + self.charset = charset + self.path = iri_to_uri(request_uri.path) + self.request_uri = path + if base_url is not None: + base_url = url_fix(iri_to_uri(base_url, charset), charset) + self.base_url = base_url # type: ignore + if isinstance(query_string, (bytes, str)): + self.query_string = query_string + else: + if query_string is None: + query_string = MultiDict() + elif not isinstance(query_string, MultiDict): + query_string = MultiDict(query_string) + self.args = query_string + self.method = method + if headers is None: + headers = Headers() + elif not isinstance(headers, Headers): + headers = Headers(headers) + self.headers = headers + if content_type is not None: + self.content_type = content_type + if errors_stream is None: + errors_stream = sys.stderr + self.errors_stream = errors_stream + self.multithread = multithread + self.multiprocess = multiprocess + self.run_once = run_once + self.environ_base = environ_base + self.environ_overrides = environ_overrides + self.input_stream = input_stream + self.content_length = content_length + self.closed = False + + if auth is not None: + if isinstance(auth, tuple): + auth = Authorization( + "basic", {"username": auth[0], "password": auth[1]} + ) + + self.headers.set("Authorization", auth.to_header()) + + if json is not None: + if data is not None: + raise TypeError("can't provide both json and data") + + data = self.json_dumps(json) + + if self.content_type is None: + self.content_type = "application/json" + + if data: + if input_stream is not None: + raise TypeError("can't provide input stream and data") + if hasattr(data, "read"): + data = data.read() # type: ignore + if isinstance(data, str): + data = data.encode(self.charset) + if isinstance(data, bytes): + self.input_stream = BytesIO(data) + if self.content_length is None: + self.content_length = len(data) + else: + for key, value in _iter_data(data): # type: ignore + if isinstance(value, (tuple, dict)) or hasattr(value, "read"): + self._add_file_from_data(key, value) + else: + self.form.setlistdefault(key).append(value) + + if mimetype is not None: + self.mimetype = mimetype + + @classmethod + def from_environ( + cls, environ: "WSGIEnvironment", **kwargs: t.Any + ) -> "EnvironBuilder": + """Turn an environ dict back into a builder. Any extra kwargs + override the args extracted from the environ. + + .. versionchanged:: 2.0 + Path and query values are passed through the WSGI decoding + dance to avoid double encoding. + + .. versionadded:: 0.15 + """ + headers = Headers(EnvironHeaders(environ)) + out = { + "path": _wsgi_decoding_dance(environ["PATH_INFO"]), + "base_url": cls._make_base_url( + environ["wsgi.url_scheme"], + headers.pop("Host"), + _wsgi_decoding_dance(environ["SCRIPT_NAME"]), + ), + "query_string": _wsgi_decoding_dance(environ["QUERY_STRING"]), + "method": environ["REQUEST_METHOD"], + "input_stream": environ["wsgi.input"], + "content_type": headers.pop("Content-Type", None), + "content_length": headers.pop("Content-Length", None), + "errors_stream": environ["wsgi.errors"], + "multithread": environ["wsgi.multithread"], + "multiprocess": environ["wsgi.multiprocess"], + "run_once": environ["wsgi.run_once"], + "headers": headers, + } + out.update(kwargs) + return cls(**out) + + def _add_file_from_data( + self, + key: str, + value: t.Union[ + t.BinaryIO, t.Tuple[t.BinaryIO, str], t.Tuple[t.BinaryIO, str, str] + ], + ) -> None: + """Called in the EnvironBuilder to add files from the data dict.""" + if isinstance(value, tuple): + self.files.add_file(key, *value) + else: + self.files.add_file(key, value) + + @staticmethod + def _make_base_url(scheme: str, host: str, script_root: str) -> str: + return url_unparse((scheme, host, script_root, "", "")).rstrip("/") + "/" + + @property + def base_url(self) -> str: + """The base URL is used to extract the URL scheme, host name, + port, and root path. + """ + return self._make_base_url(self.url_scheme, self.host, self.script_root) + + @base_url.setter + def base_url(self, value: t.Optional[str]) -> None: + if value is None: + scheme = "http" + netloc = "localhost" + script_root = "" + else: + scheme, netloc, script_root, qs, anchor = url_parse(value) + if qs or anchor: + raise ValueError("base url must not contain a query string or fragment") + self.script_root = script_root.rstrip("/") + self.host = netloc + self.url_scheme = scheme + + @property + def content_type(self) -> t.Optional[str]: + """The content type for the request. Reflected from and to + the :attr:`headers`. Do not set if you set :attr:`files` or + :attr:`form` for auto detection. + """ + ct = self.headers.get("Content-Type") + if ct is None and not self._input_stream: + if self._files: + return "multipart/form-data" + if self._form: + return "application/x-www-form-urlencoded" + return None + return ct + + @content_type.setter + def content_type(self, value: t.Optional[str]) -> None: + if value is None: + self.headers.pop("Content-Type", None) + else: + self.headers["Content-Type"] = value + + @property + def mimetype(self) -> t.Optional[str]: + """The mimetype (content type without charset etc.) + + .. versionadded:: 0.14 + """ + ct = self.content_type + return ct.split(";")[0].strip() if ct else None + + @mimetype.setter + def mimetype(self, value: str) -> None: + self.content_type = get_content_type(value, self.charset) + + @property + def mimetype_params(self) -> t.Mapping[str, str]: + """The mimetype parameters as dict. For example if the + content type is ``text/html; charset=utf-8`` the params would be + ``{'charset': 'utf-8'}``. + + .. versionadded:: 0.14 + """ + + def on_update(d: t.Mapping[str, str]) -> None: + self.headers["Content-Type"] = dump_options_header(self.mimetype, d) + + d = parse_options_header(self.headers.get("content-type", ""))[1] + return CallbackDict(d, on_update) + + @property + def content_length(self) -> t.Optional[int]: + """The content length as integer. Reflected from and to the + :attr:`headers`. Do not set if you set :attr:`files` or + :attr:`form` for auto detection. + """ + return self.headers.get("Content-Length", type=int) + + @content_length.setter + def content_length(self, value: t.Optional[int]) -> None: + if value is None: + self.headers.pop("Content-Length", None) + else: + self.headers["Content-Length"] = str(value) + + def _get_form(self, name: str, storage: t.Type[_TAnyMultiDict]) -> _TAnyMultiDict: + """Common behavior for getting the :attr:`form` and + :attr:`files` properties. + + :param name: Name of the internal cached attribute. + :param storage: Storage class used for the data. + """ + if self.input_stream is not None: + raise AttributeError("an input stream is defined") + + rv = getattr(self, name) + + if rv is None: + rv = storage() + setattr(self, name, rv) + + return rv # type: ignore + + def _set_form(self, name: str, value: MultiDict) -> None: + """Common behavior for setting the :attr:`form` and + :attr:`files` properties. + + :param name: Name of the internal cached attribute. + :param value: Value to assign to the attribute. + """ + self._input_stream = None + setattr(self, name, value) + + @property + def form(self) -> MultiDict: + """A :class:`MultiDict` of form values.""" + return self._get_form("_form", MultiDict) + + @form.setter + def form(self, value: MultiDict) -> None: + self._set_form("_form", value) + + @property + def files(self) -> FileMultiDict: + """A :class:`FileMultiDict` of uploaded files. Use + :meth:`~FileMultiDict.add_file` to add new files. + """ + return self._get_form("_files", FileMultiDict) + + @files.setter + def files(self, value: FileMultiDict) -> None: + self._set_form("_files", value) + + @property + def input_stream(self) -> t.Optional[t.BinaryIO]: + """An optional input stream. This is mutually exclusive with + setting :attr:`form` and :attr:`files`, setting it will clear + those. Do not provide this if the method is not ``POST`` or + another method that has a body. + """ + return self._input_stream + + @input_stream.setter + def input_stream(self, value: t.Optional[t.BinaryIO]) -> None: + self._input_stream = value + self._form = None + self._files = None + + @property + def query_string(self) -> str: + """The query string. If you set this to a string + :attr:`args` will no longer be available. + """ + if self._query_string is None: + if self._args is not None: + return url_encode(self._args, charset=self.charset) + return "" + return self._query_string + + @query_string.setter + def query_string(self, value: t.Optional[str]) -> None: + self._query_string = value + self._args = None + + @property + def args(self) -> MultiDict: + """The URL arguments as :class:`MultiDict`.""" + if self._query_string is not None: + raise AttributeError("a query string is defined") + if self._args is None: + self._args = MultiDict() + return self._args + + @args.setter + def args(self, value: t.Optional[MultiDict]) -> None: + self._query_string = None + self._args = value + + @property + def server_name(self) -> str: + """The server name (read-only, use :attr:`host` to set)""" + return self.host.split(":", 1)[0] + + @property + def server_port(self) -> int: + """The server port as integer (read-only, use :attr:`host` to set)""" + pieces = self.host.split(":", 1) + if len(pieces) == 2 and pieces[1].isdigit(): + return int(pieces[1]) + if self.url_scheme == "https": + return 443 + return 80 + + def __del__(self) -> None: + try: + self.close() + except Exception: + pass + + def close(self) -> None: + """Closes all files. If you put real :class:`file` objects into the + :attr:`files` dict you can call this method to automatically close + them all in one go. + """ + if self.closed: + return + try: + files = self.files.values() + except AttributeError: + files = () # type: ignore + for f in files: + try: + f.close() + except Exception: + pass + self.closed = True + + def get_environ(self) -> "WSGIEnvironment": + """Return the built environ. + + .. versionchanged:: 0.15 + The content type and length headers are set based on + input stream detection. Previously this only set the WSGI + keys. + """ + input_stream = self.input_stream + content_length = self.content_length + + mimetype = self.mimetype + content_type = self.content_type + + if input_stream is not None: + start_pos = input_stream.tell() + input_stream.seek(0, 2) + end_pos = input_stream.tell() + input_stream.seek(start_pos) + content_length = end_pos - start_pos + elif mimetype == "multipart/form-data": + input_stream, content_length, boundary = stream_encode_multipart( + CombinedMultiDict([self.form, self.files]), charset=self.charset + ) + content_type = f'{mimetype}; boundary="{boundary}"' + elif mimetype == "application/x-www-form-urlencoded": + form_encoded = url_encode(self.form, charset=self.charset).encode("ascii") + content_length = len(form_encoded) + input_stream = BytesIO(form_encoded) + else: + input_stream = BytesIO() + + result: "WSGIEnvironment" = {} + if self.environ_base: + result.update(self.environ_base) + + def _path_encode(x: str) -> str: + return _wsgi_encoding_dance(url_unquote(x, self.charset), self.charset) + + raw_uri = _wsgi_encoding_dance(self.request_uri, self.charset) + result.update( + { + "REQUEST_METHOD": self.method, + "SCRIPT_NAME": _path_encode(self.script_root), + "PATH_INFO": _path_encode(self.path), + "QUERY_STRING": _wsgi_encoding_dance(self.query_string, self.charset), + # Non-standard, added by mod_wsgi, uWSGI + "REQUEST_URI": raw_uri, + # Non-standard, added by gunicorn + "RAW_URI": raw_uri, + "SERVER_NAME": self.server_name, + "SERVER_PORT": str(self.server_port), + "HTTP_HOST": self.host, + "SERVER_PROTOCOL": self.server_protocol, + "wsgi.version": self.wsgi_version, + "wsgi.url_scheme": self.url_scheme, + "wsgi.input": input_stream, + "wsgi.errors": self.errors_stream, + "wsgi.multithread": self.multithread, + "wsgi.multiprocess": self.multiprocess, + "wsgi.run_once": self.run_once, + } + ) + + headers = self.headers.copy() + + if content_type is not None: + result["CONTENT_TYPE"] = content_type + headers.set("Content-Type", content_type) + + if content_length is not None: + result["CONTENT_LENGTH"] = str(content_length) + headers.set("Content-Length", content_length) + + combined_headers = defaultdict(list) + + for key, value in headers.to_wsgi_list(): + combined_headers[f"HTTP_{key.upper().replace('-', '_')}"].append(value) + + for key, values in combined_headers.items(): + result[key] = ", ".join(values) + + if self.environ_overrides: + result.update(self.environ_overrides) + + return result + + def get_request(self, cls: t.Optional[t.Type[Request]] = None) -> Request: + """Returns a request with the data. If the request class is not + specified :attr:`request_class` is used. + + :param cls: The request wrapper to use. + """ + if cls is None: + cls = self.request_class + + return cls(self.get_environ()) + + +class ClientRedirectError(Exception): + """If a redirect loop is detected when using follow_redirects=True with + the :cls:`Client`, then this exception is raised. + """ + + +class Client: + """This class allows you to send requests to a wrapped application. + + The use_cookies parameter indicates whether cookies should be stored and + sent for subsequent requests. This is True by default, but passing False + will disable this behaviour. + + If you want to request some subdomain of your application you may set + `allow_subdomain_redirects` to `True` as if not no external redirects + are allowed. + + .. versionchanged:: 2.0 + ``response_wrapper`` is always a subclass of + :class:``TestResponse``. + + .. versionchanged:: 0.5 + Added the ``use_cookies`` parameter. + """ + + def __init__( + self, + application: "WSGIApplication", + response_wrapper: t.Optional[t.Type["Response"]] = None, + use_cookies: bool = True, + allow_subdomain_redirects: bool = False, + ) -> None: + self.application = application + + if response_wrapper in {None, Response}: + response_wrapper = TestResponse + elif not isinstance(response_wrapper, TestResponse): + response_wrapper = type( + "WrapperTestResponse", + (TestResponse, response_wrapper), # type: ignore + {}, + ) + + self.response_wrapper = t.cast(t.Type["TestResponse"], response_wrapper) + + if use_cookies: + self.cookie_jar: t.Optional[_TestCookieJar] = _TestCookieJar() + else: + self.cookie_jar = None + + self.allow_subdomain_redirects = allow_subdomain_redirects + + def set_cookie( + self, + server_name: str, + key: str, + value: str = "", + max_age: t.Optional[t.Union[timedelta, int]] = None, + expires: t.Optional[t.Union[str, datetime, int, float]] = None, + path: str = "/", + domain: t.Optional[str] = None, + secure: bool = False, + httponly: bool = False, + samesite: t.Optional[str] = None, + charset: str = "utf-8", + ) -> None: + """Sets a cookie in the client's cookie jar. The server name + is required and has to match the one that is also passed to + the open call. + """ + assert self.cookie_jar is not None, "cookies disabled" + header = dump_cookie( + key, + value, + max_age, + expires, + path, + domain, + secure, + httponly, + charset, + samesite=samesite, + ) + environ = create_environ(path, base_url=f"http://{server_name}") + headers = [("Set-Cookie", header)] + self.cookie_jar.extract_wsgi(environ, headers) + + def delete_cookie( + self, + server_name: str, + key: str, + path: str = "/", + domain: t.Optional[str] = None, + secure: bool = False, + httponly: bool = False, + samesite: t.Optional[str] = None, + ) -> None: + """Deletes a cookie in the test client.""" + self.set_cookie( + server_name, + key, + expires=0, + max_age=0, + path=path, + domain=domain, + secure=secure, + httponly=httponly, + samesite=samesite, + ) + + def run_wsgi_app( + self, environ: "WSGIEnvironment", buffered: bool = False + ) -> t.Tuple[t.Iterable[bytes], str, Headers]: + """Runs the wrapped WSGI app with the given environment. + + :meta private: + """ + if self.cookie_jar is not None: + self.cookie_jar.inject_wsgi(environ) + + rv = run_wsgi_app(self.application, environ, buffered=buffered) + + if self.cookie_jar is not None: + self.cookie_jar.extract_wsgi(environ, rv[2]) + + return rv + + def resolve_redirect( + self, response: "TestResponse", buffered: bool = False + ) -> "TestResponse": + """Perform a new request to the location given by the redirect + response to the previous request. + + :meta private: + """ + scheme, netloc, path, qs, anchor = url_parse(response.location) + builder = EnvironBuilder.from_environ(response.request.environ, query_string=qs) + + to_name_parts = netloc.split(":", 1)[0].split(".") + from_name_parts = builder.server_name.split(".") + + if to_name_parts != [""]: + # The new location has a host, use it for the base URL. + builder.url_scheme = scheme + builder.host = netloc + else: + # A local redirect with autocorrect_location_header=False + # doesn't have a host, so use the request's host. + to_name_parts = from_name_parts + + # Explain why a redirect to a different server name won't be followed. + if to_name_parts != from_name_parts: + if to_name_parts[-len(from_name_parts) :] == from_name_parts: + if not self.allow_subdomain_redirects: + raise RuntimeError("Following subdomain redirects is not enabled.") + else: + raise RuntimeError("Following external redirects is not supported.") + + path_parts = path.split("/") + root_parts = builder.script_root.split("/") + + if path_parts[: len(root_parts)] == root_parts: + # Strip the script root from the path. + builder.path = path[len(builder.script_root) :] + else: + # The new location is not under the script root, so use the + # whole path and clear the previous root. + builder.path = path + builder.script_root = "" + + # Only 307 and 308 preserve all of the original request. + if response.status_code not in {307, 308}: + # HEAD is preserved, everything else becomes GET. + if builder.method != "HEAD": + builder.method = "GET" + + # Clear the body and the headers that describe it. + + if builder.input_stream is not None: + builder.input_stream.close() + builder.input_stream = None + + builder.content_type = None + builder.content_length = None + builder.headers.pop("Transfer-Encoding", None) + + return self.open(builder, buffered=buffered) + + def open( + self, + *args: t.Any, + as_tuple: bool = False, + buffered: bool = False, + follow_redirects: bool = False, + **kwargs: t.Any, + ) -> "TestResponse": + """Generate an environ dict from the given arguments, make a + request to the application using it, and return the response. + + :param args: Passed to :class:`EnvironBuilder` to create the + environ for the request. If a single arg is passed, it can + be an existing :class:`EnvironBuilder` or an environ dict. + :param buffered: Convert the iterator returned by the app into + a list. If the iterator has a ``close()`` method, it is + called automatically. + :param follow_redirects: Make additional requests to follow HTTP + redirects until a non-redirect status is returned. + :attr:`TestResponse.history` lists the intermediate + responses. + + .. versionchanged:: 2.0 + ``as_tuple`` is deprecated and will be removed in Werkzeug + 2.1. Use :attr:`TestResponse.request` and + ``request.environ`` instead. + + .. versionchanged:: 2.0 + The request input stream is closed when calling + ``response.close()``. Input streams for redirects are + automatically closed. + + .. versionchanged:: 0.5 + If a dict is provided as file in the dict for the ``data`` + parameter the content type has to be called ``content_type`` + instead of ``mimetype``. This change was made for + consistency with :class:`werkzeug.FileWrapper`. + + .. versionchanged:: 0.5 + Added the ``follow_redirects`` parameter. + """ + request: t.Optional["Request"] = None + + if not kwargs and len(args) == 1: + arg = args[0] + + if isinstance(arg, EnvironBuilder): + request = arg.get_request() + elif isinstance(arg, dict): + request = EnvironBuilder.from_environ(arg).get_request() + elif isinstance(arg, Request): + request = arg + + if request is None: + builder = EnvironBuilder(*args, **kwargs) + + try: + request = builder.get_request() + finally: + builder.close() + + response = self.run_wsgi_app(request.environ, buffered=buffered) + response = self.response_wrapper(*response, request=request) + + redirects = set() + history: t.List["TestResponse"] = [] + + while follow_redirects and response.status_code in { + 301, + 302, + 303, + 305, + 307, + 308, + }: + # Exhaust intermediate response bodies to ensure middleware + # that returns an iterator runs any cleanup code. + if not buffered: + response.make_sequence() + response.close() + + new_redirect_entry = (response.location, response.status_code) + + if new_redirect_entry in redirects: + raise ClientRedirectError( + f"Loop detected: A {response.status_code} redirect" + f" to {response.location} was already made." + ) + + redirects.add(new_redirect_entry) + response.history = tuple(history) + history.append(response) + response = self.resolve_redirect(response, buffered=buffered) + else: + # This is the final request after redirects, or not + # following redirects. + response.history = tuple(history) + # Close the input stream when closing the response, in case + # the input is an open temporary file. + response.call_on_close(request.input_stream.close) + + if as_tuple: + warnings.warn( + "'as_tuple' is deprecated and will be removed in" + " Werkzeug 2.1. Access 'response.request.environ'" + " instead.", + DeprecationWarning, + stacklevel=2, + ) + return request.environ, response # type: ignore + + return response + + def get(self, *args: t.Any, **kw: t.Any) -> "TestResponse": + """Call :meth:`open` with ``method`` set to ``GET``.""" + kw["method"] = "GET" + return self.open(*args, **kw) + + def post(self, *args: t.Any, **kw: t.Any) -> "TestResponse": + """Call :meth:`open` with ``method`` set to ``POST``.""" + kw["method"] = "POST" + return self.open(*args, **kw) + + def put(self, *args: t.Any, **kw: t.Any) -> "TestResponse": + """Call :meth:`open` with ``method`` set to ``PUT``.""" + kw["method"] = "PUT" + return self.open(*args, **kw) + + def delete(self, *args: t.Any, **kw: t.Any) -> "TestResponse": + """Call :meth:`open` with ``method`` set to ``DELETE``.""" + kw["method"] = "DELETE" + return self.open(*args, **kw) + + def patch(self, *args: t.Any, **kw: t.Any) -> "TestResponse": + """Call :meth:`open` with ``method`` set to ``PATCH``.""" + kw["method"] = "PATCH" + return self.open(*args, **kw) + + def options(self, *args: t.Any, **kw: t.Any) -> "TestResponse": + """Call :meth:`open` with ``method`` set to ``OPTIONS``.""" + kw["method"] = "OPTIONS" + return self.open(*args, **kw) + + def head(self, *args: t.Any, **kw: t.Any) -> "TestResponse": + """Call :meth:`open` with ``method`` set to ``HEAD``.""" + kw["method"] = "HEAD" + return self.open(*args, **kw) + + def trace(self, *args: t.Any, **kw: t.Any) -> "TestResponse": + """Call :meth:`open` with ``method`` set to ``TRACE``.""" + kw["method"] = "TRACE" + return self.open(*args, **kw) + + def __repr__(self) -> str: + return f"<{type(self).__name__} {self.application!r}>" + + +def create_environ(*args: t.Any, **kwargs: t.Any) -> "WSGIEnvironment": + """Create a new WSGI environ dict based on the values passed. The first + parameter should be the path of the request which defaults to '/'. The + second one can either be an absolute path (in that case the host is + localhost:80) or a full path to the request with scheme, netloc port and + the path to the script. + + This accepts the same arguments as the :class:`EnvironBuilder` + constructor. + + .. versionchanged:: 0.5 + This function is now a thin wrapper over :class:`EnvironBuilder` which + was added in 0.5. The `headers`, `environ_base`, `environ_overrides` + and `charset` parameters were added. + """ + builder = EnvironBuilder(*args, **kwargs) + + try: + return builder.get_environ() + finally: + builder.close() + + +def run_wsgi_app( + app: "WSGIApplication", environ: "WSGIEnvironment", buffered: bool = False +) -> t.Tuple[t.Iterable[bytes], str, Headers]: + """Return a tuple in the form (app_iter, status, headers) of the + application output. This works best if you pass it an application that + returns an iterator all the time. + + Sometimes applications may use the `write()` callable returned + by the `start_response` function. This tries to resolve such edge + cases automatically. But if you don't get the expected output you + should set `buffered` to `True` which enforces buffering. + + If passed an invalid WSGI application the behavior of this function is + undefined. Never pass non-conforming WSGI applications to this function. + + :param app: the application to execute. + :param buffered: set to `True` to enforce buffering. + :return: tuple in the form ``(app_iter, status, headers)`` + """ + # Copy environ to ensure any mutations by the app (ProxyFix, for + # example) don't affect subsequent requests (such as redirects). + environ = _get_environ(environ).copy() + status: str + response: t.Optional[t.Tuple[str, t.List[t.Tuple[str, str]]]] = None + buffer: t.List[bytes] = [] + + def start_response(status, headers, exc_info=None): # type: ignore + nonlocal response + + if exc_info: + try: + raise exc_info[1].with_traceback(exc_info[2]) + finally: + exc_info = None + + response = (status, headers) + return buffer.append + + app_rv = app(environ, start_response) + close_func = getattr(app_rv, "close", None) + app_iter: t.Iterable[bytes] = iter(app_rv) + + # when buffering we emit the close call early and convert the + # application iterator into a regular list + if buffered: + try: + app_iter = list(app_iter) + finally: + if close_func is not None: + close_func() + + # otherwise we iterate the application iter until we have a response, chain + # the already received data with the already collected data and wrap it in + # a new `ClosingIterator` if we need to restore a `close` callable from the + # original return value. + else: + for item in app_iter: + buffer.append(item) + + if response is not None: + break + + if buffer: + app_iter = chain(buffer, app_iter) + + if close_func is not None and app_iter is not app_rv: + app_iter = ClosingIterator(app_iter, close_func) + + status, headers = response # type: ignore + return app_iter, status, Headers(headers) + + +class TestResponse(Response): + """:class:`~werkzeug.wrappers.Response` subclass that provides extra + information about requests made with the test :class:`Client`. + + Test client requests will always return an instance of this class. + If a custom response class is passed to the client, it is + subclassed along with this to support test information. + + If the test request included large files, or if the application is + serving a file, call :meth:`close` to close any open files and + prevent Python showing a ``ResourceWarning``. + """ + + request: Request + """A request object with the environ used to make the request that + resulted in this response. + """ + + history: t.Tuple["TestResponse", ...] + """A list of intermediate responses. Populated when the test request + is made with ``follow_redirects`` enabled. + """ + + def __init__( + self, + response: t.Iterable[bytes], + status: str, + headers: Headers, + request: Request, + history: t.Tuple["TestResponse"] = (), # type: ignore + **kwargs: t.Any, + ) -> None: + super().__init__(response, status, headers, **kwargs) + self.request = request + self.history = history + self._compat_tuple = response, status, headers + + def __iter__(self) -> t.Iterator: + warnings.warn( + ( + "The test client no longer returns a tuple, it returns" + " a 'TestResponse'. Tuple unpacking is deprecated and" + " will be removed in Werkzeug 2.1. Access the" + " attributes 'data', 'status', and 'headers' instead." + ), + DeprecationWarning, + stacklevel=2, + ) + return iter(self._compat_tuple) + + def __getitem__(self, item: int) -> t.Any: + warnings.warn( + ( + "The test client no longer returns a tuple, it returns" + " a 'TestResponse'. Item indexing is deprecated and" + " will be removed in Werkzeug 2.1. Access the" + " attributes 'data', 'status', and 'headers' instead." + ), + DeprecationWarning, + stacklevel=2, + ) + return self._compat_tuple[item] diff --git a/venv/Lib/site-packages/werkzeug/testapp.py b/venv/Lib/site-packages/werkzeug/testapp.py new file mode 100644 index 0000000..981f887 --- /dev/null +++ b/venv/Lib/site-packages/werkzeug/testapp.py @@ -0,0 +1,240 @@ +"""A small application that can be used to test a WSGI server and check +it for WSGI compliance. +""" +import base64 +import os +import sys +import typing as t +from html import escape +from textwrap import wrap + +from . import __version__ as _werkzeug_version +from .wrappers.request import Request +from .wrappers.response import Response + +if t.TYPE_CHECKING: + from _typeshed.wsgi import StartResponse + from _typeshed.wsgi import WSGIEnvironment + + +logo = Response( + base64.b64decode( + """ +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 +=""" + ), + mimetype="image/png", +) + + +TEMPLATE = """\ + +WSGI Information + +
    + +

    WSGI Information

    +

    + This page displays all available information about the WSGI server and + the underlying Python interpreter. +

    Python Interpreter

    + + + + + + +
    Python Version + %(python_version)s +
    Platform + %(platform)s [%(os)s] +
    API Version + %(api_version)s +
    Byteorder + %(byteorder)s +
    Werkzeug Version + %(werkzeug_version)s +
    +

    WSGI Environment

    + %(wsgi_env)s
    +

    Installed Eggs

    +

    + The following python packages were installed on the system as + Python eggs: +

      %(python_eggs)s
    +

    System Path

    +

    + The following paths are the current contents of the load path. The + following entries are looked up for Python packages. Note that not + all items in this path are folders. Gray and underlined items are + entries pointing to invalid resources or used by custom import hooks + such as the zip importer. +

    + Items with a bright background were expanded for display from a relative + path. If you encounter such paths in the output you might want to check + your setup as relative paths are usually problematic in multithreaded + environments. +

      %(sys_path)s
    +
    +""" + + +def iter_sys_path() -> t.Iterator[t.Tuple[str, bool, bool]]: + if os.name == "posix": + + def strip(x: str) -> str: + prefix = os.path.expanduser("~") + if x.startswith(prefix): + x = f"~{x[len(prefix) :]}" + return x + + else: + + def strip(x: str) -> str: + return x + + cwd = os.path.abspath(os.getcwd()) + for item in sys.path: + path = os.path.join(cwd, item or os.path.curdir) + yield strip(os.path.normpath(path)), not os.path.isdir(path), path != item + + +def render_testapp(req: Request) -> bytes: + try: + import pkg_resources + except ImportError: + eggs: t.Iterable[t.Any] = () + else: + eggs = sorted( + pkg_resources.working_set, + key=lambda x: x.project_name.lower(), # type: ignore + ) + python_eggs = [] + for egg in eggs: + try: + version = egg.version + except (ValueError, AttributeError): + version = "unknown" + python_eggs.append( + f"
  • {escape(egg.project_name)} [{escape(version)}]" + ) + + wsgi_env = [] + sorted_environ = sorted(req.environ.items(), key=lambda x: repr(x[0]).lower()) + for key, value in sorted_environ: + value = "".join(wrap(escape(repr(value)))) + wsgi_env.append(f"{escape(str(key))}{value}") + + sys_path = [] + for item, virtual, expanded in iter_sys_path(): + class_ = [] + if virtual: + class_.append("virtual") + if expanded: + class_.append("exp") + class_ = f' class="{" ".join(class_)}"' if class_ else "" + sys_path.append(f"{escape(item)}") + + return ( + TEMPLATE + % { + "python_version": "
    ".join(escape(sys.version).splitlines()), + "platform": escape(sys.platform), + "os": escape(os.name), + "api_version": sys.api_version, + "byteorder": sys.byteorder, + "werkzeug_version": _werkzeug_version, + "python_eggs": "\n".join(python_eggs), + "wsgi_env": "\n".join(wsgi_env), + "sys_path": "\n".join(sys_path), + } + ).encode("utf-8") + + +def test_app( + environ: "WSGIEnvironment", start_response: "StartResponse" +) -> t.Iterable[bytes]: + """Simple test application that dumps the environment. You can use + it to check if Werkzeug is working properly: + + .. sourcecode:: pycon + + >>> from werkzeug.serving import run_simple + >>> from werkzeug.testapp import test_app + >>> run_simple('localhost', 3000, test_app) + * Running on http://localhost:3000/ + + The application displays important information from the WSGI environment, + the Python interpreter and the installed libraries. + """ + req = Request(environ, populate_request=False) + if req.args.get("resource") == "logo": + response = logo + else: + response = Response(render_testapp(req), mimetype="text/html") + return response(environ, start_response) + + +if __name__ == "__main__": + from .serving import run_simple + + run_simple("localhost", 5000, test_app, use_reloader=True) diff --git a/venv/Lib/site-packages/werkzeug/urls.py b/venv/Lib/site-packages/werkzeug/urls.py new file mode 100644 index 0000000..7566ac2 --- /dev/null +++ b/venv/Lib/site-packages/werkzeug/urls.py @@ -0,0 +1,1211 @@ +"""Functions for working with URLs. + +Contains implementations of functions from :mod:`urllib.parse` that +handle bytes and strings. +""" +import codecs +import os +import re +import typing as t +import warnings + +from ._internal import _check_str_tuple +from ._internal import _decode_idna +from ._internal import _encode_idna +from ._internal import _make_encode_wrapper +from ._internal import _to_str + +if t.TYPE_CHECKING: + from . import datastructures as ds + +# A regular expression for what a valid schema looks like +_scheme_re = re.compile(r"^[a-zA-Z0-9+-.]+$") + +# Characters that are safe in any part of an URL. +_always_safe = frozenset( + bytearray( + b"abcdefghijklmnopqrstuvwxyz" + b"ABCDEFGHIJKLMNOPQRSTUVWXYZ" + b"0123456789" + b"-._~" + ) +) + +_hexdigits = "0123456789ABCDEFabcdef" +_hextobyte = { + f"{a}{b}".encode("ascii"): int(f"{a}{b}", 16) + for a in _hexdigits + for b in _hexdigits +} +_bytetohex = [f"%{char:02X}".encode("ascii") for char in range(256)] + + +class _URLTuple(t.NamedTuple): + scheme: str + netloc: str + path: str + query: str + fragment: str + + +class BaseURL(_URLTuple): + """Superclass of :py:class:`URL` and :py:class:`BytesURL`.""" + + __slots__ = () + _at: str + _colon: str + _lbracket: str + _rbracket: str + + def __str__(self) -> str: + return self.to_url() + + def replace(self, **kwargs: t.Any) -> "BaseURL": + """Return an URL with the same values, except for those parameters + given new values by whichever keyword arguments are specified.""" + return self._replace(**kwargs) + + @property + def host(self) -> t.Optional[str]: + """The host part of the URL if available, otherwise `None`. The + host is either the hostname or the IP address mentioned in the + URL. It will not contain the port. + """ + return self._split_host()[0] + + @property + def ascii_host(self) -> t.Optional[str]: + """Works exactly like :attr:`host` but will return a result that + is restricted to ASCII. If it finds a netloc that is not ASCII + it will attempt to idna decode it. This is useful for socket + operations when the URL might include internationalized characters. + """ + rv = self.host + if rv is not None and isinstance(rv, str): + try: + rv = _encode_idna(rv) # type: ignore + except UnicodeError: + rv = rv.encode("ascii", "ignore") # type: ignore + return _to_str(rv, "ascii", "ignore") + + @property + def port(self) -> t.Optional[int]: + """The port in the URL as an integer if it was present, `None` + otherwise. This does not fill in default ports. + """ + try: + rv = int(_to_str(self._split_host()[1])) + if 0 <= rv <= 65535: + return rv + except (ValueError, TypeError): + pass + return None + + @property + def auth(self) -> t.Optional[str]: + """The authentication part in the URL if available, `None` + otherwise. + """ + return self._split_netloc()[0] + + @property + def username(self) -> t.Optional[str]: + """The username if it was part of the URL, `None` otherwise. + This undergoes URL decoding and will always be a string. + """ + rv = self._split_auth()[0] + if rv is not None: + return _url_unquote_legacy(rv) + return None + + @property + def raw_username(self) -> t.Optional[str]: + """The username if it was part of the URL, `None` otherwise. + Unlike :attr:`username` this one is not being decoded. + """ + return self._split_auth()[0] + + @property + def password(self) -> t.Optional[str]: + """The password if it was part of the URL, `None` otherwise. + This undergoes URL decoding and will always be a string. + """ + rv = self._split_auth()[1] + if rv is not None: + return _url_unquote_legacy(rv) + return None + + @property + def raw_password(self) -> t.Optional[str]: + """The password if it was part of the URL, `None` otherwise. + Unlike :attr:`password` this one is not being decoded. + """ + return self._split_auth()[1] + + def decode_query(self, *args: t.Any, **kwargs: t.Any) -> "ds.MultiDict[str, str]": + """Decodes the query part of the URL. Ths is a shortcut for + calling :func:`url_decode` on the query argument. The arguments and + keyword arguments are forwarded to :func:`url_decode` unchanged. + """ + return url_decode(self.query, *args, **kwargs) + + def join(self, *args: t.Any, **kwargs: t.Any) -> "BaseURL": + """Joins this URL with another one. This is just a convenience + function for calling into :meth:`url_join` and then parsing the + return value again. + """ + return url_parse(url_join(self, *args, **kwargs)) + + def to_url(self) -> str: + """Returns a URL string or bytes depending on the type of the + information stored. This is just a convenience function + for calling :meth:`url_unparse` for this URL. + """ + return url_unparse(self) + + def encode_netloc(self) -> str: + """Encodes the netloc part to an ASCII safe URL as bytes.""" + rv = self.ascii_host or "" + if ":" in rv: + rv = f"[{rv}]" + port = self.port + if port is not None: + rv = f"{rv}:{port}" + auth = ":".join( + filter( + None, + [ + url_quote(self.raw_username or "", "utf-8", "strict", "/:%"), + url_quote(self.raw_password or "", "utf-8", "strict", "/:%"), + ], + ) + ) + if auth: + rv = f"{auth}@{rv}" + return rv + + def decode_netloc(self) -> str: + """Decodes the netloc part into a string.""" + rv = _decode_idna(self.host or "") + + if ":" in rv: + rv = f"[{rv}]" + port = self.port + if port is not None: + rv = f"{rv}:{port}" + auth = ":".join( + filter( + None, + [ + _url_unquote_legacy(self.raw_username or "", "/:%@"), + _url_unquote_legacy(self.raw_password or "", "/:%@"), + ], + ) + ) + if auth: + rv = f"{auth}@{rv}" + return rv + + def to_uri_tuple(self) -> "BaseURL": + """Returns a :class:`BytesURL` tuple that holds a URI. This will + encode all the information in the URL properly to ASCII using the + rules a web browser would follow. + + It's usually more interesting to directly call :meth:`iri_to_uri` which + will return a string. + """ + return url_parse(iri_to_uri(self)) + + def to_iri_tuple(self) -> "BaseURL": + """Returns a :class:`URL` tuple that holds a IRI. This will try + to decode as much information as possible in the URL without + losing information similar to how a web browser does it for the + URL bar. + + It's usually more interesting to directly call :meth:`uri_to_iri` which + will return a string. + """ + return url_parse(uri_to_iri(self)) + + def get_file_location( + self, pathformat: t.Optional[str] = None + ) -> t.Tuple[t.Optional[str], t.Optional[str]]: + """Returns a tuple with the location of the file in the form + ``(server, location)``. If the netloc is empty in the URL or + points to localhost, it's represented as ``None``. + + The `pathformat` by default is autodetection but needs to be set + when working with URLs of a specific system. The supported values + are ``'windows'`` when working with Windows or DOS paths and + ``'posix'`` when working with posix paths. + + If the URL does not point to a local file, the server and location + are both represented as ``None``. + + :param pathformat: The expected format of the path component. + Currently ``'windows'`` and ``'posix'`` are + supported. Defaults to ``None`` which is + autodetect. + """ + if self.scheme != "file": + return None, None + + path = url_unquote(self.path) + host = self.netloc or None + + if pathformat is None: + if os.name == "nt": + pathformat = "windows" + else: + pathformat = "posix" + + if pathformat == "windows": + if path[:1] == "/" and path[1:2].isalpha() and path[2:3] in "|:": + path = f"{path[1:2]}:{path[3:]}" + windows_share = path[:3] in ("\\" * 3, "/" * 3) + import ntpath + + path = ntpath.normpath(path) + # Windows shared drives are represented as ``\\host\\directory``. + # That results in a URL like ``file://///host/directory``, and a + # path like ``///host/directory``. We need to special-case this + # because the path contains the hostname. + if windows_share and host is None: + parts = path.lstrip("\\").split("\\", 1) + if len(parts) == 2: + host, path = parts + else: + host = parts[0] + path = "" + elif pathformat == "posix": + import posixpath + + path = posixpath.normpath(path) + else: + raise TypeError(f"Invalid path format {pathformat!r}") + + if host in ("127.0.0.1", "::1", "localhost"): + host = None + + return host, path + + def _split_netloc(self) -> t.Tuple[t.Optional[str], str]: + if self._at in self.netloc: + auth, _, netloc = self.netloc.partition(self._at) + return auth, netloc + return None, self.netloc + + def _split_auth(self) -> t.Tuple[t.Optional[str], t.Optional[str]]: + auth = self._split_netloc()[0] + if not auth: + return None, None + if self._colon not in auth: + return auth, None + + username, _, password = auth.partition(self._colon) + return username, password + + def _split_host(self) -> t.Tuple[t.Optional[str], t.Optional[str]]: + rv = self._split_netloc()[1] + if not rv: + return None, None + + if not rv.startswith(self._lbracket): + if self._colon in rv: + host, _, port = rv.partition(self._colon) + return host, port + return rv, None + + idx = rv.find(self._rbracket) + if idx < 0: + return rv, None + + host = rv[1:idx] + rest = rv[idx + 1 :] + if rest.startswith(self._colon): + return host, rest[1:] + return host, None + + +class URL(BaseURL): + """Represents a parsed URL. This behaves like a regular tuple but + also has some extra attributes that give further insight into the + URL. + """ + + __slots__ = () + _at = "@" + _colon = ":" + _lbracket = "[" + _rbracket = "]" + + def encode(self, charset: str = "utf-8", errors: str = "replace") -> "BytesURL": + """Encodes the URL to a tuple made out of bytes. The charset is + only being used for the path, query and fragment. + """ + return BytesURL( + self.scheme.encode("ascii"), # type: ignore + self.encode_netloc(), + self.path.encode(charset, errors), # type: ignore + self.query.encode(charset, errors), # type: ignore + self.fragment.encode(charset, errors), # type: ignore + ) + + +class BytesURL(BaseURL): + """Represents a parsed URL in bytes.""" + + __slots__ = () + _at = b"@" # type: ignore + _colon = b":" # type: ignore + _lbracket = b"[" # type: ignore + _rbracket = b"]" # type: ignore + + def __str__(self) -> str: + return self.to_url().decode("utf-8", "replace") # type: ignore + + def encode_netloc(self) -> bytes: # type: ignore + """Returns the netloc unchanged as bytes.""" + return self.netloc # type: ignore + + def decode(self, charset: str = "utf-8", errors: str = "replace") -> "URL": + """Decodes the URL to a tuple made out of strings. The charset is + only being used for the path, query and fragment. + """ + return URL( + self.scheme.decode("ascii"), # type: ignore + self.decode_netloc(), + self.path.decode(charset, errors), # type: ignore + self.query.decode(charset, errors), # type: ignore + self.fragment.decode(charset, errors), # type: ignore + ) + + +_unquote_maps: t.Dict[t.FrozenSet[int], t.Dict[bytes, int]] = {frozenset(): _hextobyte} + + +def _unquote_to_bytes( + string: t.Union[str, bytes], unsafe: t.Union[str, bytes] = "" +) -> bytes: + if isinstance(string, str): + string = string.encode("utf-8") + + if isinstance(unsafe, str): + unsafe = unsafe.encode("utf-8") + + unsafe = frozenset(bytearray(unsafe)) + groups = iter(string.split(b"%")) + result = bytearray(next(groups, b"")) + + try: + hex_to_byte = _unquote_maps[unsafe] + except KeyError: + hex_to_byte = _unquote_maps[unsafe] = { + h: b for h, b in _hextobyte.items() if b not in unsafe + } + + for group in groups: + code = group[:2] + + if code in hex_to_byte: + result.append(hex_to_byte[code]) + result.extend(group[2:]) + else: + result.append(37) # % + result.extend(group) + + return bytes(result) + + +def _url_encode_impl( + obj: t.Union[t.Mapping[str, str], t.Iterable[t.Tuple[str, str]]], + charset: str, + sort: bool, + key: t.Optional[t.Callable[[t.Tuple[str, str]], t.Any]], +) -> t.Iterator[str]: + from .datastructures import iter_multi_items + + iterable: t.Iterable[t.Tuple[str, str]] = iter_multi_items(obj) + + if sort: + iterable = sorted(iterable, key=key) + + for key_str, value_str in iterable: + if value_str is None: + continue + + if not isinstance(key_str, bytes): + key_bytes = str(key_str).encode(charset) + else: + key_bytes = key_str + + if not isinstance(value_str, bytes): + value_bytes = str(value_str).encode(charset) + else: + value_bytes = value_str + + yield f"{_fast_url_quote_plus(key_bytes)}={_fast_url_quote_plus(value_bytes)}" + + +def _url_unquote_legacy(value: str, unsafe: str = "") -> str: + try: + return url_unquote(value, charset="utf-8", errors="strict", unsafe=unsafe) + except UnicodeError: + return url_unquote(value, charset="latin1", unsafe=unsafe) + + +def url_parse( + url: str, scheme: t.Optional[str] = None, allow_fragments: bool = True +) -> BaseURL: + """Parses a URL from a string into a :class:`URL` tuple. If the URL + is lacking a scheme it can be provided as second argument. Otherwise, + it is ignored. Optionally fragments can be stripped from the URL + by setting `allow_fragments` to `False`. + + The inverse of this function is :func:`url_unparse`. + + :param url: the URL to parse. + :param scheme: the default schema to use if the URL is schemaless. + :param allow_fragments: if set to `False` a fragment will be removed + from the URL. + """ + s = _make_encode_wrapper(url) + is_text_based = isinstance(url, str) + + if scheme is None: + scheme = s("") + netloc = query = fragment = s("") + i = url.find(s(":")) + if i > 0 and _scheme_re.match(_to_str(url[:i], errors="replace")): + # make sure "iri" is not actually a port number (in which case + # "scheme" is really part of the path) + rest = url[i + 1 :] + if not rest or any(c not in s("0123456789") for c in rest): + # not a port number + scheme, url = url[:i].lower(), rest + + if url[:2] == s("//"): + delim = len(url) + for c in s("/?#"): + wdelim = url.find(c, 2) + if wdelim >= 0: + delim = min(delim, wdelim) + netloc, url = url[2:delim], url[delim:] + if (s("[") in netloc and s("]") not in netloc) or ( + s("]") in netloc and s("[") not in netloc + ): + raise ValueError("Invalid IPv6 URL") + + if allow_fragments and s("#") in url: + url, fragment = url.split(s("#"), 1) + if s("?") in url: + url, query = url.split(s("?"), 1) + + result_type = URL if is_text_based else BytesURL + return result_type(scheme, netloc, url, query, fragment) + + +def _make_fast_url_quote( + charset: str = "utf-8", + errors: str = "strict", + safe: t.Union[str, bytes] = "/:", + unsafe: t.Union[str, bytes] = "", +) -> t.Callable[[bytes], str]: + """Precompile the translation table for a URL encoding function. + + Unlike :func:`url_quote`, the generated function only takes the + string to quote. + + :param charset: The charset to encode the result with. + :param errors: How to handle encoding errors. + :param safe: An optional sequence of safe characters to never encode. + :param unsafe: An optional sequence of unsafe characters to always encode. + """ + if isinstance(safe, str): + safe = safe.encode(charset, errors) + + if isinstance(unsafe, str): + unsafe = unsafe.encode(charset, errors) + + safe = (frozenset(bytearray(safe)) | _always_safe) - frozenset(bytearray(unsafe)) + table = [chr(c) if c in safe else f"%{c:02X}" for c in range(256)] + + def quote(string: bytes) -> str: + return "".join([table[c] for c in string]) + + return quote + + +_fast_url_quote = _make_fast_url_quote() +_fast_quote_plus = _make_fast_url_quote(safe=" ", unsafe="+") + + +def _fast_url_quote_plus(string: bytes) -> str: + return _fast_quote_plus(string).replace(" ", "+") + + +def url_quote( + string: t.Union[str, bytes], + charset: str = "utf-8", + errors: str = "strict", + safe: t.Union[str, bytes] = "/:", + unsafe: t.Union[str, bytes] = "", +) -> str: + """URL encode a single string with a given encoding. + + :param s: the string to quote. + :param charset: the charset to be used. + :param safe: an optional sequence of safe characters. + :param unsafe: an optional sequence of unsafe characters. + + .. versionadded:: 0.9.2 + The `unsafe` parameter was added. + """ + if not isinstance(string, (str, bytes, bytearray)): + string = str(string) + if isinstance(string, str): + string = string.encode(charset, errors) + if isinstance(safe, str): + safe = safe.encode(charset, errors) + if isinstance(unsafe, str): + unsafe = unsafe.encode(charset, errors) + safe = (frozenset(bytearray(safe)) | _always_safe) - frozenset(bytearray(unsafe)) + rv = bytearray() + for char in bytearray(string): + if char in safe: + rv.append(char) + else: + rv.extend(_bytetohex[char]) + return bytes(rv).decode(charset) + + +def url_quote_plus( + string: str, charset: str = "utf-8", errors: str = "strict", safe: str = "" +) -> str: + """URL encode a single string with the given encoding and convert + whitespace to "+". + + :param s: The string to quote. + :param charset: The charset to be used. + :param safe: An optional sequence of safe characters. + """ + return url_quote(string, charset, errors, safe + " ", "+").replace(" ", "+") + + +def url_unparse(components: t.Tuple[str, str, str, str, str]) -> str: + """The reverse operation to :meth:`url_parse`. This accepts arbitrary + as well as :class:`URL` tuples and returns a URL as a string. + + :param components: the parsed URL as tuple which should be converted + into a URL string. + """ + _check_str_tuple(components) + scheme, netloc, path, query, fragment = components + s = _make_encode_wrapper(scheme) + url = s("") + + # We generally treat file:///x and file:/x the same which is also + # what browsers seem to do. This also allows us to ignore a schema + # register for netloc utilization or having to differentiate between + # empty and missing netloc. + if netloc or (scheme and path.startswith(s("/"))): + if path and path[:1] != s("/"): + path = s("/") + path + url = s("//") + (netloc or s("")) + path + elif path: + url += path + if scheme: + url = scheme + s(":") + url + if query: + url = url + s("?") + query + if fragment: + url = url + s("#") + fragment + return url + + +def url_unquote( + s: t.Union[str, bytes], + charset: str = "utf-8", + errors: str = "replace", + unsafe: str = "", +) -> str: + """URL decode a single string with a given encoding. If the charset + is set to `None` no decoding is performed and raw bytes are + returned. + + :param s: the string to unquote. + :param charset: the charset of the query string. If set to `None` + no decoding will take place. + :param errors: the error handling for the charset decoding. + """ + rv = _unquote_to_bytes(s, unsafe) + if charset is None: + return rv + return rv.decode(charset, errors) + + +def url_unquote_plus( + s: t.Union[str, bytes], charset: str = "utf-8", errors: str = "replace" +) -> str: + """URL decode a single string with the given `charset` and decode "+" to + whitespace. + + Per default encoding errors are ignored. If you want a different behavior + you can set `errors` to ``'replace'`` or ``'strict'``. + + :param s: The string to unquote. + :param charset: the charset of the query string. If set to `None` + no decoding will take place. + :param errors: The error handling for the `charset` decoding. + """ + if isinstance(s, str): + s = s.replace("+", " ") + else: + s = s.replace(b"+", b" ") + return url_unquote(s, charset, errors) + + +def url_fix(s: str, charset: str = "utf-8") -> str: + r"""Sometimes you get an URL by a user that just isn't a real URL because + it contains unsafe characters like ' ' and so on. This function can fix + some of the problems in a similar way browsers handle data entered by the + user: + + >>> url_fix('http://de.wikipedia.org/wiki/Elf (Begriffskl\xe4rung)') + 'http://de.wikipedia.org/wiki/Elf%20(Begriffskl%C3%A4rung)' + + :param s: the string with the URL to fix. + :param charset: The target charset for the URL if the url was given + as a string. + """ + # First step is to switch to text processing and to convert + # backslashes (which are invalid in URLs anyways) to slashes. This is + # consistent with what Chrome does. + s = _to_str(s, charset, "replace").replace("\\", "/") + + # For the specific case that we look like a malformed windows URL + # we want to fix this up manually: + if s.startswith("file://") and s[7:8].isalpha() and s[8:10] in (":/", "|/"): + s = f"file:///{s[7:]}" + + url = url_parse(s) + path = url_quote(url.path, charset, safe="/%+$!*'(),") + qs = url_quote_plus(url.query, charset, safe=":&%=+$!*'(),") + anchor = url_quote_plus(url.fragment, charset, safe=":&%=+$!*'(),") + return url_unparse((url.scheme, url.encode_netloc(), path, qs, anchor)) + + +# not-unreserved characters remain quoted when unquoting to IRI +_to_iri_unsafe = "".join([chr(c) for c in range(128) if c not in _always_safe]) + + +def _codec_error_url_quote(e: UnicodeError) -> t.Tuple[str, int]: + """Used in :func:`uri_to_iri` after unquoting to re-quote any + invalid bytes. + """ + # the docs state that UnicodeError does have these attributes, + # but mypy isn't picking them up + out = _fast_url_quote(e.object[e.start : e.end]) # type: ignore + return out, e.end # type: ignore + + +codecs.register_error("werkzeug.url_quote", _codec_error_url_quote) + + +def uri_to_iri( + uri: t.Union[str, t.Tuple[str, str, str, str, str]], + charset: str = "utf-8", + errors: str = "werkzeug.url_quote", +) -> str: + """Convert a URI to an IRI. All valid UTF-8 characters are unquoted, + leaving all reserved and invalid characters quoted. If the URL has + a domain, it is decoded from Punycode. + + >>> uri_to_iri("http://xn--n3h.net/p%C3%A5th?q=%C3%A8ry%DF") + 'http://\\u2603.net/p\\xe5th?q=\\xe8ry%DF' + + :param uri: The URI to convert. + :param charset: The encoding to encode unquoted bytes with. + :param errors: Error handler to use during ``bytes.encode``. By + default, invalid bytes are left quoted. + + .. versionchanged:: 0.15 + All reserved and invalid characters remain quoted. Previously, + only some reserved characters were preserved, and invalid bytes + were replaced instead of left quoted. + + .. versionadded:: 0.6 + """ + if isinstance(uri, tuple): + uri = url_unparse(uri) + + uri = url_parse(_to_str(uri, charset)) + path = url_unquote(uri.path, charset, errors, _to_iri_unsafe) + query = url_unquote(uri.query, charset, errors, _to_iri_unsafe) + fragment = url_unquote(uri.fragment, charset, errors, _to_iri_unsafe) + return url_unparse((uri.scheme, uri.decode_netloc(), path, query, fragment)) + + +# reserved characters remain unquoted when quoting to URI +_to_uri_safe = ":/?#[]@!$&'()*+,;=%" + + +def iri_to_uri( + iri: t.Union[str, t.Tuple[str, str, str, str, str]], + charset: str = "utf-8", + errors: str = "strict", + safe_conversion: bool = False, +) -> str: + """Convert an IRI to a URI. All non-ASCII and unsafe characters are + quoted. If the URL has a domain, it is encoded to Punycode. + + >>> iri_to_uri('http://\\u2603.net/p\\xe5th?q=\\xe8ry%DF') + 'http://xn--n3h.net/p%C3%A5th?q=%C3%A8ry%DF' + + :param iri: The IRI to convert. + :param charset: The encoding of the IRI. + :param errors: Error handler to use during ``bytes.encode``. + :param safe_conversion: Return the URL unchanged if it only contains + ASCII characters and no whitespace. See the explanation below. + + There is a general problem with IRI conversion with some protocols + that are in violation of the URI specification. Consider the + following two IRIs:: + + magnet:?xt=uri:whatever + itms-services://?action=download-manifest + + After parsing, we don't know if the scheme requires the ``//``, + which is dropped if empty, but conveys different meanings in the + final URL if it's present or not. In this case, you can use + ``safe_conversion``, which will return the URL unchanged if it only + contains ASCII characters and no whitespace. This can result in a + URI with unquoted characters if it was not already quoted correctly, + but preserves the URL's semantics. Werkzeug uses this for the + ``Location`` header for redirects. + + .. versionchanged:: 0.15 + All reserved characters remain unquoted. Previously, only some + reserved characters were left unquoted. + + .. versionchanged:: 0.9.6 + The ``safe_conversion`` parameter was added. + + .. versionadded:: 0.6 + """ + if isinstance(iri, tuple): + iri = url_unparse(iri) + + if safe_conversion: + # If we're not sure if it's safe to convert the URL, and it only + # contains ASCII characters, return it unconverted. + try: + native_iri = _to_str(iri) + ascii_iri = native_iri.encode("ascii") + + # Only return if it doesn't have whitespace. (Why?) + if len(ascii_iri.split()) == 1: + return native_iri + except UnicodeError: + pass + + iri = url_parse(_to_str(iri, charset, errors)) + path = url_quote(iri.path, charset, errors, _to_uri_safe) + query = url_quote(iri.query, charset, errors, _to_uri_safe) + fragment = url_quote(iri.fragment, charset, errors, _to_uri_safe) + return url_unparse((iri.scheme, iri.encode_netloc(), path, query, fragment)) + + +def url_decode( + s: t.AnyStr, + charset: str = "utf-8", + decode_keys: None = None, + include_empty: bool = True, + errors: str = "replace", + separator: str = "&", + cls: t.Optional[t.Type["ds.MultiDict"]] = None, +) -> "ds.MultiDict[str, str]": + """Parse a query string and return it as a :class:`MultiDict`. + + :param s: The query string to parse. + :param charset: Decode bytes to string with this charset. If not + given, bytes are returned as-is. + :param include_empty: Include keys with empty values in the dict. + :param errors: Error handling behavior when decoding bytes. + :param separator: Separator character between pairs. + :param cls: Container to hold result instead of :class:`MultiDict`. + + .. versionchanged:: 2.0 + The ``decode_keys`` parameter is deprecated and will be removed + in Werkzeug 2.1. + + .. versionchanged:: 0.5 + In previous versions ";" and "&" could be used for url decoding. + Now only "&" is supported. If you want to use ";", a different + ``separator`` can be provided. + + .. versionchanged:: 0.5 + The ``cls`` parameter was added. + """ + if decode_keys is not None: + warnings.warn( + "'decode_keys' is deprecated and will be removed in Werkzeug 2.1.", + DeprecationWarning, + stacklevel=2, + ) + if cls is None: + from .datastructures import MultiDict # noqa: F811 + + cls = MultiDict + if isinstance(s, str) and not isinstance(separator, str): + separator = separator.decode(charset or "ascii") + elif isinstance(s, bytes) and not isinstance(separator, bytes): + separator = separator.encode(charset or "ascii") # type: ignore + return cls( + _url_decode_impl( + s.split(separator), charset, include_empty, errors # type: ignore + ) + ) + + +def url_decode_stream( + stream: t.BinaryIO, + charset: str = "utf-8", + decode_keys: None = None, + include_empty: bool = True, + errors: str = "replace", + separator: bytes = b"&", + cls: t.Optional[t.Type["ds.MultiDict"]] = None, + limit: t.Optional[int] = None, + return_iterator: bool = False, +) -> "ds.MultiDict[str, str]": + """Works like :func:`url_decode` but decodes a stream. The behavior + of stream and limit follows functions like + :func:`~werkzeug.wsgi.make_line_iter`. The generator of pairs is + directly fed to the `cls` so you can consume the data while it's + parsed. + + :param stream: a stream with the encoded querystring + :param charset: the charset of the query string. If set to `None` + no decoding will take place. + :param include_empty: Set to `False` if you don't want empty values to + appear in the dict. + :param errors: the decoding error behavior. + :param separator: the pair separator to be used, defaults to ``&`` + :param cls: an optional dict class to use. If this is not specified + or `None` the default :class:`MultiDict` is used. + :param limit: the content length of the URL data. Not necessary if + a limited stream is provided. + + .. versionchanged:: 2.0 + The ``decode_keys`` and ``return_iterator`` parameters are + deprecated and will be removed in Werkzeug 2.1. + + .. versionadded:: 0.8 + """ + from .wsgi import make_chunk_iter + + if decode_keys is not None: + warnings.warn( + "'decode_keys' is deprecated and will be removed in Werkzeug 2.1.", + DeprecationWarning, + stacklevel=2, + ) + + pair_iter = make_chunk_iter(stream, separator, limit) + decoder = _url_decode_impl(pair_iter, charset, include_empty, errors) + + if return_iterator: + warnings.warn( + "'return_iterator' is deprecated and will be removed in Werkzeug 2.1.", + DeprecationWarning, + stacklevel=2, + ) + return decoder # type: ignore + + if cls is None: + from .datastructures import MultiDict # noqa: F811 + + cls = MultiDict + + return cls(decoder) + + +def _url_decode_impl( + pair_iter: t.Iterable[t.AnyStr], charset: str, include_empty: bool, errors: str +) -> t.Iterator[t.Tuple[str, str]]: + for pair in pair_iter: + if not pair: + continue + s = _make_encode_wrapper(pair) + equal = s("=") + if equal in pair: + key, value = pair.split(equal, 1) + else: + if not include_empty: + continue + key = pair + value = s("") + yield ( + url_unquote_plus(key, charset, errors), + url_unquote_plus(value, charset, errors), + ) + + +def url_encode( + obj: t.Union[t.Mapping[str, str], t.Iterable[t.Tuple[str, str]]], + charset: str = "utf-8", + encode_keys: None = None, + sort: bool = False, + key: t.Optional[t.Callable[[t.Tuple[str, str]], t.Any]] = None, + separator: str = "&", +) -> str: + """URL encode a dict/`MultiDict`. If a value is `None` it will not appear + in the result string. Per default only values are encoded into the target + charset strings. + + :param obj: the object to encode into a query string. + :param charset: the charset of the query string. + :param sort: set to `True` if you want parameters to be sorted by `key`. + :param separator: the separator to be used for the pairs. + :param key: an optional function to be used for sorting. For more details + check out the :func:`sorted` documentation. + + .. versionchanged:: 2.0 + The ``encode_keys`` parameter is deprecated and will be removed + in Werkzeug 2.1. + + .. versionchanged:: 0.5 + Added the ``sort``, ``key``, and ``separator`` parameters. + """ + if encode_keys is not None: + warnings.warn( + "'encode_keys' is deprecated and will be removed in Werkzeug 2.1.", + DeprecationWarning, + stacklevel=2, + ) + separator = _to_str(separator, "ascii") + return separator.join(_url_encode_impl(obj, charset, sort, key)) + + +def url_encode_stream( + obj: t.Union[t.Mapping[str, str], t.Iterable[t.Tuple[str, str]]], + stream: t.Optional[t.TextIO] = None, + charset: str = "utf-8", + encode_keys: None = None, + sort: bool = False, + key: t.Optional[t.Callable[[t.Tuple[str, str]], t.Any]] = None, + separator: str = "&", +) -> None: + """Like :meth:`url_encode` but writes the results to a stream + object. If the stream is `None` a generator over all encoded + pairs is returned. + + :param obj: the object to encode into a query string. + :param stream: a stream to write the encoded object into or `None` if + an iterator over the encoded pairs should be returned. In + that case the separator argument is ignored. + :param charset: the charset of the query string. + :param sort: set to `True` if you want parameters to be sorted by `key`. + :param separator: the separator to be used for the pairs. + :param key: an optional function to be used for sorting. For more details + check out the :func:`sorted` documentation. + + .. versionchanged:: 2.0 + The ``encode_keys`` parameter is deprecated and will be removed + in Werkzeug 2.1. + + .. versionadded:: 0.8 + """ + if encode_keys is not None: + warnings.warn( + "'encode_keys' is deprecated and will be removed in Werkzeug 2.1.", + DeprecationWarning, + stacklevel=2, + ) + separator = _to_str(separator, "ascii") + gen = _url_encode_impl(obj, charset, sort, key) + if stream is None: + return gen # type: ignore + for idx, chunk in enumerate(gen): + if idx: + stream.write(separator) + stream.write(chunk) + return None + + +def url_join( + base: t.Union[str, t.Tuple[str, str, str, str, str]], + url: t.Union[str, t.Tuple[str, str, str, str, str]], + allow_fragments: bool = True, +) -> str: + """Join a base URL and a possibly relative URL to form an absolute + interpretation of the latter. + + :param base: the base URL for the join operation. + :param url: the URL to join. + :param allow_fragments: indicates whether fragments should be allowed. + """ + if isinstance(base, tuple): + base = url_unparse(base) + if isinstance(url, tuple): + url = url_unparse(url) + + _check_str_tuple((base, url)) + s = _make_encode_wrapper(base) + + if not base: + return url + if not url: + return base + + bscheme, bnetloc, bpath, bquery, bfragment = url_parse( + base, allow_fragments=allow_fragments + ) + scheme, netloc, path, query, fragment = url_parse(url, bscheme, allow_fragments) + if scheme != bscheme: + return url + if netloc: + return url_unparse((scheme, netloc, path, query, fragment)) + netloc = bnetloc + + if path[:1] == s("/"): + segments = path.split(s("/")) + elif not path: + segments = bpath.split(s("/")) + if not query: + query = bquery + else: + segments = bpath.split(s("/"))[:-1] + path.split(s("/")) + + # If the rightmost part is "./" we want to keep the slash but + # remove the dot. + if segments[-1] == s("."): + segments[-1] = s("") + + # Resolve ".." and "." + segments = [segment for segment in segments if segment != s(".")] + while True: + i = 1 + n = len(segments) - 1 + while i < n: + if segments[i] == s("..") and segments[i - 1] not in (s(""), s("..")): + del segments[i - 1 : i + 1] + break + i += 1 + else: + break + + # Remove trailing ".." if the URL is absolute + unwanted_marker = [s(""), s("..")] + while segments[:2] == unwanted_marker: + del segments[1] + + path = s("/").join(segments) + return url_unparse((scheme, netloc, path, query, fragment)) + + +class Href: + """Implements a callable that constructs URLs with the given base. The + function can be called with any number of positional and keyword + arguments which than are used to assemble the URL. Works with URLs + and posix paths. + + Positional arguments are appended as individual segments to + the path of the URL: + + >>> href = Href('/foo') + >>> href('bar', 23) + '/foo/bar/23' + >>> href('foo', bar=23) + '/foo/foo?bar=23' + + If any of the arguments (positional or keyword) evaluates to `None` it + will be skipped. If no keyword arguments are given the last argument + can be a :class:`dict` or :class:`MultiDict` (or any other dict subclass), + otherwise the keyword arguments are used for the query parameters, cutting + off the first trailing underscore of the parameter name: + + >>> href(is_=42) + '/foo?is=42' + >>> href({'foo': 'bar'}) + '/foo?foo=bar' + + Combining of both methods is not allowed: + + >>> href({'foo': 'bar'}, bar=42) + Traceback (most recent call last): + ... + TypeError: keyword arguments and query-dicts can't be combined + + Accessing attributes on the href object creates a new href object with + the attribute name as prefix: + + >>> bar_href = href.bar + >>> bar_href("blub") + '/foo/bar/blub' + + If `sort` is set to `True` the items are sorted by `key` or the default + sorting algorithm: + + >>> href = Href("/", sort=True) + >>> href(a=1, b=2, c=3) + '/?a=1&b=2&c=3' + + .. deprecated:: 2.0 + Will be removed in Werkzeug 2.1. Use :mod:`werkzeug.routing` + instead. + + .. versionadded:: 0.5 + `sort` and `key` were added. + """ + + def __init__( # type: ignore + self, base="./", charset="utf-8", sort=False, key=None + ): + warnings.warn( + "'Href' is deprecated and will be removed in Werkzeug 2.1." + " Use 'werkzeug.routing' instead.", + DeprecationWarning, + stacklevel=2, + ) + + if not base: + base = "./" + self.base = base + self.charset = charset + self.sort = sort + self.key = key + + def __getattr__(self, name): # type: ignore + if name[:2] == "__": + raise AttributeError(name) + base = self.base + if base[-1:] != "/": + base += "/" + return Href(url_join(base, name), self.charset, self.sort, self.key) + + def __call__(self, *path, **query): # type: ignore + if path and isinstance(path[-1], dict): + if query: + raise TypeError("keyword arguments and query-dicts can't be combined") + query, path = path[-1], path[:-1] + elif query: + query = {k[:-1] if k.endswith("_") else k: v for k, v in query.items()} + path = "/".join( + [ + _to_str(url_quote(x, self.charset), "ascii") + for x in path + if x is not None + ] + ).lstrip("/") + rv = self.base + if path: + if not rv.endswith("/"): + rv += "/" + rv = url_join(rv, f"./{path}") + if query: + rv += "?" + _to_str( + url_encode(query, self.charset, sort=self.sort, key=self.key), "ascii" + ) + return rv diff --git a/venv/Lib/site-packages/werkzeug/user_agent.py b/venv/Lib/site-packages/werkzeug/user_agent.py new file mode 100644 index 0000000..66ffcbe --- /dev/null +++ b/venv/Lib/site-packages/werkzeug/user_agent.py @@ -0,0 +1,47 @@ +import typing as t + + +class UserAgent: + """Represents a parsed user agent header value. + + The default implementation does no parsing, only the :attr:`string` + attribute is set. A subclass may parse the string to set the + common attributes or expose other information. Set + :attr:`werkzeug.wrappers.Request.user_agent_class` to use a + subclass. + + :param string: The header value to parse. + + .. versionadded:: 2.0 + This replaces the previous ``useragents`` module, but does not + provide a built-in parser. + """ + + platform: t.Optional[str] = None + """The OS name, if it could be parsed from the string.""" + + browser: t.Optional[str] = None + """The browser name, if it could be parsed from the string.""" + + version: t.Optional[str] = None + """The browser version, if it could be parsed from the string.""" + + language: t.Optional[str] = None + """The browser language, if it could be parsed from the string.""" + + def __init__(self, string: str) -> None: + self.string: str = string + """The original header value.""" + + def __repr__(self) -> str: + return f"<{type(self).__name__} {self.browser}/{self.version}>" + + def __str__(self) -> str: + return self.string + + def __bool__(self) -> bool: + return bool(self.browser) + + def to_header(self) -> str: + """Convert to a header value.""" + return self.string diff --git a/venv/Lib/site-packages/werkzeug/useragents.py b/venv/Lib/site-packages/werkzeug/useragents.py new file mode 100644 index 0000000..4deed8f --- /dev/null +++ b/venv/Lib/site-packages/werkzeug/useragents.py @@ -0,0 +1,215 @@ +import re +import typing as t +import warnings + +from .user_agent import UserAgent as _BaseUserAgent + +if t.TYPE_CHECKING: + from _typeshed.wsgi import WSGIEnvironment + + +class _UserAgentParser: + platform_rules: t.ClassVar[t.Iterable[t.Tuple[str, str]]] = ( + (" cros ", "chromeos"), + ("iphone|ios", "iphone"), + ("ipad", "ipad"), + (r"darwin\b|mac\b|os\s*x", "macos"), + ("win", "windows"), + (r"android", "android"), + ("netbsd", "netbsd"), + ("openbsd", "openbsd"), + ("freebsd", "freebsd"), + ("dragonfly", "dragonflybsd"), + ("(sun|i86)os", "solaris"), + (r"x11\b|lin(\b|ux)?", "linux"), + (r"nintendo\s+wii", "wii"), + ("irix", "irix"), + ("hp-?ux", "hpux"), + ("aix", "aix"), + ("sco|unix_sv", "sco"), + ("bsd", "bsd"), + ("amiga", "amiga"), + ("blackberry|playbook", "blackberry"), + ("symbian", "symbian"), + ) + browser_rules: t.ClassVar[t.Iterable[t.Tuple[str, str]]] = ( + ("googlebot", "google"), + ("msnbot", "msn"), + ("yahoo", "yahoo"), + ("ask jeeves", "ask"), + (r"aol|america\s+online\s+browser", "aol"), + (r"opera|opr", "opera"), + ("edge|edg", "edge"), + ("chrome|crios", "chrome"), + ("seamonkey", "seamonkey"), + ("firefox|firebird|phoenix|iceweasel", "firefox"), + ("galeon", "galeon"), + ("safari|version", "safari"), + ("webkit", "webkit"), + ("camino", "camino"), + ("konqueror", "konqueror"), + ("k-meleon", "kmeleon"), + ("netscape", "netscape"), + (r"msie|microsoft\s+internet\s+explorer|trident/.+? rv:", "msie"), + ("lynx", "lynx"), + ("links", "links"), + ("Baiduspider", "baidu"), + ("bingbot", "bing"), + ("mozilla", "mozilla"), + ) + + _browser_version_re = r"(?:{pattern})[/\sa-z(]*(\d+[.\da-z]+)?" + _language_re = re.compile( + r"(?:;\s*|\s+)(\b\w{2}\b(?:-\b\w{2}\b)?)\s*;|" + r"(?:\(|\[|;)\s*(\b\w{2}\b(?:-\b\w{2}\b)?)\s*(?:\]|\)|;)" + ) + + def __init__(self) -> None: + self.platforms = [(b, re.compile(a, re.I)) for a, b in self.platform_rules] + self.browsers = [ + (b, re.compile(self._browser_version_re.format(pattern=a), re.I)) + for a, b in self.browser_rules + ] + + def __call__( + self, user_agent: str + ) -> t.Tuple[t.Optional[str], t.Optional[str], t.Optional[str], t.Optional[str]]: + platform: t.Optional[str] + browser: t.Optional[str] + version: t.Optional[str] + language: t.Optional[str] + + for platform, regex in self.platforms: # noqa: B007 + match = regex.search(user_agent) + if match is not None: + break + else: + platform = None + + # Except for Trident, all browser key words come after the last ')' + last_closing_paren = 0 + if ( + not re.compile(r"trident/.+? rv:", re.I).search(user_agent) + and ")" in user_agent + and user_agent[-1] != ")" + ): + last_closing_paren = user_agent.rindex(")") + + for browser, regex in self.browsers: # noqa: B007 + match = regex.search(user_agent[last_closing_paren:]) + if match is not None: + version = match.group(1) + break + else: + browser = version = None + match = self._language_re.search(user_agent) + if match is not None: + language = match.group(1) or match.group(2) + else: + language = None + return platform, browser, version, language + + +# It wasn't public, but users might have imported it anyway, show a +# warning if a user created an instance. +class UserAgentParser(_UserAgentParser): + """A simple user agent parser. Used by the `UserAgent`. + + .. deprecated:: 2.0 + Will be removed in Werkzeug 2.1. Use a dedicated parser library + instead. + """ + + def __init__(self) -> None: + warnings.warn( + "'UserAgentParser' is deprecated and will be removed in" + " Werkzeug 2.1. Use a dedicated parser library instead.", + DeprecationWarning, + stacklevel=2, + ) + super().__init__() + + +class _deprecated_property(property): + def __init__(self, fget: t.Callable[["_UserAgent"], t.Any]) -> None: + super().__init__(fget) + self.message = ( + "The built-in user agent parser is deprecated and will be" + f" removed in Werkzeug 2.1. The {fget.__name__!r} property" + " will be 'None'. Subclass 'werkzeug.user_agent.UserAgent'" + " and set 'Request.user_agent_class' to use a different" + " parser." + ) + + def __get__(self, *args: t.Any, **kwargs: t.Any) -> t.Any: + warnings.warn(self.message, DeprecationWarning, stacklevel=3) + return super().__get__(*args, **kwargs) + + +# This is what Request.user_agent returns for now, only show warnings on +# attribute access, not creation. +class _UserAgent(_BaseUserAgent): + _parser = _UserAgentParser() + + def __init__(self, string: str) -> None: + super().__init__(string) + info = self._parser(string) + self._platform, self._browser, self._version, self._language = info + + @_deprecated_property + def platform(self) -> t.Optional[str]: # type: ignore + return self._platform + + @_deprecated_property + def browser(self) -> t.Optional[str]: # type: ignore + return self._browser + + @_deprecated_property + def version(self) -> t.Optional[str]: # type: ignore + return self._version + + @_deprecated_property + def language(self) -> t.Optional[str]: # type: ignore + return self._language + + +# This is what users might be importing, show warnings on create. +class UserAgent(_UserAgent): + """Represents a parsed user agent header value. + + This uses a basic parser to try to extract some information from the + header. + + :param environ_or_string: The header value to parse, or a WSGI + environ containing the header. + + .. deprecated:: 2.0 + Will be removed in Werkzeug 2.1. Subclass + :class:`werkzeug.user_agent.UserAgent` (note the new module + name) to use a dedicated parser instead. + + .. versionchanged:: 2.0 + Passing a WSGI environ is deprecated and will be removed in 2.1. + """ + + def __init__(self, environ_or_string: "t.Union[str, WSGIEnvironment]") -> None: + if isinstance(environ_or_string, dict): + warnings.warn( + "Passing an environ to 'UserAgent' is deprecated and" + " will be removed in Werkzeug 2.1. Pass the header" + " value string instead.", + DeprecationWarning, + stacklevel=2, + ) + string = environ_or_string.get("HTTP_USER_AGENT", "") + else: + string = environ_or_string + + warnings.warn( + "The 'werkzeug.useragents' module is deprecated and will be" + " removed in Werkzeug 2.1. The new base API is" + " 'werkzeug.user_agent.UserAgent'.", + DeprecationWarning, + stacklevel=2, + ) + super().__init__(string) diff --git a/venv/Lib/site-packages/werkzeug/utils.py b/venv/Lib/site-packages/werkzeug/utils.py new file mode 100644 index 0000000..7bb02bb --- /dev/null +++ b/venv/Lib/site-packages/werkzeug/utils.py @@ -0,0 +1,1091 @@ +import codecs +import io +import mimetypes +import os +import pkgutil +import re +import sys +import typing as t +import unicodedata +import warnings +from datetime import datetime +from html.entities import name2codepoint +from time import time +from zlib import adler32 + +from ._internal import _DictAccessorProperty +from ._internal import _missing +from ._internal import _parse_signature +from ._internal import _TAccessorValue +from .datastructures import Headers +from .exceptions import NotFound +from .exceptions import RequestedRangeNotSatisfiable +from .security import safe_join +from .urls import url_quote +from .wsgi import wrap_file + +if t.TYPE_CHECKING: + from _typeshed.wsgi import WSGIEnvironment + from .wrappers.request import Request + from .wrappers.response import Response + +_T = t.TypeVar("_T") + +_entity_re = re.compile(r"&([^;]+);") +_filename_ascii_strip_re = re.compile(r"[^A-Za-z0-9_.-]") +_windows_device_files = ( + "CON", + "AUX", + "COM1", + "COM2", + "COM3", + "COM4", + "LPT1", + "LPT2", + "LPT3", + "PRN", + "NUL", +) + + +class cached_property(property, t.Generic[_T]): + """A :func:`property` that is only evaluated once. Subsequent access + returns the cached value. Setting the property sets the cached + value. Deleting the property clears the cached value, accessing it + again will evaluate it again. + + .. code-block:: python + + class Example: + @cached_property + def value(self): + # calculate something important here + return 42 + + e = Example() + e.value # evaluates + e.value # uses cache + e.value = 16 # sets cache + del e.value # clears cache + + The class must have a ``__dict__`` for this to work. + + .. versionchanged:: 2.0 + ``del obj.name`` clears the cached value. + """ + + def __init__( + self, + fget: t.Callable[[t.Any], _T], + name: t.Optional[str] = None, + doc: t.Optional[str] = None, + ) -> None: + super().__init__(fget, doc=doc) + self.__name__ = name or fget.__name__ + self.__module__ = fget.__module__ + + def __set__(self, obj: object, value: _T) -> None: + obj.__dict__[self.__name__] = value + + def __get__(self, obj: object, type: type = None) -> _T: # type: ignore + if obj is None: + return self # type: ignore + + value: _T = obj.__dict__.get(self.__name__, _missing) + + if value is _missing: + value = self.fget(obj) # type: ignore + obj.__dict__[self.__name__] = value + + return value + + def __delete__(self, obj: object) -> None: + del obj.__dict__[self.__name__] + + +def invalidate_cached_property(obj: object, name: str) -> None: + """Invalidates the cache for a :class:`cached_property`: + + >>> class Test(object): + ... @cached_property + ... def magic_number(self): + ... print("recalculating...") + ... return 42 + ... + >>> var = Test() + >>> var.magic_number + recalculating... + 42 + >>> var.magic_number + 42 + >>> invalidate_cached_property(var, "magic_number") + >>> var.magic_number + recalculating... + 42 + + You must pass the name of the cached property as the second argument. + + .. deprecated:: 2.0 + Will be removed in Werkzeug 2.1. Use ``del obj.name`` instead. + """ + warnings.warn( + "'invalidate_cached_property' is deprecated and will be removed" + " in Werkzeug 2.1. Use 'del obj.name' instead.", + DeprecationWarning, + stacklevel=2, + ) + delattr(obj, name) + + +class environ_property(_DictAccessorProperty[_TAccessorValue]): + """Maps request attributes to environment variables. This works not only + for the Werkzeug request object, but also any other class with an + environ attribute: + + >>> class Test(object): + ... environ = {'key': 'value'} + ... test = environ_property('key') + >>> var = Test() + >>> var.test + 'value' + + If you pass it a second value it's used as default if the key does not + exist, the third one can be a converter that takes a value and converts + it. If it raises :exc:`ValueError` or :exc:`TypeError` the default value + is used. If no default value is provided `None` is used. + + Per default the property is read only. You have to explicitly enable it + by passing ``read_only=False`` to the constructor. + """ + + read_only = True + + def lookup(self, obj: "Request") -> "WSGIEnvironment": + return obj.environ + + +class header_property(_DictAccessorProperty[_TAccessorValue]): + """Like `environ_property` but for headers.""" + + def lookup(self, obj: t.Union["Request", "Response"]) -> Headers: + return obj.headers + + +class HTMLBuilder: + """Helper object for HTML generation. + + Per default there are two instances of that class. The `html` one, and + the `xhtml` one for those two dialects. The class uses keyword parameters + and positional parameters to generate small snippets of HTML. + + Keyword parameters are converted to XML/SGML attributes, positional + arguments are used as children. Because Python accepts positional + arguments before keyword arguments it's a good idea to use a list with the + star-syntax for some children: + + >>> html.p(class_='foo', *[html.a('foo', href='foo.html'), ' ', + ... html.a('bar', href='bar.html')]) + '

    foo bar

    ' + + This class works around some browser limitations and can not be used for + arbitrary SGML/XML generation. For that purpose lxml and similar + libraries exist. + + Calling the builder escapes the string passed: + + >>> html.p(html("")) + '

    <foo>

    ' + + .. deprecated:: 2.0 + Will be removed in Werkzeug 2.1. + """ + + _entity_re = re.compile(r"&([^;]+);") + _entities = name2codepoint.copy() + _entities["apos"] = 39 + _empty_elements = { + "area", + "base", + "basefont", + "br", + "col", + "command", + "embed", + "frame", + "hr", + "img", + "input", + "keygen", + "isindex", + "link", + "meta", + "param", + "source", + "wbr", + } + _boolean_attributes = { + "selected", + "checked", + "compact", + "declare", + "defer", + "disabled", + "ismap", + "multiple", + "nohref", + "noresize", + "noshade", + "nowrap", + } + _plaintext_elements = {"textarea"} + _c_like_cdata = {"script", "style"} + + def __init__(self, dialect): # type: ignore + self._dialect = dialect + + def __call__(self, s): # type: ignore + import html + + warnings.warn( + "'utils.HTMLBuilder' is deprecated and will be removed in Werkzeug 2.1.", + DeprecationWarning, + stacklevel=2, + ) + return html.escape(s) + + def __getattr__(self, tag): # type: ignore + import html + + warnings.warn( + "'utils.HTMLBuilder' is deprecated and will be removed in Werkzeug 2.1.", + DeprecationWarning, + stacklevel=2, + ) + if tag[:2] == "__": + raise AttributeError(tag) + + def proxy(*children, **arguments): # type: ignore + buffer = f"<{tag}" + for key, value in arguments.items(): + if value is None: + continue + if key[-1] == "_": + key = key[:-1] + if key in self._boolean_attributes: + if not value: + continue + if self._dialect == "xhtml": + value = f'="{key}"' + else: + value = "" + else: + value = f'="{html.escape(value)}"' + buffer += f" {key}{value}" + if not children and tag in self._empty_elements: + if self._dialect == "xhtml": + buffer += " />" + else: + buffer += ">" + return buffer + buffer += ">" + + children_as_string = "".join([str(x) for x in children if x is not None]) + + if children_as_string: + if tag in self._plaintext_elements: + children_as_string = html.escape(children_as_string) + elif tag in self._c_like_cdata and self._dialect == "xhtml": + children_as_string = f"/**/" + buffer += children_as_string + f"" + return buffer + + return proxy + + def __repr__(self) -> str: + return f"<{type(self).__name__} for {self._dialect!r}>" + + +html = HTMLBuilder("html") +xhtml = HTMLBuilder("xhtml") + +# https://cgit.freedesktop.org/xdg/shared-mime-info/tree/freedesktop.org.xml.in +# https://www.iana.org/assignments/media-types/media-types.xhtml +# Types listed in the XDG mime info that have a charset in the IANA registration. +_charset_mimetypes = { + "application/ecmascript", + "application/javascript", + "application/sql", + "application/xml", + "application/xml-dtd", + "application/xml-external-parsed-entity", +} + + +def get_content_type(mimetype: str, charset: str) -> str: + """Returns the full content type string with charset for a mimetype. + + If the mimetype represents text, the charset parameter will be + appended, otherwise the mimetype is returned unchanged. + + :param mimetype: The mimetype to be used as content type. + :param charset: The charset to be appended for text mimetypes. + :return: The content type. + + .. versionchanged:: 0.15 + Any type that ends with ``+xml`` gets a charset, not just those + that start with ``application/``. Known text types such as + ``application/javascript`` are also given charsets. + """ + if ( + mimetype.startswith("text/") + or mimetype in _charset_mimetypes + or mimetype.endswith("+xml") + ): + mimetype += f"; charset={charset}" + + return mimetype + + +def detect_utf_encoding(data: bytes) -> str: + """Detect which UTF encoding was used to encode the given bytes. + + The latest JSON standard (:rfc:`8259`) suggests that only UTF-8 is + accepted. Older documents allowed 8, 16, or 32. 16 and 32 can be big + or little endian. Some editors or libraries may prepend a BOM. + + :internal: + + :param data: Bytes in unknown UTF encoding. + :return: UTF encoding name + + .. deprecated:: 2.0 + Will be removed in Werkzeug 2.1. This is built in to + :func:`json.loads`. + + .. versionadded:: 0.15 + """ + warnings.warn( + "'detect_utf_encoding' is deprecated and will be removed in" + " Werkzeug 2.1. This is built in to 'json.loads'.", + DeprecationWarning, + stacklevel=2, + ) + head = data[:4] + + if head[:3] == codecs.BOM_UTF8: + return "utf-8-sig" + + if b"\x00" not in head: + return "utf-8" + + if head in (codecs.BOM_UTF32_BE, codecs.BOM_UTF32_LE): + return "utf-32" + + if head[:2] in (codecs.BOM_UTF16_BE, codecs.BOM_UTF16_LE): + return "utf-16" + + if len(head) == 4: + if head[:3] == b"\x00\x00\x00": + return "utf-32-be" + + if head[::2] == b"\x00\x00": + return "utf-16-be" + + if head[1:] == b"\x00\x00\x00": + return "utf-32-le" + + if head[1::2] == b"\x00\x00": + return "utf-16-le" + + if len(head) == 2: + return "utf-16-be" if head.startswith(b"\x00") else "utf-16-le" + + return "utf-8" + + +def format_string(string: str, context: t.Mapping[str, t.Any]) -> str: + """String-template format a string: + + >>> format_string('$foo and ${foo}s', dict(foo=42)) + '42 and 42s' + + This does not do any attribute lookup. + + :param string: the format string. + :param context: a dict with the variables to insert. + + .. deprecated:: 2.0 + Will be removed in Werkzeug 2.1. Use :class:`string.Template` + instead. + """ + from string import Template + + warnings.warn( + "'utils.format_string' is deprecated and will be removed in" + " Werkzeug 2.1. Use 'string.Template' instead.", + DeprecationWarning, + stacklevel=2, + ) + return Template(string).substitute(context) + + +def secure_filename(filename: str) -> str: + r"""Pass it a filename and it will return a secure version of it. This + filename can then safely be stored on a regular file system and passed + to :func:`os.path.join`. The filename returned is an ASCII only string + for maximum portability. + + On windows systems the function also makes sure that the file is not + named after one of the special device files. + + >>> secure_filename("My cool movie.mov") + 'My_cool_movie.mov' + >>> secure_filename("../../../etc/passwd") + 'etc_passwd' + >>> secure_filename('i contain cool \xfcml\xe4uts.txt') + 'i_contain_cool_umlauts.txt' + + The function might return an empty filename. It's your responsibility + to ensure that the filename is unique and that you abort or + generate a random filename if the function returned an empty one. + + .. versionadded:: 0.5 + + :param filename: the filename to secure + """ + filename = unicodedata.normalize("NFKD", filename) + filename = filename.encode("ascii", "ignore").decode("ascii") + + for sep in os.path.sep, os.path.altsep: + if sep: + filename = filename.replace(sep, " ") + filename = str(_filename_ascii_strip_re.sub("", "_".join(filename.split()))).strip( + "._" + ) + + # on nt a couple of special files are present in each folder. We + # have to ensure that the target file is not such a filename. In + # this case we prepend an underline + if ( + os.name == "nt" + and filename + and filename.split(".")[0].upper() in _windows_device_files + ): + filename = f"_{filename}" + + return filename + + +def escape(s: t.Any) -> str: + """Replace ``&``, ``<``, ``>``, ``"``, and ``'`` with HTML-safe + sequences. + + ``None`` is escaped to an empty string. + + .. deprecated:: 2.0 + Will be removed in Werkzeug 2.1. Use MarkupSafe instead. + """ + import html + + warnings.warn( + "'utils.escape' is deprecated and will be removed in Werkzeug" + " 2.1. Use MarkupSafe instead.", + DeprecationWarning, + stacklevel=2, + ) + + if s is None: + return "" + + if hasattr(s, "__html__"): + return s.__html__() # type: ignore + + if not isinstance(s, str): + s = str(s) + + return html.escape(s, quote=True) # type: ignore + + +def unescape(s: str) -> str: + """The reverse of :func:`escape`. This unescapes all the HTML + entities, not only those inserted by ``escape``. + + .. deprecated:: 2.0 + Will be removed in Werkzeug 2.1. Use MarkupSafe instead. + """ + import html + + warnings.warn( + "'utils.unescape' is deprecated and will be removed in Werkzueg" + " 2.1. Use MarkupSafe instead.", + DeprecationWarning, + stacklevel=2, + ) + return html.unescape(s) + + +def redirect( + location: str, code: int = 302, Response: t.Optional[t.Type["Response"]] = None +) -> "Response": + """Returns a response object (a WSGI application) that, if called, + redirects the client to the target location. Supported codes are + 301, 302, 303, 305, 307, and 308. 300 is not supported because + it's not a real redirect and 304 because it's the answer for a + request with a request with defined If-Modified-Since headers. + + .. versionadded:: 0.6 + The location can now be a unicode string that is encoded using + the :func:`iri_to_uri` function. + + .. versionadded:: 0.10 + The class used for the Response object can now be passed in. + + :param location: the location the response should redirect to. + :param code: the redirect status code. defaults to 302. + :param class Response: a Response class to use when instantiating a + response. The default is :class:`werkzeug.wrappers.Response` if + unspecified. + """ + import html + + if Response is None: + from .wrappers import Response # type: ignore + + display_location = html.escape(location) + if isinstance(location, str): + # Safe conversion is necessary here as we might redirect + # to a broken URI scheme (for instance itms-services). + from .urls import iri_to_uri + + location = iri_to_uri(location, safe_conversion=True) + response = Response( # type: ignore + '\n' + "Redirecting...\n" + "

    Redirecting...

    \n" + "

    You should be redirected automatically to target URL: " + f'{display_location}. If' + " not click the link.", + code, + mimetype="text/html", + ) + response.headers["Location"] = location + return response + + +def append_slash_redirect(environ: "WSGIEnvironment", code: int = 301) -> "Response": + """Redirects to the same URL but with a slash appended. The behavior + of this function is undefined if the path ends with a slash already. + + :param environ: the WSGI environment for the request that triggers + the redirect. + :param code: the status code for the redirect. + """ + new_path = environ["PATH_INFO"].strip("/") + "/" + query_string = environ.get("QUERY_STRING") + if query_string: + new_path += f"?{query_string}" + return redirect(new_path, code) + + +def send_file( + path_or_file: t.Union[os.PathLike, str, t.BinaryIO], + environ: "WSGIEnvironment", + mimetype: t.Optional[str] = None, + as_attachment: bool = False, + download_name: t.Optional[str] = None, + conditional: bool = True, + etag: t.Union[bool, str] = True, + last_modified: t.Optional[t.Union[datetime, int, float]] = None, + max_age: t.Optional[ + t.Union[int, t.Callable[[t.Optional[str]], t.Optional[int]]] + ] = None, + use_x_sendfile: bool = False, + response_class: t.Optional[t.Type["Response"]] = None, + _root_path: t.Optional[t.Union[os.PathLike, str]] = None, +) -> "Response": + """Send the contents of a file to the client. + + The first argument can be a file path or a file-like object. Paths + are preferred in most cases because Werkzeug can manage the file and + get extra information from the path. Passing a file-like object + requires that the file is opened in binary mode, and is mostly + useful when building a file in memory with :class:`io.BytesIO`. + + Never pass file paths provided by a user. The path is assumed to be + trusted, so a user could craft a path to access a file you didn't + intend. + + If the WSGI server sets a ``file_wrapper`` in ``environ``, it is + used, otherwise Werkzeug's built-in wrapper is used. Alternatively, + if the HTTP server supports ``X-Sendfile``, ``use_x_sendfile=True`` + will tell the server to send the given path, which is much more + efficient than reading it in Python. + + :param path_or_file: The path to the file to send, relative to the + current working directory if a relative path is given. + Alternatively, a file-like object opened in binary mode. Make + sure the file pointer is seeked to the start of the data. + :param environ: The WSGI environ for the current request. + :param mimetype: The MIME type to send for the file. If not + provided, it will try to detect it from the file name. + :param as_attachment: Indicate to a browser that it should offer to + save the file instead of displaying it. + :param download_name: The default name browsers will use when saving + the file. Defaults to the passed file name. + :param conditional: Enable conditional and range responses based on + request headers. Requires passing a file path and ``environ``. + :param etag: Calculate an ETag for the file, which requires passing + a file path. Can also be a string to use instead. + :param last_modified: The last modified time to send for the file, + in seconds. If not provided, it will try to detect it from the + file path. + :param max_age: How long the client should cache the file, in + seconds. If set, ``Cache-Control`` will be ``public``, otherwise + it will be ``no-cache`` to prefer conditional caching. + :param use_x_sendfile: Set the ``X-Sendfile`` header to let the + server to efficiently send the file. Requires support from the + HTTP server. Requires passing a file path. + :param response_class: Build the response using this class. Defaults + to :class:`~werkzeug.wrappers.Response`. + :param _root_path: Do not use. For internal use only. Use + :func:`send_from_directory` to safely send files under a path. + + .. versionadded:: 2.0 + Adapted from Flask's implementation. + + .. versionchanged:: 2.0 + ``download_name`` replaces Flask's ``attachment_filename`` + parameter. If ``as_attachment=False``, it is passed with + ``Content-Disposition: inline`` instead. + + .. versionchanged:: 2.0 + ``max_age`` replaces Flask's ``cache_timeout`` parameter. + ``conditional`` is enabled and ``max_age`` is not set by + default. + + .. versionchanged:: 2.0 + ``etag`` replaces Flask's ``add_etags`` parameter. It can be a + string to use instead of generating one. + + .. versionchanged:: 2.0 + If an encoding is returned when guessing ``mimetype`` from + ``download_name``, set the ``Content-Encoding`` header. + """ + if response_class is None: + from .wrappers import Response + + response_class = Response + + path: t.Optional[str] = None + file: t.Optional[t.BinaryIO] = None + size: t.Optional[int] = None + mtime: t.Optional[float] = None + headers = Headers() + + if isinstance(path_or_file, (os.PathLike, str)) or hasattr( + path_or_file, "__fspath__" + ): + path_or_file = t.cast(t.Union[os.PathLike, str], path_or_file) + + # Flask will pass app.root_path, allowing its send_file wrapper + # to not have to deal with paths. + if _root_path is not None: + path = os.path.join(_root_path, path_or_file) + else: + path = os.path.abspath(path_or_file) + + stat = os.stat(path) + size = stat.st_size + mtime = stat.st_mtime + else: + file = path_or_file + + if download_name is None and path is not None: + download_name = os.path.basename(path) + + if mimetype is None: + if download_name is None: + raise TypeError( + "Unable to detect the MIME type because a file name is" + " not available. Either set 'download_name', pass a" + " path instead of a file, or set 'mimetype'." + ) + + mimetype, encoding = mimetypes.guess_type(download_name) + + if mimetype is None: + mimetype = "application/octet-stream" + + if encoding is not None: + headers.set("Content-Encoding", encoding) + + if download_name is not None: + try: + download_name.encode("ascii") + except UnicodeEncodeError: + simple = unicodedata.normalize("NFKD", download_name) + simple = simple.encode("ascii", "ignore").decode("ascii") + quoted = url_quote(download_name, safe="") + names = {"filename": simple, "filename*": f"UTF-8''{quoted}"} + else: + names = {"filename": download_name} + + value = "attachment" if as_attachment else "inline" + headers.set("Content-Disposition", value, **names) + elif as_attachment: + raise TypeError( + "No name provided for attachment. Either set" + " 'download_name' or pass a path instead of a file." + ) + + if use_x_sendfile and path is not None: + headers["X-Sendfile"] = path + data = None + else: + if file is None: + file = open(path, "rb") # type: ignore + elif isinstance(file, io.BytesIO): + size = file.getbuffer().nbytes + elif isinstance(file, io.TextIOBase): + raise ValueError("Files must be opened in binary mode or use BytesIO.") + + data = wrap_file(environ, file) + + rv = response_class( + data, mimetype=mimetype, headers=headers, direct_passthrough=True + ) + + if size is not None: + rv.content_length = size + + if last_modified is not None: + rv.last_modified = last_modified # type: ignore + elif mtime is not None: + rv.last_modified = mtime # type: ignore + + rv.cache_control.no_cache = True + + # Flask will pass app.get_send_file_max_age, allowing its send_file + # wrapper to not have to deal with paths. + if callable(max_age): + max_age = max_age(path) + + if max_age is not None: + if max_age > 0: + rv.cache_control.no_cache = None + rv.cache_control.public = True + + rv.cache_control.max_age = max_age + rv.expires = int(time() + max_age) # type: ignore + + if isinstance(etag, str): + rv.set_etag(etag) + elif etag and path is not None: + check = adler32(path.encode("utf-8")) & 0xFFFFFFFF + rv.set_etag(f"{mtime}-{size}-{check}") + + if conditional: + try: + rv = rv.make_conditional(environ, accept_ranges=True, complete_length=size) + except RequestedRangeNotSatisfiable: + if file is not None: + file.close() + + raise + + # Some x-sendfile implementations incorrectly ignore the 304 + # status code and send the file anyway. + if rv.status_code == 304: + rv.headers.pop("x-sendfile", None) + + return rv + + +def send_from_directory( + directory: t.Union[os.PathLike, str], + path: t.Union[os.PathLike, str], + environ: "WSGIEnvironment", + **kwargs: t.Any, +) -> "Response": + """Send a file from within a directory using :func:`send_file`. + + This is a secure way to serve files from a folder, such as static + files or uploads. Uses :func:`~werkzeug.security.safe_join` to + ensure the path coming from the client is not maliciously crafted to + point outside the specified directory. + + If the final path does not point to an existing regular file, + returns a 404 :exc:`~werkzeug.exceptions.NotFound` error. + + :param directory: The directory that ``path`` must be located under. + :param path: The path to the file to send, relative to + ``directory``. + :param environ: The WSGI environ for the current request. + :param kwargs: Arguments to pass to :func:`send_file`. + + .. versionadded:: 2.0 + Adapted from Flask's implementation. + """ + path = safe_join(os.fspath(directory), os.fspath(path)) + + if path is None: + raise NotFound() + + # Flask will pass app.root_path, allowing its send_from_directory + # wrapper to not have to deal with paths. + if "_root_path" in kwargs: + path = os.path.join(kwargs["_root_path"], path) + + try: + if not os.path.isfile(path): + raise NotFound() + except ValueError: + # path contains null byte on Python < 3.8 + raise NotFound() + + return send_file(path, environ, **kwargs) + + +def import_string(import_name: str, silent: bool = False) -> t.Any: + """Imports an object based on a string. This is useful if you want to + use import paths as endpoints or something similar. An import path can + be specified either in dotted notation (``xml.sax.saxutils.escape``) + or with a colon as object delimiter (``xml.sax.saxutils:escape``). + + If `silent` is True the return value will be `None` if the import fails. + + :param import_name: the dotted name for the object to import. + :param silent: if set to `True` import errors are ignored and + `None` is returned instead. + :return: imported object + """ + import_name = import_name.replace(":", ".") + try: + try: + __import__(import_name) + except ImportError: + if "." not in import_name: + raise + else: + return sys.modules[import_name] + + module_name, obj_name = import_name.rsplit(".", 1) + module = __import__(module_name, globals(), locals(), [obj_name]) + try: + return getattr(module, obj_name) + except AttributeError as e: + raise ImportError(e) + + except ImportError as e: + if not silent: + raise ImportStringError(import_name, e).with_traceback(sys.exc_info()[2]) + + return None + + +def find_modules( + import_path: str, include_packages: bool = False, recursive: bool = False +) -> t.Iterator[str]: + """Finds all the modules below a package. This can be useful to + automatically import all views / controllers so that their metaclasses / + function decorators have a chance to register themselves on the + application. + + Packages are not returned unless `include_packages` is `True`. This can + also recursively list modules but in that case it will import all the + packages to get the correct load path of that module. + + :param import_path: the dotted name for the package to find child modules. + :param include_packages: set to `True` if packages should be returned, too. + :param recursive: set to `True` if recursion should happen. + :return: generator + """ + module = import_string(import_path) + path = getattr(module, "__path__", None) + if path is None: + raise ValueError(f"{import_path!r} is not a package") + basename = f"{module.__name__}." + for _importer, modname, ispkg in pkgutil.iter_modules(path): + modname = basename + modname + if ispkg: + if include_packages: + yield modname + if recursive: + yield from find_modules(modname, include_packages, True) + else: + yield modname + + +def validate_arguments(func, args, kwargs, drop_extra=True): # type: ignore + """Checks if the function accepts the arguments and keyword arguments. + Returns a new ``(args, kwargs)`` tuple that can safely be passed to + the function without causing a `TypeError` because the function signature + is incompatible. If `drop_extra` is set to `True` (which is the default) + any extra positional or keyword arguments are dropped automatically. + + The exception raised provides three attributes: + + `missing` + A set of argument names that the function expected but where + missing. + + `extra` + A dict of keyword arguments that the function can not handle but + where provided. + + `extra_positional` + A list of values that where given by positional argument but the + function cannot accept. + + This can be useful for decorators that forward user submitted data to + a view function:: + + from werkzeug.utils import ArgumentValidationError, validate_arguments + + def sanitize(f): + def proxy(request): + data = request.values.to_dict() + try: + args, kwargs = validate_arguments(f, (request,), data) + except ArgumentValidationError: + raise BadRequest('The browser failed to transmit all ' + 'the data expected.') + return f(*args, **kwargs) + return proxy + + :param func: the function the validation is performed against. + :param args: a tuple of positional arguments. + :param kwargs: a dict of keyword arguments. + :param drop_extra: set to `False` if you don't want extra arguments + to be silently dropped. + :return: tuple in the form ``(args, kwargs)``. + + .. deprecated:: 2.0 + Will be removed in Werkzeug 2.1. Use :func:`inspect.signature` + instead. + """ + warnings.warn( + "'utils.validate_arguments' is deprecated and will be removed" + " in Werkzeug 2.1. Use 'inspect.signature' instead.", + DeprecationWarning, + stacklevel=2, + ) + parser = _parse_signature(func) + args, kwargs, missing, extra, extra_positional = parser(args, kwargs)[:5] + if missing: + raise ArgumentValidationError(tuple(missing)) + elif (extra or extra_positional) and not drop_extra: + raise ArgumentValidationError(None, extra, extra_positional) + return tuple(args), kwargs + + +def bind_arguments(func, args, kwargs): # type: ignore + """Bind the arguments provided into a dict. When passed a function, + a tuple of arguments and a dict of keyword arguments `bind_arguments` + returns a dict of names as the function would see it. This can be useful + to implement a cache decorator that uses the function arguments to build + the cache key based on the values of the arguments. + + :param func: the function the arguments should be bound for. + :param args: tuple of positional arguments. + :param kwargs: a dict of keyword arguments. + :return: a :class:`dict` of bound keyword arguments. + + .. deprecated:: 2.0 + Will be removed in Werkzeug 2.1. Use :meth:`Signature.bind` + instead. + """ + warnings.warn( + "'utils.bind_arguments' is deprecated and will be removed in" + " Werkzeug 2.1. Use 'Signature.bind' instead.", + DeprecationWarning, + stacklevel=2, + ) + ( + args, + kwargs, + missing, + extra, + extra_positional, + arg_spec, + vararg_var, + kwarg_var, + ) = _parse_signature(func)(args, kwargs) + values = {} + for (name, _has_default, _default), value in zip(arg_spec, args): + values[name] = value + if vararg_var is not None: + values[vararg_var] = tuple(extra_positional) + elif extra_positional: + raise TypeError("too many positional arguments") + if kwarg_var is not None: + multikw = set(extra) & {x[0] for x in arg_spec} + if multikw: + raise TypeError( + f"got multiple values for keyword argument {next(iter(multikw))!r}" + ) + values[kwarg_var] = extra + elif extra: + raise TypeError(f"got unexpected keyword argument {next(iter(extra))!r}") + return values + + +class ArgumentValidationError(ValueError): + """Raised if :func:`validate_arguments` fails to validate + + .. deprecated:: 2.0 + Will be removed in Werkzeug 2.1 along with ``utils.bind`` and + ``validate_arguments``. + """ + + def __init__(self, missing=None, extra=None, extra_positional=None): # type: ignore + self.missing = set(missing or ()) + self.extra = extra or {} + self.extra_positional = extra_positional or [] + super().__init__( + "function arguments invalid." + f" ({len(self.missing)} missing," + f" {len(self.extra) + len(self.extra_positional)} additional)" + ) + + +class ImportStringError(ImportError): + """Provides information about a failed :func:`import_string` attempt.""" + + #: String in dotted notation that failed to be imported. + import_name: str + #: Wrapped exception. + exception: BaseException + + def __init__(self, import_name: str, exception: BaseException) -> None: + self.import_name = import_name + self.exception = exception + msg = import_name + name = "" + tracked = [] + for part in import_name.replace(":", ".").split("."): + name = f"{name}.{part}" if name else part + imported = import_string(name, silent=True) + if imported: + tracked.append((name, getattr(imported, "__file__", None))) + else: + track = [f"- {n!r} found in {i!r}." for n, i in tracked] + track.append(f"- {name!r} not found.") + track_str = "\n".join(track) + msg = ( + f"import_string() failed for {import_name!r}. Possible reasons" + f" are:\n\n" + "- missing __init__.py in a package;\n" + "- package or module path not included in sys.path;\n" + "- duplicated package or module name taking precedence in" + " sys.path;\n" + "- missing module, class, function or variable;\n\n" + f"Debugged import:\n\n{track_str}\n\n" + f"Original exception:\n\n{type(exception).__name__}: {exception}" + ) + break + + super().__init__(msg) + + def __repr__(self) -> str: + return f"<{type(self).__name__}({self.import_name!r}, {self.exception!r})>" diff --git a/venv/Lib/site-packages/werkzeug/wrappers/__init__.py b/venv/Lib/site-packages/werkzeug/wrappers/__init__.py new file mode 100644 index 0000000..eb69a99 --- /dev/null +++ b/venv/Lib/site-packages/werkzeug/wrappers/__init__.py @@ -0,0 +1,16 @@ +from .accept import AcceptMixin +from .auth import AuthorizationMixin +from .auth import WWWAuthenticateMixin +from .base_request import BaseRequest +from .base_response import BaseResponse +from .common_descriptors import CommonRequestDescriptorsMixin +from .common_descriptors import CommonResponseDescriptorsMixin +from .etag import ETagRequestMixin +from .etag import ETagResponseMixin +from .request import PlainRequest +from .request import Request as Request +from .request import StreamOnlyMixin +from .response import Response as Response +from .response import ResponseStream +from .response import ResponseStreamMixin +from .user_agent import UserAgentMixin diff --git a/venv/Lib/site-packages/werkzeug/wrappers/__pycache__/__init__.cpython-39.pyc b/venv/Lib/site-packages/werkzeug/wrappers/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..827404434265e3691061a8dd3a362b22fb43cf5e GIT binary patch literal 885 zcmaKq-EPw`6vvY`ZPR?U&AL%8*nKZx_Xr`%KmrNaw1FyFk%E_6#mr8E?QU%M-0~j0 z2Tze(t`Kj)6~|7^Ktedm(eM0y{{A>Jj^n_A;eYz^_H)N^zUk!80h;p&e)$!`%2~P0 z;VyDr%^lX^UDP$)1@@3<*ke83M}5ORHsC(;4folQ2PiN+01i=T*k=)sQEYe!oS?*T zz(#zG#)d=S37Qy=fK!wjj@b^MqN(8oco*#&9s%#6J;P&mi_g#ucyj8@)9)@^W$rfN zq9{nMUeb@Wgfv}j)VoU1YpiHhT6MZyF7*T{6)mtLRu3OzNlwUz4Ur1WQJc!TDkXvZ z&SAxQRc;HO5LpOXt4c`g-?s&=|0whX*Ge=F9MypcpK*3bl_yqe!``q*?&=m*kx7+NH{fWm`-qBH1D zF532po|fXR)NU^NT1yMlrF?bMA2HC2_z)V8D7+R!o$EK+4D5QLmLeTut@~noBavur zq{CQG1RFSYj`gv{1cWb+nCVF!em+0EpB;sTj%rdqt2oInNO_UHpl6w+irlSn waSnEwT@rDAO*ZR;OM&Z}2zij_w4^G}@6}h0|A1Gk4I>Zr!;Yx15VhBvbO{Mxg1g0mC`hD&BSMG@q;i0O;7}=wWVY)`apE{z+nbQ6w-)sT zzknl`9{5APa;o?TaABN8RYsce%#QWu&GY81R)e5rU;ljiMG5&Gi)~|Ayg+yNF-Rh* zCVjd(lYX*KWFpxak?b_-Gf6*^PI`&=NUw--+tN+p9%2G3jD7-alt!0SCO_CY8B)n) zde-dM&Phu8b;PTM>w853rGKlwDAVh(kmmNth5C5DJ}vmlngY3BS>#+pzlrW1VDMx_ zCG|;U`;AfJ*(n*NCsdJ>MeafJ+(yLR_yG-OB_i zc4%lgpBG~dGtjtphWi<1aMd(s;m)fK5d=6`w?fTTCD@P&p^WlEM0FhKp^XaIDES2q z*Mul^?wk-8O0r@U8^&dOCr@49slsMjZ~E^6;oc7Woml!F;pW?%d`j z4syHs>M6SA!Zs1Sj_zVYXi5{7(BxlzXL)x^!R{5aoygMU6A&UagqTcarDNU{;<(E7 zrbB&TduELuS{vd8Xf1@jg>%;7M$B%<5S5EB?P8MARozKL-7iX%&fbf6vKV523t@#7 T5kmHCP33v~fn1aBSSrZ!kOdlfFUOt z+ks@g5JgZCd}oKGO9d0*LA@Ovl904(kT0s1*Gs>hTQyF-%FS!_Ntul!R)wkEa#d|- zIDJ~_OKUPZR}+CmAeTYhEl{5HsGvST2wxc`p6!!fxJMP)3x3dVlmc8R8E%ZnjnhRN ziE@dMg_T+ArASgElA+Q%*_4Tu{d@p-$|NmmcgC{ZN**-tH5YBWd zq)Ys^OuD7X5K*d?A0Z;{ZB6qSd1FX||)~K38LmIGv27hV`tRQR8;S5QsH zJDK)m#<+02I=%4Kvsyn{xxJ3b&f3~41g>Jn+1Aj9Z@=b81+d=U!1peq!nnogT$+={ Us`$~AMUU}6;E>&giyc{Z~%h{DAH)9W_G%4&+hEZrn|?h zwS2QtPT|NYM=tqGedT2F4}b$zv$OFABZS0-min9N>h7xQs`@J1+S*_^;vatf{wKlM zFSJ-6Auev?G@%i>3eK4dAUN~W5Z9rKa2A{7F3c|Dw!8$nT#OOFs+YKR#eG7 z(32Br4wLr>?+udg3?#dSSvhqxFU{LYr?8J2D`O#3g9~!(F?SorX8ELqV<`K9i|3iH zit5lfL4R)hPG`NSt9#2Ww{fXlQ$#TYJ{r;V%EwGUi=W@V-1}howXVmITQmG>2G!^F z6o$u89S^@KMnh9rcxQ%ovqLb$dFTJFxz1)Y(B{?%V<7c1i-Xz1-H>wio}8jCWsFwL zCIaJvd!EO=Kf>_jjjIV7T)9dqqbOum4w{Pbf03`1M>p%y`0w(iBlJ|$v-^Q2g6V4n z6avb~>Ux(E{S0kx6Hq>uaGlO#C|75@P~JfCzYXPIuI~4HE|#*&rXZ!;kaAk9rlkK( zDepH~+3j%a#J7tXI5Dd0QjnKG4o~an30@$0k>F*51i@`}Msg_2m7%r{UZ*2euA0oz1w-vB=It6= zd~XWawx|nZTMq4FhCOSkov7iWu6ML-a02(Ct)KS#8Y5a`EiK{?m!& I{`H+d0sUaKfB*mh literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/werkzeug/wrappers/__pycache__/base_response.cpython-39.pyc b/venv/Lib/site-packages/werkzeug/wrappers/__pycache__/base_response.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..06256c6271ea8dd0f13532c558a4d49a3453e501 GIT binary patch literal 1755 zcmd5+PmkL~6rZujji4&#@;Ba zz3rl&$|vXvap_l@E2kCT066fTW3wrX5E7S;G(UUZ%)B@6&+o;%yE_a=@WZd){UjLs zg%;bx$Hg6-<}QL_if1h1&DV(>M4GYjr!-PiA~Nq;)vVeYcOSz8%RjJJzQMe!D-$_fW~a(fIAo9DgL-IekLLxB|Ve~ zlz7TVNuYXYCa`<*(|7^*>RFP-#_T_UWHEAVU|NCoHB`0E69?6IgZ@37`#8-j2o;-i z#jD2JBbT$vJ>iu&ba0J=Nv%xCX zd72*@E9lSe-EGaMMSg#?Wp^&LYlY{CvXn3;6kMvj4&KD_zVXsm%1t67tWB zB}|VXKbn4#&ZZ`<;GGif#)n|0tJZ z2#gEvI1YFI@crYrF6U@`=_+N7!Vu>PXe!8OBW2x_ZWOcmKc-Cg@U5C0A9#Af?@ra#^T4qyK@F z-`8>0?(oWrZ`LJPF)NBplSS)(oUiq@zaIDWGc~pkMjjjb3HV;WkzArR?b0we5uTzo zQd@E5Ja!+l1Fl>p&IXb9kO>y~NH@<8w#habxmLs)E8kyW=^vU+Wl&e?1{FIMIla@o zjc}RFr)(}xc*;(kpZSlRV~5)iyZ-;g9(E)ihIz3<9w%9?z=YKU2oLH!sgR;^mR9Rf zrPwj5Y(4(Bg7Hj^RHM|Td0I(H?iDkQmkC}a2odaVYbAj!>nt_3@FpFhlGS97E*R=s0dLmW z?E6c=?nUhw`?FuIOKe>m?L-e7bSEa8th@QQ{48E#QGO IaIakZ6R#_{`2YX_ literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/werkzeug/wrappers/__pycache__/common_descriptors.cpython-39.pyc b/venv/Lib/site-packages/werkzeug/wrappers/__pycache__/common_descriptors.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1a2358cfc13bc4cf61dbc0740fd39db99a8348e9 GIT binary patch literal 1346 zcmb_c&1w`u5bmCz%@{ZS1oa?9=9ERjjd~OjB>_EX6mtkH3+*`FiM{FVnRNH;x*;c% zkdr=vM=yD$xq1q|fnKbdoh%CpQ6vpjUDd@@^;h*xCv$VN1XgtL^ZjR^kRRwwFE}I5 zK(_aetq1f6wdq-~~JcF9a{(bdB6?9K%;+HAOwWg4(h7zz|i& zjwk$HfWj&FzWIk_gL0n-`^`>xNCMJnK)$eCSkC(WOurM`g|Kc#*wo0}WyZdipQK(r z^2;DGo3?Ch4ad6?t}r^KQ{@p;2yzv~E`oBTM>%yKqWR)e;`}|*3wEg_yWV&Dl~O;slQq&Eug|D*hm1Bl_6@&ZLglh1uE>#b*4d{jeRkYA~%> zXF~|5s^G%L?xTn|3Y|Kc=|o94in)ZmPL0~f9$fl}mb2C+>6Q`$p^m~b0>oNs03V=L z&ac!LfduR0P#M}H7p9CDlUh2)uwPcF4TBRg*c3>6iLq2A)iura>@1$!-}dC*Gl)SaeZg2c%UD0-g+jZ@*mjYqIznCPKiA`Wzjo)2H cg>i$?sWg)-meG?j$DZO#Vv)QNjr=RkKXW5qy8r+H literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/werkzeug/wrappers/__pycache__/cors.cpython-39.pyc b/venv/Lib/site-packages/werkzeug/wrappers/__pycache__/cors.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5cb8e4cfe4bdee9251dc6fd3cfaae3ca2f2509dd GIT binary patch literal 1254 zcmb_c&1w`u5bmCz%@{YD1QiSjb8=B|qaH;>iJ}MHNOA})3+*`FiM{FVnRNH;W|!O& z$w{BUqnEtWTs;NfKrdG93}Hz;h@_!vs=Ao^`RbcV78d3Soapn<4;`P7ALz^;xFgR& z>_bqTaH>d`PM==adri2<{UgHtL$B+D7w{0g5WIlXO>(bs3U854ihA}0wPWvtA*LC- zp78qt3a8xr<{y)7%6%RjHM`+42}rjA`NDE>ZGB^NL+lm8x_5G4>duK@28r3RWn*_# zJ&kaM(J7s&h+~8x*Fo%EP>%E|r_RFxzW9_l|B&>911iaZ_nm&F6o!R@L1(hxI$yFC zOB-{M8<8eX@Ho*t9!aI*k0Lf=kPYEZ>Uc|--H9l6;>Ybr?YLDp-HLTKf^ez|E^O>R ziFmuvsgs#blyu{mOUUcgxP9tDpC`1Ow=PL{l^6_o(-~F*mt=9>n%p zb930JP6mRpa-Ok4#tVgZld-)bQFVm6(tn|=66HeOdn%M-%-n!tm0CvYCMpCEC$(rq zbn0Is50|QWY@TT=Dk2&r*=4bAQnPNR!2i;&O7ZW!GjlM9iBRFc!7QSh0z1WdiY_i1 zuP!cnM#F)8CeL2Lel&u3i@U@Ub`CI0vem6@2e1DvTS9&ZIf(s*Iji(Vz!iMTNk!(2mocwKu&zlkT3~?2=m| zIq4I4^paPatEb=_=*6m?AuNdpku+3IRTon~Uwt#l($XS<6Mg>qq2m+s1D*K;cjPgM zy$gyHP8I3W+0*NK&k6Uqe?Yju?{$6f0v>`Ff){Z5j@)V-!&{`2qMkoN?bzF3h*`$2 zC;UNx!YTK@`G;hSa-RnW&2D%|0@7_jzOY>0c%Ss&ik(7O_e$ozl6AI7SF^8N}WM<;Z|?>O36ai%*I3_sJmGqmt}--|1IMVOS^_bf&wl(;VPp46 z#9M_Hiq?^QCLSCmP?PCx6Jfh{IbxFFd#7L;4u#8|xEj7#=pjECus~dp?n^mX` zZIKI8MvO@<9b*WQ6>7^M?gnvyv=w zOZl;9r4x5@=pyM0+Z)$|t&dHTLm|5^T+(mn6NB(JKx`8fp#k-Lk9xlw3)AbTL2REi zw}6f6WGEOb7a1F7yijO28QUomRY#~R{U^FAQ7+ZJr$Q;l%oQkBsa3SDqC)U+Qj11J zXZ|_zaHX2Z=9#vlBBDW(ofqq-HS6XI{4edQ6#vdUvjB6L2o?St%rdGOurr)z=;EUB z>g=M|&H!_=X>$!T{cTw{Ah3ez>Sdu1AA7@1a-h0d!}l(t!nnccT$+=v%IIN5o=5oU MTO?vcBmYwK56iYx15VgIVu&o+CDsi_s5Cw@;a6}aX0RkaN5j|9jBAM-aTAVoEt?f-n)mw{t zf**i5a_L|4l~ct(KpYq+QIwJ9d1l9Y^Jdn0tF=kcvaf$W{h);Wj>)7pDbZTT`I&l|{u3^jqleK7uDh zDye5t>$ijwpPZ9ndPWsFW54KkN^!2JymxxiUiE5o<+y~(LXmrrJU5b0mDc`M zh7BPKojWJQ75V$>#goAYTMn_a8|+nJ-jrh)Ou$SA@6_SIDGv`TRFNNp8%&pj?M!W6 zVI#LQbfxK5GrNV`FnLQ~2ZqwHU^dAhyYUU#MHMpS6>F$fVhg&3Ez)-m1^;-t>? zvO;}Go|^@CXe~t-ptTV8Hg;LS%^2N@5Vscpv5P@Q=Xoa$O+Tw}ZT4=wlSPR2HHJA? TWC+>rLdw(lbX;6lMziEU9K6a) literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/werkzeug/wrappers/__pycache__/request.cpython-39.pyc b/venv/Lib/site-packages/werkzeug/wrappers/__pycache__/request.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fb907eadb3f2ea51eee8bb9c401db562d1c24546 GIT binary patch literal 21272 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/werkzeug/wrappers/__pycache__/response.cpython-39.pyc b/venv/Lib/site-packages/werkzeug/wrappers/__pycache__/response.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c7939fbd4615a1d95bb7a9cb241baccbbdc187a1 GIT binary patch literal 29666 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 B?au%J literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/werkzeug/wrappers/__pycache__/user_agent.cpython-39.pyc b/venv/Lib/site-packages/werkzeug/wrappers/__pycache__/user_agent.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ed2d5b7dbbf96087e7e8e6b86fb6534be9a2de9f GIT binary patch literal 804 zcmY*XO>Yx15VhBvbcxdPAtA072cjU63XT;*R3L!_1O!5rqDW@Do(3n5v$eemiF#{M zPw)#ka_O0W<143%e*hQ8NmR8X&3IBn~Tz|G+ zYVt(e`6Aqp7Z+{GlL%etM0x}TS*puHXM bWHH44C4?nbL None: + warnings.warn( + "'AcceptMixin' is deprecated and will be removed in" + " Werkzeug 2.1. 'Request' now includes the functionality" + " directly.", + DeprecationWarning, + stacklevel=2, + ) + super().__init__(*args, **kwargs) # type: ignore diff --git a/venv/Lib/site-packages/werkzeug/wrappers/auth.py b/venv/Lib/site-packages/werkzeug/wrappers/auth.py new file mode 100644 index 0000000..da31b7c --- /dev/null +++ b/venv/Lib/site-packages/werkzeug/wrappers/auth.py @@ -0,0 +1,26 @@ +import typing as t +import warnings + + +class AuthorizationMixin: + def __init__(self, *args: t.Any, **kwargs: t.Any) -> None: + warnings.warn( + "'AuthorizationMixin' is deprecated and will be removed in" + " Werkzeug 2.1. 'Request' now includes the functionality" + " directly.", + DeprecationWarning, + stacklevel=2, + ) + super().__init__(*args, **kwargs) # type: ignore + + +class WWWAuthenticateMixin: + def __init__(self, *args: t.Any, **kwargs: t.Any) -> None: + warnings.warn( + "'WWWAuthenticateMixin' is deprecated and will be removed" + " in Werkzeug 2.1. 'Response' now includes the" + " functionality directly.", + DeprecationWarning, + stacklevel=2, + ) + super().__init__(*args, **kwargs) # type: ignore diff --git a/venv/Lib/site-packages/werkzeug/wrappers/base_request.py b/venv/Lib/site-packages/werkzeug/wrappers/base_request.py new file mode 100644 index 0000000..451989f --- /dev/null +++ b/venv/Lib/site-packages/werkzeug/wrappers/base_request.py @@ -0,0 +1,36 @@ +import typing as t +import warnings + +from .request import Request + + +class _FakeSubclassCheck(type): + def __subclasscheck__(cls, subclass: t.Type) -> bool: + warnings.warn( + "'BaseRequest' is deprecated and will be removed in" + " Werkzeug 2.1. Use 'issubclass(cls, Request)' instead.", + DeprecationWarning, + stacklevel=2, + ) + return issubclass(subclass, Request) + + def __instancecheck__(cls, instance: t.Any) -> bool: + warnings.warn( + "'BaseRequest' is deprecated and will be removed in" + " Werkzeug 2.1. Use 'isinstance(obj, Request)' instead.", + DeprecationWarning, + stacklevel=2, + ) + return isinstance(instance, Request) + + +class BaseRequest(Request, metaclass=_FakeSubclassCheck): + def __init__(self, *args: t.Any, **kwargs: t.Any) -> None: + warnings.warn( + "'BaseRequest' is deprecated and will be removed in" + " Werkzeug 2.1. 'Request' now includes the functionality" + " directly.", + DeprecationWarning, + stacklevel=2, + ) + super().__init__(*args, **kwargs) diff --git a/venv/Lib/site-packages/werkzeug/wrappers/base_response.py b/venv/Lib/site-packages/werkzeug/wrappers/base_response.py new file mode 100644 index 0000000..3e0dc67 --- /dev/null +++ b/venv/Lib/site-packages/werkzeug/wrappers/base_response.py @@ -0,0 +1,36 @@ +import typing as t +import warnings + +from .response import Response + + +class _FakeSubclassCheck(type): + def __subclasscheck__(cls, subclass: t.Type) -> bool: + warnings.warn( + "'BaseResponse' is deprecated and will be removed in" + " Werkzeug 2.1. Use 'issubclass(cls, Response)' instead.", + DeprecationWarning, + stacklevel=2, + ) + return issubclass(subclass, Response) + + def __instancecheck__(cls, instance: t.Any) -> bool: + warnings.warn( + "'BaseResponse' is deprecated and will be removed in" + " Werkzeug 2.1. Use 'isinstance(obj, Response)' instead.", + DeprecationWarning, + stacklevel=2, + ) + return isinstance(instance, Response) + + +class BaseResponse(Response, metaclass=_FakeSubclassCheck): + def __init__(self, *args: t.Any, **kwargs: t.Any) -> None: + warnings.warn( + "'BaseResponse' is deprecated and will be removed in" + " Werkzeug 2.1. 'Response' now includes the functionality" + " directly.", + DeprecationWarning, + stacklevel=2, + ) + super().__init__(*args, **kwargs) diff --git a/venv/Lib/site-packages/werkzeug/wrappers/common_descriptors.py b/venv/Lib/site-packages/werkzeug/wrappers/common_descriptors.py new file mode 100644 index 0000000..db87ea5 --- /dev/null +++ b/venv/Lib/site-packages/werkzeug/wrappers/common_descriptors.py @@ -0,0 +1,26 @@ +import typing as t +import warnings + + +class CommonRequestDescriptorsMixin: + def __init__(self, *args: t.Any, **kwargs: t.Any) -> None: + warnings.warn( + "'CommonRequestDescriptorsMixin' is deprecated and will be" + " removed in Werkzeug 2.1. 'Request' now includes the" + " functionality directly.", + DeprecationWarning, + stacklevel=2, + ) + super().__init__(*args, **kwargs) # type: ignore + + +class CommonResponseDescriptorsMixin: + def __init__(self, *args: t.Any, **kwargs: t.Any) -> None: + warnings.warn( + "'CommonResponseDescriptorsMixin' is deprecated and will be" + " removed in Werkzeug 2.1. 'Response' now includes the" + " functionality directly.", + DeprecationWarning, + stacklevel=2, + ) + super().__init__(*args, **kwargs) # type: ignore diff --git a/venv/Lib/site-packages/werkzeug/wrappers/cors.py b/venv/Lib/site-packages/werkzeug/wrappers/cors.py new file mode 100644 index 0000000..89cf83e --- /dev/null +++ b/venv/Lib/site-packages/werkzeug/wrappers/cors.py @@ -0,0 +1,26 @@ +import typing as t +import warnings + + +class CORSRequestMixin: + def __init__(self, *args: t.Any, **kwargs: t.Any) -> None: + warnings.warn( + "'CORSRequestMixin' is deprecated and will be removed in" + " Werkzeug 2.1. 'Request' now includes the functionality" + " directly.", + DeprecationWarning, + stacklevel=2, + ) + super().__init__(*args, **kwargs) # type: ignore + + +class CORSResponseMixin: + def __init__(self, *args: t.Any, **kwargs: t.Any) -> None: + warnings.warn( + "'CORSResponseMixin' is deprecated and will be removed in" + " Werkzeug 2.1. 'Response' now includes the functionality" + " directly.", + DeprecationWarning, + stacklevel=2, + ) + super().__init__(*args, **kwargs) # type: ignore diff --git a/venv/Lib/site-packages/werkzeug/wrappers/etag.py b/venv/Lib/site-packages/werkzeug/wrappers/etag.py new file mode 100644 index 0000000..2e9015a --- /dev/null +++ b/venv/Lib/site-packages/werkzeug/wrappers/etag.py @@ -0,0 +1,26 @@ +import typing as t +import warnings + + +class ETagRequestMixin: + def __init__(self, *args: t.Any, **kwargs: t.Any) -> None: + warnings.warn( + "'ETagRequestMixin' is deprecated and will be removed in" + " Werkzeug 2.1. 'Request' now includes the functionality" + " directly.", + DeprecationWarning, + stacklevel=2, + ) + super().__init__(*args, **kwargs) # type: ignore + + +class ETagResponseMixin: + def __init__(self, *args: t.Any, **kwargs: t.Any) -> None: + warnings.warn( + "'ETagResponseMixin' is deprecated and will be removed in" + " Werkzeug 2.1. 'Response' now includes the functionality" + " directly.", + DeprecationWarning, + stacklevel=2, + ) + super().__init__(*args, **kwargs) # type: ignore diff --git a/venv/Lib/site-packages/werkzeug/wrappers/json.py b/venv/Lib/site-packages/werkzeug/wrappers/json.py new file mode 100644 index 0000000..ab6ed7b --- /dev/null +++ b/venv/Lib/site-packages/werkzeug/wrappers/json.py @@ -0,0 +1,13 @@ +import typing as t +import warnings + + +class JSONMixin: + def __init__(self, *args: t.Any, **kwargs: t.Any) -> None: + warnings.warn( + "'JSONMixin' is deprecated and will be removed in Werkzeug" + " 2.1. 'Request' now includes the functionality directly.", + DeprecationWarning, + stacklevel=2, + ) + super().__init__(*args, **kwargs) # type: ignore diff --git a/venv/Lib/site-packages/werkzeug/wrappers/request.py b/venv/Lib/site-packages/werkzeug/wrappers/request.py new file mode 100644 index 0000000..60c3b5f --- /dev/null +++ b/venv/Lib/site-packages/werkzeug/wrappers/request.py @@ -0,0 +1,660 @@ +import functools +import json +import typing +import typing as t +import warnings +from io import BytesIO + +from .._internal import _wsgi_decoding_dance +from ..datastructures import CombinedMultiDict +from ..datastructures import EnvironHeaders +from ..datastructures import FileStorage +from ..datastructures import ImmutableMultiDict +from ..datastructures import iter_multi_items +from ..datastructures import MultiDict +from ..formparser import default_stream_factory +from ..formparser import FormDataParser +from ..sansio.request import Request as _SansIORequest +from ..utils import cached_property +from ..utils import environ_property +from ..wsgi import _get_server +from ..wsgi import get_input_stream +from werkzeug.exceptions import BadRequest + +if t.TYPE_CHECKING: + import typing_extensions as te + from _typeshed.wsgi import WSGIApplication + from _typeshed.wsgi import WSGIEnvironment + + +class Request(_SansIORequest): + """Represents an incoming WSGI HTTP request, with headers and body + taken from the WSGI environment. Has properties and methods for + using the functionality defined by various HTTP specs. The data in + requests object is read-only. + + Text data is assumed to use UTF-8 encoding, which should be true for + the vast majority of modern clients. Using an encoding set by the + client is unsafe in Python due to extra encodings it provides, such + as ``zip``. To change the assumed encoding, subclass and replace + :attr:`charset`. + + :param environ: The WSGI environ is generated by the WSGI server and + contains information about the server configuration and client + request. + :param populate_request: Add this request object to the WSGI environ + as ``environ['werkzeug.request']``. Can be useful when + debugging. + :param shallow: Makes reading from :attr:`stream` (and any method + that would read from it) raise a :exc:`RuntimeError`. Useful to + prevent consuming the form data in middleware, which would make + it unavailable to the final application. + + .. versionchanged:: 2.0 + Combine ``BaseRequest`` and mixins into a single ``Request`` + class. Using the old classes is deprecated and will be removed + in Werkzeug 2.1. + + .. versionchanged:: 0.5 + Read-only mode is enforced with immutable classes for all data. + """ + + #: the maximum content length. This is forwarded to the form data + #: parsing function (:func:`parse_form_data`). When set and the + #: :attr:`form` or :attr:`files` attribute is accessed and the + #: parsing fails because more than the specified value is transmitted + #: a :exc:`~werkzeug.exceptions.RequestEntityTooLarge` exception is raised. + #: + #: Have a look at :doc:`/request_data` for more details. + #: + #: .. versionadded:: 0.5 + max_content_length: t.Optional[int] = None + + #: the maximum form field size. This is forwarded to the form data + #: parsing function (:func:`parse_form_data`). When set and the + #: :attr:`form` or :attr:`files` attribute is accessed and the + #: data in memory for post data is longer than the specified value a + #: :exc:`~werkzeug.exceptions.RequestEntityTooLarge` exception is raised. + #: + #: Have a look at :doc:`/request_data` for more details. + #: + #: .. versionadded:: 0.5 + max_form_memory_size: t.Optional[int] = None + + #: The form data parser that shoud be used. Can be replaced to customize + #: the form date parsing. + form_data_parser_class: t.Type[FormDataParser] = FormDataParser + + #: Disable the :attr:`data` property to avoid reading from the input + #: stream. + #: + #: .. deprecated:: 2.0 + #: Will be removed in Werkzeug 2.1. Create the request with + #: ``shallow=True`` instead. + #: + #: .. versionadded:: 0.9 + disable_data_descriptor: t.Optional[bool] = None + + #: The WSGI environment containing HTTP headers and information from + #: the WSGI server. + environ: "WSGIEnvironment" + + #: Set when creating the request object. If ``True``, reading from + #: the request body will cause a ``RuntimeException``. Useful to + #: prevent modifying the stream from middleware. + shallow: bool + + def __init__( + self, + environ: "WSGIEnvironment", + populate_request: bool = True, + shallow: bool = False, + ) -> None: + super().__init__( + method=environ.get("REQUEST_METHOD", "GET"), + scheme=environ.get("wsgi.url_scheme", "http"), + server=_get_server(environ), + root_path=_wsgi_decoding_dance( + environ.get("SCRIPT_NAME") or "", self.charset, self.encoding_errors + ), + path=_wsgi_decoding_dance( + environ.get("PATH_INFO") or "", self.charset, self.encoding_errors + ), + query_string=environ.get("QUERY_STRING", "").encode("latin1"), + headers=EnvironHeaders(environ), + remote_addr=environ.get("REMOTE_ADDR"), + ) + self.environ = environ + + if self.disable_data_descriptor is not None: + warnings.warn( + "'disable_data_descriptor' is deprecated and will be" + " removed in Werkzeug 2.1. Create the request with" + " 'shallow=True' instead.", + DeprecationWarning, + stacklevel=2, + ) + shallow = shallow or self.disable_data_descriptor + + self.shallow = shallow + + if populate_request and not shallow: + self.environ["werkzeug.request"] = self + + @classmethod + def from_values(cls, *args: t.Any, **kwargs: t.Any) -> "Request": + """Create a new request object based on the values provided. If + environ is given missing values are filled from there. This method is + useful for small scripts when you need to simulate a request from an URL. + Do not use this method for unittesting, there is a full featured client + object (:class:`Client`) that allows to create multipart requests, + support for cookies etc. + + This accepts the same options as the + :class:`~werkzeug.test.EnvironBuilder`. + + .. versionchanged:: 0.5 + This method now accepts the same arguments as + :class:`~werkzeug.test.EnvironBuilder`. Because of this the + `environ` parameter is now called `environ_overrides`. + + :return: request object + """ + from ..test import EnvironBuilder + + charset = kwargs.pop("charset", cls.charset) + kwargs["charset"] = charset + builder = EnvironBuilder(*args, **kwargs) + try: + return builder.get_request(cls) + finally: + builder.close() + + @classmethod + def application( + cls, f: t.Callable[["Request"], "WSGIApplication"] + ) -> "WSGIApplication": + """Decorate a function as responder that accepts the request as + the last argument. This works like the :func:`responder` + decorator but the function is passed the request object as the + last argument and the request object will be closed + automatically:: + + @Request.application + def my_wsgi_app(request): + return Response('Hello World!') + + As of Werkzeug 0.14 HTTP exceptions are automatically caught and + converted to responses instead of failing. + + :param f: the WSGI callable to decorate + :return: a new WSGI callable + """ + #: return a callable that wraps the -2nd argument with the request + #: and calls the function with all the arguments up to that one and + #: the request. The return value is then called with the latest + #: two arguments. This makes it possible to use this decorator for + #: both standalone WSGI functions as well as bound methods and + #: partially applied functions. + from ..exceptions import HTTPException + + @functools.wraps(f) + def application(*args): # type: ignore + request = cls(args[-2]) + with request: + try: + resp = f(*args[:-2] + (request,)) + except HTTPException as e: + resp = e.get_response(args[-2]) + return resp(*args[-2:]) + + return t.cast("WSGIApplication", application) + + def _get_file_stream( + self, + total_content_length: t.Optional[int], + content_type: t.Optional[str], + filename: t.Optional[str] = None, + content_length: t.Optional[int] = None, + ) -> t.BinaryIO: + """Called to get a stream for the file upload. + + This must provide a file-like class with `read()`, `readline()` + and `seek()` methods that is both writeable and readable. + + The default implementation returns a temporary file if the total + content length is higher than 500KB. Because many browsers do not + provide a content length for the files only the total content + length matters. + + :param total_content_length: the total content length of all the + data in the request combined. This value + is guaranteed to be there. + :param content_type: the mimetype of the uploaded file. + :param filename: the filename of the uploaded file. May be `None`. + :param content_length: the length of this file. This value is usually + not provided because webbrowsers do not provide + this value. + """ + return default_stream_factory( + total_content_length=total_content_length, + filename=filename, + content_type=content_type, + content_length=content_length, + ) + + @property + def want_form_data_parsed(self) -> bool: + """``True`` if the request method carries content. By default + this is true if a ``Content-Type`` is sent. + + .. versionadded:: 0.8 + """ + return bool(self.environ.get("CONTENT_TYPE")) + + def make_form_data_parser(self) -> FormDataParser: + """Creates the form data parser. Instantiates the + :attr:`form_data_parser_class` with some parameters. + + .. versionadded:: 0.8 + """ + return self.form_data_parser_class( + self._get_file_stream, + self.charset, + self.encoding_errors, + self.max_form_memory_size, + self.max_content_length, + self.parameter_storage_class, + ) + + def _load_form_data(self) -> None: + """Method used internally to retrieve submitted data. After calling + this sets `form` and `files` on the request object to multi dicts + filled with the incoming form data. As a matter of fact the input + stream will be empty afterwards. You can also call this method to + force the parsing of the form data. + + .. versionadded:: 0.8 + """ + # abort early if we have already consumed the stream + if "form" in self.__dict__: + return + + if self.want_form_data_parsed: + parser = self.make_form_data_parser() + data = parser.parse( + self._get_stream_for_parsing(), + self.mimetype, + self.content_length, + self.mimetype_params, + ) + else: + data = ( + self.stream, + self.parameter_storage_class(), + self.parameter_storage_class(), + ) + + # inject the values into the instance dict so that we bypass + # our cached_property non-data descriptor. + d = self.__dict__ + d["stream"], d["form"], d["files"] = data + + def _get_stream_for_parsing(self) -> t.BinaryIO: + """This is the same as accessing :attr:`stream` with the difference + that if it finds cached data from calling :meth:`get_data` first it + will create a new stream out of the cached data. + + .. versionadded:: 0.9.3 + """ + cached_data = getattr(self, "_cached_data", None) + if cached_data is not None: + return BytesIO(cached_data) + return self.stream + + def close(self) -> None: + """Closes associated resources of this request object. This + closes all file handles explicitly. You can also use the request + object in a with statement which will automatically close it. + + .. versionadded:: 0.9 + """ + files = self.__dict__.get("files") + for _key, value in iter_multi_items(files or ()): + value.close() + + def __enter__(self) -> "Request": + return self + + def __exit__(self, exc_type, exc_value, tb) -> None: # type: ignore + self.close() + + @cached_property + def stream(self) -> t.BinaryIO: + """ + If the incoming form data was not encoded with a known mimetype + the data is stored unmodified in this stream for consumption. Most + of the time it is a better idea to use :attr:`data` which will give + you that data as a string. The stream only returns the data once. + + Unlike :attr:`input_stream` this stream is properly guarded that you + can't accidentally read past the length of the input. Werkzeug will + internally always refer to this stream to read data which makes it + possible to wrap this object with a stream that does filtering. + + .. versionchanged:: 0.9 + This stream is now always available but might be consumed by the + form parser later on. Previously the stream was only set if no + parsing happened. + """ + if self.shallow: + raise RuntimeError( + "This request was created with 'shallow=True', reading" + " from the input stream is disabled." + ) + + return get_input_stream(self.environ) + + input_stream = environ_property[t.BinaryIO]( + "wsgi.input", + doc="""The WSGI input stream. + + In general it's a bad idea to use this one because you can + easily read past the boundary. Use the :attr:`stream` + instead.""", + ) + + @cached_property + def data(self) -> bytes: + """ + Contains the incoming request data as string in case it came with + a mimetype Werkzeug does not handle. + """ + return self.get_data(parse_form_data=True) + + @typing.overload + def get_data( # type: ignore + self, + cache: bool = True, + as_text: "te.Literal[False]" = False, + parse_form_data: bool = False, + ) -> bytes: + ... + + @typing.overload + def get_data( + self, + cache: bool = True, + as_text: "te.Literal[True]" = ..., + parse_form_data: bool = False, + ) -> str: + ... + + def get_data( + self, cache: bool = True, as_text: bool = False, parse_form_data: bool = False + ) -> t.Union[bytes, str]: + """This reads the buffered incoming data from the client into one + bytes object. By default this is cached but that behavior can be + changed by setting `cache` to `False`. + + Usually it's a bad idea to call this method without checking the + content length first as a client could send dozens of megabytes or more + to cause memory problems on the server. + + Note that if the form data was already parsed this method will not + return anything as form data parsing does not cache the data like + this method does. To implicitly invoke form data parsing function + set `parse_form_data` to `True`. When this is done the return value + of this method will be an empty string if the form parser handles + the data. This generally is not necessary as if the whole data is + cached (which is the default) the form parser will used the cached + data to parse the form data. Please be generally aware of checking + the content length first in any case before calling this method + to avoid exhausting server memory. + + If `as_text` is set to `True` the return value will be a decoded + string. + + .. versionadded:: 0.9 + """ + rv = getattr(self, "_cached_data", None) + if rv is None: + if parse_form_data: + self._load_form_data() + rv = self.stream.read() + if cache: + self._cached_data = rv + if as_text: + rv = rv.decode(self.charset, self.encoding_errors) + return rv # type: ignore + + @cached_property + def form(self) -> "ImmutableMultiDict[str, str]": + """The form parameters. By default an + :class:`~werkzeug.datastructures.ImmutableMultiDict` + is returned from this function. This can be changed by setting + :attr:`parameter_storage_class` to a different type. This might + be necessary if the order of the form data is important. + + Please keep in mind that file uploads will not end up here, but instead + in the :attr:`files` attribute. + + .. versionchanged:: 0.9 + + Previous to Werkzeug 0.9 this would only contain form data for POST + and PUT requests. + """ + self._load_form_data() + return self.form + + @cached_property + def values(self) -> "CombinedMultiDict[str, str]": + """A :class:`werkzeug.datastructures.CombinedMultiDict` that + combines :attr:`args` and :attr:`form`. + + For GET requests, only ``args`` are present, not ``form``. + + .. versionchanged:: 2.0 + For GET requests, only ``args`` are present, not ``form``. + """ + sources = [self.args] + + if self.method != "GET": + # GET requests can have a body, and some caching proxies + # might not treat that differently than a normal GET + # request, allowing form data to "invisibly" affect the + # cache without indication in the query string / URL. + sources.append(self.form) + + args = [] + + for d in sources: + if not isinstance(d, MultiDict): + d = MultiDict(d) + + args.append(d) + + return CombinedMultiDict(args) + + @cached_property + def files(self) -> "ImmutableMultiDict[str, FileStorage]": + """:class:`~werkzeug.datastructures.MultiDict` object containing + all uploaded files. Each key in :attr:`files` is the name from the + ````. Each value in :attr:`files` is a + Werkzeug :class:`~werkzeug.datastructures.FileStorage` object. + + It basically behaves like a standard file object you know from Python, + with the difference that it also has a + :meth:`~werkzeug.datastructures.FileStorage.save` function that can + store the file on the filesystem. + + Note that :attr:`files` will only contain data if the request method was + POST, PUT or PATCH and the ``

    `` that posted to the request had + ``enctype="multipart/form-data"``. It will be empty otherwise. + + See the :class:`~werkzeug.datastructures.MultiDict` / + :class:`~werkzeug.datastructures.FileStorage` documentation for + more details about the used data structure. + """ + self._load_form_data() + return self.files + + @property + def script_root(self) -> str: + """Alias for :attr:`self.root_path`. ``environ["SCRIPT_ROOT"]`` + without a trailing slash. + """ + return self.root_path + + @cached_property + def url_root(self) -> str: + """Alias for :attr:`root_url`. The URL with scheme, host, and + root path. For example, ``https://example.com/app/``. + """ + return self.root_url + + remote_user = environ_property[str]( + "REMOTE_USER", + doc="""If the server supports user authentication, and the + script is protected, this attribute contains the username the + user has authenticated as.""", + ) + is_multithread = environ_property[bool]( + "wsgi.multithread", + doc="""boolean that is `True` if the application is served by a + multithreaded WSGI server.""", + ) + is_multiprocess = environ_property[bool]( + "wsgi.multiprocess", + doc="""boolean that is `True` if the application is served by a + WSGI server that spawns multiple processes.""", + ) + is_run_once = environ_property[bool]( + "wsgi.run_once", + doc="""boolean that is `True` if the application will be + executed only once in a process lifetime. This is the case for + CGI for example, but it's not guaranteed that the execution only + happens one time.""", + ) + + # JSON + + #: A module or other object that has ``dumps`` and ``loads`` + #: functions that match the API of the built-in :mod:`json` module. + json_module = json + + @property + def json(self) -> t.Optional[t.Any]: + """The parsed JSON data if :attr:`mimetype` indicates JSON + (:mimetype:`application/json`, see :meth:`is_json`). + + Calls :meth:`get_json` with default arguments. + """ + return self.get_json() + + # Cached values for ``(silent=False, silent=True)``. Initialized + # with sentinel values. + _cached_json: t.Tuple[t.Any, t.Any] = (Ellipsis, Ellipsis) + + def get_json( + self, force: bool = False, silent: bool = False, cache: bool = True + ) -> t.Optional[t.Any]: + """Parse :attr:`data` as JSON. + + If the mimetype does not indicate JSON + (:mimetype:`application/json`, see :meth:`is_json`), this + returns ``None``. + + If parsing fails, :meth:`on_json_loading_failed` is called and + its return value is used as the return value. + + :param force: Ignore the mimetype and always try to parse JSON. + :param silent: Silence parsing errors and return ``None`` + instead. + :param cache: Store the parsed JSON to return for subsequent + calls. + """ + if cache and self._cached_json[silent] is not Ellipsis: + return self._cached_json[silent] + + if not (force or self.is_json): + return None + + data = self.get_data(cache=cache) + + try: + rv = self.json_module.loads(data) + except ValueError as e: + if silent: + rv = None + + if cache: + normal_rv, _ = self._cached_json + self._cached_json = (normal_rv, rv) + else: + rv = self.on_json_loading_failed(e) + + if cache: + _, silent_rv = self._cached_json + self._cached_json = (rv, silent_rv) + else: + if cache: + self._cached_json = (rv, rv) + + return rv + + def on_json_loading_failed(self, e: ValueError) -> t.Any: + """Called if :meth:`get_json` parsing fails and isn't silenced. + If this method returns a value, it is used as the return value + for :meth:`get_json`. The default implementation raises + :exc:`~werkzeug.exceptions.BadRequest`. + """ + raise BadRequest(f"Failed to decode JSON object: {e}") + + +class StreamOnlyMixin: + """Mixin to create a ``Request`` that disables the ``data``, + ``form``, and ``files`` properties. Only ``stream`` is available. + + .. deprecated:: 2.0 + Will be removed in Werkzeug 2.1. Create the request with + ``shallow=True`` instead. + + .. versionadded:: 0.9 + """ + + def __init__(self, *args: t.Any, **kwargs: t.Any) -> None: + warnings.warn( + "'StreamOnlyMixin' is deprecated and will be removed in" + " Werkzeug 2.1. Create the request with 'shallow=True'" + " instead.", + DeprecationWarning, + stacklevel=2, + ) + kwargs["shallow"] = True + super().__init__(*args, **kwargs) # type: ignore + + +class PlainRequest(StreamOnlyMixin, Request): + """A request object without ``data``, ``form``, and ``files``. + + .. deprecated:: 2.0 + Will be removed in Werkzeug 2.1. Create the request with + ``shallow=True`` instead. + + .. versionadded:: 0.9 + """ + + def __init__(self, *args: t.Any, **kwargs: t.Any) -> None: + warnings.warn( + "'PlainRequest' is deprecated and will be removed in" + " Werkzeug 2.1. Create the request with 'shallow=True'" + " instead.", + DeprecationWarning, + stacklevel=2, + ) + + # Don't show the DeprecationWarning for StreamOnlyMixin. + with warnings.catch_warnings(): + warnings.simplefilter("ignore", DeprecationWarning) + super().__init__(*args, **kwargs) diff --git a/venv/Lib/site-packages/werkzeug/wrappers/response.py b/venv/Lib/site-packages/werkzeug/wrappers/response.py new file mode 100644 index 0000000..a43c8bc --- /dev/null +++ b/venv/Lib/site-packages/werkzeug/wrappers/response.py @@ -0,0 +1,890 @@ +import json +import typing +import typing as t +import warnings +from http import HTTPStatus + +from .._internal import _to_bytes +from ..datastructures import Headers +from ..http import remove_entity_headers +from ..sansio.response import Response as _SansIOResponse +from ..urls import iri_to_uri +from ..urls import url_join +from ..utils import cached_property +from ..wsgi import ClosingIterator +from ..wsgi import get_current_url +from werkzeug._internal import _get_environ +from werkzeug.http import generate_etag +from werkzeug.http import http_date +from werkzeug.http import is_resource_modified +from werkzeug.http import parse_etags +from werkzeug.http import parse_range_header +from werkzeug.wsgi import _RangeWrapper + +if t.TYPE_CHECKING: + import typing_extensions as te + from _typeshed.wsgi import StartResponse + from _typeshed.wsgi import WSGIApplication + from _typeshed.wsgi import WSGIEnvironment + + +def _warn_if_string(iterable: t.Iterable) -> None: + """Helper for the response objects to check if the iterable returned + to the WSGI server is not a string. + """ + if isinstance(iterable, str): + warnings.warn( + "Response iterable was set to a string. This will appear to" + " work but means that the server will send the data to the" + " client one character at a time. This is almost never" + " intended behavior, use 'response.data' to assign strings" + " to the response object.", + stacklevel=2, + ) + + +def _iter_encoded( + iterable: t.Iterable[t.Union[str, bytes]], charset: str +) -> t.Iterator[bytes]: + for item in iterable: + if isinstance(item, str): + yield item.encode(charset) + else: + yield item + + +def _clean_accept_ranges(accept_ranges: t.Union[bool, str]) -> str: + if accept_ranges is True: + return "bytes" + elif accept_ranges is False: + return "none" + elif isinstance(accept_ranges, str): + return accept_ranges + raise ValueError("Invalid accept_ranges value") + + +class Response(_SansIOResponse): + """Represents an outgoing WSGI HTTP response with body, status, and + headers. Has properties and methods for using the functionality + defined by various HTTP specs. + + The response body is flexible to support different use cases. The + simple form is passing bytes, or a string which will be encoded as + UTF-8. Passing an iterable of bytes or strings makes this a + streaming response. A generator is particularly useful for building + a CSV file in memory or using SSE (Server Sent Events). A file-like + object is also iterable, although the + :func:`~werkzeug.utils.send_file` helper should be used in that + case. + + The response object is itself a WSGI application callable. When + called (:meth:`__call__`) with ``environ`` and ``start_response``, + it will pass its status and headers to ``start_response`` then + return its body as an iterable. + + .. code-block:: python + + from werkzeug.wrappers.response import Response + + def index(): + return Response("Hello, World!") + + def application(environ, start_response): + path = environ.get("PATH_INFO") or "/" + + if path == "/": + response = index() + else: + response = Response("Not Found", status=404) + + return response(environ, start_response) + + :param response: The data for the body of the response. A string or + bytes, or tuple or list of strings or bytes, for a fixed-length + response, or any other iterable of strings or bytes for a + streaming response. Defaults to an empty body. + :param status: The status code for the response. Either an int, in + which case the default status message is added, or a string in + the form ``{code} {message}``, like ``404 Not Found``. Defaults + to 200. + :param headers: A :class:`~werkzeug.datastructures.Headers` object, + or a list of ``(key, value)`` tuples that will be converted to a + ``Headers`` object. + :param mimetype: The mime type (content type without charset or + other parameters) of the response. If the value starts with + ``text/`` (or matches some other special cases), the charset + will be added to create the ``content_type``. + :param content_type: The full content type of the response. + Overrides building the value from ``mimetype``. + :param direct_passthrough: Pass the response body directly through + as the WSGI iterable. This can be used when the body is a binary + file or other iterator of bytes, to skip some unnecessary + checks. Use :func:`~werkzeug.utils.send_file` instead of setting + this manually. + + .. versionchanged:: 2.0 + Combine ``BaseResponse`` and mixins into a single ``Response`` + class. Using the old classes is deprecated and will be removed + in Werkzeug 2.1. + + .. versionchanged:: 0.5 + The ``direct_passthrough`` parameter was added. + """ + + #: if set to `False` accessing properties on the response object will + #: not try to consume the response iterator and convert it into a list. + #: + #: .. versionadded:: 0.6.2 + #: + #: That attribute was previously called `implicit_seqence_conversion`. + #: (Notice the typo). If you did use this feature, you have to adapt + #: your code to the name change. + implicit_sequence_conversion = True + + #: Should this response object correct the location header to be RFC + #: conformant? This is true by default. + #: + #: .. versionadded:: 0.8 + autocorrect_location_header = True + + #: Should this response object automatically set the content-length + #: header if possible? This is true by default. + #: + #: .. versionadded:: 0.8 + automatically_set_content_length = True + + #: The response body to send as the WSGI iterable. A list of strings + #: or bytes represents a fixed-length response, any other iterable + #: is a streaming response. Strings are encoded to bytes as UTF-8. + #: + #: Do not set to a plain string or bytes, that will cause sending + #: the response to be very inefficient as it will iterate one byte + #: at a time. + response: t.Union[t.Iterable[str], t.Iterable[bytes]] + + def __init__( + self, + response: t.Optional[ + t.Union[t.Iterable[bytes], bytes, t.Iterable[str], str] + ] = None, + status: t.Optional[t.Union[int, str, HTTPStatus]] = None, + headers: t.Optional[ + t.Union[ + t.Mapping[str, t.Union[str, int, t.Iterable[t.Union[str, int]]]], + t.Iterable[t.Tuple[str, t.Union[str, int]]], + ] + ] = None, + mimetype: t.Optional[str] = None, + content_type: t.Optional[str] = None, + direct_passthrough: bool = False, + ) -> None: + super().__init__( + status=status, + headers=headers, + mimetype=mimetype, + content_type=content_type, + ) + + #: Pass the response body directly through as the WSGI iterable. + #: This can be used when the body is a binary file or other + #: iterator of bytes, to skip some unnecessary checks. Use + #: :func:`~werkzeug.utils.send_file` instead of setting this + #: manually. + self.direct_passthrough = direct_passthrough + self._on_close: t.List[t.Callable[[], t.Any]] = [] + + # we set the response after the headers so that if a class changes + # the charset attribute, the data is set in the correct charset. + if response is None: + self.response = [] + elif isinstance(response, (str, bytes, bytearray)): + self.set_data(response) + else: + self.response = response + + def call_on_close(self, func: t.Callable[[], t.Any]) -> t.Callable[[], t.Any]: + """Adds a function to the internal list of functions that should + be called as part of closing down the response. Since 0.7 this + function also returns the function that was passed so that this + can be used as a decorator. + + .. versionadded:: 0.6 + """ + self._on_close.append(func) + return func + + def __repr__(self) -> str: + if self.is_sequence: + body_info = f"{sum(map(len, self.iter_encoded()))} bytes" + else: + body_info = "streamed" if self.is_streamed else "likely-streamed" + return f"<{type(self).__name__} {body_info} [{self.status}]>" + + @classmethod + def force_type( + cls, response: "Response", environ: t.Optional["WSGIEnvironment"] = None + ) -> "Response": + """Enforce that the WSGI response is a response object of the current + type. Werkzeug will use the :class:`Response` internally in many + situations like the exceptions. If you call :meth:`get_response` on an + exception you will get back a regular :class:`Response` object, even + if you are using a custom subclass. + + This method can enforce a given response type, and it will also + convert arbitrary WSGI callables into response objects if an environ + is provided:: + + # convert a Werkzeug response object into an instance of the + # MyResponseClass subclass. + response = MyResponseClass.force_type(response) + + # convert any WSGI application into a response object + response = MyResponseClass.force_type(response, environ) + + This is especially useful if you want to post-process responses in + the main dispatcher and use functionality provided by your subclass. + + Keep in mind that this will modify response objects in place if + possible! + + :param response: a response object or wsgi application. + :param environ: a WSGI environment object. + :return: a response object. + """ + if not isinstance(response, Response): + if environ is None: + raise TypeError( + "cannot convert WSGI application into response" + " objects without an environ" + ) + + from ..test import run_wsgi_app + + response = Response(*run_wsgi_app(response, environ)) + + response.__class__ = cls + return response + + @classmethod + def from_app( + cls, app: "WSGIApplication", environ: "WSGIEnvironment", buffered: bool = False + ) -> "Response": + """Create a new response object from an application output. This + works best if you pass it an application that returns a generator all + the time. Sometimes applications may use the `write()` callable + returned by the `start_response` function. This tries to resolve such + edge cases automatically. But if you don't get the expected output + you should set `buffered` to `True` which enforces buffering. + + :param app: the WSGI application to execute. + :param environ: the WSGI environment to execute against. + :param buffered: set to `True` to enforce buffering. + :return: a response object. + """ + from ..test import run_wsgi_app + + return cls(*run_wsgi_app(app, environ, buffered)) + + @typing.overload + def get_data(self, as_text: "te.Literal[False]" = False) -> bytes: + ... + + @typing.overload + def get_data(self, as_text: "te.Literal[True]") -> str: + ... + + def get_data(self, as_text: bool = False) -> t.Union[bytes, str]: + """The string representation of the response body. Whenever you call + this property the response iterable is encoded and flattened. This + can lead to unwanted behavior if you stream big data. + + This behavior can be disabled by setting + :attr:`implicit_sequence_conversion` to `False`. + + If `as_text` is set to `True` the return value will be a decoded + string. + + .. versionadded:: 0.9 + """ + self._ensure_sequence() + rv = b"".join(self.iter_encoded()) + + if as_text: + return rv.decode(self.charset) + + return rv + + def set_data(self, value: t.Union[bytes, str]) -> None: + """Sets a new string as response. The value must be a string or + bytes. If a string is set it's encoded to the charset of the + response (utf-8 by default). + + .. versionadded:: 0.9 + """ + # if a string is set, it's encoded directly so that we + # can set the content length + if isinstance(value, str): + value = value.encode(self.charset) + else: + value = bytes(value) + self.response = [value] + if self.automatically_set_content_length: + self.headers["Content-Length"] = str(len(value)) + + data = property( + get_data, + set_data, + doc="A descriptor that calls :meth:`get_data` and :meth:`set_data`.", + ) + + def calculate_content_length(self) -> t.Optional[int]: + """Returns the content length if available or `None` otherwise.""" + try: + self._ensure_sequence() + except RuntimeError: + return None + return sum(len(x) for x in self.iter_encoded()) + + def _ensure_sequence(self, mutable: bool = False) -> None: + """This method can be called by methods that need a sequence. If + `mutable` is true, it will also ensure that the response sequence + is a standard Python list. + + .. versionadded:: 0.6 + """ + if self.is_sequence: + # if we need a mutable object, we ensure it's a list. + if mutable and not isinstance(self.response, list): + self.response = list(self.response) # type: ignore + return + if self.direct_passthrough: + raise RuntimeError( + "Attempted implicit sequence conversion but the" + " response object is in direct passthrough mode." + ) + if not self.implicit_sequence_conversion: + raise RuntimeError( + "The response object required the iterable to be a" + " sequence, but the implicit conversion was disabled." + " Call make_sequence() yourself." + ) + self.make_sequence() + + def make_sequence(self) -> None: + """Converts the response iterator in a list. By default this happens + automatically if required. If `implicit_sequence_conversion` is + disabled, this method is not automatically called and some properties + might raise exceptions. This also encodes all the items. + + .. versionadded:: 0.6 + """ + if not self.is_sequence: + # if we consume an iterable we have to ensure that the close + # method of the iterable is called if available when we tear + # down the response + close = getattr(self.response, "close", None) + self.response = list(self.iter_encoded()) + if close is not None: + self.call_on_close(close) + + def iter_encoded(self) -> t.Iterator[bytes]: + """Iter the response encoded with the encoding of the response. + If the response object is invoked as WSGI application the return + value of this method is used as application iterator unless + :attr:`direct_passthrough` was activated. + """ + if __debug__: + _warn_if_string(self.response) + # Encode in a separate function so that self.response is fetched + # early. This allows us to wrap the response with the return + # value from get_app_iter or iter_encoded. + return _iter_encoded(self.response, self.charset) + + @property + def is_streamed(self) -> bool: + """If the response is streamed (the response is not an iterable with + a length information) this property is `True`. In this case streamed + means that there is no information about the number of iterations. + This is usually `True` if a generator is passed to the response object. + + This is useful for checking before applying some sort of post + filtering that should not take place for streamed responses. + """ + try: + len(self.response) # type: ignore + except (TypeError, AttributeError): + return True + return False + + @property + def is_sequence(self) -> bool: + """If the iterator is buffered, this property will be `True`. A + response object will consider an iterator to be buffered if the + response attribute is a list or tuple. + + .. versionadded:: 0.6 + """ + return isinstance(self.response, (tuple, list)) + + def close(self) -> None: + """Close the wrapped response if possible. You can also use the object + in a with statement which will automatically close it. + + .. versionadded:: 0.9 + Can now be used in a with statement. + """ + if hasattr(self.response, "close"): + self.response.close() # type: ignore + for func in self._on_close: + func() + + def __enter__(self) -> "Response": + return self + + def __exit__(self, exc_type, exc_value, tb): # type: ignore + self.close() + + def freeze(self, no_etag: None = None) -> None: + """Make the response object ready to be pickled. Does the + following: + + * Buffer the response into a list, ignoring + :attr:`implicity_sequence_conversion` and + :attr:`direct_passthrough`. + * Set the ``Content-Length`` header. + * Generate an ``ETag`` header if one is not already set. + + .. versionchanged:: 2.0 + An ``ETag`` header is added, the ``no_etag`` parameter is + deprecated and will be removed in Werkzeug 2.1. + + .. versionchanged:: 0.6 + The ``Content-Length`` header is set. + """ + # Always freeze the encoded response body, ignore + # implicit_sequence_conversion and direct_passthrough. + self.response = list(self.iter_encoded()) + self.headers["Content-Length"] = str(sum(map(len, self.response))) + + if no_etag is not None: + warnings.warn( + "The 'no_etag' parameter is deprecated and will be" + " removed in Werkzeug 2.1.", + DeprecationWarning, + stacklevel=2, + ) + + self.add_etag() + + def get_wsgi_headers(self, environ: "WSGIEnvironment") -> Headers: + """This is automatically called right before the response is started + and returns headers modified for the given environment. It returns a + copy of the headers from the response with some modifications applied + if necessary. + + For example the location header (if present) is joined with the root + URL of the environment. Also the content length is automatically set + to zero here for certain status codes. + + .. versionchanged:: 0.6 + Previously that function was called `fix_headers` and modified + the response object in place. Also since 0.6, IRIs in location + and content-location headers are handled properly. + + Also starting with 0.6, Werkzeug will attempt to set the content + length if it is able to figure it out on its own. This is the + case if all the strings in the response iterable are already + encoded and the iterable is buffered. + + :param environ: the WSGI environment of the request. + :return: returns a new :class:`~werkzeug.datastructures.Headers` + object. + """ + headers = Headers(self.headers) + location: t.Optional[str] = None + content_location: t.Optional[str] = None + content_length: t.Optional[t.Union[str, int]] = None + status = self.status_code + + # iterate over the headers to find all values in one go. Because + # get_wsgi_headers is used each response that gives us a tiny + # speedup. + for key, value in headers: + ikey = key.lower() + if ikey == "location": + location = value + elif ikey == "content-location": + content_location = value + elif ikey == "content-length": + content_length = value + + # make sure the location header is an absolute URL + if location is not None: + old_location = location + if isinstance(location, str): + # Safe conversion is necessary here as we might redirect + # to a broken URI scheme (for instance itms-services). + location = iri_to_uri(location, safe_conversion=True) + + if self.autocorrect_location_header: + current_url = get_current_url(environ, strip_querystring=True) + if isinstance(current_url, str): + current_url = iri_to_uri(current_url) + location = url_join(current_url, location) + if location != old_location: + headers["Location"] = location + + # make sure the content location is a URL + if content_location is not None and isinstance(content_location, str): + headers["Content-Location"] = iri_to_uri(content_location) + + if 100 <= status < 200 or status == 204: + # Per section 3.3.2 of RFC 7230, "a server MUST NOT send a + # Content-Length header field in any response with a status + # code of 1xx (Informational) or 204 (No Content)." + headers.remove("Content-Length") + elif status == 304: + remove_entity_headers(headers) + + # if we can determine the content length automatically, we + # should try to do that. But only if this does not involve + # flattening the iterator or encoding of strings in the + # response. We however should not do that if we have a 304 + # response. + if ( + self.automatically_set_content_length + and self.is_sequence + and content_length is None + and status not in (204, 304) + and not (100 <= status < 200) + ): + try: + content_length = sum(len(_to_bytes(x, "ascii")) for x in self.response) + except UnicodeError: + # Something other than bytes, can't safely figure out + # the length of the response. + pass + else: + headers["Content-Length"] = str(content_length) + + return headers + + def get_app_iter(self, environ: "WSGIEnvironment") -> t.Iterable[bytes]: + """Returns the application iterator for the given environ. Depending + on the request method and the current status code the return value + might be an empty response rather than the one from the response. + + If the request method is `HEAD` or the status code is in a range + where the HTTP specification requires an empty response, an empty + iterable is returned. + + .. versionadded:: 0.6 + + :param environ: the WSGI environment of the request. + :return: a response iterable. + """ + status = self.status_code + if ( + environ["REQUEST_METHOD"] == "HEAD" + or 100 <= status < 200 + or status in (204, 304) + ): + iterable: t.Iterable[bytes] = () + elif self.direct_passthrough: + if __debug__: + _warn_if_string(self.response) + return self.response # type: ignore + else: + iterable = self.iter_encoded() + return ClosingIterator(iterable, self.close) + + def get_wsgi_response( + self, environ: "WSGIEnvironment" + ) -> t.Tuple[t.Iterable[bytes], str, t.List[t.Tuple[str, str]]]: + """Returns the final WSGI response as tuple. The first item in + the tuple is the application iterator, the second the status and + the third the list of headers. The response returned is created + specially for the given environment. For example if the request + method in the WSGI environment is ``'HEAD'`` the response will + be empty and only the headers and status code will be present. + + .. versionadded:: 0.6 + + :param environ: the WSGI environment of the request. + :return: an ``(app_iter, status, headers)`` tuple. + """ + headers = self.get_wsgi_headers(environ) + app_iter = self.get_app_iter(environ) + return app_iter, self.status, headers.to_wsgi_list() + + def __call__( + self, environ: "WSGIEnvironment", start_response: "StartResponse" + ) -> t.Iterable[bytes]: + """Process this response as WSGI application. + + :param environ: the WSGI environment. + :param start_response: the response callable provided by the WSGI + server. + :return: an application iterator + """ + app_iter, status, headers = self.get_wsgi_response(environ) + start_response(status, headers) + return app_iter + + # JSON + + #: A module or other object that has ``dumps`` and ``loads`` + #: functions that match the API of the built-in :mod:`json` module. + json_module = json + + @property + def json(self) -> t.Optional[t.Any]: + """The parsed JSON data if :attr:`mimetype` indicates JSON + (:mimetype:`application/json`, see :meth:`is_json`). + + Calls :meth:`get_json` with default arguments. + """ + return self.get_json() + + def get_json(self, force: bool = False, silent: bool = False) -> t.Optional[t.Any]: + """Parse :attr:`data` as JSON. Useful during testing. + + If the mimetype does not indicate JSON + (:mimetype:`application/json`, see :meth:`is_json`), this + returns ``None``. + + Unlike :meth:`Request.get_json`, the result is not cached. + + :param force: Ignore the mimetype and always try to parse JSON. + :param silent: Silence parsing errors and return ``None`` + instead. + """ + if not (force or self.is_json): + return None + + data = self.get_data() + + try: + return self.json_module.loads(data) + except ValueError: + if not silent: + raise + + return None + + # Stream + + @cached_property + def stream(self) -> "ResponseStream": + """The response iterable as write-only stream.""" + return ResponseStream(self) + + def _wrap_range_response(self, start: int, length: int) -> None: + """Wrap existing Response in case of Range Request context.""" + if self.status_code == 206: + self.response = _RangeWrapper(self.response, start, length) # type: ignore + + def _is_range_request_processable(self, environ: "WSGIEnvironment") -> bool: + """Return ``True`` if `Range` header is present and if underlying + resource is considered unchanged when compared with `If-Range` header. + """ + return ( + "HTTP_IF_RANGE" not in environ + or not is_resource_modified( + environ, + self.headers.get("etag"), + None, + self.headers.get("last-modified"), + ignore_if_range=False, + ) + ) and "HTTP_RANGE" in environ + + def _process_range_request( + self, + environ: "WSGIEnvironment", + complete_length: t.Optional[int] = None, + accept_ranges: t.Optional[t.Union[bool, str]] = None, + ) -> bool: + """Handle Range Request related headers (RFC7233). If `Accept-Ranges` + header is valid, and Range Request is processable, we set the headers + as described by the RFC, and wrap the underlying response in a + RangeWrapper. + + Returns ``True`` if Range Request can be fulfilled, ``False`` otherwise. + + :raises: :class:`~werkzeug.exceptions.RequestedRangeNotSatisfiable` + if `Range` header could not be parsed or satisfied. + + .. versionchanged:: 2.0 + Returns ``False`` if the length is 0. + """ + from ..exceptions import RequestedRangeNotSatisfiable + + if ( + accept_ranges is None + or complete_length is None + or complete_length == 0 + or not self._is_range_request_processable(environ) + ): + return False + + parsed_range = parse_range_header(environ.get("HTTP_RANGE")) + + if parsed_range is None: + raise RequestedRangeNotSatisfiable(complete_length) + + range_tuple = parsed_range.range_for_length(complete_length) + content_range_header = parsed_range.to_content_range_header(complete_length) + + if range_tuple is None or content_range_header is None: + raise RequestedRangeNotSatisfiable(complete_length) + + content_length = range_tuple[1] - range_tuple[0] + self.headers["Content-Length"] = content_length + self.headers["Accept-Ranges"] = accept_ranges + self.content_range = content_range_header # type: ignore + self.status_code = 206 + self._wrap_range_response(range_tuple[0], content_length) + return True + + def make_conditional( + self, + request_or_environ: "WSGIEnvironment", + accept_ranges: t.Union[bool, str] = False, + complete_length: t.Optional[int] = None, + ) -> "Response": + """Make the response conditional to the request. This method works + best if an etag was defined for the response already. The `add_etag` + method can be used to do that. If called without etag just the date + header is set. + + This does nothing if the request method in the request or environ is + anything but GET or HEAD. + + For optimal performance when handling range requests, it's recommended + that your response data object implements `seekable`, `seek` and `tell` + methods as described by :py:class:`io.IOBase`. Objects returned by + :meth:`~werkzeug.wsgi.wrap_file` automatically implement those methods. + + It does not remove the body of the response because that's something + the :meth:`__call__` function does for us automatically. + + Returns self so that you can do ``return resp.make_conditional(req)`` + but modifies the object in-place. + + :param request_or_environ: a request object or WSGI environment to be + used to make the response conditional + against. + :param accept_ranges: This parameter dictates the value of + `Accept-Ranges` header. If ``False`` (default), + the header is not set. If ``True``, it will be set + to ``"bytes"``. If ``None``, it will be set to + ``"none"``. If it's a string, it will use this + value. + :param complete_length: Will be used only in valid Range Requests. + It will set `Content-Range` complete length + value and compute `Content-Length` real value. + This parameter is mandatory for successful + Range Requests completion. + :raises: :class:`~werkzeug.exceptions.RequestedRangeNotSatisfiable` + if `Range` header could not be parsed or satisfied. + + .. versionchanged:: 2.0 + Range processing is skipped if length is 0 instead of + raising a 416 Range Not Satisfiable error. + """ + environ = _get_environ(request_or_environ) + if environ["REQUEST_METHOD"] in ("GET", "HEAD"): + # if the date is not in the headers, add it now. We however + # will not override an already existing header. Unfortunately + # this header will be overriden by many WSGI servers including + # wsgiref. + if "date" not in self.headers: + self.headers["Date"] = http_date() + accept_ranges = _clean_accept_ranges(accept_ranges) + is206 = self._process_range_request(environ, complete_length, accept_ranges) + if not is206 and not is_resource_modified( + environ, + self.headers.get("etag"), + None, + self.headers.get("last-modified"), + ): + if parse_etags(environ.get("HTTP_IF_MATCH")): + self.status_code = 412 + else: + self.status_code = 304 + if ( + self.automatically_set_content_length + and "content-length" not in self.headers + ): + length = self.calculate_content_length() + if length is not None: + self.headers["Content-Length"] = length + return self + + def add_etag(self, overwrite: bool = False, weak: bool = False) -> None: + """Add an etag for the current response if there is none yet. + + .. versionchanged:: 2.0 + SHA-1 is used to generate the value. MD5 may not be + available in some environments. + """ + if overwrite or "etag" not in self.headers: + self.set_etag(generate_etag(self.get_data()), weak) + + +class ResponseStream: + """A file descriptor like object used by the :class:`ResponseStreamMixin` to + represent the body of the stream. It directly pushes into the response + iterable of the response object. + """ + + mode = "wb+" + + def __init__(self, response: Response): + self.response = response + self.closed = False + + def write(self, value: bytes) -> int: + if self.closed: + raise ValueError("I/O operation on closed file") + self.response._ensure_sequence(mutable=True) + self.response.response.append(value) # type: ignore + self.response.headers.pop("Content-Length", None) + return len(value) + + def writelines(self, seq: t.Iterable[bytes]) -> None: + for item in seq: + self.write(item) + + def close(self) -> None: + self.closed = True + + def flush(self) -> None: + if self.closed: + raise ValueError("I/O operation on closed file") + + def isatty(self) -> bool: + if self.closed: + raise ValueError("I/O operation on closed file") + return False + + def tell(self) -> int: + self.response._ensure_sequence() + return sum(map(len, self.response.response)) + + @property + def encoding(self) -> str: + return self.response.charset + + +class ResponseStreamMixin: + def __init__(self, *args: t.Any, **kwargs: t.Any) -> None: + warnings.warn( + "'ResponseStreamMixin' is deprecated and will be removed in" + " Werkzeug 2.1. 'Response' now includes the functionality" + " directly.", + DeprecationWarning, + stacklevel=2, + ) + super().__init__(*args, **kwargs) # type: ignore diff --git a/venv/Lib/site-packages/werkzeug/wrappers/user_agent.py b/venv/Lib/site-packages/werkzeug/wrappers/user_agent.py new file mode 100644 index 0000000..184ffd0 --- /dev/null +++ b/venv/Lib/site-packages/werkzeug/wrappers/user_agent.py @@ -0,0 +1,14 @@ +import typing as t +import warnings + + +class UserAgentMixin: + def __init__(self, *args: t.Any, **kwargs: t.Any) -> None: + warnings.warn( + "'UserAgentMixin' is deprecated and will be removed in" + " Werkzeug 2.1. 'Request' now includes the functionality" + " directly.", + DeprecationWarning, + stacklevel=2, + ) + super().__init__(*args, **kwargs) # type: ignore diff --git a/venv/Lib/site-packages/werkzeug/wsgi.py b/venv/Lib/site-packages/werkzeug/wsgi.py new file mode 100644 index 0000000..9439a1e --- /dev/null +++ b/venv/Lib/site-packages/werkzeug/wsgi.py @@ -0,0 +1,982 @@ +import io +import re +import typing as t +from functools import partial +from functools import update_wrapper +from itertools import chain + +from ._internal import _make_encode_wrapper +from ._internal import _to_bytes +from ._internal import _to_str +from .sansio import utils as _sansio_utils +from .sansio.utils import host_is_trusted # noqa: F401 # Imported as part of API +from .urls import _URLTuple +from .urls import uri_to_iri +from .urls import url_join +from .urls import url_parse +from .urls import url_quote + +if t.TYPE_CHECKING: + from _typeshed.wsgi import WSGIApplication + from _typeshed.wsgi import WSGIEnvironment + + +def responder(f: t.Callable[..., "WSGIApplication"]) -> "WSGIApplication": + """Marks a function as responder. Decorate a function with it and it + will automatically call the return value as WSGI application. + + Example:: + + @responder + def application(environ, start_response): + return Response('Hello World!') + """ + return update_wrapper(lambda *a: f(*a)(*a[-2:]), f) + + +def get_current_url( + environ: "WSGIEnvironment", + root_only: bool = False, + strip_querystring: bool = False, + host_only: bool = False, + trusted_hosts: t.Optional[t.Iterable[str]] = None, +) -> str: + """Recreate the URL for a request from the parts in a WSGI + environment. + + The URL is an IRI, not a URI, so it may contain Unicode characters. + Use :func:`~werkzeug.urls.iri_to_uri` to convert it to ASCII. + + :param environ: The WSGI environment to get the URL parts from. + :param root_only: Only build the root path, don't include the + remaining path or query string. + :param strip_querystring: Don't include the query string. + :param host_only: Only build the scheme and host. + :param trusted_hosts: A list of trusted host names to validate the + host against. + """ + parts = { + "scheme": environ["wsgi.url_scheme"], + "host": get_host(environ, trusted_hosts), + } + + if not host_only: + parts["root_path"] = environ.get("SCRIPT_NAME", "") + + if not root_only: + parts["path"] = environ.get("PATH_INFO", "") + + if not strip_querystring: + parts["query_string"] = environ.get("QUERY_STRING", "").encode("latin1") + + return _sansio_utils.get_current_url(**parts) + + +def _get_server( + environ: "WSGIEnvironment", +) -> t.Optional[t.Tuple[str, t.Optional[int]]]: + name = environ.get("SERVER_NAME") + + if name is None: + return None + + try: + port: t.Optional[int] = int(environ.get("SERVER_PORT", None)) + except (TypeError, ValueError): + # unix socket + port = None + + return name, port + + +def get_host( + environ: "WSGIEnvironment", trusted_hosts: t.Optional[t.Iterable[str]] = None +) -> str: + """Return the host for the given WSGI environment. + + The ``Host`` header is preferred, then ``SERVER_NAME`` if it's not + set. The returned host will only contain the port if it is different + than the standard port for the protocol. + + Optionally, verify that the host is trusted using + :func:`host_is_trusted` and raise a + :exc:`~werkzeug.exceptions.SecurityError` if it is not. + + :param environ: A WSGI environment dict. + :param trusted_hosts: A list of trusted host names. + + :return: Host, with port if necessary. + :raise ~werkzeug.exceptions.SecurityError: If the host is not + trusted. + """ + return _sansio_utils.get_host( + environ["wsgi.url_scheme"], + environ.get("HTTP_HOST"), + _get_server(environ), + trusted_hosts, + ) + + +def get_content_length(environ: "WSGIEnvironment") -> t.Optional[int]: + """Returns the content length from the WSGI environment as + integer. If it's not available or chunked transfer encoding is used, + ``None`` is returned. + + .. versionadded:: 0.9 + + :param environ: the WSGI environ to fetch the content length from. + """ + if environ.get("HTTP_TRANSFER_ENCODING", "") == "chunked": + return None + + content_length = environ.get("CONTENT_LENGTH") + if content_length is not None: + try: + return max(0, int(content_length)) + except (ValueError, TypeError): + pass + return None + + +def get_input_stream( + environ: "WSGIEnvironment", safe_fallback: bool = True +) -> t.BinaryIO: + """Returns the input stream from the WSGI environment and wraps it + in the most sensible way possible. The stream returned is not the + raw WSGI stream in most cases but one that is safe to read from + without taking into account the content length. + + If content length is not set, the stream will be empty for safety reasons. + If the WSGI server supports chunked or infinite streams, it should set + the ``wsgi.input_terminated`` value in the WSGI environ to indicate that. + + .. versionadded:: 0.9 + + :param environ: the WSGI environ to fetch the stream from. + :param safe_fallback: use an empty stream as a safe fallback when the + content length is not set. Disabling this allows infinite streams, + which can be a denial-of-service risk. + """ + stream = t.cast(t.BinaryIO, environ["wsgi.input"]) + content_length = get_content_length(environ) + + # A wsgi extension that tells us if the input is terminated. In + # that case we return the stream unchanged as we know we can safely + # read it until the end. + if environ.get("wsgi.input_terminated"): + return stream + + # If the request doesn't specify a content length, returning the stream is + # potentially dangerous because it could be infinite, malicious or not. If + # safe_fallback is true, return an empty stream instead for safety. + if content_length is None: + return io.BytesIO() if safe_fallback else stream + + # Otherwise limit the stream to the content length + return t.cast(t.BinaryIO, LimitedStream(stream, content_length)) + + +def get_query_string(environ: "WSGIEnvironment") -> str: + """Returns the ``QUERY_STRING`` from the WSGI environment. This also + takes care of the WSGI decoding dance. The string returned will be + restricted to ASCII characters. + + :param environ: WSGI environment to get the query string from. + + .. versionadded:: 0.9 + """ + qs = environ.get("QUERY_STRING", "").encode("latin1") + # QUERY_STRING really should be ascii safe but some browsers + # will send us some unicode stuff (I am looking at you IE). + # In that case we want to urllib quote it badly. + return url_quote(qs, safe=":&%=+$!*'(),") + + +def get_path_info( + environ: "WSGIEnvironment", charset: str = "utf-8", errors: str = "replace" +) -> str: + """Return the ``PATH_INFO`` from the WSGI environment and decode it + unless ``charset`` is ``None``. + + :param environ: WSGI environment to get the path from. + :param charset: The charset for the path info, or ``None`` if no + decoding should be performed. + :param errors: The decoding error handling. + + .. versionadded:: 0.9 + """ + path = environ.get("PATH_INFO", "").encode("latin1") + return _to_str(path, charset, errors, allow_none_charset=True) # type: ignore + + +def get_script_name( + environ: "WSGIEnvironment", charset: str = "utf-8", errors: str = "replace" +) -> str: + """Return the ``SCRIPT_NAME`` from the WSGI environment and decode + it unless `charset` is set to ``None``. + + :param environ: WSGI environment to get the path from. + :param charset: The charset for the path, or ``None`` if no decoding + should be performed. + :param errors: The decoding error handling. + + .. versionadded:: 0.9 + """ + path = environ.get("SCRIPT_NAME", "").encode("latin1") + return _to_str(path, charset, errors, allow_none_charset=True) # type: ignore + + +def pop_path_info( + environ: "WSGIEnvironment", charset: str = "utf-8", errors: str = "replace" +) -> t.Optional[str]: + """Removes and returns the next segment of `PATH_INFO`, pushing it onto + `SCRIPT_NAME`. Returns `None` if there is nothing left on `PATH_INFO`. + + If the `charset` is set to `None` bytes are returned. + + If there are empty segments (``'/foo//bar``) these are ignored but + properly pushed to the `SCRIPT_NAME`: + + >>> env = {'SCRIPT_NAME': '/foo', 'PATH_INFO': '/a/b'} + >>> pop_path_info(env) + 'a' + >>> env['SCRIPT_NAME'] + '/foo/a' + >>> pop_path_info(env) + 'b' + >>> env['SCRIPT_NAME'] + '/foo/a/b' + + .. versionadded:: 0.5 + + .. versionchanged:: 0.9 + The path is now decoded and a charset and encoding + parameter can be provided. + + :param environ: the WSGI environment that is modified. + :param charset: The ``encoding`` parameter passed to + :func:`bytes.decode`. + :param errors: The ``errors`` paramater passed to + :func:`bytes.decode`. + """ + path = environ.get("PATH_INFO") + if not path: + return None + + script_name = environ.get("SCRIPT_NAME", "") + + # shift multiple leading slashes over + old_path = path + path = path.lstrip("/") + if path != old_path: + script_name += "/" * (len(old_path) - len(path)) + + if "/" not in path: + environ["PATH_INFO"] = "" + environ["SCRIPT_NAME"] = script_name + path + rv = path.encode("latin1") + else: + segment, path = path.split("/", 1) + environ["PATH_INFO"] = f"/{path}" + environ["SCRIPT_NAME"] = script_name + segment + rv = segment.encode("latin1") + + return _to_str(rv, charset, errors, allow_none_charset=True) # type: ignore + + +def peek_path_info( + environ: "WSGIEnvironment", charset: str = "utf-8", errors: str = "replace" +) -> t.Optional[str]: + """Returns the next segment on the `PATH_INFO` or `None` if there + is none. Works like :func:`pop_path_info` without modifying the + environment: + + >>> env = {'SCRIPT_NAME': '/foo', 'PATH_INFO': '/a/b'} + >>> peek_path_info(env) + 'a' + >>> peek_path_info(env) + 'a' + + If the `charset` is set to `None` bytes are returned. + + .. versionadded:: 0.5 + + .. versionchanged:: 0.9 + The path is now decoded and a charset and encoding + parameter can be provided. + + :param environ: the WSGI environment that is checked. + """ + segments = environ.get("PATH_INFO", "").lstrip("/").split("/", 1) + if segments: + return _to_str( # type: ignore + segments[0].encode("latin1"), charset, errors, allow_none_charset=True + ) + return None + + +def extract_path_info( + environ_or_baseurl: t.Union[str, "WSGIEnvironment"], + path_or_url: t.Union[str, _URLTuple], + charset: str = "utf-8", + errors: str = "werkzeug.url_quote", + collapse_http_schemes: bool = True, +) -> t.Optional[str]: + """Extracts the path info from the given URL (or WSGI environment) and + path. The path info returned is a string. The URLs might also be IRIs. + + If the path info could not be determined, `None` is returned. + + Some examples: + + >>> extract_path_info('http://example.com/app', '/app/hello') + '/hello' + >>> extract_path_info('http://example.com/app', + ... 'https://example.com/app/hello') + '/hello' + >>> extract_path_info('http://example.com/app', + ... 'https://example.com/app/hello', + ... collapse_http_schemes=False) is None + True + + Instead of providing a base URL you can also pass a WSGI environment. + + :param environ_or_baseurl: a WSGI environment dict, a base URL or + base IRI. This is the root of the + application. + :param path_or_url: an absolute path from the server root, a + relative path (in which case it's the path info) + or a full URL. + :param charset: the charset for byte data in URLs + :param errors: the error handling on decode + :param collapse_http_schemes: if set to `False` the algorithm does + not assume that http and https on the + same server point to the same + resource. + + .. versionchanged:: 0.15 + The ``errors`` parameter defaults to leaving invalid bytes + quoted instead of replacing them. + + .. versionadded:: 0.6 + """ + + def _normalize_netloc(scheme: str, netloc: str) -> str: + parts = netloc.split("@", 1)[-1].split(":", 1) + port: t.Optional[str] + + if len(parts) == 2: + netloc, port = parts + if (scheme == "http" and port == "80") or ( + scheme == "https" and port == "443" + ): + port = None + else: + netloc = parts[0] + port = None + + if port is not None: + netloc += f":{port}" + + return netloc + + # make sure whatever we are working on is a IRI and parse it + path = uri_to_iri(path_or_url, charset, errors) + if isinstance(environ_or_baseurl, dict): + environ_or_baseurl = get_current_url(environ_or_baseurl, root_only=True) + base_iri = uri_to_iri(environ_or_baseurl, charset, errors) + base_scheme, base_netloc, base_path = url_parse(base_iri)[:3] + cur_scheme, cur_netloc, cur_path = url_parse(url_join(base_iri, path))[:3] + + # normalize the network location + base_netloc = _normalize_netloc(base_scheme, base_netloc) + cur_netloc = _normalize_netloc(cur_scheme, cur_netloc) + + # is that IRI even on a known HTTP scheme? + if collapse_http_schemes: + for scheme in base_scheme, cur_scheme: + if scheme not in ("http", "https"): + return None + else: + if not (base_scheme in ("http", "https") and base_scheme == cur_scheme): + return None + + # are the netlocs compatible? + if base_netloc != cur_netloc: + return None + + # are we below the application path? + base_path = base_path.rstrip("/") + if not cur_path.startswith(base_path): + return None + + return f"/{cur_path[len(base_path) :].lstrip('/')}" + + +class ClosingIterator: + """The WSGI specification requires that all middlewares and gateways + respect the `close` callback of the iterable returned by the application. + Because it is useful to add another close action to a returned iterable + and adding a custom iterable is a boring task this class can be used for + that:: + + return ClosingIterator(app(environ, start_response), [cleanup_session, + cleanup_locals]) + + If there is just one close function it can be passed instead of the list. + + A closing iterator is not needed if the application uses response objects + and finishes the processing if the response is started:: + + try: + return response(environ, start_response) + finally: + cleanup_session() + cleanup_locals() + """ + + def __init__( + self, + iterable: t.Iterable[bytes], + callbacks: t.Optional[ + t.Union[t.Callable[[], None], t.Iterable[t.Callable[[], None]]] + ] = None, + ) -> None: + iterator = iter(iterable) + self._next = t.cast(t.Callable[[], bytes], partial(next, iterator)) + if callbacks is None: + callbacks = [] + elif callable(callbacks): + callbacks = [callbacks] + else: + callbacks = list(callbacks) + iterable_close = getattr(iterable, "close", None) + if iterable_close: + callbacks.insert(0, iterable_close) + self._callbacks = callbacks + + def __iter__(self) -> "ClosingIterator": + return self + + def __next__(self) -> bytes: + return self._next() + + def close(self) -> None: + for callback in self._callbacks: + callback() + + +def wrap_file( + environ: "WSGIEnvironment", file: t.BinaryIO, buffer_size: int = 8192 +) -> t.Iterable[bytes]: + """Wraps a file. This uses the WSGI server's file wrapper if available + or otherwise the generic :class:`FileWrapper`. + + .. versionadded:: 0.5 + + If the file wrapper from the WSGI server is used it's important to not + iterate over it from inside the application but to pass it through + unchanged. If you want to pass out a file wrapper inside a response + object you have to set :attr:`Response.direct_passthrough` to `True`. + + More information about file wrappers are available in :pep:`333`. + + :param file: a :class:`file`-like object with a :meth:`~file.read` method. + :param buffer_size: number of bytes for one iteration. + """ + return environ.get("wsgi.file_wrapper", FileWrapper)( # type: ignore + file, buffer_size + ) + + +class FileWrapper: + """This class can be used to convert a :class:`file`-like object into + an iterable. It yields `buffer_size` blocks until the file is fully + read. + + You should not use this class directly but rather use the + :func:`wrap_file` function that uses the WSGI server's file wrapper + support if it's available. + + .. versionadded:: 0.5 + + If you're using this object together with a :class:`Response` you have + to use the `direct_passthrough` mode. + + :param file: a :class:`file`-like object with a :meth:`~file.read` method. + :param buffer_size: number of bytes for one iteration. + """ + + def __init__(self, file: t.BinaryIO, buffer_size: int = 8192) -> None: + self.file = file + self.buffer_size = buffer_size + + def close(self) -> None: + if hasattr(self.file, "close"): + self.file.close() + + def seekable(self) -> bool: + if hasattr(self.file, "seekable"): + return self.file.seekable() + if hasattr(self.file, "seek"): + return True + return False + + def seek(self, *args: t.Any) -> None: + if hasattr(self.file, "seek"): + self.file.seek(*args) + + def tell(self) -> t.Optional[int]: + if hasattr(self.file, "tell"): + return self.file.tell() + return None + + def __iter__(self) -> "FileWrapper": + return self + + def __next__(self) -> bytes: + data = self.file.read(self.buffer_size) + if data: + return data + raise StopIteration() + + +class _RangeWrapper: + # private for now, but should we make it public in the future ? + + """This class can be used to convert an iterable object into + an iterable that will only yield a piece of the underlying content. + It yields blocks until the underlying stream range is fully read. + The yielded blocks will have a size that can't exceed the original + iterator defined block size, but that can be smaller. + + If you're using this object together with a :class:`Response` you have + to use the `direct_passthrough` mode. + + :param iterable: an iterable object with a :meth:`__next__` method. + :param start_byte: byte from which read will start. + :param byte_range: how many bytes to read. + """ + + def __init__( + self, + iterable: t.Union[t.Iterable[bytes], t.BinaryIO], + start_byte: int = 0, + byte_range: t.Optional[int] = None, + ): + self.iterable = iter(iterable) + self.byte_range = byte_range + self.start_byte = start_byte + self.end_byte = None + + if byte_range is not None: + self.end_byte = start_byte + byte_range + + self.read_length = 0 + self.seekable = ( + hasattr(iterable, "seekable") and iterable.seekable() # type: ignore + ) + self.end_reached = False + + def __iter__(self) -> "_RangeWrapper": + return self + + def _next_chunk(self) -> bytes: + try: + chunk = next(self.iterable) + self.read_length += len(chunk) + return chunk + except StopIteration: + self.end_reached = True + raise + + def _first_iteration(self) -> t.Tuple[t.Optional[bytes], int]: + chunk = None + if self.seekable: + self.iterable.seek(self.start_byte) # type: ignore + self.read_length = self.iterable.tell() # type: ignore + contextual_read_length = self.read_length + else: + while self.read_length <= self.start_byte: + chunk = self._next_chunk() + if chunk is not None: + chunk = chunk[self.start_byte - self.read_length :] + contextual_read_length = self.start_byte + return chunk, contextual_read_length + + def _next(self) -> bytes: + if self.end_reached: + raise StopIteration() + chunk = None + contextual_read_length = self.read_length + if self.read_length == 0: + chunk, contextual_read_length = self._first_iteration() + if chunk is None: + chunk = self._next_chunk() + if self.end_byte is not None and self.read_length >= self.end_byte: + self.end_reached = True + return chunk[: self.end_byte - contextual_read_length] + return chunk + + def __next__(self) -> bytes: + chunk = self._next() + if chunk: + return chunk + self.end_reached = True + raise StopIteration() + + def close(self) -> None: + if hasattr(self.iterable, "close"): + self.iterable.close() # type: ignore + + +def _make_chunk_iter( + stream: t.Union[t.Iterable[bytes], t.BinaryIO], + limit: t.Optional[int], + buffer_size: int, +) -> t.Iterator[bytes]: + """Helper for the line and chunk iter functions.""" + if isinstance(stream, (bytes, bytearray, str)): + raise TypeError( + "Passed a string or byte object instead of true iterator or stream." + ) + if not hasattr(stream, "read"): + for item in stream: + if item: + yield item + return + stream = t.cast(t.BinaryIO, stream) + if not isinstance(stream, LimitedStream) and limit is not None: + stream = t.cast(t.BinaryIO, LimitedStream(stream, limit)) + _read = stream.read + while True: + item = _read(buffer_size) + if not item: + break + yield item + + +def make_line_iter( + stream: t.Union[t.Iterable[bytes], t.BinaryIO], + limit: t.Optional[int] = None, + buffer_size: int = 10 * 1024, + cap_at_buffer: bool = False, +) -> t.Iterator[bytes]: + """Safely iterates line-based over an input stream. If the input stream + is not a :class:`LimitedStream` the `limit` parameter is mandatory. + + This uses the stream's :meth:`~file.read` method internally as opposite + to the :meth:`~file.readline` method that is unsafe and can only be used + in violation of the WSGI specification. The same problem applies to the + `__iter__` function of the input stream which calls :meth:`~file.readline` + without arguments. + + If you need line-by-line processing it's strongly recommended to iterate + over the input stream using this helper function. + + .. versionchanged:: 0.8 + This function now ensures that the limit was reached. + + .. versionadded:: 0.9 + added support for iterators as input stream. + + .. versionadded:: 0.11.10 + added support for the `cap_at_buffer` parameter. + + :param stream: the stream or iterate to iterate over. + :param limit: the limit in bytes for the stream. (Usually + content length. Not necessary if the `stream` + is a :class:`LimitedStream`. + :param buffer_size: The optional buffer size. + :param cap_at_buffer: if this is set chunks are split if they are longer + than the buffer size. Internally this is implemented + that the buffer size might be exhausted by a factor + of two however. + """ + _iter = _make_chunk_iter(stream, limit, buffer_size) + + first_item = next(_iter, "") + if not first_item: + return + + s = _make_encode_wrapper(first_item) + empty = t.cast(bytes, s("")) + cr = t.cast(bytes, s("\r")) + lf = t.cast(bytes, s("\n")) + crlf = t.cast(bytes, s("\r\n")) + + _iter = t.cast(t.Iterator[bytes], chain((first_item,), _iter)) + + def _iter_basic_lines() -> t.Iterator[bytes]: + _join = empty.join + buffer: t.List[bytes] = [] + while True: + new_data = next(_iter, "") + if not new_data: + break + new_buf: t.List[bytes] = [] + buf_size = 0 + for item in t.cast( + t.Iterator[bytes], chain(buffer, new_data.splitlines(True)) + ): + new_buf.append(item) + buf_size += len(item) + if item and item[-1:] in crlf: + yield _join(new_buf) + new_buf = [] + elif cap_at_buffer and buf_size >= buffer_size: + rv = _join(new_buf) + while len(rv) >= buffer_size: + yield rv[:buffer_size] + rv = rv[buffer_size:] + new_buf = [rv] + buffer = new_buf + if buffer: + yield _join(buffer) + + # This hackery is necessary to merge 'foo\r' and '\n' into one item + # of 'foo\r\n' if we were unlucky and we hit a chunk boundary. + previous = empty + for item in _iter_basic_lines(): + if item == lf and previous[-1:] == cr: + previous += item + item = empty + if previous: + yield previous + previous = item + if previous: + yield previous + + +def make_chunk_iter( + stream: t.Union[t.Iterable[bytes], t.BinaryIO], + separator: bytes, + limit: t.Optional[int] = None, + buffer_size: int = 10 * 1024, + cap_at_buffer: bool = False, +) -> t.Iterator[bytes]: + """Works like :func:`make_line_iter` but accepts a separator + which divides chunks. If you want newline based processing + you should use :func:`make_line_iter` instead as it + supports arbitrary newline markers. + + .. versionadded:: 0.8 + + .. versionadded:: 0.9 + added support for iterators as input stream. + + .. versionadded:: 0.11.10 + added support for the `cap_at_buffer` parameter. + + :param stream: the stream or iterate to iterate over. + :param separator: the separator that divides chunks. + :param limit: the limit in bytes for the stream. (Usually + content length. Not necessary if the `stream` + is otherwise already limited). + :param buffer_size: The optional buffer size. + :param cap_at_buffer: if this is set chunks are split if they are longer + than the buffer size. Internally this is implemented + that the buffer size might be exhausted by a factor + of two however. + """ + _iter = _make_chunk_iter(stream, limit, buffer_size) + + first_item = next(_iter, b"") + if not first_item: + return + + _iter = t.cast(t.Iterator[bytes], chain((first_item,), _iter)) + if isinstance(first_item, str): + separator = _to_str(separator) + _split = re.compile(f"({re.escape(separator)})").split + _join = "".join + else: + separator = _to_bytes(separator) + _split = re.compile(b"(" + re.escape(separator) + b")").split + _join = b"".join + + buffer: t.List[bytes] = [] + while True: + new_data = next(_iter, b"") + if not new_data: + break + chunks = _split(new_data) + new_buf: t.List[bytes] = [] + buf_size = 0 + for item in chain(buffer, chunks): + if item == separator: + yield _join(new_buf) + new_buf = [] + buf_size = 0 + else: + buf_size += len(item) + new_buf.append(item) + + if cap_at_buffer and buf_size >= buffer_size: + rv = _join(new_buf) + while len(rv) >= buffer_size: + yield rv[:buffer_size] + rv = rv[buffer_size:] + new_buf = [rv] + buf_size = len(rv) + + buffer = new_buf + if buffer: + yield _join(buffer) + + +class LimitedStream(io.IOBase): + """Wraps a stream so that it doesn't read more than n bytes. If the + stream is exhausted and the caller tries to get more bytes from it + :func:`on_exhausted` is called which by default returns an empty + string. The return value of that function is forwarded + to the reader function. So if it returns an empty string + :meth:`read` will return an empty string as well. + + The limit however must never be higher than what the stream can + output. Otherwise :meth:`readlines` will try to read past the + limit. + + .. admonition:: Note on WSGI compliance + + calls to :meth:`readline` and :meth:`readlines` are not + WSGI compliant because it passes a size argument to the + readline methods. Unfortunately the WSGI PEP is not safely + implementable without a size argument to :meth:`readline` + because there is no EOF marker in the stream. As a result + of that the use of :meth:`readline` is discouraged. + + For the same reason iterating over the :class:`LimitedStream` + is not portable. It internally calls :meth:`readline`. + + We strongly suggest using :meth:`read` only or using the + :func:`make_line_iter` which safely iterates line-based + over a WSGI input stream. + + :param stream: the stream to wrap. + :param limit: the limit for the stream, must not be longer than + what the string can provide if the stream does not + end with `EOF` (like `wsgi.input`) + """ + + def __init__(self, stream: t.BinaryIO, limit: int) -> None: + self._read = stream.read + self._readline = stream.readline + self._pos = 0 + self.limit = limit + + def __iter__(self) -> "LimitedStream": + return self + + @property + def is_exhausted(self) -> bool: + """If the stream is exhausted this attribute is `True`.""" + return self._pos >= self.limit + + def on_exhausted(self) -> bytes: + """This is called when the stream tries to read past the limit. + The return value of this function is returned from the reading + function. + """ + # Read null bytes from the stream so that we get the + # correct end of stream marker. + return self._read(0) + + def on_disconnect(self) -> bytes: + """What should happen if a disconnect is detected? The return + value of this function is returned from read functions in case + the client went away. By default a + :exc:`~werkzeug.exceptions.ClientDisconnected` exception is raised. + """ + from .exceptions import ClientDisconnected + + raise ClientDisconnected() + + def exhaust(self, chunk_size: int = 1024 * 64) -> None: + """Exhaust the stream. This consumes all the data left until the + limit is reached. + + :param chunk_size: the size for a chunk. It will read the chunk + until the stream is exhausted and throw away + the results. + """ + to_read = self.limit - self._pos + chunk = chunk_size + while to_read > 0: + chunk = min(to_read, chunk) + self.read(chunk) + to_read -= chunk + + def read(self, size: t.Optional[int] = None) -> bytes: + """Read `size` bytes or if size is not provided everything is read. + + :param size: the number of bytes read. + """ + if self._pos >= self.limit: + return self.on_exhausted() + if size is None or size == -1: # -1 is for consistence with file + size = self.limit + to_read = min(self.limit - self._pos, size) + try: + read = self._read(to_read) + except (OSError, ValueError): + return self.on_disconnect() + if to_read and len(read) != to_read: + return self.on_disconnect() + self._pos += len(read) + return read + + def readline(self, size: t.Optional[int] = None) -> bytes: + """Reads one line from the stream.""" + if self._pos >= self.limit: + return self.on_exhausted() + if size is None: + size = self.limit - self._pos + else: + size = min(size, self.limit - self._pos) + try: + line = self._readline(size) + except (ValueError, OSError): + return self.on_disconnect() + if size and not line: + return self.on_disconnect() + self._pos += len(line) + return line + + def readlines(self, size: t.Optional[int] = None) -> t.List[bytes]: + """Reads a file into a list of strings. It calls :meth:`readline` + until the file is read to the end. It does support the optional + `size` argument if the underlying stream supports it for + `readline`. + """ + last_pos = self._pos + result = [] + if size is not None: + end = min(self.limit, last_pos + size) + else: + end = self.limit + while True: + if size is not None: + size -= last_pos - self._pos + if self._pos >= end: + break + result.append(self.readline(size)) + if size is not None: + last_pos = self._pos + return result + + def tell(self) -> int: + """Returns the position of the stream. + + .. versionadded:: 0.9 + """ + return self._pos + + def __next__(self) -> bytes: + line = self.readline() + if not line: + raise StopIteration() + return line + + def readable(self) -> bool: + return True diff --git a/venv/Scripts/Activate.ps1 b/venv/Scripts/Activate.ps1 new file mode 100644 index 0000000..8571c79 --- /dev/null +++ b/venv/Scripts/Activate.ps1 @@ -0,0 +1,398 @@ +<# +.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 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 virutal 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 + } +} + +# 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 +# MIIcvgYJKoZIhvcNAQcCoIIcrzCCHKsCAQExDzANBglghkgBZQMEAgEFADB5Bgor +# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG +# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCAwnDYwEHaCQq0n +# 8NAvsN7H7BO7/48rXCNwrg891FS5vaCCC38wggUwMIIEGKADAgECAhAECRgbX9W7 +# ZnVTQ7VvlVAIMA0GCSqGSIb3DQEBCwUAMGUxCzAJBgNVBAYTAlVTMRUwEwYDVQQK +# EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xJDAiBgNV +# BAMTG0RpZ2lDZXJ0IEFzc3VyZWQgSUQgUm9vdCBDQTAeFw0xMzEwMjIxMjAwMDBa +# Fw0yODEwMjIxMjAwMDBaMHIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2Vy +# dCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xMTAvBgNVBAMTKERpZ2lD +# ZXJ0IFNIQTIgQXNzdXJlZCBJRCBDb2RlIFNpZ25pbmcgQ0EwggEiMA0GCSqGSIb3 +# DQEBAQUAA4IBDwAwggEKAoIBAQD407Mcfw4Rr2d3B9MLMUkZz9D7RZmxOttE9X/l +# qJ3bMtdx6nadBS63j/qSQ8Cl+YnUNxnXtqrwnIal2CWsDnkoOn7p0WfTxvspJ8fT +# eyOU5JEjlpB3gvmhhCNmElQzUHSxKCa7JGnCwlLyFGeKiUXULaGj6YgsIJWuHEqH +# CN8M9eJNYBi+qsSyrnAxZjNxPqxwoqvOf+l8y5Kh5TsxHM/q8grkV7tKtel05iv+ +# bMt+dDk2DZDv5LVOpKnqagqrhPOsZ061xPeM0SAlI+sIZD5SlsHyDxL0xY4PwaLo +# LFH3c7y9hbFig3NBggfkOItqcyDQD2RzPJ6fpjOp/RnfJZPRAgMBAAGjggHNMIIB +# yTASBgNVHRMBAf8ECDAGAQH/AgEAMA4GA1UdDwEB/wQEAwIBhjATBgNVHSUEDDAK +# BggrBgEFBQcDAzB5BggrBgEFBQcBAQRtMGswJAYIKwYBBQUHMAGGGGh0dHA6Ly9v +# Y3NwLmRpZ2ljZXJ0LmNvbTBDBggrBgEFBQcwAoY3aHR0cDovL2NhY2VydHMuZGln +# aWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9vdENBLmNydDCBgQYDVR0fBHow +# eDA6oDigNoY0aHR0cDovL2NybDQuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJl +# ZElEUm9vdENBLmNybDA6oDigNoY0aHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0Rp +# Z2lDZXJ0QXNzdXJlZElEUm9vdENBLmNybDBPBgNVHSAESDBGMDgGCmCGSAGG/WwA +# AgQwKjAoBggrBgEFBQcCARYcaHR0cHM6Ly93d3cuZGlnaWNlcnQuY29tL0NQUzAK +# BghghkgBhv1sAzAdBgNVHQ4EFgQUWsS5eyoKo6XqcQPAYPkt9mV1DlgwHwYDVR0j +# BBgwFoAUReuir/SSy4IxLVGLp6chnfNtyA8wDQYJKoZIhvcNAQELBQADggEBAD7s +# DVoks/Mi0RXILHwlKXaoHV0cLToaxO8wYdd+C2D9wz0PxK+L/e8q3yBVN7Dh9tGS +# dQ9RtG6ljlriXiSBThCk7j9xjmMOE0ut119EefM2FAaK95xGTlz/kLEbBw6RFfu6 +# r7VRwo0kriTGxycqoSkoGjpxKAI8LpGjwCUR4pwUR6F6aGivm6dcIFzZcbEMj7uo +# +MUSaJ/PQMtARKUT8OZkDCUIQjKyNookAv4vcn4c10lFluhZHen6dGRrsutmQ9qz +# sIzV6Q3d9gEgzpkxYz0IGhizgZtPxpMQBvwHgfqL2vmCSfdibqFT+hKUGIUukpHq +# aGxEMrJmoecYpJpkUe8wggZHMIIFL6ADAgECAhADPtXtoGXRuMkd/PkqbJvYMA0G +# CSqGSIb3DQEBCwUAMHIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ +# bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xMTAvBgNVBAMTKERpZ2lDZXJ0 +# IFNIQTIgQXNzdXJlZCBJRCBDb2RlIFNpZ25pbmcgQ0EwHhcNMTgxMjE4MDAwMDAw +# WhcNMjExMjIyMTIwMDAwWjCBgzELMAkGA1UEBhMCVVMxFjAUBgNVBAgTDU5ldyBI +# YW1wc2hpcmUxEjAQBgNVBAcTCVdvbGZlYm9ybzEjMCEGA1UEChMaUHl0aG9uIFNv +# ZnR3YXJlIEZvdW5kYXRpb24xIzAhBgNVBAMTGlB5dGhvbiBTb2Z0d2FyZSBGb3Vu +# ZGF0aW9uMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAqr2kS7J1uW7o +# JRxlsdrETAjKarfoH5TI8PWST6Yb2xPooP7vHT4iaVXyL5Lze1f53Jw67Sp+u524 +# fJXf30qHViEWxumy2RWG0nciU2d+mMqzjlaAWSZNF0u4RcvyDJokEV0RUOqI5CG5 +# zPI3W9uQ6LiUk3HCYW6kpH177A5T3pw/Po8O8KErJGn1anaqtIICq99ySxrMad/2 +# hPMBRf6Ndah7f7HPn1gkSSTAoejyuqF5h+B0qI4+JK5+VLvz659VTbAWJsYakkxZ +# xVWYpFv4KeQSSwoo0DzMvmERsTzNvVBMWhu9OriJNg+QfFmf96zVTu93cZ+r7xMp +# bXyfIOGKhHMaRuZ8ihuWIx3gI9WHDFX6fBKR8+HlhdkaiBEWIsXRoy+EQUyK7zUs +# +FqOo2sRYttbs8MTF9YDKFZwyPjn9Wn+gLGd5NUEVyNvD9QVGBEtN7vx87bduJUB +# 8F4DylEsMtZTfjw/au6AmOnmneK5UcqSJuwRyZaGNk7y3qj06utx+HTTqHgi975U +# pxfyrwAqkovoZEWBVSpvku8PVhkBXcLmNe6MEHlFiaMoiADAeKmX5RFRkN+VrmYG +# Tg4zajxfdHeIY8TvLf48tTfmnQJd98geJQv/01NUy/FxuwqAuTkaez5Nl1LxP0Cp +# THhghzO4FRD4itT2wqTh4jpojw9QZnsCAwEAAaOCAcUwggHBMB8GA1UdIwQYMBaA +# FFrEuXsqCqOl6nEDwGD5LfZldQ5YMB0GA1UdDgQWBBT8Kr9+1L6s84KcpM97IgE7 +# uI8H8jAOBgNVHQ8BAf8EBAMCB4AwEwYDVR0lBAwwCgYIKwYBBQUHAwMwdwYDVR0f +# BHAwbjA1oDOgMYYvaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL3NoYTItYXNzdXJl +# ZC1jcy1nMS5jcmwwNaAzoDGGL2h0dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9zaGEy +# LWFzc3VyZWQtY3MtZzEuY3JsMEwGA1UdIARFMEMwNwYJYIZIAYb9bAMBMCowKAYI +# KwYBBQUHAgEWHGh0dHBzOi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMwCAYGZ4EMAQQB +# MIGEBggrBgEFBQcBAQR4MHYwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2lj +# ZXJ0LmNvbTBOBggrBgEFBQcwAoZCaHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29t +# L0RpZ2lDZXJ0U0hBMkFzc3VyZWRJRENvZGVTaWduaW5nQ0EuY3J0MAwGA1UdEwEB +# /wQCMAAwDQYJKoZIhvcNAQELBQADggEBAEt1oS21X0axiafPjyY+vlYqjWKuUu/Y +# FuYWIEq6iRRaFabNDhj9RBFQF/aJiE5msrQEOfAD6/6gVSH91lZWBqg6NEeG9T9S +# XbiAPvJ9CEWFsdkXUrjbWhvCnuZ7kqUuU5BAumI1QRbpYgZL3UA+iZXkmjbGh1ln +# 8rUhWIxbBYL4Sg2nqpB44p7CUFYkPj/MbwU2gvBV2pXjj5WaskoZtsACMv5g42BN +# oVLoRAi+ev6s07POt+JtHRIm87lTyuc8wh0swTPUwksKbLU1Zdj9CpqtzXnuVE0w +# 50exJvRSK3Vt4g+0vigpI3qPmDdpkf9+4Mvy0XMNcqrthw20R+PkIlMxghCVMIIQ +# kQIBATCBhjByMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkw +# FwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMTEwLwYDVQQDEyhEaWdpQ2VydCBTSEEy +# IEFzc3VyZWQgSUQgQ29kZSBTaWduaW5nIENBAhADPtXtoGXRuMkd/PkqbJvYMA0G +# CWCGSAFlAwQCAQUAoIGYMBkGCSqGSIb3DQEJAzEMBgorBgEEAYI3AgEEMBwGCisG +# AQQBgjcCAQsxDjAMBgorBgEEAYI3AgEVMCwGCisGAQQBgjcCAQwxHjAcoBqAGABQ +# AHkAdABoAG8AbgAgADMALgA5AC4ANTAvBgkqhkiG9w0BCQQxIgQgBrni4mcRv7sM +# JHsxpROjRopOz2wuQVrJnn+lD7X7y+gwDQYJKoZIhvcNAQEBBQAEggIAfa/HLI8D +# GRTQjHbg8SUWGNLXsp08sG0LmtlbsGjTnpfxO4cbVP29YkZ44QaZvGfRczR2MEQc +# BsQ7HwbkKpbGrcADViQGB0THzO6jbr2SPq6weqO0acBthegPH/ZhVzDm1GNxScbZ +# +NRAPq9NDbknSBMKcEOOhqoUcBOfJSYwxV1+aNxS2LJkUqjzWpmBd7PC9YQYDbYb +# e93ClwH8gDujsFg1mksvNz+0jBDIX+hHKXECe8rEx5x26rxHsx1QbofD+L7xrvwh +# 0cv13hfqitPf60Wqa9l+r1v8KpOx/6w4lXA73UY6cWMSg13Wd/vvY/rOATRJB1FL +# /Zl1yRs5qsZJI8igX1WVg1raYX1kpE8gzh8EimOxoN545lxDRzb5LJxZ93zpA8jd +# 5pEj6ggyIct1EEYPAXqJ2IfKGBzdoLAFhXvre/2OgCgRgWkdWLLyq0BTmNeZqXzL +# /d2wF799esEuqAOrvq6Vb1lY8lR3OkYS6vhm1yznsSUH+If3asAVK4jlPNl0Xo6z +# qJA1RUvS8DUtofxOuzJvAGunLgjsdsoQNbLBmJOFx8wqG0R8j5tex+nG0k3z9kRK +# ZbxUbDMGMKFht669aaETctJL7+EMkwXgx5CIrycXUNmsdErVy7hdKQZ/cltL9L2f +# Gg02MnhsLFhmr87wVQ+/sWNLn9IAZ8mLLx6hgg1EMIINQAYKKwYBBAGCNwMDATGC +# DTAwgg0sBgkqhkiG9w0BBwKggg0dMIINGQIBAzEPMA0GCWCGSAFlAwQCAQUAMHcG +# CyqGSIb3DQEJEAEEoGgEZjBkAgEBBglghkgBhv1sBwEwMTANBglghkgBZQMEAgEF +# AAQg/qSbYAGCU5MoZyHzXgphXixjAtLi3mp3rPftbFQ7uQECEAnu03VwGsvEEBsL +# Mi/0TP0YDzIwMjEwNTAzMTczMzMxWqCCCjcwggT+MIID5qADAgECAhANQkrgvjqI +# /2BAIc4UAPDdMA0GCSqGSIb3DQEBCwUAMHIxCzAJBgNVBAYTAlVTMRUwEwYDVQQK +# EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xMTAvBgNV +# BAMTKERpZ2lDZXJ0IFNIQTIgQXNzdXJlZCBJRCBUaW1lc3RhbXBpbmcgQ0EwHhcN +# MjEwMTAxMDAwMDAwWhcNMzEwMTA2MDAwMDAwWjBIMQswCQYDVQQGEwJVUzEXMBUG +# A1UEChMORGlnaUNlcnQsIEluYy4xIDAeBgNVBAMTF0RpZ2lDZXJ0IFRpbWVzdGFt +# cCAyMDIxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwuZhhGfFivUN +# CKRFymNrUdc6EUK9CnV1TZS0DFC1JhD+HchvkWsMlucaXEjvROW/m2HNFZFiWrj/ +# ZwucY/02aoH6KfjdK3CF3gIY83htvH35x20JPb5qdofpir34hF0edsnkxnZ2OlPR +# 0dNaNo/Go+EvGzq3YdZz7E5tM4p8XUUtS7FQ5kE6N1aG3JMjjfdQJehk5t3Tjy9X +# tYcg6w6OLNUj2vRNeEbjA4MxKUpcDDGKSoyIxfcwWvkUrxVfbENJCf0mI1P2jWPo +# GqtbsR0wwptpgrTb/FZUvB+hh6u+elsKIC9LCcmVp42y+tZji06lchzun3oBc/gZ +# 1v4NSYS9AQIDAQABo4IBuDCCAbQwDgYDVR0PAQH/BAQDAgeAMAwGA1UdEwEB/wQC +# MAAwFgYDVR0lAQH/BAwwCgYIKwYBBQUHAwgwQQYDVR0gBDowODA2BglghkgBhv1s +# BwEwKTAnBggrBgEFBQcCARYbaHR0cDovL3d3dy5kaWdpY2VydC5jb20vQ1BTMB8G +# A1UdIwQYMBaAFPS24SAd/imu0uRhpbKiJbLIFzVuMB0GA1UdDgQWBBQ2RIaOpLqw +# Zr68KC0dRDbd42p6vDBxBgNVHR8EajBoMDKgMKAuhixodHRwOi8vY3JsMy5kaWdp +# Y2VydC5jb20vc2hhMi1hc3N1cmVkLXRzLmNybDAyoDCgLoYsaHR0cDovL2NybDQu +# ZGlnaWNlcnQuY29tL3NoYTItYXNzdXJlZC10cy5jcmwwgYUGCCsGAQUFBwEBBHkw +# dzAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tME8GCCsGAQUF +# BzAChkNodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRTSEEyQXNz +# dXJlZElEVGltZXN0YW1waW5nQ0EuY3J0MA0GCSqGSIb3DQEBCwUAA4IBAQBIHNy1 +# 6ZojvOca5yAOjmdG/UJyUXQKI0ejq5LSJcRwWb4UoOUngaVNFBUZB3nw0QTDhtk7 +# vf5EAmZN7WmkD/a4cM9i6PVRSnh5Nnont/PnUp+Tp+1DnnvntN1BIon7h6JGA078 +# 9P63ZHdjXyNSaYOC+hpT7ZDMjaEXcw3082U5cEvznNZ6e9oMvD0y0BvL9WH8dQgA +# dryBDvjA4VzPxBFy5xtkSdgimnUVQvUtMjiB2vRgorq0Uvtc4GEkJU+y38kpqHND +# Udq9Y9YfW5v3LhtPEx33Sg1xfpe39D+E68Hjo0mh+s6nv1bPull2YYlffqe0jmd4 +# +TaY4cso2luHpoovMIIFMTCCBBmgAwIBAgIQCqEl1tYyG35B5AXaNpfCFTANBgkq +# hkiG9w0BAQsFADBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5j +# MRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBB +# c3N1cmVkIElEIFJvb3QgQ0EwHhcNMTYwMTA3MTIwMDAwWhcNMzEwMTA3MTIwMDAw +# WjByMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQL +# ExB3d3cuZGlnaWNlcnQuY29tMTEwLwYDVQQDEyhEaWdpQ2VydCBTSEEyIEFzc3Vy +# ZWQgSUQgVGltZXN0YW1waW5nIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB +# CgKCAQEAvdAy7kvNj3/dqbqCmcU5VChXtiNKxA4HRTNREH3Q+X1NaH7ntqD0jbOI +# 5Je/YyGQmL8TvFfTw+F+CNZqFAA49y4eO+7MpvYyWf5fZT/gm+vjRkcGGlV+Cyd+ +# wKL1oODeIj8O/36V+/OjuiI+GKwR5PCZA207hXwJ0+5dyJoLVOOoCXFr4M8iEA91 +# z3FyTgqt30A6XLdR4aF5FMZNJCMwXbzsPGBqrC8HzP3w6kfZiFBe/WZuVmEnKYmE +# UeaC50ZQ/ZQqLKfkdT66mA+Ef58xFNat1fJky3seBdCEGXIX8RcG7z3N1k3vBkL9 +# olMqT4UdxB08r8/arBD13ays6Vb/kwIDAQABo4IBzjCCAcowHQYDVR0OBBYEFPS2 +# 4SAd/imu0uRhpbKiJbLIFzVuMB8GA1UdIwQYMBaAFEXroq/0ksuCMS1Ri6enIZ3z +# bcgPMBIGA1UdEwEB/wQIMAYBAf8CAQAwDgYDVR0PAQH/BAQDAgGGMBMGA1UdJQQM +# MAoGCCsGAQUFBwMIMHkGCCsGAQUFBwEBBG0wazAkBggrBgEFBQcwAYYYaHR0cDov +# L29jc3AuZGlnaWNlcnQuY29tMEMGCCsGAQUFBzAChjdodHRwOi8vY2FjZXJ0cy5k +# aWdpY2VydC5jb20vRGlnaUNlcnRBc3N1cmVkSURSb290Q0EuY3J0MIGBBgNVHR8E +# ejB4MDqgOKA2hjRodHRwOi8vY3JsNC5kaWdpY2VydC5jb20vRGlnaUNlcnRBc3N1 +# cmVkSURSb290Q0EuY3JsMDqgOKA2hjRodHRwOi8vY3JsMy5kaWdpY2VydC5jb20v +# RGlnaUNlcnRBc3N1cmVkSURSb290Q0EuY3JsMFAGA1UdIARJMEcwOAYKYIZIAYb9 +# bAACBDAqMCgGCCsGAQUFBwIBFhxodHRwczovL3d3dy5kaWdpY2VydC5jb20vQ1BT +# MAsGCWCGSAGG/WwHATANBgkqhkiG9w0BAQsFAAOCAQEAcZUS6VGHVmnN793afKpj +# erN4zwY3QITvS4S/ys8DAv3Fp8MOIEIsr3fzKx8MIVoqtwU0HWqumfgnoma/Capg +# 33akOpMP+LLR2HwZYuhegiUexLoceywh4tZbLBQ1QwRostt1AuByx5jWPGTlH0gQ +# GF+JOGFNYkYkh2OMkVIsrymJ5Xgf1gsUpYDXEkdws3XVk4WTfraSZ/tTYYmo9WuW +# wPRYaQ18yAGxuSh1t5ljhSKMYcp5lH5Z/IwP42+1ASa2bKXuh1Eh5Fhgm7oMLStt +# osR+u8QlK0cCCHxJrhO24XxCQijGGFbPQTS2Zl22dHv1VjMiLyI2skuiSpXY9aaO +# UjGCAk0wggJJAgEBMIGGMHIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2Vy +# dCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xMTAvBgNVBAMTKERpZ2lD +# ZXJ0IFNIQTIgQXNzdXJlZCBJRCBUaW1lc3RhbXBpbmcgQ0ECEA1CSuC+Ooj/YEAh +# zhQA8N0wDQYJYIZIAWUDBAIBBQCggZgwGgYJKoZIhvcNAQkDMQ0GCyqGSIb3DQEJ +# EAEEMBwGCSqGSIb3DQEJBTEPFw0yMTA1MDMxNzMzMzFaMCsGCyqGSIb3DQEJEAIM +# MRwwGjAYMBYEFOHXgqjhkb7va8oWkbWqtJSmJJvzMC8GCSqGSIb3DQEJBDEiBCAQ +# oGsafDstXll3ot5bx/8PLQS720KhIbACU4IesmNHujANBgkqhkiG9w0BAQEFAASC +# AQCICALSl0rnE1Sf/HFyIQUZGwv0NtTNht9v/Qtr3eo7J+9QhOLytlvBnlXai6jV +# qcX/maiZS8yIwwOXh1t0uFYKKO4uomq7UWTD0AuWDLDD0NkHClu756OObY7hH2YF +# 6H4oHbrnPj+tw+0lFuo6FxAQJN/qAWg7hcEUSpnfqEbMuHTFCV65IPcdMPHilnH3 +# EvTmeMvUCzqz4mSYuQsr7pnCc+EHuBXzZadWPwzrtMIyrtpFVA2hhhkN97j1t+BW +# D4MZTevHrWOmFLRrFk6x9AcNt29fnSmLUImV2g8C/81fU7CPBYLn/A3GAzcwpWjc +# Evh1My2GZkDQltZ1ceUb/Qfs +# SIG # End signature block diff --git a/venv/Scripts/activate b/venv/Scripts/activate new file mode 100644 index 0000000..fd1c2b7 --- /dev/null +++ b/venv/Scripts/activate @@ -0,0 +1,66 @@ +# 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 + + # This should detect bash and zsh, which have a hash command that must + # be called to get it to forget past commands. Without forgetting + # past commands the $PATH changes we made may not be respected + if [ -n "${BASH:-}" -o -n "${ZSH_VERSION:-}" ] ; then + hash -r 2> /dev/null + fi + + if [ -n "${_OLD_VIRTUAL_PS1:-}" ] ; then + PS1="${_OLD_VIRTUAL_PS1:-}" + export PS1 + unset _OLD_VIRTUAL_PS1 + fi + + unset VIRTUAL_ENV + if [ ! "${1:-}" = "nondestructive" ] ; then + # Self destruct! + unset -f deactivate + fi +} + +# unset irrelevant variables +deactivate nondestructive + +VIRTUAL_ENV="C:\Projects\OpenHome\venv" +export VIRTUAL_ENV + +_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 +fi + +# This should detect bash and zsh, which have a hash command that must +# be called to get it to forget past commands. Without forgetting +# past commands the $PATH changes we made may not be respected +if [ -n "${BASH:-}" -o -n "${ZSH_VERSION:-}" ] ; then + hash -r 2> /dev/null +fi diff --git a/venv/Scripts/activate.bat b/venv/Scripts/activate.bat new file mode 100644 index 0000000..8953dbb --- /dev/null +++ b/venv/Scripts/activate.bat @@ -0,0 +1,33 @@ +@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=C:\Projects\OpenHome\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% + +:END +if defined _OLD_CODEPAGE ( + "%SystemRoot%\System32\chcp.com" %_OLD_CODEPAGE% > nul + set _OLD_CODEPAGE= +) diff --git a/venv/Scripts/deactivate.bat b/venv/Scripts/deactivate.bat new file mode 100644 index 0000000..1205c61 --- /dev/null +++ b/venv/Scripts/deactivate.bat @@ -0,0 +1,21 @@ +@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= + +:END diff --git a/venv/Scripts/flask.exe b/venv/Scripts/flask.exe new file mode 100644 index 0000000000000000000000000000000000000000..298253d92d2da253b6a286bae35634863322e61b GIT binary patch literal 106342 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 literal 0 HcmV?d00001 diff --git a/venv/Scripts/pip.exe b/venv/Scripts/pip.exe new file mode 100644 index 0000000000000000000000000000000000000000..1c125e6ac125cdf86dbf4189bc56d16ce662432e GIT binary patch literal 106355 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
    T}2U-+ymkfBp40ufP8KlOKKbQHjR-4j9q-FX>xb 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 literal 0 HcmV?d00001 diff --git a/venv/Scripts/pip3.9.exe b/venv/Scripts/pip3.9.exe new file mode 100644 index 0000000000000000000000000000000000000000..1c125e6ac125cdf86dbf4189bc56d16ce662432e GIT binary patch literal 106355 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
    T}2U-+ymkfBp40ufP8KlOKKbQHjR-4j9q-FX>xb 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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..1c125e6ac125cdf86dbf4189bc56d16ce662432e GIT binary patch literal 106355 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
    T}2U-+ymkfBp40ufP8KlOKKbQHjR-4j9q-FX>xb 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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..e6ed79f9d12c94d28ad4e1310b8b3f6303dcba77 GIT binary patch literal 106355 zcmeFadwf*owfH^BWXJ#sdr(FK3XTvIjhE0=O&rh+%*Y;@2r6h)P&62^qEeUtotB*9DH^Zx#M z|9Sc7?EO6ZxvpnD>sf0(YpvAWu-4^vxm*SOZ`&?cD^K}Xt$zRUkHzN^r*9bH`tPCJ z&uGnyZ9ik~;yacHmM**J_GP!+6{x%A?z``a2X4JBuq<(R;EuZk;n~*&?z(5uZRZyk z4=c?!{p(8>-uvE-BPQkkkNbZ(>0Q!CxBPa}7WMqir0=We+DRYs{BYu$SlZ0ZU{1v4TJ-H9t_RLKHb0klz%{`&Jb#$WwV#~-baJ~c z;^|ZG)p_!e_k5SjBR~AhJzYN104>p+5B#bdbCt4nDd{wldq~}Ej=Z`aJ3r4gRlVf7 zelv%cwRx`7hD%27U%qPz11NWspUe7RJ@Z_x&QQO!^!f4IR>t}A;rsl^fMo8n_=Elh zT&{)ZFI#j={1%tXx>!CikV+m0}DYHtETx(sFWQ<}(`v&e7D2l5lFe zt*2t8<$5w)8nAvF097haqD(4GUP@o6r~Lbh@?4f(>~gJ_b+P?xKXSRYb!^-A6@Ah& zeO3(WlbnChXX8Tp+%)pUKK~$n&KT3*=V{qK_2m3gubzyT`mWQB{Q=YSU(=bJd000; zuGkwhyJM;8N42MRMa^!j`DE#~OK)zAk25`{Dz_sP%!_K_m!o!jw2Z>xs-u}*x*0F6 z)XfgvoX?z%O@W&`w)OW@q9<3C2Iht4hUSH?4PB?3`{}njW~O5)&shu-_$<9z9yOJb zinn9Q+bXSv?1_-Mt+|bFMHJC~&~EKIZri#^8Q_{^} zn(dILAB|MBnJ-!C(`61)ZB=RBQw6|3WWE$Nw};IwmZyXzG`H*KF6&*@`W~6;>5OEb z^fF35%=;a!*V)msW4ilD`a3M&laPx7bF1}J&FPm;AqYpB8Qp<_e!rRRH*9u9&6jj@ zhxMb;QhtXtx{}_QAG5o1I5TIS<{s_gc5DAJ=1A|l`CO<~=!f;<?!jGBax;eL5W#I~_?c-=>$4wl3nT4|+}_JK?D@ z-^tWVYpEY8`0ZvM&jUZ}_g`r7*;8^YJ~?dg(5KMom8tnNFoSzu5c> z8EHN-wnFwo=|YzDxuI;lTV=7y-;(jDPE|YBS{XHaWKQqv`l)UD#LeuL@|$lOm}~#O ztk%s}bn}qyPtm?^OmuZZP2@CtN~WL&(iJne>gG%A?r<_D*d8kltQSVc_TNXz7-g7dPhlR|(pk}Mop#8!&9Gqj+|pWBBk37-T^@zQ z(kxiN(Dr{n`&w%}13XU6rDUJXVIGoB`H#{flMhLAG0E?+ILxwpRrVZ66E7{f4tjsB z95A~1KD9oimcr-rKoQ7%=qd1q97S=%+PYcZdeE?}-Z(TNJ}G3rXsze$0h7m2_b*a6 zHOp)J4+!*Coy0c1d2f7p)D3#~rgutPDgTct7-|)MN;h{}bwhKM>X+mqbbIBc-z#ohc-wN4G;S|A#u%u&$Tl#+LkS@ggZc&KaAfo3GV}tImv%(bf%@ ze2{rU(7WQab)m&;W;icz@S+><1J=}1`0Dyl z^6S@b@w8Osx#n0Cff~ng%D-WVTDR=kT@K07Q-(CIo5zLR1@|l;-B48=*BYvZ#fRy3 zyB_RX_F=}&KA=AQLdyR=nvfO$1QJx;aQP^?j-44|%08u$wh)Fh0~m`rdZiPUL^mp|^MY(%X?56z?@a%I66Srb}-TbDtwEL@GWAnVa?IZtdYV7G<>c zt%;m^F8D*2Rmf{aTe^{VRc5y;6MvNigz+3FwZmEqlPvTc%$_6rx!Af$wZT%lGEYCA2!EFg| z2?w-oTlF<^Iz>%z@fqEGnRz7q);eg+JB!NfPpu*&?za|76M$^EbuDkO4b@4n zh>It-!76MCl~8bZVzqVsRH`Ir_;hn^n}9!gvTnAts<&BQJ?K9M2O2-cZ0I7Z+4D5# zNWyDPy+levU_JkNHk+wxhBtnyZqD$TEvi`YBT{Ur6`7*iW(YHUJ*tKL#3)0R$=@=g zB#%SKm;Z^jI&bh8`_Ht+tlv_E+LeLOTu`VQZYFA4&YlRFn`%VZct!>aMvb*@3-mAK zL9o3QE^>AH_v-WR_#48tf`iXmhhZCIAZj2|RW~YenO@ebtvl_~dgDlF*)V=@SW!@K zbOeMP8+|IPPi3_Qgi7o7_IPzY{7|qyxF^0P^L3aNp}zs^BcRABpc2};J=W_2Rbdyh zwT4M8kJQ@6!Ktn5C~FT_!jr~}ge5FDekpJ}rbHGw>a*JjioKY%s}9WvfdIke3O3R1 znE7&*=kiJ*yaE`+zm=Uolg=XYL4+(df9fJ%G&BEL*()=&bwww`_o-POQnP9gaB81a zZyZ*6hgIIjK-AcnAGN#UjJaFJ{7ih4wr-=guDh%Y#FZvttF3v$l&khn)N{xdHxBJv zvC0w0n!9x^atL(4>tdn0-HCwp-gKBihUl^$sOHU-PRvn54`})=o-USNCU%xGEYGr9P1@Dez2r zzBw+>)#1=5)ARO%JlB(=3!ulsR#EU}Ji!hv)}hyRZGg#hB|YsFv5rOBdHMH|<{C-U_c^dS+2L^R5t- zl>f+Sd9FxGcSp^xSjzt~Y!rl3Z}0OMZ=4=A3pVO^cGt$tQF&40unkvk96lcR)Uc0- zbmp@jcGPZ@)}wZJ;%~I4w!Pqu6^y!E4bv80l;?8AJ=XTi6|{H97!XUCz6Gu!OQ&V| zQpL3lLl3^Z>{5XA>gn>nXT{g#IBfm>zpH=e=w;99z3=Poham#b=mS|VD=1^l0=)RPZXqf66S$oI!H z%!+cj1ai|0K%?fi2X7ZifBHVX_ha4Y%U@PI z3j*rX8xOfS30F+fQz)*2?JI`qtp`M0N4(LEeFv<^7@c0WPk7^U81MMmorT-Bu>nrD zUIfM9xa4rsI$eMNyDUqmF9V_(z_STUSHlu*w{909!ej+aR?uVx zO;#{Ls&D_ys-zY=x!dCpKO9fxY)_^Yln&zIwS=K@r%IqQV0lb|<_EySf%&GfC38tHWEp1?}Wraqt z&M-aE-cMt}u6xhcjpKIQhhDQ{x2QGSWIauhq2j+DRIqQw!%;N&+875m7Q2>Euh}v6_ zQ4~aE4=E6kV`XYZY$7`PLwdh|+tTbtT9zdzup0iBit&M7P)`jaSP_ z3rR#oj+u*KXOuvo^q~k@uwpfwZ{|iF{g+iOFm%xWEBJQB{!JFny@%#=ynBhYi~(k` z-S#WqJ^eZZmohmyD3)4;68j7pf6vU4YOVR(6p$6GpX;pHIY!^{_$0k-aK8ub9ZgjJ*tc2a7-yD^hjQOynvV#x|Tvc(<@geCds;wl~(*P3J4(C(^^jI zsJp1GCsf%GKiS&C0JCGgM#j3sX2YH%Bl#1vF!$7$LMXC2!=2VvhL;m5>R6JsQu3gX zFcB#xBU&k;q8?a!l}rJ@CzSt{`e0W=1g1!<92}&U`#70=XCdyd>(0xkwc z;~<+`S{^prZU4*{fLk{R;?dUeL0i|Zt=l?LxIGcK6z>_S*jr=nLWl#85~HopV3o2H zdWctu-1h~vFq>}+n|EQ~S8* z9?>P%gn=pj5e*|`F?|C-v@W@t#Qk15cONJ)>b!_;=nBz+=UKPkBMU&22V~kH>Y<2-KO0uKekpeGzakM8`wHM8}qcLKk`vVm?*6HApI*6 zW%v7P%>6ayr|$c`(e~q>knzsxv&@16HFthc8|n#r=xtSQ7WvjM7r0!(Es2RrgxjgR zyK;l*RD)<=_Hplw5?26nFasntUu5>yUDSahw!8@aQQUH{Z^g)-871EMa48I%VD`n` z=KZDcY-d;Jxvrph)pJ2S-|j5yO@%LHD-EbNMXw3H5K2HM5Q#3-n3t4aV}ouymjtN=LnYX zXv3lq)+qL0zo&GoAUeo+`+@o{0z1A7Arjr4S zxR3vLMH|r+*_Yirv@^1Ym(`iV8L5KOWCUG8jUF>2?8Ta0(AALrf^bPa@%bQC)UMgH z5_vqbtEEJKWi^tKU71mOYThnnu*Mlo8uD|7e3Y^UEhQOW_T!@L#{$T*R<&SH{q*Gg z`s3Q89jO_|<(gy;7lMey%O`Uo$i?7Wxy!&TYzE&isG|fmRMbpIg(}I783&2h^s$<9 zTf#3}eTlD zyXdE&^IY7Bl1bFC*41*@^&L+vwVJ49R8G*Eze_{by`+*Q=>~cK2Jf`>)_h?cxNv4i ztM*vtFSI9O5>#Tz&BvwHvBK}Lnv#CZEp$eM0w>_Ie#9_9#T?HEW$K4FEUq$=D4N5N5S!L82dh|_#jCcqc0CN%Xm@x9)k@6>3?3u_{|$jB29bm8x}I&IvP&i zSdtkV>gmXfkK)%G9}&_vyftiDVdsoe5pt!{^++LMvr}<84_~iv3f1W5R76dzTqed8 z&@Vf?$Kg}ims~#$Y|fCmM+SVNdTr;3eo)QlRYrdvnvh|}k-WIaIFg_EyVdkD`xU*j z@bNpX4`tKtk+*__yuqu^|B}9eSI(}&nD)#xD6MXetK*R4>RM|uKnme*D)g#xmy#Jz zSV!(4E9seY1~U4(#X`C68*06KySyZ@lo)rG)Ma3^Wb0in*GB)rN5$L>2aV$u)}xXR zcHTQiH;307Q}3IW&>ZQ*`lw!-i4Q@-@@97GrkmS^mH9bV2pwFfU~-74S4LT9(_B`OGM-lxgn`S8n$JsBSX+V8DXObj z@+@bB`Dg%9+WHk&h(3sOL9V8)-NO~L^3^P0RtFHNK#$cepdBGR!%$%=#;#vU z@_CeX38k|8x0B%x@624@6Dl#{mskrgl11NY_F20HVb~g%!W07p+rb$R&14|RvnI>P zhgp-~mu*}(*=5v~xSSJ4sV|g%i8JQJvx~}uj;~SHU+6qLj>~w3PM^s*s^de9TS{D+ z1J*Y_%${Tya$-0q*+*n$*eJ3o9F%hI50vFbYt0RE(dPLHx5{YE_hu^fI!`wVh~u~A z;cjoN6tl#{TkD5|2=!HZNn%gMUZb^%H6C&A(5grJc+np2VCdD>Xe3BhWr8s+fMO#b zz0r9WpszcPB38$_InCYBvq>&FD_8V0lw49YUy4FBUDhN0MPHjtvilwo#H!;ndvMr# z^bRiT42szPtNbyR6U3q|I++vxZ96n`9}b)>_D5 zK#M|FY&)4T({t%WG>S>jWju7#AK+mYpTe&-?OlPXoH0-esjx^IUcpahwAp8@Dy>G* zP4@NVY_sm+cdfI)I)E={fuYlrtvi_w>B;GP*>FM^VO6+wZDCjd{re1``+S*~=~*S( zA^NKoJ|D(=p~#B0)(dSiQ@NL+&pEDmNar51lKM0dMuy@O)@`Wwo#P|rnM$Mb9*9vN z@ro8jY*@(VGiWO_K{uO9)c}$nuk@M9CXF`8rsrX)ZhAgct$1!0MIYtYN`FbuLUKDj z7m+!%z}432Dd!F1Diw;6^QGIxybsO3FSY#_b&F#3G0HhBFam(co$o2+1A&{j%F5=E zFs6NrLU6}Uxp!G$+h5Yft)g@Vp|SnDN$HK7WbE*M%0}=;Z!~#lNi?}UAohZT^&-_Z z=6&88bBY-%h?@6R)|BjTs75 zd;pVHQ`Y%-AResPT{Ze%6sEJiW{A19Eh{whc-&iLBX+m@f}@w0WZpppcek0bP9N;s z5OYaqQN|sH#{+JdTm&y(K2Nu~seG$IcfW4VKtpt3S(O8|Myaew& z8lP+gT`+;*;!2piKj(#*jvfZGHSW%ky(>5LW&fjKkTpvao3uNtVM7PoqzUBtY6yBzZj zt*L`tc;2Q@fj`$e#-VFg-xvQzsBEX!^ekCMdU$-M-5tNwNSDOVGSb81V~j%uiSI^) zPyROwM9f{rPG9=BQhmcmg=xXQ>Yh&26oO&K&g%3URccRW71{ZTdyV&w8}A-9cIImv zJ}k^ErJ=;FG!hzaXX=df-1uxGJt97pF3*v^M;nKRXw756k={;M8+-2}dKrNmG_cjm ze@9f(YBh&3jFU1~awl+}D#DgfMP7fqzle__BQs?bnV^akW{dn)715f9Ih~E5nD2z4 zgsUpFX2&uVy<-Fk-|S?kiiubQ3vC(8oq4>B+ROHQb_yFBa+pk%BqOJVlL>B`6O3gu z4*)_JLLfGg$H=vTrH!tX2}TVAm@H7n2h{S;yRY*BItr(Hb*txambjK8iI zvO7Txm5r$fTybnj3l8*Dml%n8z11bI2G%x~nt9CV^R4iuX8WvFYZRl)jA8Bd$y-4J>fJ_DNma z|MW&VrN`+~#60bYuu;N>k89+GS&6a*{>sPCM0tVHnsu7(oFEOb5OQw}n5!LiWA!tS(So1 zE(KxYdNR^r`+wUm2e8>^`~QVE=|H#r4ZN~CK2#S)#t|C^X{)v9c0QXanY>=H&6@Xj z7Ay6$Qh^Sd0nVZ2N-Hq`X1Nc6*Kx?_hS8kXp_HCy{fvFYy0>wHOP*i|j1YHe!|7}= z{dN{Xai|>5AjlPCunsd{jtWbA5dMhrVRLKlE@!)d>x`JNG%@Zt0yby2TH+<5QFhGV z;J^As>VS0<15r9kc;ZE+0nUYfabyLb7?#M{*!A4v#^j<6y<#|3?F|l#m)UJm_b#LF zyk!Sdp%09{kt>F@BLBEL8r#EEY(+E6l_3K2Ghv-iy}TQ?3WQ_)|ByS(Xq;P&@a@&pzIvD6$N3l?NZ zp(JOJqmu>1gZ>S&H)`C!hc&IKXshAcSuBZS!dF=W>} zm2-crw9+SA-*$2qO3n(!2-u!~ADQPuX9!d2O4P+tlfE{ZiP!Z-jj2ani86JcWDPkJ zv`iKp6`+^ssTl!fvyyZx&!gmw(&P+pW=zy9Ix1=nA4mEOuRQeREYNRwx?BYy>`$rH3=qvT)yaqP?+Nim!#{5|BMdq*q@vym%$9yH6 z$dU+wS<3&l*0fh`+gio(gY?X9ZxtoSxz?RzWW~rn`bAG4u3YeVe7J5#9y1>6VjYg5 zcS(;QCZsmfAlE=!QN>RVnFqrxdv(M-9Kxz3Iqy%X<3G@v-W&?t%muBA`g5HJI}}b` z-z7443=)GzqUC9dAdGLW50!P)b8F`3&@bKTA4 zPYLa*QTgqM3+Q)=`Hb*Rr+PU)&=XFiNqO$brqO1rbba}+1VkiU&I81 z?b`Rej8khW1;SYFXiZzdCZlhL)}*VKh}QJq>SdpcRim#~Yr31dT$aNz z_1&U1{ZM_c)0&`DE~R*nnnR+-7EX8}Kfo`jo7^UFP<`#`^JoK&+S|jImuOFm_dqR` zTt6<`_-tR;>`Tiw2y0JQ3Z!e(Nm6K=?kEN!*wMEvg$EQxNMGizQ12%3cuKe^mS zquOS$Zr$DzvOD<=2klj_h#pUkI*iTcQmy%32!5z%Q?=FEmKgBep^p1*cDP8r>_A5osky#Rv&R^)^lcI7O;&Ylp^NG&9;`jnzai( z4OXDH1#anw)mq-BeRni^UDi6elezFTW*Cu2Q8Qn^3pY4k0P-(>VH z*P2#ww5?BMKfNgBRyv914!)#9f6PQ!{M^K46@D>XR9 zw8n9(x4IetV)H(fCwM<(S>eBl$embe?NOe^Y=DWAFfbd&0&kLUG zsb*^YQ3jGjQj}#p*1a~0<5&z8|G3gEMheq zdI-$V-w-AHmn@_`bxg18p;nvipD3)N>=0&JZq~G5lFpm3g>BdeAV~>+!w!YaqmA#e zQm*)^5m4+D8f~Ca+y5py0onVI7JHY%d^Lx$*+SQ-LVp`vNYR1n%3#8)7DuFg$kH?5 zkw6d9BqZ#4aEay3i)*cD!5|CVWu)JBGV|jnw+3>Vsg-XqLOnB-DeEdbOf&Oi=91Et zk+R-!Suf2LB~DUz&t?}YW^v}2I-OCQiPr3mG#JkZx&9Gzr{#R466U4+79{+t(0W<7 zZ0+MAIZ-ixtxa%x*$>{Ln@2(>(o$rtLv3QEi?Y;*J0*LEwSBSLB(XXRE2l|HTOn88 ziyWKU6*L!hA7kdtJ*zjUk!Q|U4{q!kQ8iZ3u+%7@82d{A%Ngc2s!>OP*4(plf{ZnO znln~`PIjzUQz{Erv1FMOdQv_zR0m}uPyo1S>$&I9OoB9WGH@t6rP5`5l_S^ai^k^| zeT(BW)-R!UusvR)4r;U+TJsoHXv6;DX^l6m^1bR?VuT#tvcyH{o;=zyw)xT@@WNS> z-X|GClIlZ7m=in6vCR)-*R$pCnpsOI0?CJ=gq4%&EZXs%q41p)Y>rl?KzTb?YyiXle*=qMEIKn>J4G5)pn zvWHl;iR*=P;ANCT=U}_DQa8}3H-q)xwt`HQ-@MEWS%kvOR1*1_iIj=SDV z%a0y0-;`;{du`?7OtG9c*L5=vc|_kVp77OiZnQL zr;x9om6nU_*|wLczmTEMRbRtfIfu=lMfp}!-;@?03_B3Ih}*?(bRhz{o&(|(Gy;fkZD+-dy| z0gueB!pZ%m(_O@bA43aw{$5LR;y`mW{ z5Y7ul#jAhjj!gE098*(y%5?-5X)SqJ7ufB=j%A;%371~G1(qxzhMd=C&eoo|E-$P- z(H0JFTyaXMj1#Esid3vX+(7gG60m+!N*5TquPJP5OFU;@UW620sg_#AmU8p*0>pdX zILexrLYI_QTx8QQ6u$c#?94@_)h>#e*A|giiF#!zLRGmGm@HHjL%)uSZnCg{g?xXZ zc(X8%C)Nllo0M#&yQsv$xHLxpl+?>!jHMoxk?5%_$HmIFgnHb0@u3YveQUzQ-pY(1 znIHEx3=M?VguQRIGzzdXgYHI$;(PU75=SH?JHA9DWf>RR@f|F)O?@lbRmL z6mdB}X2l3v0eL^y1}b;}{oFE)S5s)2mNo-~3aKJG{_1*Z#| zpL)O^4*!tyw0V7_2wk`3QNFS{Mr-25qH|pM`zL{4R zG^T$8?U!qcg7~RM8gELj5eg7## z)l(1ppmgg+5QEGqOU$Zqt5LFQ&8?i!qJqH4P`2E_#1;kwrgQJ&XWWv{K>YSM3;ssK zuGy*ZIX;{qLX{=)DV5jf#n08A7^yuG$_wsVF$R+GwQ->}?vVTWkT*|qYuwwgECTlJ z`IQ&~!tHo#+^bq2e7L-d(xTOlQOkf z*^7Xi!TM&UR-Ni~_AG0WPc$fQD8d zhHpq0glZ5Xek=L9`9o))c7;eV3CsM?#lg zP@EG@l@$$cll|Y#5Rz&L2W)rGx4S5uuQea$(c^iNqb1L|V0}tx3_$p-L~h4t6eK;r z2HVXU-lXT}>ZK^@`LVpbgc)SPzuPwaNx(Slc>q({XS8+USw0+ooAi~}BfV_Qyh)4& zzBe8goPXeCimVBbIc<7NQ{K{_nZbT zJ79ZdO2t0johdyi3zHmYAC!-7#vB?A8kb=`mpBtRtou+3zKYzA{Bt#BE&uyDty;!Y z0q{N&|4K&@9se@ZW~C!Hrp*(bQDW430B&1D!TV0nWn_^l=d9?557@Z7HTuXA7Rjxs zX=C8TWXXxi^1;bes5aCp=*SJ%*M)9Z%{d^-KA+gp&>RZlm3_(|0mr2NthRvovtWSK zSW9CE?1qIrFfT&m_9NO7SBnGTJdTh4krj{z9Q{MfrE_D;rE`OG(t}6$Lx8PD#|4ub zofP3tR)z;%b%vMCbH;~*s58EBUW*J6J77hx*)=(PFG@^SUohrri{FRh@u%P=2EXyU zbkoRz^%kSjm6)%arUTgS_$fveF1Xf;EwZ^xX~9|!=fS%(pZ*f_29Q9ZCBV)nc@eA}M z8|)eDd=MQ6v^d^r&shIKB4k`5zRoGnB5*Sn+yyzggl!wxneZ`>MY1jI@%oZhy z@(67%zV!eHP)R>8Gs60t`u<285Xh9R7xvs*GfEhmlqq@KYzm)iUCUmh8K=MK7Q%@Qy%T)8X{tVB*)~T_Ky3Qgp*8%$p zHE!GQ{VjC5_!3%>i^0RBfEW8GLENmo4PA1iOoEm>nehs|?G$*o z1FWR&e?{^P;)EpKIA)i2C}s)%WrHfKZe+7kQ+A!d=`4_R=uPQ9YYKSVzbuLdoeiJ{ zm|VFaF{71&ZysyYMp@lix|4dsN!2>3$DPz-C-oC2wbV&{*Ga8(QV*(>*`NR_&EDl? zJSG__&r477P`vLv@}E}c+D>a6KxLIoStX^FleSKi^KvwG42#?x(>%mFjf!hIu`PID zXH8xksjBBzF># zx;dsg3s>16))Gxv$@oGj;h)v=%=ir_zo&){#5P=4%e$VEE-N%#Ml1^-pJEo53DuA_ zKKN_Z!gz!kPQM~Ky8J!lW!Jb>>ax&VVMY3Pu(L0G$^j*3ISM{#`+}W}k&` z2?JlS&$xe-D{+>#ZXUAH)A%Kh5kKpVfrba5O`Kgd2eO<#j>eg#+PWH_5`^(RUOq`l zi`Gd<4WQ2u!fE+3)1(BuM~JKTM1ePRt~m>v_(&k6=BeWJ5FQEnIE=`651R?jhl+8c zn?%0YsX%ryTYip;59PpCoa%a+IywyT5WW2~frbb&kH|>RRi7 zAz%F3FBJ_@y8HAFR%+We=Y8V{dC#unZ6dpKe@;BC5o&8}wJv&HvbI{+szYk4b$Ryr zin_Jms(MU|jq)}eW0#-z1tNvj8bi*Pv320a|N62I22+QD;w-3yqjW_obV6X>Ba?QS_6&6lCtsp2}`t)I_Sxa5_|Uo9EM*8nKuBMH1x#hpB?2LTRU z-9Y-22>3D31pG4m#VLG)Ym?RhcOd9zxeTDmaPO$<0IG_ zI9fe;eA!a#7JSt7s=`Em=3U9SnUmc1`&9isR#-kJ3+?A2M`c7H)F`+^9N3eLr#JqG4h^f)9`Yx*z`Me>zy>!CY^)Pgc1ph?Cz$pFENjcGgfDO{S*herD- zBi5RPoa(9b-a(HL`s*mSh+&>b{wN)8mmora-$fUA;%UvJD2T%0Ln)|YDb*)0Oapmr z(ro{TN6AGy_a6P6Lknlpf)k4HXEeap_YYXX2-*d#%2xrRIQ2ev5uFKC`ljAHQ!+M^ zK@)p{T4+53VtBF0U*Wx@Wt+LYB<3MkC)PHY;V)}<-(K3K`dX?hmx1lp7*#Y8!hb!R zQ|RPy;Q3FJZd!dX=FHf7x1K9@_y(3TXSCxCH!012J~KWz(tv2? z8i(I(6HQ;Zw0h0(P>Z*|svn#)zvNkU0T5sTRZ0nD3oQ^ zT$HWmPKf|0;IsV&KwLM!t588i{ZfuQF_;o$aSW#J#9(T9W!9C-;lbcB6-2F@001}= zAMGS(JMb81O#8!YUPH8@f%1u**F!7H7edk2Iuxq84*ju zQOF_0OQCaA5AfMp+NX5Z1Q>MO%0ck8&LYdSBEW1zE$P%Zx>%3#tUq?O@CCG-@QT*v zPT37f&mu1?=5evv&F#tJOC=TDwLHS+BH+~(y>@-)blWv7oLuJS?E=@ZEz_q+YG$}) z*$g(*B&lF*tR>(=uhWb~>Dp`-e~R9YJM(zytyJeB`T}Y3ohL%0|g9=P5&>**HbMrTIiiNA z%8|k-cG&*w)F^(Q9YwPoHRdOb;?q#@Q&9~3!%<{;!9jOo%8!<%5W{>9jrT>dN#p@# z+KC_dHtWtW4#w9%m}h<@Aju7;4}GvRn9oAN&k|3{U|0>Yz;c$PT9{xb%-8^rCju`a zY*VxItea8eu1($S=8O*n$9b^Ve&9B}?h|Oy%VPSg45?|W=zwzm@>#QRk&;7Wh}{WW zR%#p>wQ355{~(1a8C@ zW71z|uUWUV4cYS^=zS(2{@c|I0)O-F?F9SzW54r)V`kSn4{lBug@Vs zt>ya#^4%=jr81QSixdRd(yA6d?yMCEK@?x{L|-Ti2Hz^4=&Epf7}W-^Uv}O? zdr%?IeG}r-Q?WN{9yL~b^Acz3bz2;oxJAb-08#&IpRkgtqAooNYd`4+>M%Hy`(LBe zXB;VA)vZo%XTj9!F$f38=M#gfLx*oQN;g3vGkXW0>k?EkC z!lMCt0P29u%C^&UgH(2Rvq`#8uYLN@q*!f7XY0U79LNKD-OFN0LYvcW&hSi(wqE5J z;{Mc%6BN?ndo~bH2ooON4R3W`9t}s0RmZ@^0>XOTw|+9!tRo@}IRs6!?%qAf8lYAg zv{|r}qPE%UR85?hJ(>QCfk6aE3s&FrC)D#_8>ripDUK%RA9H1fSabPA?c!28xBX{Q zDPw%uqKL9U%~L_2$#JtkXP-b~FSO-#(b;~+i6>lCN*`%WBgiBWdVOF+0;{&~e*so1 zhU@<(7D1_py66V|);FHbT~%1UyVOlv=HC851Q1^*zyL>~y*d_rgV1@L4BE_gIE!7K zCq^kC9zlNqf(ilQ=Db7l&iEWlxP1c3#nx6D7&{$Iou_=Q*n954Z6mQ3YzOMNB;#RiGK}+KDQ#cyLsK zg>oW__-lzRra1O5vCbEONmK!0D6IggWJ%^hYcwzLXj5ruAfy0|aT|e6g5!ITYfSi> zE#cE`fHDwK;6)5*Xg5(|ZR0IWM1iw0gPgpjP?Z{IJwa}NK!M+>#3?d@i=>_tP@sD7 ziRVPdD2EoYl`8w4A0|5<57sXj1N2J#92_}0BJ;;1uA3MDeW4y#LCkzMPTbyVZ%y4C ztd?T#X9-smoA_+Bt^?xeQ=va}ukN1Z?FqTHcoEmCZbEwLkHp+vv5IGi$>|&y=lvcc z$QUN$aL73L@T`>twH)H5B$mN6Qk@9VI#}90=3(<=oXsBOOxh)T@M7jG5u6q)_f=r4 z^mY>0Dqy}8HoJsBdHQ=SIHU(y3_3!U-T=Xjdxw({9rEyC5_wkQzHD6f;U@s$3;zcB zM;QBY+!<9W&O6>3{uBe(?Z%Dow;W5j#y4FDYEnN%MQ?|; zxFt7nfbe^z5<$`nJbZN3Z;P|IguC4UAx9m8U~-xDigjG%rCB9<-GQF=hoE>*p~viW z4W$cpWFuaQ%+u3e9WSz*oGpgK4xceiQ9w5IR_i~Oai9~fh2FKM z6wPyBz-17o25YN4Ix%OI+FiI+G=K2mm@pQZJFFkpQK~O z<^{{6@|L{JDWcitFe5w>Ma|9DsjBPXF|BzsCAB9++r}DzfJ+8&!@2ixmVVHBqsK7% zyvwf9p4c5-pO^hd@Umygu3k1??|s>LqcA=sR@Sa3eFVQDHdWNvcUiPOJtR@(BnnBm z<0I?q>({Q8i!Y)#N{q!%#SVE`%Sf>a;&!#CLp#0NC58AeO02xoT(0HiQa*VVr{PsT z>Q(dH!~grJ&%@$>l!sUKCH7=~koCvWI!5YR2Q~O{s_?Q$QmPV9OA-gyjreKO#M@qFCSngjtJuhyDH%lUXdhksXq$RcU( z28h;?$E$-{h1RO2atolFArxlZVDGfVVXI*j=QKAe@-v%EN)J-r#deud4^)$$wOf}Z0@J(}?d?`V&4 z0Kq%$tro%_w%Z=#T|zZ|_fX(&RgYS)CPcppc(xP-EeN9bquy`!xk(J~z@RUOE| zk-nMFVe>ul$i0-;$FbMANLq(RJ{w-MWJ)DEM9M|-KM3u@$o{GA;g-7=V&XFjJRWX# z^zM2*FaEgk*72BmFtae5e&pFqD2Uzu^gR%aCWv6n3CMb?)r*NlHeyJT8Ust^O7DXu zf!n}rTw-JGL}XxEMNBJZ?wMsasVPBr%d2w60o|p$24$^K&1mbBWX$N1ZVPb({)^s48_X$t??(<*#Cr2s<}LY4C0T=@4ka z{1#xW*Ufts&!(1Dyi+K+OZ(0@c|}E<_Z?UP_nUOuC#x%yZqS-8u&CU7BwDu#1y7CnVbr}vPev>itbnMfsF3BZQWQl~$7)UQ%ljpp z;>F6a6a`Uw8#(ZAmTq@(Gq8MgG!@B{0AslBY|hU-$i+bV*A!u9YDh9O*t}Yqn&a?E zBiT6yTh!?>%=WKmN#M`ws~&hYehc$D``flXcv5 zEQIQITld`oRz=>9nRm?zmA&??g=uY#xkb3rirwlj8Av31^t#8IgdXe@Hk$kYW-4`A zjSO0b`wWN^?BH4!q4cgM+rAdWY&j*o8nv+yOAgJ1@qFvuYi{eVOEX{VvYqd`J)NG#85sLr2m6% z1vmfBGY73KZtih#6Nn=lZqCml=g*lTa~)y(Ph;Y8eey#JfS?X@0}eGApGVT5nq7U> zygfwq=1*~~i9n^CeITg1Ci3#2WL0iOTjrKul8Ffx`}*rA@Uc2Mb1_S$cW#uk00QW? zcH9nb2>|JR2)(PGPRSJI@(wRHNx9}-_E}7^U##$AmIAe+is{R-g2RS2+O||_OdN=(Yzf-H$GtolyF@@E{f@ND8W z%Q!$boxgrC5N_A;7k9X@jjEE2#+vO^%DBzYX@HY!p3mzAqv9Zc0BtUT_LT4RwN4`s zP%{?>Y$)%HYO1iIC+QfJ6G)a*=|#&sl^NqvFJWEfZ+}Qsv(0+&$nqj~wy}P#ah8Qr zbIaLWtG`W``a@|sxXxA7E+NSL9f1xWa@X421!WNJx$==-D%{s%G!+ewlQeX05r(Wh zYWw}8W2ENu|6FU_FVO1DZ_D{dKPGly=UTJK$TGisp3eD4KO$x)k+p;Tqc_06ilUMj zmesH=^Hw8gH2)SrDOptpoAUd1PzKH8WEj2p#8_P$1<$3RSSlO)ka-SyYVK^St#LPX z%K@K}$hs66N|8`cHPK?vmfGW`_81j&cB2HERX0BpZ1xB3iY=H<#MpDKA28PJu+QMt zaqB*D*dgNox*4{3ipi~+;6Z0(4SUY<>{h-(S>JAaO9@yb93igVp(kB{otsdB-D2_R z{vBWBf@t5=+7%~7wWl_*yT0q)cM_p+zu?NvrymS+AwxKh+zTB??yDGxIBtM+qV!CMM&Basd&^n;oI7?%YpNuvoVZ_L9gIGlxaCgJ=);M7 zoO-z?9#; z55^)RP*6-R@eDifPo5P zozk;8FxVYhK`^~k78C$E?$GAk(pc6J+Da4(eiSY5_lG`TEv>XdEX~dRPSB$rCupC_ z8{`D7(u4h-9Wd`TK^I>a6 zgTFTf&r|Ns9|-?1w0$o~0>rD?Sppvki!fhnzJY10^_wC%;9XuQD0d!i>OGtD;yy`~ zDaUmH63dJvH$Se51Tq%)HnFe@drq@U!)1$TwCp{KDPMjW8ekO9X}9cbB^?XP+nvIA(E`I8W1O&p%z{GmFr#o3t| zh1F5UHeBeOQk_E!FN?1gf(ji`>qP(Aci^S4+N+`D-E!(@m&=L zV}M&-&;fo#O}!}L4>hdJa~!3`xB3GuT?3c*+U1P_R0rJ+Vz4N7nbtV2yeJ8>(9Te;v2zHQTKJnaxbeSsY$7 z0hNW~nbdhN+x*0$YbcssgY>_^)G+sR5-0=uiv*U8$_HaRw+$H$B&$`<(X`??N7ts$b}9zqAx1GVK84@1 z_ym5>|gh3SmgB{bMB&1apxQ|vhsn_L*}%Qa;J)P6*k|@N>?RT1I-%&msQ(8y!7`V!Oh(( zmj|brZ=#OAQ#W6anIA>lk0DZBxRxxmt2)|M#G(%os7jPT6+z_r(|ku*`miU=ErF7i z*v5Pie|u!5Q>=skodbeZ=ydD|OXGnPV#%r2#}ts^bPp7~RvGX$Rur;ucWTLKAgJgjA$;> z6iU>-p-^uEC=8A?wdS9kJne}SB296jT|_*XcCK*HYu!d6eAbKdLhb1SxmjEsG7fpU zX_5xbZZ0CVrYo`{N)34;vh-!szs)|^W}lJl^DIYnX`YiERDbNLlk$btzmNk*#h%&* z*;Qf-+Cp9sTSUdE#Fjs+7h+Gfv-nDM5q4K%Pt8`br+%isBf3oBB@6C ztfXQ!U4Q}y@+YyHdXR4*r%uRpsQKa@C?#9=`k(WT0^Bp67o|NPKui zCumjX`x3DVswvbmEY=U>)@_tU+G_oAlHv-uut?twLJy7yg$1Ynl`*TXVK!h-HfGfw zsx=Ws{%H)Y5VuNe^6`?3UG+P*yCdfiA7RTt?5Y>j@5_PkB|)e{>cUWkrcpCd!9OHo z(bo|W7Qt<(I8?WNE)LZqSS0?Y(}Zkq_YIf2O9p~aMa*OA2k7zh5vWvb0nGg1m=^5f z&wp@aiWD^vg-TC9N?J)(mDJBgq3Z09LM1G>lCCy^2K`Z}ex-0?Y5W!?Vf|iea(t)& zRiX&(k3#hsjY||Ne4_R`GZ(4q)OHbDSw_y5e-w!7_ndw?`6?TT%8{+u^Glx+#Xux= zhcH|Bt&%uYXhxTm&KFrrz1p5|Ju+T$_Dd!Wb?6vVc@4 z2xJ5|_>zEBc&TS2Qaz`F{^iDeRvN*@%B>Vl^ovCIkA zH8>j8!*{V`|L>wv9YmpP`|;|hfv=24wOJLqU~nNtm%b2?0WnJas*qF*PY6kM$#}J0J|B{5q2lkYx8X?#LQ)A!xH5B|dTU3hLs+-A4g#u3Lt4YY9o%oV+P%1N~m5xm2gsM`S6RY$ywFv1QkaH(Y72>oKx737l zVX83Y(~?K&-aO7dimnVWPK;8er?Gp0cTrKQ^z>FW)US+Er6e%Xe*!@#N>y!Iu2=d6 zF`{4P1hEDw_WveI)pa!L&0Hl-XD;VAFHSad=D{?wlr6>HgVQn3MWah*_)hoAz znCt!@_Ra)8>grnjce0Qn3zGoRu*rZRQ3N7H4F+sR5}atFVH32diCG{uBr%y0P|!ev zC5(BcYFlfyrE0D9)s|;n0IP;Yh>8$gQEN%9+Fy)I+#o74|L?i?Hcc+H8b;JN1)p&EvOroS)6(iGf{P9LTQGdQxSN;I@9w)l2xQ z8G0PJFHDaLP)!egz9n)f-So&C{{rnTil>Kr7n?_zdl!3K=rv-y z*iVOwZ6fCMtUa5)#eFr`W5`R%%P=qaKl38a#oe`Fi%0_sJvg7_o}ZRS6rss12DK4x zvTolr^>bAL>r{65C1c#o5zlk=OYS5FlOHO@S25ave9I70(og7E2a(m2%~F3uo|XdL*sL|JSDT9r|fwL_w`FQX+0`G)50)YL;Sg1#rYk#0oF}WZxW# z;C30qP}$#9?eIFBeG7uTq?t6iGjntO4@E#FL z4I~sk!P)AqCdRqo?FY%QUH?7z^TIj_Ca{wJ z{DJFKnmHnwRBA65k$&zX>x2BUL$Rv=8(gR00&co}2G=P=bDhp6?QnMd$2zIr7nZyUpf{#zI*VPcMbnV?Xxk$!s z<8%Hfa~1b0_R~O-4r9sT4Xob)X_330I+c5$O{<&5#CtAsnezRRnO8rfaOZJld11@d zAd8i}fX4|d1})DRkbI5yC*(EeI#FA9Sc@QIDFsux(#*ZwR1teUzW$B^|Z zvBo#n2zoU8=j_z(&Oir9D?HC@_Y zqD_W+N3U+)M}4N%PoKV*c>U4VD=6cq)QncWZY^dwrhy3E>rmmWI&B4bX|`jn%bnsp0~0ks2QSbyNBrO zM(Y9N!q5;Mxu1yqj}hr`B9-{ER}!v%Y&=G)d>lFvF4=RuA==DfdIIepqOB+IGNbcD zjPcgzD|B?f0$1%yuS5En(?V~vit61$l;d-q&{NOYng_Ex@S10rC}*JfFZg2e8WAYl z;hge8UFK+i5{&i_vK}4nx~-Y5b--dh8qC2TFJ7#RTpQyJ?s7dkMO^k+MHfrKIcVtR z0oSaCgT7(x-X6@VJL2~B<8OceFC~)xJI{w54NvO1DF-2wtKqNYqArs&<+{xNejcOS z-tn=vm$kXvz~S|(X=5aNo?t&)p8>OaaC>lTUFJd`ag6q#)$pu;1mZcI+RZ>Rb2QN~ zY{!X`1mrSqYYueoYwt)xSe*3x?TlGS86?ZB9Xq6X_%7ysSm!ji@BC@~eKR1)*{&yB ztcHt(IzdXoBUJ0i@OE8z324)yBMv7BvR&*n4G@OBRI0%4bEVt>AwN9m^)GnSzQ=?1~Rn0x-z(wq5l?Lu!c zvIJgKJJrtO`GJqUnfq#3W<6^?u^sOU zn%&$X9JZ3MP16Sh`qtla^jabu?$Z@I-1~rU6VBXrWW99#U4&z-NmJgZCf|Kv!cRFJ z<%LeRFNYYXqf2n+jZE2j1(SDu7dJ^inEWs(w+eEnyn%j|9{6qI1>YGV$Lq0>y;?>d zi$vMU@WbZh{oYMe?Bwz?59GPBsizSi-pQz_~C>V`qbpCj*X|;+CBKx9R(&q|fjoE6AJk(m>=CE)6im0O5Pvx=A;mVWTj0hb` znu`%=A*R4nf}Tg}c%y->^R65#1)J=qMUKXm`?J=rT;Oe7*_qSuywBOVvdi;WVnv|m{nmMT(l}jfPUW~oi{h;5^d}zLsj^}iMyBTM_eJK!ejV6jbd|^=x!H5_ zGbsFJEcShuD-9mL49mynqcMZCLhAyskjUgKKVdNmMeZEaf`7yV>Hs~(1F{319YeAX z?sWQ`B&kU90}msX%IZK~r!$aW$WvdI$ap=zSE|wNWe+c zRTSX#=_(qKI$iYx3}DMYqJ0cilM{HSW02>MxG4lu{)krwrJTTDHrIhQ=I{2b>GYkj zF8VaqG6!2n=PbUzuF12?mED39CCl=i;M&qY6o$=*iS^G$krnKvRIV-W#@F`q#M%Cs z`tUcbBbG3Uz8LV~c(fLOhcqJPczcwU2sI6j-~F+y{iT+zH$VfbUG|DF5wo%bIXlqs zRj^A6i|9IyXT_K_+77Cn^DSNgkRgrT*y#(XkH(xfeIaa30Kc30nmvJ?CvWA{cZR-T znAOnfn@Sv^NGZg@k$pxe1qvp=I=?$oKO*&U9D4t3yL8a4J?^Nn-`FYV?ni>jf1XDk zTdet%!5Sz9$!Px>^wpcIfkeijd7+7B?l(pA6CI7{^CAvP-xf^16D!txzp)NKK2o!-E_wm_U!m`Soa!|!biW!Sz3fW$yfY?tI(9*@sn zy8;y)#SGbflqsXmvu@WI@7kPJ*P42g%xQql_$!*4r{Qy-KMQCh2OAG#o z&7^Cvr`)h@@`*nokhA~fZT_gZk2@mbI;r$+ zH1`?PWu@sml`R!uG^PmM9kKv&nK4S~?N*fXkH}t|v!LU|&GK%e-C|<7;k2M5N`@QL zlMw=>33_;7F*~rbxp8HSYt1jj0?AFv+I;d>VpLhK1`!_>w9Z$Zxz)8s7{mJRNR1$w z?_8VcsXrWb?F9Ztb0mwU>&g5D+`W<`fqLoXuq>>4Uc<)ui9TC7t=eCP>F^D0#_BOlO?0G&H2nDvp?!Cp zJg3ub4?nwP_;IcI5!v=Mbdp05)1#k7=&i?C6dr~cln(JsNWR4(rwF0Z!d?v~=fRED z^f;4u5+r1c^)d1ldBwwWxxOGQ8M?LbVx&ap)s>_;k5G}Z88o08xDvW#&uVe;FHjVO zxOgCbkGC-@78&pfUuZ^w?rkip8DHI2?t0mDh1O?TdYvR|xfSqmIcoS(GaWa@nnVsl zQ{&@=2yE8^L-j7%-NHH$Z@$-fk7^k@WIczr-be+@M5|bv;PRBdvYjpb&TQm50$XJb zEh{eTb&j3_@-{{~fzz1E@IA^~jJ)4gU2{#zgPB!j3}yuLBKxGr-+;^d3k8;2e>Jo; zve7P!6SLT6$*J|HaR1#C*eVAHg}i;5$MS-?gvQP6fwX9LfGLB6*yprN4eM076A$CV zpTbJW^_WAr=L5?!Bhc(F7sl%~ciI0gF0RL7$Foq9^-=v7NBjxaKnP;^SsmxW%$k^) z;C%vS7K%N1(JWc`i$@Q+QViFV*-oxyXLSs;Ui?8QxK#)WL51C;>x5-f#Td8ENXud^ z`}p3N9@<20@u%2+1>FVV3CeLBkAo>5La zI?4&(93>Z3h3hO)M%q!LL}#yc5C*a2a*P<-g#KRTvG18*k2)6F=Y?399_0T!2F5jRYV_B8cJ;dYGg=5?|oa=3>7&C@TzROPF zvaj3&ro_qn_+!)3}B!pYp+^fu7m_yMDOnt$N&eQ&Ls4TU9QJ=c4T>rFBY-& zBaIh3sq<5ar>yY|-nlP6AM55L`iAo|nsH27W16=<23ES>Exk(itj!)NIn7_hP@`zM z(r~L~>$J>ln1lxz?vt`-y73pty2omQ#j#J6ZM(kVMUMCSJM@l)keYc6d%F=1nlz(l z9Nwu3V_4nM3t7wB{F83I^7Cx{A?!KL9U`sq=LO#&k;NL24U=K4oG?To+A&JT1pQF0 zPfmCk9rBP|mh7SpmDPBgoLW77wVYaA-j*}9c(DIu*_QWnJqiILvolJ&^hKIZ`yfd# z(mEb=J?dhq&}Ow!GT}M?M3*qXEj!Q{PlMx3&v8SVC-dVK3Pv7%VP!zku_EiH7u#;^v5+1A?;iib(H;6ELc z?DdY)e}IYu?{C<3D4(lr{W_HXG&j89yYl`R|EIZ|f=Bf4hFso+(Z5wFYe(w=joq0S z`K^gp1uqAVQ(*nneh`|2r zK0u zxtls^2>e_;BX$M+sHXGUau4yyMps15#TPc^O-S^j0D_&v($l<69v7Mim%@&x@3wVX z*FDb2FuqM5*U1ug+i!Qp?1t;rG057e>s+5l#qLsXzDape4kdng4NmU)Y9=BX6qzjg zh-5E$5Sf!smPfX-1AaA14uJXN_Q+%C9Aoa%>kl8NC8!}0pCVhx=9Apztm*P`ZM9lX z38Zsne(d@ID!1r!Ig6Q1Q^VnjOY_^!i%h}2hhSb&aFjddot2oI*|L;} z=S`twyvfr@9F1s)hWuE^rG3|;BmA_oZOgZlG4G5Kgdm@~NH)PPM?3tVJF?TTe z4hSGBQ+?9{Io0HdjKjp?Kpg%QgE6%hCuPyggN_8dYcJNtft11Ib%cj+)^uU#s;NSA zf3$UR85wE1xZC1fECOg%%XfOGJa46zNIq$t0UBq3#@SSw7-AxX^+E{`R6p8NEouSx z$t+gDtxlxLEuX~JFh*8V*{~v-f!aBn;U))}m3UhlKJ#BfSCMS>`+bOnPT5pc06U#3D zOC&b3{TfE$p7E{cJW?K}t9fJ-5h_@Bf38AHJaww+?z<$oY|l_e=40VKdx zFPSu&dNxy;$Ce+RLF;oPQ9N{X1$l$dgz89Fkhi`)qDLj^3c@ZbTuGq{D(J4D`gW(# zR1?nO4_8o(sUQw|!byC~`pJ&%5=wNEuvAbAb&)6)1mOmoWIQ~ToaBF5S5K{}p6>eA z^~3DB)YK1kA=MJDCR0CKd(=;!ou1IQOXv&1^I{?W+*qlETubcQ#BRUXwURGgLsEUS zsK`8%GgCoMER(*eezs6Q`qcbww(j~ta9KSEa-G&Wh0^;kjR~WoN@M?os3tnRIWr8m-c%9&R245?9mciEx zo^J5l1y42jV!?+S{C>d`4ZczED1&bjyz6pZ_GZD~H+YNSZ3b@@{3U~L5WL0U`vw1_ z!P^AiXmCsLdkx+x`0WPo68vU^%dvu0XK;BU-SQbcQSikEPZ4~f!QFxv7(7+*Y=fr> zo?-9|!B00htXT9W8r&=RV1pM3?lkxU!4EIgWiJ%G)8LB*f7{^Ig6}u@GQoEnyiV|D zgRd3*VS}$1{CaCo~c=jZM0-LE%ns5`yf z6g#9PbW&ZdUF5%8t8|C1V zE&>q9Q#|YcfZ+ZCYm=-iB;aTg?06a_HqV9^MBVER7DIV~XJrjEY@Or0b%Xn#v(0}A z8VHDLzW2~p*(UqnUEjSOzMyGv|FTtY1zlyUzU*=>eU3#i3NvXU+x$=EZV7Fl^CDmH z)_2mN&s7*NDZ*g(^Nw?(V*RHZ9fa8VKeVTQ|43o?xQshHVy&a_V=jzuN9`TC zTF*)@!gn_1@n#akcTw#}GiMt2=V>i}po#wJptR2H*cAUnS&)g^!{=pQ53MhL779O1 zmmTL1WeLcwF-Q^q0`cfHZ1K9DVIyo(57$iZ@=2!srjoiVLCQMPR2K!I#^$q}^j$=q zT@b3Xzx1l8eLX7bX`Q!v%h_FF*P_L-Gf1`B)wQ)FUPu$7`nRvEwGxa%2;bO>U*TBBxLx@&ejb&eao2#n_loX22o?76Wt| zfrNQt6C8VRD#C@Dmzb#aF7?#8loogm^@C`zo^mj-ul_x_yib!K5Z_huCtv<7sDCfg zH>du+DBr~T_xkxx2tMmO(;Bs0*kvc++4|iw*j!ogn&12x=>-yA0kq4}2Uf2es}}(s zD==>}=EuccVKs2-WW-R6IH8=Hb&Dv7k2HXQSxf-RyL>2-mPs>-pFkt!Dt<2 ztc@0L5y+W06*=<*r;q7ylUlY(Z8{)y;jxf+e==kxZ{?!PTkk&)lhu4=xMDp``H|Lb zKjkn4E{YTN#oqhS?_B?t)0b5LRh%!r{;Md2$Y6Y?cATCUcv6-|d9u0n*54;MZ`3;d zgR%pUZUohL)Rk~JF@&!2P(#(rCwXfkxE@g7WW4*C0zAdS)ce?q%wuNb{okO3e&LGl74b^%0o>nbFw zd`OEE^~&JMmJ0QM?8K97EJPcC0&Xf_{g{LhKS6MP9T zF$cM)fkZaiB9b}a2_$%QYI}X@!Q|hin{1zoY_DNFj>JQ%?O{+bxykmx9$H>{!%raL ziysRSYi*ZAu71E~LXn*ILOW@eLm;ml0tGLo9dMQsQgd+mckOq4UGimtcxCGzB2uO${YECR#7oWHuRqt{BAt(QphtbPRQ9naYVi0 zkPb_)&cLiMIGhb-aSeDVi?Etdc$Uk#ntyoy_}9r)MA?kSs6n}$vdX#ZB;f(IcckWx z-#3FZk)gc)8<{KekGKgV3L#V04{vLYceo8BLD!l}209&OTv_A7Sw|39FX&h=xu}&~ zNRit8c+vAOCwA`oFCuP8sQ)6;e?lO7@fw=hs6ccfurc8>F%7aZ31`o8E!S`=sTCTA zY>cQQD7MH*0~E#cM% zlgp>*wo5bhSMm1C4_V;T@1L{IKq!bJkN4Jp)pqR@VlxsO>uz#ml-;Qa02T_8wVXQU2$F&V%_y(fyuO%@V5!bkf ziUc7NcPNh>g&Gx;w@*Cle69?c?F+La4ra9;LDD-y%X@SG2Dvk>6ZsC$ z!E6^=%M-Xq`<&KVerOOC@SOG10jWe+!?SEANhF6vE(k=m;XOu9um6Cxb$Fc~%Q?he z$f~eekK@t9@HzF;!IBeXI9#sVwg;0hrtT!Nm4t$m&F!Cqt_Il>bKZgz6hPkNO_;$8 zbC3#e$j3#ztZAU#twUJ6?u%H?f^p9yD_dA1%4;f~`V}V@D4*N2F8jp1wRvNTJhJgs zYqL?UR9}LVoURvkpzZG&>xRGTCYhc~^^M=28_9~97w!J-K|RC3p*BHj1y&S3wN%nW z;)clka9cu$79zZC>#uLw9)2hu5Io7yf729$;zG^?#}t}Nvic^|lov#LBU&iKVWDul zd7qZ`GD=B=9v4Xzgky>=8RHf@oAqdXi->}A-b4X}h&h2B!Q`t5CxPU6i?@`T%U~)e@?w#b6cosNZH_L?x zbf#tV?)Y`I9EWZ>5&o07T*twCS$$V*8Rg+(>}@+lv|G*}@?_lz=;8ew*JDDoAD;{- zJQMH!MfJNPMBr+at=c)Tn`xm0FSTJWBq<5&qR8py)1J(owWqYd_jNFcuzyqXX4ZGX zT@>am&)RHP9?kMC&#vs40%)MfORB*B_V+Pp+YS&Yd_AFs5W3;hl8<05 z)5JTv#mUtM-3CX%9&MVFAQ}a-y-km}>2W;5$!WUD&N$Dys4=<09n)g{acfU7Iy~6A z@qcYUlzMOq6r>;3?D39TC@S98NO;t-W{+p`%%;A18}z4A_wie`8Y)?#>zbB&_oCrU z{0Eb(CYUOp#0)@fpqqsz^kxzlxXJozVITSVg0WX`pECjQ$$g&xx7U2FD- z3MCvY?eTcUn#`m|x$1XBNCo>54mrU?g^7MOJvB2umo>6D#<=Q>BT~Zc$1h>hw^@Cev>21Q2WtwMB|_^mZHD)BS0Jdv{;MzDU~*l`XkJdSN=*FLG@WFBlI)=ytcn$FFWq21td6G} z?6$;Xbc6BGCz4%*x}b&V276_3n4}$`6wK%bi%5c`q8sdGV{1Lw?eQG3>QgtEluxUc z?!J4f^+_jMmEqu8y8&_xYgy%?MEb5DQKFS{afrvT%)QgQv9e2qjHTQ=HQLTZHS{)D z_}-~#I~$KxCRTbUvV~^A+Jj5A&Es@~U?)i9Nw$(m9A(h&aV%{sgVV~QPl7s>ageny z>|k918ooBfitecUsD0=>8ymd9xh%mOh**m#ScL1*tsPF8rho8LqCuuMs()k;6=!GfUgYF=z|Lf6KHc+&cao?Ht`0{^z$MWKWs3#l!vEv)`K98k$SS83*u&eSm=4=oy#p%`@EbL`r zTdBB-)`z1ND2ou-8*qF*Xri$7K3_hzr{3r9$cnZpImL&c%$>f}9(teC@tFI~dY_Z< z64v{?^IPhDzLUJ#**+DtuWYk6Z68CnrMQ8)@OfCz??U(EQF@eZ^*-B*)tb4bG}HBHL;qG>JzFibs_B(v7fMiMKJ^4z zSfaZcipiOX!ru%lOJKSUKeg@uY{NTk*gzIUWPXff<)5zzIwrS%ms2({lR^s7zP%#o zjeeoybJqR)8RPp>1U-_erl%t4UEin(y4*z9ry}TZNUaF^Vx&@fD1zR|&_v}^h@%ui zpZ|YN5p*H_3VQxC6+wSTs@r<%B|SLkRR_~G`f0heTh@3ss>se};qnhCg4WHaW1_^W zW9e1|eSTMmD1rur6+weX>0XCFH|No!}`pUJ8m&a8Ejl5;T6E$qcg?K#`L8p$Q z9sHLRLEk{M!Q?i##M74|=u5PFb5HkU6hXg0BZ1?RMbBbn`yW*V{e9t12XZ#(3(m4c zFX*9e>?9Udw4mcCg3cqTUVb)DMaTTNQUrZXoIQMe8%59?j1nJLmZg7K6ZBIf5TIK(T5EznlZ7%9 zjxW|z-xY)Ud8qWwilJ-HF^lMLQVcyE#lwqz6Zsob485M~JRih$G}fI{!JU!dHZjJx zFO>-o)zIz2o&<5XGgk-K8AZ@2haOyao#=*^4U`0MwaW~NZfLPbHMDJyYUqh#U&6x% z0?Sca~jn1yezw3~V z!{KGKQGW2!FrBu6LMOZUaM1hKA0>Ckv|PEHd|s28@Q0hoXSsfWc*0ZQ=vvaZ34`SG z4aw)%yfi19+8nZ*67-#0KmBZ--Elp#JFJiFPI)1iyi*tu5{0)uK9W0Z_l>o zqLx9s$HwG=`9iYf8R zpWbwFe{0-LA|Rm6Lz#-FB--ys*QV$v&|f(D%V74Dc=OcsR}E~2d8O{cK>WM-9g-MK ze*Z*v|Lm2+XCO?@S;DIIn)a;aICO~zl8>Wrt4fK9CXp*TV}DCL!uROwTs_OEPJB0K z$_GtXh{~>j5W?-Dxmt5`Jt?-(fcXBJ# z!NB=lrWZCL*{Br$n|R&~y_NOIYME5gl5o^TJeo_EIXBk)JtvG=BuqF(Gq?NThI1;% z&63yTFw9)-lOwx`QD{MG=S-4AvS)me_5Fjk8p>;vt*m+72e-TDGTm?QC_&vomR$6+ z4ooq({5Jm*0@I|{E9ekCzM^PvA!>p?;^T{#*yS|%7bv$@MBOQ{~A+sSp1 zQv-Nz{dPstfO#RZOL5m;d&>#kJ#3H0Twj_BEBr!+{v0lQ$V91cKIb*%WSDDytnEd* zhxH35P3x2Ork#3()!lEtc2c(7+z} zi#(Z)qy)FyTC6Dgo`@iDwy{_wPYSt%1)W=EPPSwSc*EzWB@d_Isrm}Z&cMrDak4Lp zMNry~6UXn@+69`tM_k^mTHhe!KsGFPxsk<`1B=}UL!Q`W0v2tH=KMB=wN7HsGhEb8 zPWd44B_ck7H)(1-GyIp?(h%s*%Bloy{}L=OFbefiMpf39=~##`&a^aXY8JhY^HcGZ z*=982mrY$9;SHR5`_*ztz%#YC?eb=xc?%|g6&KqBAJVZz-&MzDoUk~#)H`*6|MOsT zSchfdbwVGy1%n$`P@25`t*2{sRnQrleZ#!tKazdM8aPs-3XN?jBQCNI&3 z6ndGr@ysD4NIIeC-=e?x9?c}^%au5?t=~ULjE&Jzr4;k(-%5X8zTCQlXVG!3w%(i- zqJf^r!|lFX28;HeLu^q@rUxYHlbgIw>y+g>(jSnLq(YBRg%0br@u1(WHPTrQ;TDA`{vu3#Z^t?dZ1{bVJIOf@tn) zb=AwN6h^^qaE3jbs3~RrNXktquJ5QJC)W$h*yN<0%0&vU6yiQ^BTvrK)x0y(Nfj@ zNilmWx43J*&2?n3ki^`_>e!RB$9-BdFb>wiKxYyv$RW!Nb-ZZ$M6*ohghJO~z zD7g$Smgh5;pXQBxg$(Dqa$XK5{{n^{eg?2awtj}pkQq*;TR%O)5R+Htc3Yb;kR`M< z+|5MNtzu8A+HGBO5nB}T_Cw>X{SG{Z&IW9`mMjqf(RUHup1>Du5iASOlC@O1vFvGB z5jny?lBSd_c5b8=vKVmn4d#<~if9vsjMmaFecfed3}NID?dr^3ECK`jJe#>?3a_%6 z+tSG0pp3Q8F^@fqQ6m<3Z%R_QTavKm)k+Iqt~|o;nFlxs$#LcH!usSlnR3WVy!UpKlN*M0ykUKjk8MV@KhD|< zW_0~{(OD|*=j^d=)mgoZqf)IywndiNzsA%tZ~5gAipcSF%g3gWMprWy4}K=q#Qw1Y zuZQ+~haq2h04)Jt7FYhUR#`Y9>v~WvDKrqDven^0L$eWxTwXifW1Sg}{1EM()q()M z*39Gil%^5OuamJtKWUk3KWT|Tz;oxV%XVaN08`OD9?v(vVp zI+6*hBQ_9ySrzngKyleRg!)Ovn3T{VBa<(pU+f31jCC}XIVoJ9KDcc)8j`w*#y;`8 zFvYz|YoW-XpB&ryN;Gr+NJ~#ZgcpCG+ysKxGmAuuntST4SnkfyU@ltDS;U& zxYf6PRNoTOI3wjZatYf%$+~iaRDUx!JoftrShI|&5EE~;@3Ag@T#qQUaP%j427`xY zu)SlorghT<#(M*E631Vi$dz z9j;rDSH4hVcI1ffB#{F}2&gH!b{Xp*6tuvC&`Me&0k;(?_)BYl2zq?HMDthr2NU+#9 zdqp`+ytP@^WWp=PCP-_PR?solNHW+`Dsx3}ike|)YGS2N=3jF?md!e=UaO@EwK;oi zPSb1oXMA~9+C5B85t2fa*THJW3XT)9>M3TTmzVFg0@oI6BUQ(=fy&Tb9VsT|?n%L# z$x*E+AT}c$auOtqhH=V7aWIsin1??snDvT~s$D-;#_DIbkTQ3Y8UKUHKZ+$6jnN-| zS4zIaYxLtVJ-?|f(4Z181o8C?COnZA!h5>J>0`i z^-t6hExRhS60GmbkGD9Vys?r`?z)z$2n>GKit9m;V=BOuFQd<>0tsU-k!E`e#5<~f zr1Vm8Q|a;{hfvH%mxdMJlxJ3DL@U+ox@~KKf4%FuekGcrrmz96u3wpsMmKLUvbK8b z%s%|HS~L8hA4+!6Mn6=nwe`b3>al)hq0*N-u4X|P%2k+lR%1yYwx}eue0F3<*DWnx zS)=-j$#6jW^>8}6$YwkLE(@JdCZy8-_3KH2+s}{zQK|cExXFe)ZP;eRPi)w4vhhFM zh8Z@TYr`@duCU=PHvF9pci3>h4J{jX*)Va6iGQ>Wcb{#{TWt7%4cFUnh3#*x4R5pI zZ*924hOgMrvf*JHrlgzr&$8hKHoU@y%WQbF4ezkwHXFWR!?$eMWy5}Fns^7>&~3xh zYFiZ1|83ciQj;8@_GBPiz=znE8!`IP-m$;m18Wm{Y5HQ%}^JsY;EgRUUiOI z!oPEfM`AL+5@r6KuH59o{BvtNu~}~all?+l-#*+zzUSbl8k^oRc$8l);;Y3?eiwjOkdx3)%$0-+{XE1{qssAP ze)*~hbFo@%n`h$pDs24PzGpl|#M5nS%A=IYzk;5UU#@xUd`j6RU!nXMSczHElUPkY zj9I8*(iMM_j>J<$e139LVu!$z-%OqRZo9eUTzu8`@;9G+l<1Nl?J^hNr9FJ-L*vRG zVdvm}v{~{IN>|a!Bt4}}{9=~)q#P2D;}AE?sg}X}F`-7m)3KQ=BtVSp6oHqU3?__z-n~|L}^L%ga1sCS!UvzQ7tl4ws!scCY z>1E$tc=;7q78YGqTvA%LXmR=XuC7>8Syg>aO|8#=?b2n-ue*N5${TJ}GpcHGmX-So zYO0D$rFNIlmWrwS8d^cAnn+8k(0xmKP$ey=93Q2O7}Do!v_H2lM}m@dm$aWe`pz8w z_4E^RmG+cNA3Ogzt}?D%OxyElUwy?eoAEDAP2r!!Ie~aQ2ks`x7-h~zV0 zrOWjg0ewBN;)s1~emGZ}AWY?OXjPN^4Rs?`0rT#s!%;}Z9B(k#cl zg1^_<{-pQB>fUAI7k?$V7i)Lvv67~n)MQ+7<5J1r<>XOP6}M{sNsJ~$IWCpdha1XB zDNU?Pu$7V0t$kii{!QL}^lB-+)M70$R%ky}sth}cPwF&OG8vz`=`=ypX$fh|m?~qA zTct816l1DUr(!B2zDmqeX33M-NJ|iUN{No8RHe?Nv>-DFNcp6N^$eM<^CY9Gs`_a(R~K_o{L%PN9w@17)lGxB%c%iDeWUvo)F#A!sQ6%DMY`%N>CD} zyP-yi9+O#zg!-G*ev$4ard-n7`ije~+n}`LP@cN!J6W9_jxUs-Z&#m7NvrP^`>s<% zhslf@q5OaQ^rUA=pZ(9IcV;-fYTBr21J@E)4ROk^JLeP}wj9%?YawRd!_+Z8y8Na0M^fd>B;_7ZsXY^=KlHX(FTLRT(6ckD<*7Z@O z$2K!YTz%YhLizpAw4b9>k~N;tyeGB0>D}E=rB-Cr@Gv!;$To90rGK3Rj5`;i^l!aw9%!4hZ1W)7+?HVcBZZ`Y)wX$vZFbw{p|*Kryz!63 znf_(j=Ha%vGtRi5WSj4|%_D7dTdZ+++vaN9JjyoLIgLA~1o~HKn?noeEZcmY?e4bC zhix-Q7JA*x~fq@K*EH$#o*pPLy{daCqDv!cuclbxEh z5|fKqdrc_`Ow|8)XN|g+*cWM^vgVN4$iyJ=U9DTdQvRN+^VK_*9KxA(>nLK6WpCRv zwsVNj{8EWQMvMyjp!`xR{S_6U{p7zxaYz~2PxXsPjLON$iI(4)X~ZQS-5CW7Vw~#i zw6ysJuwUJ7-Nc-QiwpTFwXAv>KPNtTNyg~}IQb{WfBm3<`JjDzOiv2MrOc&V9h z`q!Y2{dctgRjT`+Lw&n{J!4p{y8lJM^Z7RaLgC&2Y6HjAzs!LD!!5wED*VrARsZ{c zLp3OHwWIrAgyY-&3xz+nMgOBVf3F8fN`v_qN>NPRc%rRG{_mIA_~`Bb+m*K4SEB01 z4d!5U?f%uRT3z3;=BDqjZCn?)x#{12u>Oa)+gzu550yYIR8 zSNHw;{@*CHbMX#2}se|`I%cmHO!zt{2p2Ooaa`SB;8e)jpnLtS5d z`PE@mas8JWG{8D#(4<&Wn471@LEZvX;fG>BueP-2;;X(_TI|cMEUT(nq8;WFMt->G71jDY#lG@uOAD&1 z{ncT6V`rjM`EW6d7L}e?wakQ^2mddJwdNFd6cgbtqC&<5wEy<2tGlUgRUHeu$eZeJ zT3t6dI+_*Tnl)=6d|FyvLET#ARH@@K3g*|bUSm;LP_UMu?$o-qb%atZ>lQCw>~zK~ ztFB&JU46`YPEKYn;*;~6G5DXUcQR%r+>?hY`x)Wl73o#6oL`8mtVhSPb`I@A2w&tY zs&JRq)Kt~D%PZX#MgGd-#icdpxX0FNPc^KeINMOo_*C-xK{t zXvdFxmEU)K54c05(x~t0E)gfNH_?$?*%lJaSNz{KWDNdpuC6!6I$*w%~%UM=U z2Qf8kYL0l9EGeQ6sXd_}WE(e;`W`1(?c&m_imS%luuJKp-O5L=P9?kQ3nVxn`-?);Uz3|h{Rr+w%CeYj-$(Z<;mirbpb8 z)#%j!kz{-HBVAsbp2%7Ct_Mh_%V+v!PrB=z_4Hp-s+&SjKW=}m5N6)onG?*3Z%_X^ z<#8vEa~IjAkXF<)G$|bGf7CcgTTxN9R3etpy_$m|*fHUbuF+np^pQ?c%_6^4c&$6N z^jb!m@-lbnl4{@bQ~!Q?SJBk$L8yp~($7o7jaeG3dr9e%D*H%pwB6H2>k(1s#nMD}7>hi5W-@nU4Ec;!YamRD(+5)u8k^HE6c0HK94KI+bb^Uehg1 z*pKj~cbO=*fbZ#HP8u4ehE6`AI=OIgnuL+~HpA5Ut1x!#Fpk&=6+5|K+K>qeXO7(A zQp0=$)QKetq!+JTQ(|lSwMDf?zW`H&uKWh02@~t5Tq8%G@}WLRnH~4{jaUoLHSSxStwa;-oAwQWi~T37U;t;ahB{y9fNQJF+5%k zFL9~ia|fv5)bsG!DV-;@*)(wVQ!eVt1x;PEyJ)9+Iw9e1juTa#&ntt?Q7OzN*r@;#zXDtTC)l>P^Gl4GMvw9~F8?Ica77){qu z8>*S5)H8g44CQ~MleF2J)^xX5Y2z8>@9(wS{qvM+xTHI-Bxw(mBf@=b#$`%f%J-_B zmdTH)XUUJWjaYZ$B9nH-2Upsxj^dt z#L0uIwY&Hk-d_#BoAR|KwYr)Us^bge(qd`rNs&2ls5%C>Y!SellY)Vo0(~13q$36Frd@{zHoe+UIU<4 z0`!VkgKvRelE&Ov(qQ~x>@f9D9WhQ1p|0)mzd0$XpGusX z{QmJ-rOHEeJ&F0}mbkY5tuf8f)lr3!1rcdNSE0p_v*Og)^lKu=I?5vZnj_r9$e;At z$-DmO80N?FL(R2WQY5%mXAvN7JmHFc7cBS6u`-APj0z9EZsTXat zBbl*}_LTh4fa-+8_yRpHV`e?nIj}9U)wJf=g5#{WI%U1(h>lRv>6~N?lztFPKLAcP zAszi4s{d8A8R>tkfqD$G`)&ahV?g|Dv(|Ksj8`LlNor(CBI}0%YGn8PX3E7F)MLJBll9(^vlG-Q zzQgL2lCRV$>0hc-9G|K1tjHKE`B={}o6i4vj29E7^_ySX6u}*8nJtShw$<3(9?|W` z`0W1sFZp&un}5l-8#?@7k#8UA=qbk8w7`mYte1C2zM_8@!HHBh5ie>!OsP|R2&7&-}gU(hnDynKj zrVDdsUzC$KW%9(53RbrPCG?*STjN??ggG$t=BpgX9A6Fpb1BU^+6Pq!<4sC8$D23b zQ;@5JzZ&5!EvlYbQ%e3`)VN33Ch8NFQwjTNMoqa7W@*J77#qS;SDBG{rA6149%El^ z%34F+&0StCsodPFy?E4~s1PTuoBnS_&8u9j=~I%ktQbLUQlTP9n)yrUb6n?$$lTiO z(yRQ77M0c%)RfjrlQ<=6wy)xn@*1DNsA66vT&fbKMv7ftRn^u0>X|UMB>{>iET9x| znNd`YbhflEU+FTR8Y^}tXwEX#5s_O70g5Whuj^f8Pi4uR>hj7NResX_5NZkkt)Qx0 zsHUD1+4LUfH#B9B?jK4$AT+xK29l=i%i53WDTs7v>J>-}RF#5zW-v3IDw~*Bmvcq7)hXNs)Oo@{6iz(X=p9+a5WaoJxdB`6M+#L*!SB z98%PrZq~60S36(*Me@;?gBsFZCW%W%0{XB!I@HDIR)zb$`i&VM3QBAAX+&i)?T2B%3Mw@`fC?UWas(I%4ljz-6quPF)EcHufL?a zsHQYb+fwn-gGQGW)szcUb-pSxE+rS2NtEogr5tv#WE@fIPo|~QU${4IT7*5qk^STR z>Z*;LSI9YJKI+syG30uDC~IFc!yeyHPZ#ko-@ktUqQJi>@SmqZsLxHl`@n>sj#ujW z%iS-Oy(G#H%un1;;0yIPIlmX2t)EKai{?w<>&M3yk27&|uFqCbpYMxZJYOuIxW(~> z+$3HJE6~L!@ybvkc1e7&+4Lv&qxi%g*1GoRvCT7VGef8jGuyVGV?!CaB>qeJByAR5 zI-Vs!Hy^{Eez1Whi_X84L;TnANuF2Pa5YfMQqL#u4SbTHAM%~b2MbJ_e+iWQ-peQH z!K%{sj{&7jd-%ltRX%Y~fha;B`GhY2++X5xelcpyhF|IsvzSn3y?({(Zgu7B-+O&>FW-#EFYf=doB^D1g9(Ysq2P=jzP$FmgKQgS z*>IW-Gi;b{!!#SF+R$yo6dO8i*wxR_`F$I<+3-&`+;78|Y}jhU-8O8o;SL)%+whMz z++@RtZMe~f_uKGx8{TZg1{;RrUtyblHmtB=p$!+<&}+jC8>ZRtbQ`*D=(J&1v?+Ig zCVWQ^I(ORkmJQo%xZj4YHf*tBvkf=eaDxrk+i;l;3vF0n!wegy*)Y|HZX2f9Fwuri z8!8)iMVb6}+R(CLn+^Bdu*HTOZMeaP>unf{zs@#S+py4vUK?iE&}~Df4G%|}e0*lZ zHXClT;RYM_q;U^&|F@$J7nuAUFXI1gccH^K(V}y9-}x^bY}a>+fz?9|TyK}RAm5l7 zHuM^|8;1J(Rdzp4J!tgs{CB~LBrIQOylJz?on^%)AOBT&qy2l^ zj(3F}?>`EqzeqlN_Z!)3%1_ow@>3T^%NF;)@5ip8Ms^OIvm)A{-sS6@;7}IuVm7=B zPj#pQ;136JR}(+C0ap%I>U8irUafVBZBib0oZH@C@K`KJl{xIKpjk zH}I@caK?F!GXvPlCus@1X|yR9x}p?%pLAG(Kj9NUw*$Yj?GFPdj4^&T0q;3QsTHJq zFYqJ2dnG@>q2rJh10N2Y14CgG_*~#ue68SzfkRG1h2>cM052F1&Bs6!;6r>;mWP40 zr<*+ZfTz(QQt@*-uz@cdT;R_qaZa9!&MDvrX~;Ta-w7OWhKWBBxQ%ZGes%!QWf@+F zpDf^4d{U=}fk&p0XY5rv=Vg3C!wTTLe4W@^z>8qm90o4{?m7#e3;AyWzRoAK`V;V! z4DyD($V`kqhj;`BMo%Yi;7;I`=TZjn#lSy&N2%X}KMZ__PvWtF^Rs9J)Yk&wwR}RW zW?&ni_z}qU1dR)v$tQU(1UB&P$NzfZ{d{fU8-f49_qN0X+{$Nx?*RVjJmfUMZwKz> zI}F|m+>sA&>=gU}hhAjT8V-DvPiV3Un0>LKt-$nI)Div#e#qwq?*!J(CN0V$@bkIw zt+4L`zH$jqK7*s5Oq4X~vZO6g>NhaBq+WgtjJ(X0D+;)rZxjC40w3fPI&1`%vK8Bp z{bJzze3CbTi3?3wfio_LF9m(Fflu=Zty+M0UBUhld;{<`KC%B3@Dm%4zmmSsC-w!v zdcL{f4ZtV(B&}v(RiVMFfx#m7t@z2fN~tUOB<#(=_7dbdz~2W>;#@-Vp8>p@PyEP9 z#<`1?dKf$l_#|H|cr$QDxxur6&)E2G;N0&)Tl@$-!l!8GTohN!`GkfmfGvCyzrcqp z@PeOaU^a}y#oz*;@&>*em{?`XCGa4h^tCQv)-~jZ_yu0UC+)KkxSdbZ z64{l%@JSip26}2ZlOb#!a1UQ6cq{O7AEMyk)xgXAq(__!fxo-fo)s{DGJq%EOuNKS3h-h+$#Vhl zmwXcTUf{V+hPGM2J8n09;ZER=pVDXXBXGeTCJ#Q~)Sn@5jr}y>HFp~N_<&#V32hGp zH{E6EDe(HA6F>e}0RO-zd3YH3IiJuCJ$)+i7X}yDw!y?BF!63a`jo%}_n5J<4fx8v z45irb2k!or8S@23-DlDjIL*cde#Dn2eG}&HR=x$`JAf6x=j<0;;JF)Vx8Pa88a}D( z4Zt9u~B1Mhv3HViKCmTlx4{5GK4Zsrkzu{(@?Ja7r0 z(76tn_B3V0e-= zBXG)o!h)v*<6fgI;PJrOd=md$U^}0T5AOpXf7|qhKLTgHW9n!w@a%VK(}c|c2KXfG z&A_RDGwp2}@Lj%6{8+$+mdU3;M>}O>&2u_1y#tzp3+#HI^#r)U_zz5*5%>_Fj2jOF zt3HP2_^AeV@X6WL9f1s5oC^MVUZ_`={KZ!hxhVlPl+#swF++{Q(2T;#jOUZBW>3NG+P z8y7yJ$OMbMK#_Zuya^PURIlh`>>~Vs=_|(CGawFw11&^#JKi2_O~C${{G|GYaQ`@#NTop|ND<)Z}nj>eAq7R zop&>?K)kn20aWL`teLS7nN#j_sQaDW=H}ng{~&6}J@sMS$99`rU&EZ(ZC>^s{)s!} zzwJZJlqqEPe&j%AsoR{2o0~6-56NNv9{)FS;zV`+`RA+o^XIGb@^a<(`&FHIudCyK zox1(@+tsgs{cE*(^JdlD+^k-G^;LD`$Pp#mSMjAiW9Sr9y!yfJI_|ygTDp{>9^>BN zM~Ca;4=-K1Vug74D7gFZ-r(*-IPb#j#DK2zAm*h@#cb_G>9;mx8&ppId=xxfrrnpW z=ybkM;NVW%ymYU#OTw3x5x@Ly6#u*TmX+-#eQnn9mzD9*K@dMTO8kd$mmhw#e+e(Y zibI$Wlm6bF+Dsx6{{cx~{|=EpZ#(QIf5cW+Ciy$O_lpCV4vGhz|J8@r?LNHwpu{2O zBeNIg;^A-w@nequ<1>R#y>s_oiclu>aqfR`)gU1NKZaE0{Cdsgq`cjG@o_WWiT^iu zoRMKXXmi)|d+#0n+uho)xD)Pu&$M6{!Q-|6y}S3^Gk15_;k|XuVun7!ujf70byz!# zf9TtOXID@=Yx+wRmT?yUTIu?J?%4&lHaUnIDL zPdAO@Kyep;J;O;neSJ4#AFNXjzDT|pJ{RA}ptSQuJ~!XrYv<|d>FB>jbmQ$ z(|HTE@%8K1s|Ox?w8Q zQy)E5c6F7ykt!;CDj2-+sg5gY30L3v;pbOA3UcGm-{D2jugX?F^Ul0^^PVcpOaFJ^ zl~-SI&BejsBUc7*XdL&{cjsNHZVcY@)Fbo$UwdZ)US*N&{YGV2R2I=8;F3ew0uciu zvW6riED1!xxx=91jR7Nz0)YU+D4Q~_43g1N6cLiRAPERAh@xz6h*xD(aSbXeqDWLg zT;*iSlr-le*$s;jE2tKRb-`g?LF{ZBF5_3gJyoR{LO<*P+U zTb*jr=8g?l(Jt0iUW0qMpIW<&Pw*(o^GSy2Fr{hJrYxx`JrtcM#iWQZF+e7GAiVuTwrM(+`o{g^aqlFJ{L=!*WH z;2xda*~+S~^peBkBcv(G+rdv@=1 zAAM)8w6xTH`|Y=0R$%Kf@!G5O)iXE}n(DLUyDyRONZD(ZN>1%0mtbw&#n#VFw9#(1 z<+;agsoP>31AFRD7JN)!t*<%GCCrj9yF|~+2!2SZ)=I1EZWjDKf?q87)q>w7`0Ww! z%>c`IWPro4G{s2*Ix{)pvyjzg6(>3;rX)e&Rq z!vsIEuCtrlIGfwg*~6oqt;tiLOPzhZ@c_J>kG{tV{!fChA^4htm;EV~6Jw#1oONyE zY+yfUe;@7a&OB$!mO9(A@c_K5rT1Rcm%r1<&Co(^0^fR|_w7GBd%zd}i+z%VA9R5_nw-S7! z;4cvTV8M?U{EdRYtFGy{9n3w}&)nM4=3dJ)_wG`2pKm+>UtREU;1t21D)>f%ZzlLN z1>d! z@*9%7rKhE(rlzMPCMKt5wu)`uylIo3JsW3+Z%9qkW&Ne;LuzVfTnldK*)tTro}Q7? zJv~v~cTY)9?3SE)CO0%~8q+9zJuM?CIXyWgHAV1XcGj73ab3eWa!YzfdQf_t8dX2@ zEZsm4O=22_8VkuZ`cF?!OH9u^y(%R>;5&%(R5sFe;hze8a=NacT<`SWVchz(1O>roCMIW`m!94|J+mPl zG>EES|Ia6!aKdSQz>({`v^_(kWHvnLhD_MN^_1=zY3b=1-7}&Oxl^%4E_pW=rIxR%)^N+@t7{`vmfbj}cqGrFf|cp^NG0sn=6 zW@?)xP)N_n$ZXi7N4m-90@u zLtO3=?WsJyM_4=(J0&NdeQfQjtrIgdGBeV;XOv+n6H->vQqJj?5`SEkn&*Wtz^k-W znXB;i%#QJOYW%56mAbuzAt+{~CrXeGTu*M_u65fQ$5uZnO&3$TNtVw!Cne>;^~}s} z$>(>jRjul|8X+Z<-qTVKjGvi;+@0U~#AB)@aXlqb;+h_Si+}J(0+w`q-CE~oreq{` zmaLUu*7czDs#CXPMzZ*r7I7K=r}{f|zJ1%2&Qg`g%f5fjZ+fZ(F|B*MP8BXk^sk9d zOYU?qwDjZPl5tSyvahy!%Kq9@6Sv8^Sgks>to|NM4h>@cQZp-5mKFOXMp&_uO;t)mLBj zzQOhnOWcPae(1Ju-|jyD{B!rk7hkxqzuN76gS~tAx}Sdf$>n`(ZmE2QjXy-#+4x3U z=-O+clU~jmtc7l(7P?!t&^_Sl+GDPbt#titof~bNU7o$`mfFW!zYnZ4E?x^_-zdRr zv7L3M;LjC&lHk(?f3e_43;qVd&lUV5f`3WyCFSePAAXA1_lKYI|D~VO(3O>j4I4^{ z6xVLR|AtXb;{UspHOI`PCaPHWUOHvagUv1f!&ghH_`IZ#Bm>E$%*(n(Wg|aQY}XJ#>bytqbmLXIqH~t zIy$~_%^Ef0v=3<1^weWd(^Y|-u49cKciPF#8^^`R$2AxJ8oy;+T$8xCMsc3L(^Pm8 zxKSg2=%&kSSZKB$&MvWTU0XbO)I3*WasXNsZRRfOeU zANtkq=7_3q(ohO@x_Vf zs_5oZ6-T^P`)FIWYL$KZ>8Gak)D(9iKxHK*C8qtiaa}Z+_TcaB*s#Bka^I9`p`3#S__Gi`8RqHEO(RTm-{mKkzTXf|3HxR#H zlMbFkAMM+>Zw7wC%j3U({d!Z3)P$ccU%uSqtr(Jh^2sN*VZ#PfOu^p=jC=&nZx_tG zrAwDidg6&ECduvVe%*D~ou8AFlR9(e%)aug!L5aAT>bj>+bXtk(b;F84ez2-Q&VH< zSD$|q{@;A_jmekEYuvbTJMnPl&Ye3=G2L=LzxLW|_SRc(c|0{g9(U$u+qP}?&O7h; zV`vJhd>;Q?6c3# z&^*RSCZS+&$JAA`R~x7Lo?)<${Z+eW-C^#FwMEi zXZAYG955$}tN9A<>(;F^bY<(-t^PAKU;~gh^n^JAw`KbM(CoL(5??ht=OwfF=gs2Q zn4PuS?1J@X3qLm7yKmoS=?eOPB*o*ua^=drCQX{OlMg>r_R=&To(60Va{ynVhq(ZE zJ~RLCzyH3ke1_-n8+?%kXaR5dkBk<4VV3%aS<)u6&I-Y|SI(gAI??c)SxeE->>0C| zr_5%)tK7hkJXLW-#p5qqZ$SnBpaydhv`3>S=mGNbxA)C@Zq;`=UKjtNL342K%VuYb zhLLY7-|?g3-TIB_)+@bODYAG(rGet4$nuP~ZQI637N$do=E2hw&_N3?kt6tD86EH) zo-;?t4f2oO)t=2GbiVNK{Z1JT-Sqv*uEMpGXiz-aazq#S58G_k* z?vAgN(a>7Dpg6h3uJX5IM~|d<{AG*t6ytBFd6`h`=q?(T?25eOke|QOmAel?OoQ*f`_8n^98{qpr~w_7%}GE@BppBe z@Plo9a<1KfO}gDZI??VJmE;k-=o7QQeq`2fn^^`lycO1G$xN3(pRq~OXKYe4=>huO zO7etG|ExS)B{X>aHQzH7yNHoppAIe9L)w4_TI3$Nu1H59U&uPLRPsNM*@IVgvBGf) zc5iN1PXh#8@|oF%VH*0DnDr4(y+lK~K0`xTpU=W3$)+}yO==_>qL-K1R&_J20u3Jj zn3$My`>!>gb^aIgf&Q`{1oMGDa2)6f_{;9u>N_v9qKRGXfvXa%Ks3x14U{a|&w-+0 zfNcDKeP{*^fj&b+pwHN(HZO|r&ziM>2Jt`iq}lnhQ!1}khQI8+mw)NnO#C5qE}$cz zg*jk7h@6vv7FzJfPCh;@*%n{j*%pe1`$a?k*fRWccbi@MxoFs7_BYXhK4X*8Ws?GZ zhK4|&p&`&`Y*OqBkH4PmIjA&1&sbmNXO&lI2x>(-0$quugU`@Fd16XOd*qr{4KT@gt}U7A&x$q9VgD4QenSw1?9Z#bs^7y;-*Ay7u<;G|?bkcvLhz z9M)%SQvSHEHg`-{o1K$rw~tJ+Tdz#ETSUY4qTyQ6FiA8}D*71zE!|N0h5Z44$%2=E z$@O$-x&Hd=t+=?@(-Y7@dnh`f3E2X6$Hqr(?ezAxS~RQ>4PKu`12!qp=lQZp_l!xj zJ9CokHqkIsG~6s2{vkP?3_!y5$?^S+b|G__y|IVE|Hvc=FegmIza&R9uSm9=hLzC(qTa`l zzuiZ5uI$y%W-nKeeGVHi=URXB^c+L_$dM!0gDd)o4osdr+0zgyUlnPoI42xq6VQcU zD{nrxxZKA;pZ^)w=L&F#^?$&hx$o7h7xQ2bJ@kV-2%uLg>8HQh%o11H6$BwlbGiG?bR>;TO z9ekn6Xo2^v4cIDtA^cJH0~GZ6wFk!8kKb#*Q8?bO&yhaHU15EmC0(FYhHP+7v5MOA z_5Vx99{*OYTAiy$dq(Ofwm+92?dPGOpuo;Q|9sQ4MfT5s{?n9GvfFRJ-Q&al9)3du zYXR#7az$ki0}a?yFx;`p#Q7;FU9_D zQYv&B@*`Q#)4so*o`Wft4x6r-e)GeL$zZK5HIWp96iyCPnJ=|2#1dKX6XK>%+AjJ9fNX&-C~>2xEtJ+4PK}>6s)` zOv_rdXkqfLJucvZzZ6-aA*cZzwDdk)t*^`-WKaJiN$Yc&kHPv}G%3ZNxNV5N z^YSv=w|DPm&G!+@vaC;^K3=wzllS<;Yj_SV;Ef(&e>l$`0r|n#UL`$12ha)n z#Rd!apViOwsHmtU&3l33PCJA&IUn*mM^e215Xd0@Prz63DJd!O_-JlTz6DPedwtM+ zo8-;Q0BaO{=6)*2(10C+g9niU*E7b$TuWpe^u#MlQ8 z9B6tj*UNXn^T?L_kR@cD^Q-~j9^^-~N76SAj~aK6KVt;@U#&eekxSMf=s*YI6ZQjJ z$9fbwC-8RJvSoJn-FJI=hTlPjZe$1D!EfRinV~<{0@=(5_3DJ9D8rw<&Zyq95p;J=UmNWc94SL^q5Xo3H< z0Ufjf9klQi`+_b|nH$bA57l||rbAimmXJ|y$B}YHYN7$jcVqN7Ki{G*~f#0u{lXL^mApD=KjKQ-HJg4!yi%r$y zvg)dbpwo14?Hr!SVx3$A4PgTr6S?61;;&Y#(S}v#b z{oa1F@yhk|dZR4IMl6_|!r9903{-6RjjugQhrjRNdc3HAeo%?jg!?kS8M+nxyzGIdAgCTfQ-SO;I_0ABpm% z?mtvHT{gT>WA4X(Vk6lvfde(jVT67DpJi9SkomN~SOR=Z^;DGPM-jnqayUtpKt;UzdPvIJ$DHkhOMb4948#x}HUm!<94?sNg`wzEj}|>({Q2-?j&@>+kat#KS3rb}8Srvn+4=7vVtO zmuDb|?UVBj|q`!|_3> z0UpFn;01;FI=Lot9OTN#Gm+mQPezW3+y*%^@=MnYE~|H3JIuhr%cJtHLGGLWBFD!D zQ1NZ3k$8X$^S|bXL?hqzh;SgLNo<@P3-U;wi~I_?MxWyfk3Z?M9t`DmB9O-f@j<10 z{_)L}hse_RF8gB(*zeH06*ioYGr_{v3mkeG;9ca_gg>zxXA^UXK={T(=A7tsUofCtPEy!mFw z+xBYy753zH$^Kb@KpqPw_OM6(onxPGmveGJ)(&Dr*REaLD&9L0I+Vv8HE7VF7xn#Z zyW)x~d>mNMNcz}Cu=j!I@B{v{ze5(_2l!Vk^r`zcNwy-_+JnaDKzXshO7HzXkhPw? z47nxwS*GW{J>9|n3p}Xs13W6u4fwHcVLRBDs{3 z!D001(Vp+v1J*(M4rG9_unU2%YMuB|d6^-S`bD|TsYwgSIay!@eq^o4H+ zzlr4{gT&IwYoH(C8N{xb1MC8D-~unO+t^I-T(xS|ts1!2uMMv2UjLQ9&C>Vi`ZG3t zGSA?k_XL>UD`4lHcb>-$9H`ub4saiGgP(-G;JHM_!t>QHdxaw@-u_~L^c?+g`YTaP zo;4qvLvBIuEbuvI^aA`S@F9rt;xCg^!EYB0_h`@~9UJ`o_<=mbS1Nc=8IScy??~{r zfoD~r9hqak;5Bq(lNEb;;5Ux#$7dYb8gKtA!z1v4^_>IL_Z@w#7{8F16Y}^w#`fbw zZ?OI%OVCT7RBRqPhi*eBGRE^eJbR(<1$g^#lyoyTB^vrG@;#FO%mEeKi|u2c^-c)n zZH>nTeCr4ZOu1TGvtmxOT*|$UD^<%WYvs+b0PnF;DHAYvOCHF z>{cJ67A{=q&+DG!3JVL<6wh9{Xwf1+pTQc&Jjv*m)QG2U7+6SsU5%j^m$TU7&;Lij)g)Pg}zKJ~(F@#NWYtGSgohpHU z?8qN57P^LAq9U)rQnIu?VGsO7nM>x+h5zkUyA0ce9)T;q1G2s0>H92iRGDu$IJ|#j z|H%H8y#jlc*4iuGCA`SBvL^?3mVc0`G`tQ+3ACcvAnOI^v#1YuD!9V7SH3&b5vf}#g-rr*X!QO+t556w@ zjpBQ5GWuX2-b;8ee|qEy1q zbK7RmPxes%80N>{6UYy;hpor%Fqiu7y5}jjnz?*`ZGk;7(7>m=i|91T4a;^aE2?$~g3Y7bR|d$^xkn|rG$kQoZq zQG1+!pZL#GorDh9-=-a%65o%(yWlJLL`@nmr;bx7CPYgU5`@9d-HGR)a^4=#-T^ z;;iv)VnQRbMh?3?J2&_h%7mCUXSI&$dScB`sLR+f<8uA)xgKF}ZOh;UnVUU$oPNe} zqVFf9(q&Bc=y4h)`?9`chK<)R2M)>3Ex#Cfx%&ix@(Z1Pvd3p%8M>1H2{Bo@X(Pvv zx*~f_OlaJ&n;p}&OMH3X2LqS)A92SoM(+}T5UyR~%ZV0vm-v96 zUHc~YO-@ZsJFn+}0pSmS9RKI|fqe7j-VFs?3%2POeq3So!oGzA3SAw&2UO?R&EGnI z$Nc^CtLI1M$L4p)@0C9we`x-M{2TLU=P$@#p1&@CYyOV>{rS}kq6&C({}#cx`E}+` zeQ4Ig*$d|{T(EG-!sQEBFI=~9!@{i#w=LYUuyo=6g|4W2QJtcwqEJz6QR|`(MM*`y ziux7}D7v(0Xi-klgrccMHx|t*nq4%%XhG4EqUA-ai`ErwDB4=It!RIdE3RH#r#PxO zR2*B}y0}AeQgN^1zQqHIFD)KgoKrlZcxv&D#j}cM7tb$VP`spgdGYGvb;TQsw-#?J z-cejyyua8js=lbsqNqioMX`%oFY2%;X;H65%NK23w0}|5;tq=^ES|M^_Tu@A7c5?~ zc=ck@(pT?jSCdmPpYr*J~y)Iu2ZNBM&T|8H@iaihVV2IhVWYS}8JUt-pe26qaL8+&=H4xFhuY(&neF=InxvV%{#6LV``K4#R2 zkjBwM6$;-lxa`MueTOxNT^`EJ9I2lL&ddxYB!rp=<23K2totB*9DH^Zx#M z|9Sc7?EO6ZxvpnD>sf0(YpvAWu-4^vxm*SOZ`&?cD^K}Xt$zRUkHzN^r*9bH`tPCJ z&uGnyZ9ik~;yacHmM**J_GP!+6{x%A?z``a2X4JBuq<(R;EuZk;n~*&?z(5uZRZyk z4=c?!{p(8>-uvE-BPQkkkNbZ(>0Q!CxBPa}7WMqir0=We+DRYs{BYu$SlZ0ZU{1v4TJ-H9t_RLKHb0klz%{`&Jb#$WwV#~-baJ~c z;^|ZG)p_!e_k5SjBR~AhJzYN104>p+5B#bdbCt4nDd{wldq~}Ej=Z`aJ3r4gRlVf7 zelv%cwRx`7hD%27U%qPz11NWspUe7RJ@Z_x&QQO!^!f4IR>t}A;rsl^fMo8n_=Elh zT&{)ZFI#j={1%tXx>!CikV+m0}DYHtETx(sFWQ<}(`v&e7D2l5lFe zt*2t8<$5w)8nAvF097haqD(4GUP@o6r~Lbh@?4f(>~gJ_b+P?xKXSRYb!^-A6@Ah& zeO3(WlbnChXX8Tp+%)pUKK~$n&KT3*=V{qK_2m3gubzyT`mWQB{Q=YSU(=bJd000; zuGkwhyJM;8N42MRMa^!j`DE#~OK)zAk25`{Dz_sP%!_K_m!o!jw2Z>xs-u}*x*0F6 z)XfgvoX?z%O@W&`w)OW@q9<3C2Iht4hUSH?4PB?3`{}njW~O5)&shu-_$<9z9yOJb zinn9Q+bXSv?1_-Mt+|bFMHJC~&~EKIZri#^8Q_{^} zn(dILAB|MBnJ-!C(`61)ZB=RBQw6|3WWE$Nw};IwmZyXzG`H*KF6&*@`W~6;>5OEb z^fF35%=;a!*V)msW4ilD`a3M&laPx7bF1}J&FPm;AqYpB8Qp<_e!rRRH*9u9&6jj@ zhxMb;QhtXtx{}_QAG5o1I5TIS<{s_gc5DAJ=1A|l`CO<~=!f;<?!jGBax;eL5W#I~_?c-=>$4wl3nT4|+}_JK?D@ z-^tWVYpEY8`0ZvM&jUZ}_g`r7*;8^YJ~?dg(5KMom8tnNFoSzu5c> z8EHN-wnFwo=|YzDxuI;lTV=7y-;(jDPE|YBS{XHaWKQqv`l)UD#LeuL@|$lOm}~#O ztk%s}bn}qyPtm?^OmuZZP2@CtN~WL&(iJne>gG%A?r<_D*d8kltQSVc_TNXz7-g7dPhlR|(pk}Mop#8!&9Gqj+|pWBBk37-T^@zQ z(kxiN(Dr{n`&w%}13XU6rDUJXVIGoB`H#{flMhLAG0E?+ILxwpRrVZ66E7{f4tjsB z95A~1KD9oimcr-rKoQ7%=qd1q97S=%+PYcZdeE?}-Z(TNJ}G3rXsze$0h7m2_b*a6 zHOp)J4+!*Coy0c1d2f7p)D3#~rgutPDgTct7-|)MN;h{}bwhKM>X+mqbbIBc-z#ohc-wN4G;S|A#u%u&$Tl#+LkS@ggZc&KaAfo3GV}tImv%(bf%@ ze2{rU(7WQab)m&;W;icz@S+><1J=}1`0Dyl z^6S@b@w8Osx#n0Cff~ng%D-WVTDR=kT@K07Q-(CIo5zLR1@|l;-B48=*BYvZ#fRy3 zyB_RX_F=}&KA=AQLdyR=nvfO$1QJx;aQP^?j-44|%08u$wh)Fh0~m`rdZiPUL^mp|^MY(%X?56z?@a%I66Srb}-TbDtwEL@GWAnVa?IZtdYV7G<>c zt%;m^F8D*2Rmf{aTe^{VRc5y;6MvNigz+3FwZmEqlPvTc%$_6rx!Af$wZT%lGEYCA2!EFg| z2?w-oTlF<^Iz>%z@fqEGnRz7q);eg+JB!NfPpu*&?za|76M$^EbuDkO4b@4n zh>It-!76MCl~8bZVzqVsRH`Ir_;hn^n}9!gvTnAts<&BQJ?K9M2O2-cZ0I7Z+4D5# zNWyDPy+levU_JkNHk+wxhBtnyZqD$TEvi`YBT{Ur6`7*iW(YHUJ*tKL#3)0R$=@=g zB#%SKm;Z^jI&bh8`_Ht+tlv_E+LeLOTu`VQZYFA4&YlRFn`%VZct!>aMvb*@3-mAK zL9o3QE^>AH_v-WR_#48tf`iXmhhZCIAZj2|RW~YenO@ebtvl_~dgDlF*)V=@SW!@K zbOeMP8+|IPPi3_Qgi7o7_IPzY{7|qyxF^0P^L3aNp}zs^BcRABpc2};J=W_2Rbdyh zwT4M8kJQ@6!Ktn5C~FT_!jr~}ge5FDekpJ}rbHGw>a*JjioKY%s}9WvfdIke3O3R1 znE7&*=kiJ*yaE`+zm=Uolg=XYL4+(df9fJ%G&BEL*()=&bwww`_o-POQnP9gaB81a zZyZ*6hgIIjK-AcnAGN#UjJaFJ{7ih4wr-=guDh%Y#FZvttF3v$l&khn)N{xdHxBJv zvC0w0n!9x^atL(4>tdn0-HCwp-gKBihUl^$sOHU-PRvn54`})=o-USNCU%xGEYGr9P1@Dez2r zzBw+>)#1=5)ARO%JlB(=3!ulsR#EU}Ji!hv)}hyRZGg#hB|YsFv5rOBdHMH|<{C-U_c^dS+2L^R5t- zl>f+Sd9FxGcSp^xSjzt~Y!rl3Z}0OMZ=4=A3pVO^cGt$tQF&40unkvk96lcR)Uc0- zbmp@jcGPZ@)}wZJ;%~I4w!Pqu6^y!E4bv80l;?8AJ=XTi6|{H97!XUCz6Gu!OQ&V| zQpL3lLl3^Z>{5XA>gn>nXT{g#IBfm>zpH=e=w;99z3=Poham#b=mS|VD=1^l0=)RPZXqf66S$oI!H z%!+cj1ai|0K%?fi2X7ZifBHVX_ha4Y%U@PI z3j*rX8xOfS30F+fQz)*2?JI`qtp`M0N4(LEeFv<^7@c0WPk7^U81MMmorT-Bu>nrD zUIfM9xa4rsI$eMNyDUqmF9V_(z_STUSHlu*w{909!ej+aR?uVx zO;#{Ls&D_ys-zY=x!dCpKO9fxY)_^Yln&zIwS=K@r%IqQV0lb|<_EySf%&GfC38tHWEp1?}Wraqt z&M-aE-cMt}u6xhcjpKIQhhDQ{x2QGSWIauhq2j+DRIqQw!%;N&+875m7Q2>Euh}v6_ zQ4~aE4=E6kV`XYZY$7`PLwdh|+tTbtT9zdzup0iBit&M7P)`jaSP_ z3rR#oj+u*KXOuvo^q~k@uwpfwZ{|iF{g+iOFm%xWEBJQB{!JFny@%#=ynBhYi~(k` z-S#WqJ^eZZmohmyD3)4;68j7pf6vU4YOVR(6p$6GpX;pHIY!^{_$0k-aK8ub9ZgjJ*tc2a7-yD^hjQOynvV#x|Tvc(<@geCds;wl~(*P3J4(C(^^jI zsJp1GCsf%GKiS&C0JCGgM#j3sX2YH%Bl#1vF!$7$LMXC2!=2VvhL;m5>R6JsQu3gX zFcB#xBU&k;q8?a!l}rJ@CzSt{`e0W=1g1!<92}&U`#70=XCdyd>(0xkwc z;~<+`S{^prZU4*{fLk{R;?dUeL0i|Zt=l?LxIGcK6z>_S*jr=nLWl#85~HopV3o2H zdWctu-1h~vFq>}+n|EQ~S8* z9?>P%gn=pj5e*|`F?|C-v@W@t#Qk15cONJ)>b!_;=nBz+=UKPkBMU&22V~kH>Y<2-KO0uKekpeGzakM8`wHM8}qcLKk`vVm?*6HApI*6 zW%v7P%>6ayr|$c`(e~q>knzsxv&@16HFthc8|n#r=xtSQ7WvjM7r0!(Es2RrgxjgR zyK;l*RD)<=_Hplw5?26nFasntUu5>yUDSahw!8@aQQUH{Z^g)-871EMa48I%VD`n` z=KZDcY-d;Jxvrph)pJ2S-|j5yO@%LHD-EbNMXw3H5K2HM5Q#3-n3t4aV}ouymjtN=LnYX zXv3lq)+qL0zo&GoAUeo+`+@o{0z1A7Arjr4S zxR3vLMH|r+*_Yirv@^1Ym(`iV8L5KOWCUG8jUF>2?8Ta0(AALrf^bPa@%bQC)UMgH z5_vqbtEEJKWi^tKU71mOYThnnu*Mlo8uD|7e3Y^UEhQOW_T!@L#{$T*R<&SH{q*Gg z`s3Q89jO_|<(gy;7lMey%O`Uo$i?7Wxy!&TYzE&isG|fmRMbpIg(}I783&2h^s$<9 zTf#3}eTlD zyXdE&^IY7Bl1bFC*41*@^&L+vwVJ49R8G*Eze_{by`+*Q=>~cK2Jf`>)_h?cxNv4i ztM*vtFSI9O5>#Tz&BvwHvBK}Lnv#CZEp$eM0w>_Ie#9_9#T?HEW$K4FEUq$=D4N5N5S!L82dh|_#jCcqc0CN%Xm@x9)k@6>3?3u_{|$jB29bm8x}I&IvP&i zSdtkV>gmXfkK)%G9}&_vyftiDVdsoe5pt!{^++LMvr}<84_~iv3f1W5R76dzTqed8 z&@Vf?$Kg}ims~#$Y|fCmM+SVNdTr;3eo)QlRYrdvnvh|}k-WIaIFg_EyVdkD`xU*j z@bNpX4`tKtk+*__yuqu^|B}9eSI(}&nD)#xD6MXetK*R4>RM|uKnme*D)g#xmy#Jz zSV!(4E9seY1~U4(#X`C68*06KySyZ@lo)rG)Ma3^Wb0in*GB)rN5$L>2aV$u)}xXR zcHTQiH;307Q}3IW&>ZQ*`lw!-i4Q@-@@97GrkmS^mH9bV2pwFfU~-74S4LT9(_B`OGM-lxgn`S8n$JsBSX+V8DXObj z@+@bB`Dg%9+WHk&h(3sOL9V8)-NO~L^3^P0RtFHNK#$cepdBGR!%$%=#;#vU z@_CeX38k|8x0B%x@624@6Dl#{mskrgl11NY_F20HVb~g%!W07p+rb$R&14|RvnI>P zhgp-~mu*}(*=5v~xSSJ4sV|g%i8JQJvx~}uj;~SHU+6qLj>~w3PM^s*s^de9TS{D+ z1J*Y_%${Tya$-0q*+*n$*eJ3o9F%hI50vFbYt0RE(dPLHx5{YE_hu^fI!`wVh~u~A z;cjoN6tl#{TkD5|2=!HZNn%gMUZb^%H6C&A(5grJc+np2VCdD>Xe3BhWr8s+fMO#b zz0r9WpszcPB38$_InCYBvq>&FD_8V0lw49YUy4FBUDhN0MPHjtvilwo#H!;ndvMr# z^bRiT42szPtNbyR6U3q|I++vxZ96n`9}b)>_D5 zK#M|FY&)4T({t%WG>S>jWju7#AK+mYpTe&-?OlPXoH0-esjx^IUcpahwAp8@Dy>G* zP4@NVY_sm+cdfI)I)E={fuYlrtvi_w>B;GP*>FM^VO6+wZDCjd{re1``+S*~=~*S( zA^NKoJ|D(=p~#B0)(dSiQ@NL+&pEDmNar51lKM0dMuy@O)@`Wwo#P|rnM$Mb9*9vN z@ro8jY*@(VGiWO_K{uO9)c}$nuk@M9CXF`8rsrX)ZhAgct$1!0MIYtYN`FbuLUKDj z7m+!%z}432Dd!F1Diw;6^QGIxybsO3FSY#_b&F#3G0HhBFam(co$o2+1A&{j%F5=E zFs6NrLU6}Uxp!G$+h5Yft)g@Vp|SnDN$HK7WbE*M%0}=;Z!~#lNi?}UAohZT^&-_Z z=6&88bBY-%h?@6R)|BjTs75 zd;pVHQ`Y%-AResPT{Ze%6sEJiW{A19Eh{whc-&iLBX+m@f}@w0WZpppcek0bP9N;s z5OYaqQN|sH#{+JdTm&y(K2Nu~seG$IcfW4VKtpt3S(O8|Myaew& z8lP+gT`+;*;!2piKj(#*jvfZGHSW%ky(>5LW&fjKkTpvao3uNtVM7PoqzUBtY6yBzZj zt*L`tc;2Q@fj`$e#-VFg-xvQzsBEX!^ekCMdU$-M-5tNwNSDOVGSb81V~j%uiSI^) zPyROwM9f{rPG9=BQhmcmg=xXQ>Yh&26oO&K&g%3URccRW71{ZTdyV&w8}A-9cIImv zJ}k^ErJ=;FG!hzaXX=df-1uxGJt97pF3*v^M;nKRXw756k={;M8+-2}dKrNmG_cjm ze@9f(YBh&3jFU1~awl+}D#DgfMP7fqzle__BQs?bnV^akW{dn)715f9Ih~E5nD2z4 zgsUpFX2&uVy<-Fk-|S?kiiubQ3vC(8oq4>B+ROHQb_yFBa+pk%BqOJVlL>B`6O3gu z4*)_JLLfGg$H=vTrH!tX2}TVAm@H7n2h{S;yRY*BItr(Hb*txambjK8iI zvO7Txm5r$fTybnj3l8*Dml%n8z11bI2G%x~nt9CV^R4iuX8WvFYZRl)jA8Bd$y-4J>fJ_DNma z|MW&VrN`+~#60bYuu;N>k89+GS&6a*{>sPCM0tVHnsu7(oFEOb5OQw}n5!LiWA!tS(So1 zE(KxYdNR^r`+wUm2e8>^`~QVE=|H#r4ZN~CK2#S)#t|C^X{)v9c0QXanY>=H&6@Xj z7Ay6$Qh^Sd0nVZ2N-Hq`X1Nc6*Kx?_hS8kXp_HCy{fvFYy0>wHOP*i|j1YHe!|7}= z{dN{Xai|>5AjlPCunsd{jtWbA5dMhrVRLKlE@!)d>x`JNG%@Zt0yby2TH+<5QFhGV z;J^As>VS0<15r9kc;ZE+0nUYfabyLb7?#M{*!A4v#^j<6y<#|3?F|l#m)UJm_b#LF zyk!Sdp%09{kt>F@BLBEL8r#EEY(+E6l_3K2Ghv-iy}TQ?3WQ_)|ByS(Xq;P&@a@&pzIvD6$N3l?NZ zp(JOJqmu>1gZ>S&H)`C!hc&IKXshAcSuBZS!dF=W>} zm2-crw9+SA-*$2qO3n(!2-u!~ADQPuX9!d2O4P+tlfE{ZiP!Z-jj2ani86JcWDPkJ zv`iKp6`+^ssTl!fvyyZx&!gmw(&P+pW=zy9Ix1=nA4mEOuRQeREYNRwx?BYy>`$rH3=qvT)yaqP?+Nim!#{5|BMdq*q@vym%$9yH6 z$dU+wS<3&l*0fh`+gio(gY?X9ZxtoSxz?RzWW~rn`bAG4u3YeVe7J5#9y1>6VjYg5 zcS(;QCZsmfAlE=!QN>RVnFqrxdv(M-9Kxz3Iqy%X<3G@v-W&?t%muBA`g5HJI}}b` z-z7443=)GzqUC9dAdGLW50!P)b8F`3&@bKTA4 zPYLa*QTgqM3+Q)=`Hb*Rr+PU)&=XFiNqO$brqO1rbba}+1VkiU&I81 z?b`Rej8khW1;SYFXiZzdCZlhL)}*VKh}QJq>SdpcRim#~Yr31dT$aNz z_1&U1{ZM_c)0&`DE~R*nnnR+-7EX8}Kfo`jo7^UFP<`#`^JoK&+S|jImuOFm_dqR` zTt6<`_-tR;>`Tiw2y0JQ3Z!e(Nm6K=?kEN!*wMEvg$EQxNMGizQ12%3cuKe^mS zquOS$Zr$DzvOD<=2klj_h#pUkI*iTcQmy%32!5z%Q?=FEmKgBep^p1*cDP8r>_A5osky#Rv&R^)^lcI7O;&Ylp^NG&9;`jnzai( z4OXDH1#anw)mq-BeRni^UDi6elezFTW*Cu2Q8Qn^3pY4k0P-(>VH z*P2#ww5?BMKfNgBRyv914!)#9f6PQ!{M^K46@D>XR9 zw8n9(x4IetV)H(fCwM<(S>eBl$embe?NOe^Y=DWAFfbd&0&kLUG zsb*^YQ3jGjQj}#p*1a~0<5&z8|G3gEMheq zdI-$V-w-AHmn@_`bxg18p;nvipD3)N>=0&JZq~G5lFpm3g>BdeAV~>+!w!YaqmA#e zQm*)^5m4+D8f~Ca+y5py0onVI7JHY%d^Lx$*+SQ-LVp`vNYR1n%3#8)7DuFg$kH?5 zkw6d9BqZ#4aEay3i)*cD!5|CVWu)JBGV|jnw+3>Vsg-XqLOnB-DeEdbOf&Oi=91Et zk+R-!Suf2LB~DUz&t?}YW^v}2I-OCQiPr3mG#JkZx&9Gzr{#R466U4+79{+t(0W<7 zZ0+MAIZ-ixtxa%x*$>{Ln@2(>(o$rtLv3QEi?Y;*J0*LEwSBSLB(XXRE2l|HTOn88 ziyWKU6*L!hA7kdtJ*zjUk!Q|U4{q!kQ8iZ3u+%7@82d{A%Ngc2s!>OP*4(plf{ZnO znln~`PIjzUQz{Erv1FMOdQv_zR0m}uPyo1S>$&I9OoB9WGH@t6rP5`5l_S^ai^k^| zeT(BW)-R!UusvR)4r;U+TJsoHXv6;DX^l6m^1bR?VuT#tvcyH{o;=zyw)xT@@WNS> z-X|GClIlZ7m=in6vCR)-*R$pCnpsOI0?CJ=gq4%&EZXs%q41p)Y>rl?KzTb?YyiXle*=qMEIKn>J4G5)pn zvWHl;iR*=P;ANCT=U}_DQa8}3H-q)xwt`HQ-@MEWS%kvOR1*1_iIj=SDV z%a0y0-;`;{du`?7OtG9c*L5=vc|_kVp77OiZnQL zr;x9om6nU_*|wLczmTEMRbRtfIfu=lMfp}!-;@?03_B3Ih}*?(bRhz{o&(|(Gy;fkZD+-dy| z0gueB!pZ%m(_O@bA43aw{$5LR;y`mW{ z5Y7ul#jAhjj!gE098*(y%5?-5X)SqJ7ufB=j%A;%371~G1(qxzhMd=C&eoo|E-$P- z(H0JFTyaXMj1#Esid3vX+(7gG60m+!N*5TquPJP5OFU;@UW620sg_#AmU8p*0>pdX zILexrLYI_QTx8QQ6u$c#?94@_)h>#e*A|giiF#!zLRGmGm@HHjL%)uSZnCg{g?xXZ zc(X8%C)Nllo0M#&yQsv$xHLxpl+?>!jHMoxk?5%_$HmIFgnHb0@u3YveQUzQ-pY(1 znIHEx3=M?VguQRIGzzdXgYHI$;(PU75=SH?JHA9DWf>RR@f|F)O?@lbRmL z6mdB}X2l3v0eL^y1}b;}{oFE)S5s)2mNo-~3aKJG{_1*Z#| zpL)O^4*!tyw0V7_2wk`3QNFS{Mr-25qH|pM`zL{4R zG^T$8?U!qcg7~RM8gELj5eg7## z)l(1ppmgg+5QEGqOU$Zqt5LFQ&8?i!qJqH4P`2E_#1;kwrgQJ&XWWv{K>YSM3;ssK zuGy*ZIX;{qLX{=)DV5jf#n08A7^yuG$_wsVF$R+GwQ->}?vVTWkT*|qYuwwgECTlJ z`IQ&~!tHo#+^bq2e7L-d(xTOlQOkf z*^7Xi!TM&UR-Ni~_AG0WPc$fQD8d zhHpq0glZ5Xek=L9`9o))c7;eV3CsM?#lg zP@EG@l@$$cll|Y#5Rz&L2W)rGx4S5uuQea$(c^iNqb1L|V0}tx3_$p-L~h4t6eK;r z2HVXU-lXT}>ZK^@`LVpbgc)SPzuPwaNx(Slc>q({XS8+USw0+ooAi~}BfV_Qyh)4& zzBe8goPXeCimVBbIc<7NQ{K{_nZbT zJ79ZdO2t0johdyi3zHmYAC!-7#vB?A8kb=`mpBtRtou+3zKYzA{Bt#BE&uyDty;!Y z0q{N&|4K&@9se@ZW~C!Hrp*(bQDW430B&1D!TV0nWn_^l=d9?557@Z7HTuXA7Rjxs zX=C8TWXXxi^1;bes5aCp=*SJ%*M)9Z%{d^-KA+gp&>RZlm3_(|0mr2NthRvovtWSK zSW9CE?1qIrFfT&m_9NO7SBnGTJdTh4krj{z9Q{MfrE_D;rE`OG(t}6$Lx8PD#|4ub zofP3tR)z;%b%vMCbH;~*s58EBUW*J6J77hx*)=(PFG@^SUohrri{FRh@u%P=2EXyU zbkoRz^%kSjm6)%arUTgS_$fveF1Xf;EwZ^xX~9|!=fS%(pZ*f_29Q9ZCBV)nc@eA}M z8|)eDd=MQ6v^d^r&shIKB4k`5zRoGnB5*Sn+yyzggl!wxneZ`>MY1jI@%oZhy z@(67%zV!eHP)R>8Gs60t`u<285Xh9R7xvs*GfEhmlqq@KYzm)iUCUmh8K=MK7Q%@Qy%T)8X{tVB*)~T_Ky3Qgp*8%$p zHE!GQ{VjC5_!3%>i^0RBfEW8GLENmo4PA1iOoEm>nehs|?G$*o z1FWR&e?{^P;)EpKIA)i2C}s)%WrHfKZe+7kQ+A!d=`4_R=uPQ9YYKSVzbuLdoeiJ{ zm|VFaF{71&ZysyYMp@lix|4dsN!2>3$DPz-C-oC2wbV&{*Ga8(QV*(>*`NR_&EDl? zJSG__&r477P`vLv@}E}c+D>a6KxLIoStX^FleSKi^KvwG42#?x(>%mFjf!hIu`PID zXH8xksjBBzF># zx;dsg3s>16))Gxv$@oGj;h)v=%=ir_zo&){#5P=4%e$VEE-N%#Ml1^-pJEo53DuA_ zKKN_Z!gz!kPQM~Ky8J!lW!Jb>>ax&VVMY3Pu(L0G$^j*3ISM{#`+}W}k&` z2?JlS&$xe-D{+>#ZXUAH)A%Kh5kKpVfrba5O`Kgd2eO<#j>eg#+PWH_5`^(RUOq`l zi`Gd<4WQ2u!fE+3)1(BuM~JKTM1ePRt~m>v_(&k6=BeWJ5FQEnIE=`651R?jhl+8c zn?%0YsX%ryTYip;59PpCoa%a+IywyT5WW2~frbb&kH|>RRi7 zAz%F3FBJ_@y8HAFR%+We=Y8V{dC#unZ6dpKe@;BC5o&8}wJv&HvbI{+szYk4b$Ryr zin_Jms(MU|jq)}eW0#-z1tNvj8bi*Pv320a|N62I22+QD;w-3yqjW_obV6X>Ba?QS_6&6lCtsp2}`t)I_Sxa5_|Uo9EM*8nKuBMH1x#hpB?2LTRU z-9Y-22>3D31pG4m#VLG)Ym?RhcOd9zxeTDmaPO$<0IG_ zI9fe;eA!a#7JSt7s=`Em=3U9SnUmc1`&9isR#-kJ3+?A2M`c7H)F`+^9N3eLr#JqG4h^f)9`Yx*z`Me>zy>!CY^)Pgc1ph?Cz$pFENjcGgfDO{S*herD- zBi5RPoa(9b-a(HL`s*mSh+&>b{wN)8mmora-$fUA;%UvJD2T%0Ln)|YDb*)0Oapmr z(ro{TN6AGy_a6P6Lknlpf)k4HXEeap_YYXX2-*d#%2xrRIQ2ev5uFKC`ljAHQ!+M^ zK@)p{T4+53VtBF0U*Wx@Wt+LYB<3MkC)PHY;V)}<-(K3K`dX?hmx1lp7*#Y8!hb!R zQ|RPy;Q3FJZd!dX=FHf7x1K9@_y(3TXSCxCH!012J~KWz(tv2? z8i(I(6HQ;Zw0h0(P>Z*|svn#)zvNkU0T5sTRZ0nD3oQ^ zT$HWmPKf|0;IsV&KwLM!t588i{ZfuQF_;o$aSW#J#9(T9W!9C-;lbcB6-2F@001}= zAMGS(JMb81O#8!YUPH8@f%1u**F!7H7edk2Iuxq84*ju zQOF_0OQCaA5AfMp+NX5Z1Q>MO%0ck8&LYdSBEW1zE$P%Zx>%3#tUq?O@CCG-@QT*v zPT37f&mu1?=5evv&F#tJOC=TDwLHS+BH+~(y>@-)blWv7oLuJS?E=@ZEz_q+YG$}) z*$g(*B&lF*tR>(=uhWb~>Dp`-e~R9YJM(zytyJeB`T}Y3ohL%0|g9=P5&>**HbMrTIiiNA z%8|k-cG&*w)F^(Q9YwPoHRdOb;?q#@Q&9~3!%<{;!9jOo%8!<%5W{>9jrT>dN#p@# z+KC_dHtWtW4#w9%m}h<@Aju7;4}GvRn9oAN&k|3{U|0>Yz;c$PT9{xb%-8^rCju`a zY*VxItea8eu1($S=8O*n$9b^Ve&9B}?h|Oy%VPSg45?|W=zwzm@>#QRk&;7Wh}{WW zR%#p>wQ355{~(1a8C@ zW71z|uUWUV4cYS^=zS(2{@c|I0)O-F?F9SzW54r)V`kSn4{lBug@Vs zt>ya#^4%=jr81QSixdRd(yA6d?yMCEK@?x{L|-Ti2Hz^4=&Epf7}W-^Uv}O? zdr%?IeG}r-Q?WN{9yL~b^Acz3bz2;oxJAb-08#&IpRkgtqAooNYd`4+>M%Hy`(LBe zXB;VA)vZo%XTj9!F$f38=M#gfLx*oQN;g3vGkXW0>k?EkC z!lMCt0P29u%C^&UgH(2Rvq`#8uYLN@q*!f7XY0U79LNKD-OFN0LYvcW&hSi(wqE5J z;{Mc%6BN?ndo~bH2ooON4R3W`9t}s0RmZ@^0>XOTw|+9!tRo@}IRs6!?%qAf8lYAg zv{|r}qPE%UR85?hJ(>QCfk6aE3s&FrC)D#_8>ripDUK%RA9H1fSabPA?c!28xBX{Q zDPw%uqKL9U%~L_2$#JtkXP-b~FSO-#(b;~+i6>lCN*`%WBgiBWdVOF+0;{&~e*so1 zhU@<(7D1_py66V|);FHbT~%1UyVOlv=HC851Q1^*zyL>~y*d_rgV1@L4BE_gIE!7K zCq^kC9zlNqf(ilQ=Db7l&iEWlxP1c3#nx6D7&{$Iou_=Q*n954Z6mQ3YzOMNB;#RiGK}+KDQ#cyLsK zg>oW__-lzRra1O5vCbEONmK!0D6IggWJ%^hYcwzLXj5ruAfy0|aT|e6g5!ITYfSi> zE#cE`fHDwK;6)5*Xg5(|ZR0IWM1iw0gPgpjP?Z{IJwa}NK!M+>#3?d@i=>_tP@sD7 ziRVPdD2EoYl`8w4A0|5<57sXj1N2J#92_}0BJ;;1uA3MDeW4y#LCkzMPTbyVZ%y4C ztd?T#X9-smoA_+Bt^?xeQ=va}ukN1Z?FqTHcoEmCZbEwLkHp+vv5IGi$>|&y=lvcc z$QUN$aL73L@T`>twH)H5B$mN6Qk@9VI#}90=3(<=oXsBOOxh)T@M7jG5u6q)_f=r4 z^mY>0Dqy}8HoJsBdHQ=SIHU(y3_3!U-T=Xjdxw({9rEyC5_wkQzHD6f;U@s$3;zcB zM;QBY+!<9W&O6>3{uBe(?Z%Dow;W5j#y4FDYEnN%MQ?|; zxFt7nfbe^z5<$`nJbZN3Z;P|IguC4UAx9m8U~-xDigjG%rCB9<-GQF=hoE>*p~viW z4W$cpWFuaQ%+u3e9WSz*oGpgK4xceiQ9w5IR_i~Oai9~fh2FKM z6wPyBz-17o25YN4Ix%OI+FiI+G=K2mm@pQZJFFkpQK~O z<^{{6@|L{JDWcitFe5w>Ma|9DsjBPXF|BzsCAB9++r}DzfJ+8&!@2ixmVVHBqsK7% zyvwf9p4c5-pO^hd@Umygu3k1??|s>LqcA=sR@Sa3eFVQDHdWNvcUiPOJtR@(BnnBm z<0I?q>({Q8i!Y)#N{q!%#SVE`%Sf>a;&!#CLp#0NC58AeO02xoT(0HiQa*VVr{PsT z>Q(dH!~grJ&%@$>l!sUKCH7=~koCvWI!5YR2Q~O{s_?Q$QmPV9OA-gyjreKO#M@qFCSngjtJuhyDH%lUXdhksXq$RcU( z28h;?$E$-{h1RO2atolFArxlZVDGfVVXI*j=QKAe@-v%EN)J-r#deud4^)$$wOf}Z0@J(}?d?`V&4 z0Kq%$tro%_w%Z=#T|zZ|_fX(&RgYS)CPcppc(xP-EeN9bquy`!xk(J~z@RUOE| zk-nMFVe>ul$i0-;$FbMANLq(RJ{w-MWJ)DEM9M|-KM3u@$o{GA;g-7=V&XFjJRWX# z^zM2*FaEgk*72BmFtae5e&pFqD2Uzu^gR%aCWv6n3CMb?)r*NlHeyJT8Ust^O7DXu zf!n}rTw-JGL}XxEMNBJZ?wMsasVPBr%d2w60o|p$24$^K&1mbBWX$N1ZVPb({)^s48_X$t??(<*#Cr2s<}LY4C0T=@4ka z{1#xW*Ufts&!(1Dyi+K+OZ(0@c|}E<_Z?UP_nUOuC#x%yZqS-8u&CU7BwDu#1y7CnVbr}vPev>itbnMfsF3BZQWQl~$7)UQ%ljpp z;>F6a6a`Uw8#(ZAmTq@(Gq8MgG!@B{0AslBY|hU-$i+bV*A!u9YDh9O*t}Yqn&a?E zBiT6yTh!?>%=WKmN#M`ws~&hYehc$D``flXcv5 zEQIQITld`oRz=>9nRm?zmA&??g=uY#xkb3rirwlj8Av31^t#8IgdXe@Hk$kYW-4`A zjSO0b`wWN^?BH4!q4cgM+rAdWY&j*o8nv+yOAgJ1@qFvuYi{eVOEX{VvYqd`J)NG#85sLr2m6% z1vmfBGY73KZtih#6Nn=lZqCml=g*lTa~)y(Ph;Y8eey#JfS?X@0}eGApGVT5nq7U> zygfwq=1*~~i9n^CeITg1Ci3#2WL0iOTjrKul8Ffx`}*rA@Uc2Mb1_S$cW#uk00QW? zcH9nb2>|JR2)(PGPRSJI@(wRHNx9}-_E}7^U##$AmIAe+is{R-g2RS2+O||_OdN=(Yzf-H$GtolyF@@E{f@ND8W z%Q!$boxgrC5N_A;7k9X@jjEE2#+vO^%DBzYX@HY!p3mzAqv9Zc0BtUT_LT4RwN4`s zP%{?>Y$)%HYO1iIC+QfJ6G)a*=|#&sl^NqvFJWEfZ+}Qsv(0+&$nqj~wy}P#ah8Qr zbIaLWtG`W``a@|sxXxA7E+NSL9f1xWa@X421!WNJx$==-D%{s%G!+ewlQeX05r(Wh zYWw}8W2ENu|6FU_FVO1DZ_D{dKPGly=UTJK$TGisp3eD4KO$x)k+p;Tqc_06ilUMj zmesH=^Hw8gH2)SrDOptpoAUd1PzKH8WEj2p#8_P$1<$3RSSlO)ka-SyYVK^St#LPX z%K@K}$hs66N|8`cHPK?vmfGW`_81j&cB2HERX0BpZ1xB3iY=H<#MpDKA28PJu+QMt zaqB*D*dgNox*4{3ipi~+;6Z0(4SUY<>{h-(S>JAaO9@yb93igVp(kB{otsdB-D2_R z{vBWBf@t5=+7%~7wWl_*yT0q)cM_p+zu?NvrymS+AwxKh+zTB??yDGxIBtM+qV!CMM&Basd&^n;oI7?%YpNuvoVZ_L9gIGlxaCgJ=);M7 zoO-z?9#; z55^)RP*6-R@eDifPo5P zozk;8FxVYhK`^~k78C$E?$GAk(pc6J+Da4(eiSY5_lG`TEv>XdEX~dRPSB$rCupC_ z8{`D7(u4h-9Wd`TK^I>a6 zgTFTf&r|Ns9|-?1w0$o~0>rD?Sppvki!fhnzJY10^_wC%;9XuQD0d!i>OGtD;yy`~ zDaUmH63dJvH$Se51Tq%)HnFe@drq@U!)1$TwCp{KDPMjW8ekO9X}9cbB^?XP+nvIA(E`I8W1O&p%z{GmFr#o3t| zh1F5UHeBeOQk_E!FN?1gf(ji`>qP(Aci^S4+N+`D-E!(@m&=L zV}M&-&;fo#O}!}L4>hdJa~!3`xB3GuT?3c*+U1P_R0rJ+Vz4N7nbtV2yeJ8>(9Te;v2zHQTKJnaxbeSsY$7 z0hNW~nbdhN+x*0$YbcssgY>_^)G+sR5-0=uiv*U8$_HaRw+$H$B&$`<(X`??N7ts$b}9zqAx1GVK84@1 z_ym5>|gh3SmgB{bMB&1apxQ|vhsn_L*}%Qa;J)P6*k|@N>?RT1I-%&msQ(8y!7`V!Oh(( zmj|brZ=#OAQ#W6anIA>lk0DZBxRxxmt2)|M#G(%os7jPT6+z_r(|ku*`miU=ErF7i z*v5Pie|u!5Q>=skodbeZ=ydD|OXGnPV#%r2#}ts^bPp7~RvGX$Rur;ucWTLKAgJgjA$;> z6iU>-p-^uEC=8A?wdS9kJne}SB296jT|_*XcCK*HYu!d6eAbKdLhb1SxmjEsG7fpU zX_5xbZZ0CVrYo`{N)34;vh-!szs)|^W}lJl^DIYnX`YiERDbNLlk$btzmNk*#h%&* z*;Qf-+Cp9sTSUdE#Fjs+7h+Gfv-nDM5q4K%Pt8`br+%isBf3oBB@6C ztfXQ!U4Q}y@+YyHdXR4*r%uRpsQKa@C?#9=`k(WT0^Bp67o|NPKui zCumjX`x3DVswvbmEY=U>)@_tU+G_oAlHv-uut?twLJy7yg$1Ynl`*TXVK!h-HfGfw zsx=Ws{%H)Y5VuNe^6`?3UG+P*yCdfiA7RTt?5Y>j@5_PkB|)e{>cUWkrcpCd!9OHo z(bo|W7Qt<(I8?WNE)LZqSS0?Y(}Zkq_YIf2O9p~aMa*OA2k7zh5vWvb0nGg1m=^5f z&wp@aiWD^vg-TC9N?J)(mDJBgq3Z09LM1G>lCCy^2K`Z}ex-0?Y5W!?Vf|iea(t)& zRiX&(k3#hsjY||Ne4_R`GZ(4q)OHbDSw_y5e-w!7_ndw?`6?TT%8{+u^Glx+#Xux= zhcH|Bt&%uYXhxTm&KFrrz1p5|Ju+T$_Dd!Wb?6vVc@4 z2xJ5|_>zEBc&TS2Qaz`F{^iDeRvN*@%B>Vl^ovCIkA zH8>j8!*{V`|L>wv9YmpP`|;|hfv=24wOJLqU~nNtm%b2?0WnJas*qF*PY6kM$#}J0J|B{5q2lkYx8X?#LQ)A!xH5B|dTU3hLs+-A4g#u3Lt4YY9o%oV+P%1N~m5xm2gsM`S6RY$ywFv1QkaH(Y72>oKx737l zVX83Y(~?K&-aO7dimnVWPK;8er?Gp0cTrKQ^z>FW)US+Er6e%Xe*!@#N>y!Iu2=d6 zF`{4P1hEDw_WveI)pa!L&0Hl-XD;VAFHSad=D{?wlr6>HgVQn3MWah*_)hoAz znCt!@_Ra)8>grnjce0Qn3zGoRu*rZRQ3N7H4F+sR5}atFVH32diCG{uBr%y0P|!ev zC5(BcYFlfyrE0D9)s|;n0IP;Yh>8$gQEN%9+Fy)I+#o74|L?i?Hcc+H8b;JN1)p&EvOroS)6(iGf{P9LTQGdQxSN;I@9w)l2xQ z8G0PJFHDaLP)!egz9n)f-So&C{{rnTil>Kr7n?_zdl!3K=rv-y z*iVOwZ6fCMtUa5)#eFr`W5`R%%P=qaKl38a#oe`Fi%0_sJvg7_o}ZRS6rss12DK4x zvTolr^>bAL>r{65C1c#o5zlk=OYS5FlOHO@S25ave9I70(og7E2a(m2%~F3uo|XdL*sL|JSDT9r|fwL_w`FQX+0`G)50)YL;Sg1#rYk#0oF}WZxW# z;C30qP}$#9?eIFBeG7uTq?t6iGjntO4@E#FL z4I~sk!P)AqCdRqo?FY%QUH?7z^TIj_Ca{wJ z{DJFKnmHnwRBA65k$&zX>x2BUL$Rv=8(gR00&co}2G=P=bDhp6?QnMd$2zIr7nZyUpf{#zI*VPcMbnV?Xxk$!s z<8%Hfa~1b0_R~O-4r9sT4Xob)X_330I+c5$O{<&5#CtAsnezRRnO8rfaOZJld11@d zAd8i}fX4|d1})DRkbI5yC*(EeI#FA9Sc@QIDFsux(#*ZwR1teUzW$B^|Z zvBo#n2zoU8=j_z(&Oir9D?HC@_Y zqD_W+N3U+)M}4N%PoKV*c>U4VD=6cq)QncWZY^dwrhy3E>rmmWI&B4bX|`jn%bnsp0~0ks2QSbyNBrO zM(Y9N!q5;Mxu1yqj}hr`B9-{ER}!v%Y&=G)d>lFvF4=RuA==DfdIIepqOB+IGNbcD zjPcgzD|B?f0$1%yuS5En(?V~vit61$l;d-q&{NOYng_Ex@S10rC}*JfFZg2e8WAYl z;hge8UFK+i5{&i_vK}4nx~-Y5b--dh8qC2TFJ7#RTpQyJ?s7dkMO^k+MHfrKIcVtR z0oSaCgT7(x-X6@VJL2~B<8OceFC~)xJI{w54NvO1DF-2wtKqNYqArs&<+{xNejcOS z-tn=vm$kXvz~S|(X=5aNo?t&)p8>OaaC>lTUFJd`ag6q#)$pu;1mZcI+RZ>Rb2QN~ zY{!X`1mrSqYYueoYwt)xSe*3x?TlGS86?ZB9Xq6X_%7ysSm!ji@BC@~eKR1)*{&yB ztcHt(IzdXoBUJ0i@OE8z324)yBMv7BvR&*n4G@OBRI0%4bEVt>AwN9m^)GnSzQ=?1~Rn0x-z(wq5l?Lu!c zvIJgKJJrtO`GJqUnfq#3W<6^?u^sOU zn%&$X9JZ3MP16Sh`qtla^jabu?$Z@I-1~rU6VBXrWW99#U4&z-NmJgZCf|Kv!cRFJ z<%LeRFNYYXqf2n+jZE2j1(SDu7dJ^inEWs(w+eEnyn%j|9{6qI1>YGV$Lq0>y;?>d zi$vMU@WbZh{oYMe?Bwz?59GPBsizSi-pQz_~C>V`qbpCj*X|;+CBKx9R(&q|fjoE6AJk(m>=CE)6im0O5Pvx=A;mVWTj0hb` znu`%=A*R4nf}Tg}c%y->^R65#1)J=qMUKXm`?J=rT;Oe7*_qSuywBOVvdi;WVnv|m{nmMT(l}jfPUW~oi{h;5^d}zLsj^}iMyBTM_eJK!ejV6jbd|^=x!H5_ zGbsFJEcShuD-9mL49mynqcMZCLhAyskjUgKKVdNmMeZEaf`7yV>Hs~(1F{319YeAX z?sWQ`B&kU90}msX%IZK~r!$aW$WvdI$ap=zSE|wNWe+c zRTSX#=_(qKI$iYx3}DMYqJ0cilM{HSW02>MxG4lu{)krwrJTTDHrIhQ=I{2b>GYkj zF8VaqG6!2n=PbUzuF12?mED39CCl=i;M&qY6o$=*iS^G$krnKvRIV-W#@F`q#M%Cs z`tUcbBbG3Uz8LV~c(fLOhcqJPczcwU2sI6j-~F+y{iT+zH$VfbUG|DF5wo%bIXlqs zRj^A6i|9IyXT_K_+77Cn^DSNgkRgrT*y#(XkH(xfeIaa30Kc30nmvJ?CvWA{cZR-T znAOnfn@Sv^NGZg@k$pxe1qvp=I=?$oKO*&U9D4t3yL8a4J?^Nn-`FYV?ni>jf1XDk zTdet%!5Sz9$!Px>^wpcIfkeijd7+7B?l(pA6CI7{^CAvP-xf^16D!txzp)NKK2o!-E_wm_U!m`Soa!|!biW!Sz3fW$yfY?tI(9*@sn zy8;y)#SGbflqsXmvu@WI@7kPJ*P42g%xQql_$!*4r{Qy-KMQCh2OAG#o z&7^Cvr`)h@@`*nokhA~fZT_gZk2@mbI;r$+ zH1`?PWu@sml`R!uG^PmM9kKv&nK4S~?N*fXkH}t|v!LU|&GK%e-C|<7;k2M5N`@QL zlMw=>33_;7F*~rbxp8HSYt1jj0?AFv+I;d>VpLhK1`!_>w9Z$Zxz)8s7{mJRNR1$w z?_8VcsXrWb?F9Ztb0mwU>&g5D+`W<`fqLoXuq>>4Uc<)ui9TC7t=eCP>F^D0#_BOlO?0G&H2nDvp?!Cp zJg3ub4?nwP_;IcI5!v=Mbdp05)1#k7=&i?C6dr~cln(JsNWR4(rwF0Z!d?v~=fRED z^f;4u5+r1c^)d1ldBwwWxxOGQ8M?LbVx&ap)s>_;k5G}Z88o08xDvW#&uVe;FHjVO zxOgCbkGC-@78&pfUuZ^w?rkip8DHI2?t0mDh1O?TdYvR|xfSqmIcoS(GaWa@nnVsl zQ{&@=2yE8^L-j7%-NHH$Z@$-fk7^k@WIczr-be+@M5|bv;PRBdvYjpb&TQm50$XJb zEh{eTb&j3_@-{{~fzz1E@IA^~jJ)4gU2{#zgPB!j3}yuLBKxGr-+;^d3k8;2e>Jo; zve7P!6SLT6$*J|HaR1#C*eVAHg}i;5$MS-?gvQP6fwX9LfGLB6*yprN4eM076A$CV zpTbJW^_WAr=L5?!Bhc(F7sl%~ciI0gF0RL7$Foq9^-=v7NBjxaKnP;^SsmxW%$k^) z;C%vS7K%N1(JWc`i$@Q+QViFV*-oxyXLSs;Ui?8QxK#)WL51C;>x5-f#Td8ENXud^ z`}p3N9@<20@u%2+1>FVV3CeLBkAo>5La zI?4&(93>Z3h3hO)M%q!LL}#yc5C*a2a*P<-g#KRTvG18*k2)6F=Y?399_0T!2F5jRYV_B8cJ;dYGg=5?|oa=3>7&C@TzROPF zvaj3&ro_qn_+!)3}B!pYp+^fu7m_yMDOnt$N&eQ&Ls4TU9QJ=c4T>rFBY-& zBaIh3sq<5ar>yY|-nlP6AM55L`iAo|nsH27W16=<23ES>Exk(itj!)NIn7_hP@`zM z(r~L~>$J>ln1lxz?vt`-y73pty2omQ#j#J6ZM(kVMUMCSJM@l)keYc6d%F=1nlz(l z9Nwu3V_4nM3t7wB{F83I^7Cx{A?!KL9U`sq=LO#&k;NL24U=K4oG?To+A&JT1pQF0 zPfmCk9rBP|mh7SpmDPBgoLW77wVYaA-j*}9c(DIu*_QWnJqiILvolJ&^hKIZ`yfd# z(mEb=J?dhq&}Ow!GT}M?M3*qXEj!Q{PlMx3&v8SVC-dVK3Pv7%VP!zku_EiH7u#;^v5+1A?;iib(H;6ELc z?DdY)e}IYu?{C<3D4(lr{W_HXG&j89yYl`R|EIZ|f=Bf4hFso+(Z5wFYe(w=joq0S z`K^gp1uqAVQ(*nneh`|2r zK0u zxtls^2>e_;BX$M+sHXGUau4yyMps15#TPc^O-S^j0D_&v($l<69v7Mim%@&x@3wVX z*FDb2FuqM5*U1ug+i!Qp?1t;rG057e>s+5l#qLsXzDape4kdng4NmU)Y9=BX6qzjg zh-5E$5Sf!smPfX-1AaA14uJXN_Q+%C9Aoa%>kl8NC8!}0pCVhx=9Apztm*P`ZM9lX z38Zsne(d@ID!1r!Ig6Q1Q^VnjOY_^!i%h}2hhSb&aFjddot2oI*|L;} z=S`twyvfr@9F1s)hWuE^rG3|;BmA_oZOgZlG4G5Kgdm@~NH)PPM?3tVJF?TTe z4hSGBQ+?9{Io0HdjKjp?Kpg%QgE6%hCuPyggN_8dYcJNtft11Ib%cj+)^uU#s;NSA zf3$UR85wE1xZC1fECOg%%XfOGJa46zNIq$t0UBq3#@SSw7-AxX^+E{`R6p8NEouSx z$t+gDtxlxLEuX~JFh*8V*{~v-f!aBn;U))}m3UhlKJ#BfSCMS>`+bOnPT5pc06U#3D zOC&b3{TfE$p7E{cJW?K}t9fJ-5h_@Bf38AHJaww+?z<$oY|l_e=40VKdx zFPSu&dNxy;$Ce+RLF;oPQ9N{X1$l$dgz89Fkhi`)qDLj^3c@ZbTuGq{D(J4D`gW(# zR1?nO4_8o(sUQw|!byC~`pJ&%5=wNEuvAbAb&)6)1mOmoWIQ~ToaBF5S5K{}p6>eA z^~3DB)YK1kA=MJDCR0CKd(=;!ou1IQOXv&1^I{?W+*qlETubcQ#BRUXwURGgLsEUS zsK`8%GgCoMER(*eezs6Q`qcbww(j~ta9KSEa-G&Wh0^;kjR~WoN@M?os3tnRIWr8m-c%9&R245?9mciEx zo^J5l1y42jV!?+S{C>d`4ZczED1&bjyz6pZ_GZD~H+YNSZ3b@@{3U~L5WL0U`vw1_ z!P^AiXmCsLdkx+x`0WPo68vU^%dvu0XK;BU-SQbcQSikEPZ4~f!QFxv7(7+*Y=fr> zo?-9|!B00htXT9W8r&=RV1pM3?lkxU!4EIgWiJ%G)8LB*f7{^Ig6}u@GQoEnyiV|D zgRd3*VS}$1{CaCo~c=jZM0-LE%ns5`yf z6g#9PbW&ZdUF5%8t8|C1V zE&>q9Q#|YcfZ+ZCYm=-iB;aTg?06a_HqV9^MBVER7DIV~XJrjEY@Or0b%Xn#v(0}A z8VHDLzW2~p*(UqnUEjSOzMyGv|FTtY1zlyUzU*=>eU3#i3NvXU+x$=EZV7Fl^CDmH z)_2mN&s7*NDZ*g(^Nw?(V*RHZ9fa8VKeVTQ|43o?xQshHVy&a_V=jzuN9`TC zTF*)@!gn_1@n#akcTw#}GiMt2=V>i}po#wJptR2H*cAUnS&)g^!{=pQ53MhL779O1 zmmTL1WeLcwF-Q^q0`cfHZ1K9DVIyo(57$iZ@=2!srjoiVLCQMPR2K!I#^$q}^j$=q zT@b3Xzx1l8eLX7bX`Q!v%h_FF*P_L-Gf1`B)wQ)FUPu$7`nRvEwGxa%2;bO>U*TBBxLx@&ejb&eao2#n_loX22o?76Wt| zfrNQt6C8VRD#C@Dmzb#aF7?#8loogm^@C`zo^mj-ul_x_yib!K5Z_huCtv<7sDCfg zH>du+DBr~T_xkxx2tMmO(;Bs0*kvc++4|iw*j!ogn&12x=>-yA0kq4}2Uf2es}}(s zD==>}=EuccVKs2-WW-R6IH8=Hb&Dv7k2HXQSxf-RyL>2-mPs>-pFkt!Dt<2 ztc@0L5y+W06*=<*r;q7ylUlY(Z8{)y;jxf+e==kxZ{?!PTkk&)lhu4=xMDp``H|Lb zKjkn4E{YTN#oqhS?_B?t)0b5LRh%!r{;Md2$Y6Y?cATCUcv6-|d9u0n*54;MZ`3;d zgR%pUZUohL)Rk~JF@&!2P(#(rCwXfkxE@g7WW4*C0zAdS)ce?q%wuNb{okO3e&LGl74b^%0o>nbFw zd`OEE^~&JMmJ0QM?8K97EJPcC0&Xf_{g{LhKS6MP9T zF$cM)fkZaiB9b}a2_$%QYI}X@!Q|hin{1zoY_DNFj>JQ%?O{+bxykmx9$H>{!%raL ziysRSYi*ZAu71E~LXn*ILOW@eLm;ml0tGLo9dMQsQgd+mckOq4UGimtcxCGzB2uO${YECR#7oWHuRqt{BAt(QphtbPRQ9naYVi0 zkPb_)&cLiMIGhb-aSeDVi?Etdc$Uk#ntyoy_}9r)MA?kSs6n}$vdX#ZB;f(IcckWx z-#3FZk)gc)8<{KekGKgV3L#V04{vLYceo8BLD!l}209&OTv_A7Sw|39FX&h=xu}&~ zNRit8c+vAOCwA`oFCuP8sQ)6;e?lO7@fw=hs6ccfurc8>F%7aZ31`o8E!S`=sTCTA zY>cQQD7MH*0~E#cM% zlgp>*wo5bhSMm1C4_V;T@1L{IKq!bJkN4Jp)pqR@VlxsO>uz#ml-;Qa02T_8wVXQU2$F&V%_y(fyuO%@V5!bkf ziUc7NcPNh>g&Gx;w@*Cle69?c?F+La4ra9;LDD-y%X@SG2Dvk>6ZsC$ z!E6^=%M-Xq`<&KVerOOC@SOG10jWe+!?SEANhF6vE(k=m;XOu9um6Cxb$Fc~%Q?he z$f~eekK@t9@HzF;!IBeXI9#sVwg;0hrtT!Nm4t$m&F!Cqt_Il>bKZgz6hPkNO_;$8 zbC3#e$j3#ztZAU#twUJ6?u%H?f^p9yD_dA1%4;f~`V}V@D4*N2F8jp1wRvNTJhJgs zYqL?UR9}LVoURvkpzZG&>xRGTCYhc~^^M=28_9~97w!J-K|RC3p*BHj1y&S3wN%nW z;)clka9cu$79zZC>#uLw9)2hu5Io7yf729$;zG^?#}t}Nvic^|lov#LBU&iKVWDul zd7qZ`GD=B=9v4Xzgky>=8RHf@oAqdXi->}A-b4X}h&h2B!Q`t5CxPU6i?@`T%U~)e@?w#b6cosNZH_L?x zbf#tV?)Y`I9EWZ>5&o07T*twCS$$V*8Rg+(>}@+lv|G*}@?_lz=;8ew*JDDoAD;{- zJQMH!MfJNPMBr+at=c)Tn`xm0FSTJWBq<5&qR8py)1J(owWqYd_jNFcuzyqXX4ZGX zT@>am&)RHP9?kMC&#vs40%)MfORB*B_V+Pp+YS&Yd_AFs5W3;hl8<05 z)5JTv#mUtM-3CX%9&MVFAQ}a-y-km}>2W;5$!WUD&N$Dys4=<09n)g{acfU7Iy~6A z@qcYUlzMOq6r>;3?D39TC@S98NO;t-W{+p`%%;A18}z4A_wie`8Y)?#>zbB&_oCrU z{0Eb(CYUOp#0)@fpqqsz^kxzlxXJozVITSVg0WX`pECjQ$$g&xx7U2FD- z3MCvY?eTcUn#`m|x$1XBNCo>54mrU?g^7MOJvB2umo>6D#<=Q>BT~Zc$1h>hw^@Cev>21Q2WtwMB|_^mZHD)BS0Jdv{;MzDU~*l`XkJdSN=*FLG@WFBlI)=ytcn$FFWq21td6G} z?6$;Xbc6BGCz4%*x}b&V276_3n4}$`6wK%bi%5c`q8sdGV{1Lw?eQG3>QgtEluxUc z?!J4f^+_jMmEqu8y8&_xYgy%?MEb5DQKFS{afrvT%)QgQv9e2qjHTQ=HQLTZHS{)D z_}-~#I~$KxCRTbUvV~^A+Jj5A&Es@~U?)i9Nw$(m9A(h&aV%{sgVV~QPl7s>ageny z>|k918ooBfitecUsD0=>8ymd9xh%mOh**m#ScL1*tsPF8rho8LqCuuMs()k;6=!GfUgYF=z|Lf6KHc+&cao?Ht`0{^z$MWKWs3#l!vEv)`K98k$SS83*u&eSm=4=oy#p%`@EbL`r zTdBB-)`z1ND2ou-8*qF*Xri$7K3_hzr{3r9$cnZpImL&c%$>f}9(teC@tFI~dY_Z< z64v{?^IPhDzLUJ#**+DtuWYk6Z68CnrMQ8)@OfCz??U(EQF@eZ^*-B*)tb4bG}HBHL;qG>JzFibs_B(v7fMiMKJ^4z zSfaZcipiOX!ru%lOJKSUKeg@uY{NTk*gzIUWPXff<)5zzIwrS%ms2({lR^s7zP%#o zjeeoybJqR)8RPp>1U-_erl%t4UEin(y4*z9ry}TZNUaF^Vx&@fD1zR|&_v}^h@%ui zpZ|YN5p*H_3VQxC6+wSTs@r<%B|SLkRR_~G`f0heTh@3ss>se};qnhCg4WHaW1_^W zW9e1|eSTMmD1rur6+weX>0XCFH|No!}`pUJ8m&a8Ejl5;T6E$qcg?K#`L8p$Q z9sHLRLEk{M!Q?i##M74|=u5PFb5HkU6hXg0BZ1?RMbBbn`yW*V{e9t12XZ#(3(m4c zFX*9e>?9Udw4mcCg3cqTUVb)DMaTTNQUrZXoIQMe8%59?j1nJLmZg7K6ZBIf5TIK(T5EznlZ7%9 zjxW|z-xY)Ud8qWwilJ-HF^lMLQVcyE#lwqz6Zsob485M~JRih$G}fI{!JU!dHZjJx zFO>-o)zIz2o&<5XGgk-K8AZ@2haOyao#=*^4U`0MwaW~NZfLPbHMDJyYUqh#U&6x% z0?Sca~jn1yezw3~V z!{KGKQGW2!FrBu6LMOZUaM1hKA0>Ckv|PEHd|s28@Q0hoXSsfWc*0ZQ=vvaZ34`SG z4aw)%yfi19+8nZ*67-#0KmBZ--Elp#JFJiFPI)1iyi*tu5{0)uK9W0Z_l>o zqLx9s$HwG=`9iYf8R zpWbwFe{0-LA|Rm6Lz#-FB--ys*QV$v&|f(D%V74Dc=OcsR}E~2d8O{cK>WM-9g-MK ze*Z*v|Lm2+XCO?@S;DIIn)a;aICO~zl8>Wrt4fK9CXp*TV}DCL!uROwTs_OEPJB0K z$_GtXh{~>j5W?-Dxmt5`Jt?-(fcXBJ# z!NB=lrWZCL*{Br$n|R&~y_NOIYME5gl5o^TJeo_EIXBk)JtvG=BuqF(Gq?NThI1;% z&63yTFw9)-lOwx`QD{MG=S-4AvS)me_5Fjk8p>;vt*m+72e-TDGTm?QC_&vomR$6+ z4ooq({5Jm*0@I|{E9ekCzM^PvA!>p?;^T{#*yS|%7bv$@MBOQ{~A+sSp1 zQv-Nz{dPstfO#RZOL5m;d&>#kJ#3H0Twj_BEBr!+{v0lQ$V91cKIb*%WSDDytnEd* zhxH35P3x2Ork#3()!lEtc2c(7+z} zi#(Z)qy)FyTC6Dgo`@iDwy{_wPYSt%1)W=EPPSwSc*EzWB@d_Isrm}Z&cMrDak4Lp zMNry~6UXn@+69`tM_k^mTHhe!KsGFPxsk<`1B=}UL!Q`W0v2tH=KMB=wN7HsGhEb8 zPWd44B_ck7H)(1-GyIp?(h%s*%Bloy{}L=OFbefiMpf39=~##`&a^aXY8JhY^HcGZ z*=982mrY$9;SHR5`_*ztz%#YC?eb=xc?%|g6&KqBAJVZz-&MzDoUk~#)H`*6|MOsT zSchfdbwVGy1%n$`P@25`t*2{sRnQrleZ#!tKazdM8aPs-3XN?jBQCNI&3 z6ndGr@ysD4NIIeC-=e?x9?c}^%au5?t=~ULjE&Jzr4;k(-%5X8zTCQlXVG!3w%(i- zqJf^r!|lFX28;HeLu^q@rUxYHlbgIw>y+g>(jSnLq(YBRg%0br@u1(WHPTrQ;TDA`{vu3#Z^t?dZ1{bVJIOf@tn) zb=AwN6h^^qaE3jbs3~RrNXktquJ5QJC)W$h*yN<0%0&vU6yiQ^BTvrK)x0y(Nfj@ zNilmWx43J*&2?n3ki^`_>e!RB$9-BdFb>wiKxYyv$RW!Nb-ZZ$M6*ohghJO~z zD7g$Smgh5;pXQBxg$(Dqa$XK5{{n^{eg?2awtj}pkQq*;TR%O)5R+Htc3Yb;kR`M< z+|5MNtzu8A+HGBO5nB}T_Cw>X{SG{Z&IW9`mMjqf(RUHup1>Du5iASOlC@O1vFvGB z5jny?lBSd_c5b8=vKVmn4d#<~if9vsjMmaFecfed3}NID?dr^3ECK`jJe#>?3a_%6 z+tSG0pp3Q8F^@fqQ6m<3Z%R_QTavKm)k+Iqt~|o;nFlxs$#LcH!usSlnR3WVy!UpKlN*M0ykUKjk8MV@KhD|< zW_0~{(OD|*=j^d=)mgoZqf)IywndiNzsA%tZ~5gAipcSF%g3gWMprWy4}K=q#Qw1Y zuZQ+~haq2h04)Jt7FYhUR#`Y9>v~WvDKrqDven^0L$eWxTwXifW1Sg}{1EM()q()M z*39Gil%^5OuamJtKWUk3KWT|Tz;oxV%XVaN08`OD9?v(vVp zI+6*hBQ_9ySrzngKyleRg!)Ovn3T{VBa<(pU+f31jCC}XIVoJ9KDcc)8j`w*#y;`8 zFvYz|YoW-XpB&ryN;Gr+NJ~#ZgcpCG+ysKxGmAuuntST4SnkfyU@ltDS;U& zxYf6PRNoTOI3wjZatYf%$+~iaRDUx!JoftrShI|&5EE~;@3Ag@T#qQUaP%j427`xY zu)SlorghT<#(M*E631Vi$dz z9j;rDSH4hVcI1ffB#{F}2&gH!b{Xp*6tuvC&`Me&0k;(?_)BYl2zq?HMDthr2NU+#9 zdqp`+ytP@^WWp=PCP-_PR?solNHW+`Dsx3}ike|)YGS2N=3jF?md!e=UaO@EwK;oi zPSb1oXMA~9+C5B85t2fa*THJW3XT)9>M3TTmzVFg0@oI6BUQ(=fy&Tb9VsT|?n%L# z$x*E+AT}c$auOtqhH=V7aWIsin1??snDvT~s$D-;#_DIbkTQ3Y8UKUHKZ+$6jnN-| zS4zIaYxLtVJ-?|f(4Z181o8C?COnZA!h5>J>0`i z^-t6hExRhS60GmbkGD9Vys?r`?z)z$2n>GKit9m;V=BOuFQd<>0tsU-k!E`e#5<~f zr1Vm8Q|a;{hfvH%mxdMJlxJ3DL@U+ox@~KKf4%FuekGcrrmz96u3wpsMmKLUvbK8b z%s%|HS~L8hA4+!6Mn6=nwe`b3>al)hq0*N-u4X|P%2k+lR%1yYwx}eue0F3<*DWnx zS)=-j$#6jW^>8}6$YwkLE(@JdCZy8-_3KH2+s}{zQK|cExXFe)ZP;eRPi)w4vhhFM zh8Z@TYr`@duCU=PHvF9pci3>h4J{jX*)Va6iGQ>Wcb{#{TWt7%4cFUnh3#*x4R5pI zZ*924hOgMrvf*JHrlgzr&$8hKHoU@y%WQbF4ezkwHXFWR!?$eMWy5}Fns^7>&~3xh zYFiZ1|83ciQj;8@_GBPiz=znE8!`IP-m$;m18Wm{Y5HQ%}^JsY;EgRUUiOI z!oPEfM`AL+5@r6KuH59o{BvtNu~}~all?+l-#*+zzUSbl8k^oRc$8l);;Y3?eiwjOkdx3)%$0-+{XE1{qssAP ze)*~hbFo@%n`h$pDs24PzGpl|#M5nS%A=IYzk;5UU#@xUd`j6RU!nXMSczHElUPkY zj9I8*(iMM_j>J<$e139LVu!$z-%OqRZo9eUTzu8`@;9G+l<1Nl?J^hNr9FJ-L*vRG zVdvm}v{~{IN>|a!Bt4}}{9=~)q#P2D;}AE?sg}X}F`-7m)3KQ=BtVSp6oHqU3?__z-n~|L}^L%ga1sCS!UvzQ7tl4ws!scCY z>1E$tc=;7q78YGqTvA%LXmR=XuC7>8Syg>aO|8#=?b2n-ue*N5${TJ}GpcHGmX-So zYO0D$rFNIlmWrwS8d^cAnn+8k(0xmKP$ey=93Q2O7}Do!v_H2lM}m@dm$aWe`pz8w z_4E^RmG+cNA3Ogzt}?D%OxyElUwy?eoAEDAP2r!!Ie~aQ2ks`x7-h~zV0 zrOWjg0ewBN;)s1~emGZ}AWY?OXjPN^4Rs?`0rT#s!%;}Z9B(k#cl zg1^_<{-pQB>fUAI7k?$V7i)Lvv67~n)MQ+7<5J1r<>XOP6}M{sNsJ~$IWCpdha1XB zDNU?Pu$7V0t$kii{!QL}^lB-+)M70$R%ky}sth}cPwF&OG8vz`=`=ypX$fh|m?~qA zTct816l1DUr(!B2zDmqeX33M-NJ|iUN{No8RHe?Nv>-DFNcp6N^$eM<^CY9Gs`_a(R~K_o{L%PN9w@17)lGxB%c%iDeWUvo)F#A!sQ6%DMY`%N>CD} zyP-yi9+O#zg!-G*ev$4ard-n7`ije~+n}`LP@cN!J6W9_jxUs-Z&#m7NvrP^`>s<% zhslf@q5OaQ^rUA=pZ(9IcV;-fYTBr21J@E)4ROk^JLeP}wj9%?YawRd!_+Z8y8Na0M^fd>B;_7ZsXY^=KlHX(FTLRT(6ckD<*7Z@O z$2K!YTz%YhLizpAw4b9>k~N;tyeGB0>D}E=rB-Cr@Gv!;$To90rGK3Rj5`;i^l!aw9%!4hZ1W)7+?HVcBZZ`Y)wX$vZFbw{p|*Kryz!63 znf_(j=Ha%vGtRi5WSj4|%_D7dTdZ+++vaN9JjyoLIgLA~1o~HKn?noeEZcmY?e4bC zhix-Q7JA*x~fq@K*EH$#o*pPLy{daCqDv!cuclbxEh z5|fKqdrc_`Ow|8)XN|g+*cWM^vgVN4$iyJ=U9DTdQvRN+^VK_*9KxA(>nLK6WpCRv zwsVNj{8EWQMvMyjp!`xR{S_6U{p7zxaYz~2PxXsPjLON$iI(4)X~ZQS-5CW7Vw~#i zw6ysJuwUJ7-Nc-QiwpTFwXAv>KPNtTNyg~}IQb{WfBm3<`JjDzOiv2MrOc&V9h z`q!Y2{dctgRjT`+Lw&n{J!4p{y8lJM^Z7RaLgC&2Y6HjAzs!LD!!5wED*VrARsZ{c zLp3OHwWIrAgyY-&3xz+nMgOBVf3F8fN`v_qN>NPRc%rRG{_mIA_~`Bb+m*K4SEB01 z4d!5U?f%uRT3z3;=BDqjZCn?)x#{12u>Oa)+gzu550yYIR8 zSNHw;{@*CHbMX#2}se|`I%cmHO!zt{2p2Ooaa`SB;8e)jpnLtS5d z`PE@mas8JWG{8D#(4<&Wn471@LEZvX;fG>BueP-2;;X(_TI|cMEUT(nq8;WFMt->G71jDY#lG@uOAD&1 z{ncT6V`rjM`EW6d7L}e?wakQ^2mddJwdNFd6cgbtqC&<5wEy<2tGlUgRUHeu$eZeJ zT3t6dI+_*Tnl)=6d|FyvLET#ARH@@K3g*|bUSm;LP_UMu?$o-qb%atZ>lQCw>~zK~ ztFB&JU46`YPEKYn;*;~6G5DXUcQR%r+>?hY`x)Wl73o#6oL`8mtVhSPb`I@A2w&tY zs&JRq)Kt~D%PZX#MgGd-#icdpxX0FNPc^KeINMOo_*C-xK{t zXvdFxmEU)K54c05(x~t0E)gfNH_?$?*%lJaSNz{KWDNdpuC6!6I$*w%~%UM=U z2Qf8kYL0l9EGeQ6sXd_}WE(e;`W`1(?c&m_imS%luuJKp-O5L=P9?kQ3nVxn`-?);Uz3|h{Rr+w%CeYj-$(Z<;mirbpb8 z)#%j!kz{-HBVAsbp2%7Ct_Mh_%V+v!PrB=z_4Hp-s+&SjKW=}m5N6)onG?*3Z%_X^ z<#8vEa~IjAkXF<)G$|bGf7CcgTTxN9R3etpy_$m|*fHUbuF+np^pQ?c%_6^4c&$6N z^jb!m@-lbnl4{@bQ~!Q?SJBk$L8yp~($7o7jaeG3dr9e%D*H%pwB6H2>k(1s#nMD}7>hi5W-@nU4Ec;!YamRD(+5)u8k^HE6c0HK94KI+bb^Uehg1 z*pKj~cbO=*fbZ#HP8u4ehE6`AI=OIgnuL+~HpA5Ut1x!#Fpk&=6+5|K+K>qeXO7(A zQp0=$)QKetq!+JTQ(|lSwMDf?zW`H&uKWh02@~t5Tq8%G@}WLRnH~4{jaUoLHSSxStwa;-oAwQWi~T37U;t;ahB{y9fNQJF+5%k zFL9~ia|fv5)bsG!DV-;@*)(wVQ!eVt1x;PEyJ)9+Iw9e1juTa#&ntt?Q7OzN*r@;#zXDtTC)l>P^Gl4GMvw9~F8?Ica77){qu z8>*S5)H8g44CQ~MleF2J)^xX5Y2z8>@9(wS{qvM+xTHI-Bxw(mBf@=b#$`%f%J-_B zmdTH)XUUJWjaYZ$B9nH-2Upsxj^dt z#L0uIwY&Hk-d_#BoAR|KwYr)Us^bge(qd`rNs&2ls5%C>Y!SellY)Vo0(~13q$36Frd@{zHoe+UIU<4 z0`!VkgKvRelE&Ov(qQ~x>@f9D9WhQ1p|0)mzd0$XpGusX z{QmJ-rOHEeJ&F0}mbkY5tuf8f)lr3!1rcdNSE0p_v*Og)^lKu=I?5vZnj_r9$e;At z$-DmO80N?FL(R2WQY5%mXAvN7JmHFc7cBS6u`-APj0z9EZsTXat zBbl*}_LTh4fa-+8_yRpHV`e?nIj}9U)wJf=g5#{WI%U1(h>lRv>6~N?lztFPKLAcP zAszi4s{d8A8R>tkfqD$G`)&ahV?g|Dv(|Ksj8`LlNor(CBI}0%YGn8PX3E7F)MLJBll9(^vlG-Q zzQgL2lCRV$>0hc-9G|K1tjHKE`B={}o6i4vj29E7^_ySX6u}*8nJtShw$<3(9?|W` z`0W1sFZp&un}5l-8#?@7k#8UA=qbk8w7`mYte1C2zM_8@!HHBh5ie>!OsP|R2&7&-}gU(hnDynKj zrVDdsUzC$KW%9(53RbrPCG?*STjN??ggG$t=BpgX9A6Fpb1BU^+6Pq!<4sC8$D23b zQ;@5JzZ&5!EvlYbQ%e3`)VN33Ch8NFQwjTNMoqa7W@*J77#qS;SDBG{rA6149%El^ z%34F+&0StCsodPFy?E4~s1PTuoBnS_&8u9j=~I%ktQbLUQlTP9n)yrUb6n?$$lTiO z(yRQ77M0c%)RfjrlQ<=6wy)xn@*1DNsA66vT&fbKMv7ftRn^u0>X|UMB>{>iET9x| znNd`YbhflEU+FTR8Y^}tXwEX#5s_O70g5Whuj^f8Pi4uR>hj7NResX_5NZkkt)Qx0 zsHUD1+4LUfH#B9B?jK4$AT+xK29l=i%i53WDTs7v>J>-}RF#5zW-v3IDw~*Bmvcq7)hXNs)Oo@{6iz(X=p9+a5WaoJxdB`6M+#L*!SB z98%PrZq~60S36(*Me@;?gBsFZCW%W%0{XB!I@HDIR)zb$`i&VM3QBAAX+&i)?T2B%3Mw@`fC?UWas(I%4ljz-6quPF)EcHufL?a zsHQYb+fwn-gGQGW)szcUb-pSxE+rS2NtEogr5tv#WE@fIPo|~QU${4IT7*5qk^STR z>Z*;LSI9YJKI+syG30uDC~IFc!yeyHPZ#ko-@ktUqQJi>@SmqZsLxHl`@n>sj#ujW z%iS-Oy(G#H%un1;;0yIPIlmX2t)EKai{?w<>&M3yk27&|uFqCbpYMxZJYOuIxW(~> z+$3HJE6~L!@ybvkc1e7&+4Lv&qxi%g*1GoRvCT7VGef8jGuyVGV?!CaB>qeJByAR5 zI-Vs!Hy^{Eez1Whi_X84L;TnANuF2Pa5YfMQqL#u4SbTHAM%~b2MbJ_e+iWQ-peQH z!K%{sj{&7jd-%ltRX%Y~fha;B`GhY2++X5xelcpyhF|IsvzSn3y?({(Zgu7B-+O&>FW-#EFYf=doB^D1g9(Ysq2P=jzP$FmgKQgS z*>IW-Gi;b{!!#SF+R$yo6dO8i*wxR_`F$I<+3-&`+;78|Y}jhU-8O8o;SL)%+whMz z++@RtZMe~f_uKGx8{TZg1{;RrUtyblHmtB=p$!+<&}+jC8>ZRtbQ`*D=(J&1v?+Ig zCVWQ^I(ORkmJQo%xZj4YHf*tBvkf=eaDxrk+i;l;3vF0n!wegy*)Y|HZX2f9Fwuri z8!8)iMVb6}+R(CLn+^Bdu*HTOZMeaP>unf{zs@#S+py4vUK?iE&}~Df4G%|}e0*lZ zHXClT;RYM_q;U^&|F@$J7nuAUFXI1gccH^K(V}y9-}x^bY}a>+fz?9|TyK}RAm5l7 zHuM^|8;1J(Rdzp4J!tgs{CB~LBrIQOylJz?on^%)AOBT&qy2l^ zj(3F}?>`EqzeqlN_Z!)3%1_ow@>3T^%NF;)@5ip8Ms^OIvm)A{-sS6@;7}IuVm7=B zPj#pQ;136JR}(+C0ap%I>U8irUafVBZBib0oZH@C@K`KJl{xIKpjk zH}I@caK?F!GXvPlCus@1X|yR9x}p?%pLAG(Kj9NUw*$Yj?GFPdj4^&T0q;3QsTHJq zFYqJ2dnG@>q2rJh10N2Y14CgG_*~#ue68SzfkRG1h2>cM052F1&Bs6!;6r>;mWP40 zr<*+ZfTz(QQt@*-uz@cdT;R_qaZa9!&MDvrX~;Ta-w7OWhKWBBxQ%ZGes%!QWf@+F zpDf^4d{U=}fk&p0XY5rv=Vg3C!wTTLe4W@^z>8qm90o4{?m7#e3;AyWzRoAK`V;V! z4DyD($V`kqhj;`BMo%Yi;7;I`=TZjn#lSy&N2%X}KMZ__PvWtF^Rs9J)Yk&wwR}RW zW?&ni_z}qU1dR)v$tQU(1UB&P$NzfZ{d{fU8-f49_qN0X+{$Nx?*RVjJmfUMZwKz> zI}F|m+>sA&>=gU}hhAjT8V-DvPiV3Un0>LKt-$nI)Div#e#qwq?*!J(CN0V$@bkIw zt+4L`zH$jqK7*s5Oq4X~vZO6g>NhaBq+WgtjJ(X0D+;)rZxjC40w3fPI&1`%vK8Bp z{bJzze3CbTi3?3wfio_LF9m(Fflu=Zty+M0UBUhld;{<`KC%B3@Dm%4zmmSsC-w!v zdcL{f4ZtV(B&}v(RiVMFfx#m7t@z2fN~tUOB<#(=_7dbdz~2W>;#@-Vp8>p@PyEP9 z#<`1?dKf$l_#|H|cr$QDxxur6&)E2G;N0&)Tl@$-!l!8GTohN!`GkfmfGvCyzrcqp z@PeOaU^a}y#oz*;@&>*em{?`XCGa4h^tCQv)-~jZ_yu0UC+)KkxSdbZ z64{l%@JSip26}2ZlOb#!a1UQ6cq{O7AEMyk)xgXAq(__!fxo-fo)s{DGJq%EOuNKS3h-h+$#Vhl zmwXcTUf{V+hPGM2J8n09;ZER=pVDXXBXGeTCJ#Q~)Sn@5jr}y>HFp~N_<&#V32hGp zH{E6EDe(HA6F>e}0RO-zd3YH3IiJuCJ$)+i7X}yDw!y?BF!63a`jo%}_n5J<4fx8v z45irb2k!or8S@23-DlDjIL*cde#Dn2eG}&HR=x$`JAf6x=j<0;;JF)Vx8Pa88a}D( z4Zt9u~B1Mhv3HViKCmTlx4{5GK4Zsrkzu{(@?Ja7r0 z(76tn_B3V0e-= zBXG)o!h)v*<6fgI;PJrOd=md$U^}0T5AOpXf7|qhKLTgHW9n!w@a%VK(}c|c2KXfG z&A_RDGwp2}@Lj%6{8+$+mdU3;M>}O>&2u_1y#tzp3+#HI^#r)U_zz5*5%>_Fj2jOF zt3HP2_^AeV@X6WL9f1s5oC^MVUZ_`={KZ!hxhVlPl+#swF++{Q(2T;#jOUZBW>3NG+P z8y7yJ$OMbMK#_Zuya^PURIlh`>>~Vs=_|(CGawFw11&^#JKi2_O~C${{G|GYaQ`@#NTop|ND<)Z}nj>eAq7R zop&>?K)kn20aWL`teLS7nN#j_sQaDW=H}ng{~&6}J@sMS$99`rU&EZ(ZC>^s{)s!} zzwJZJlqqEPe&j%AsoR{2o0~6-56NNv9{)FS;zV`+`RA+o^XIGb@^a<(`&FHIudCyK zox1(@+tsgs{cE*(^JdlD+^k-G^;LD`$Pp#mSMjAiW9Sr9y!yfJI_|ygTDp{>9^>BN zM~Ca;4=-K1Vug74D7gFZ-r(*-IPb#j#DK2zAm*h@#cb_G>9;mx8&ppId=xxfrrnpW z=ybkM;NVW%ymYU#OTw3x5x@Ly6#u*TmX+-#eQnn9mzD9*K@dMTO8kd$mmhw#e+e(Y zibI$Wlm6bF+Dsx6{{cx~{|=EpZ#(QIf5cW+Ciy$O_lpCV4vGhz|J8@r?LNHwpu{2O zBeNIg;^A-w@nequ<1>R#y>s_oiclu>aqfR`)gU1NKZaE0{Cdsgq`cjG@o_WWiT^iu zoRMKXXmi)|d+#0n+uho)xD)Pu&$M6{!Q-|6y}S3^Gk15_;k|XuVun7!ujf70byz!# zf9TtOXID@=Yx+wRmT?yUTIu?J?%4&lHaUnIDL zPdAO@Kyep;J;O;neSJ4#AFNXjzDT|pJ{RA}ptSQuJ~!XrYv<|d>FB>jbmQ$ z(|HTE@%8K1s|Ox?w8Q zQy)E5c6F7ykt!;CDj2-+sg5gY30L3v;pbOA3UcGm-{D2jugX?F^Ul0^^PVcpOaFJ^ zl~-SI&BejsBUc7*XdL&{cjsNHZVcY@)Fbo$UwdZ)US*N&{YFI=l|^(2xa1JFK!kvZ ztRV>rO9D}F?l7o$W5CEFOGp4=lua2|2FZ-0C?X_rK@t#L5JlPCP_N3S;u=&`L=jX( zT;*iSlr-le*$s;jE2tKRb-`g?p9{ZBR9_3gJyotNUPY&j0C(Yq7rIL>xx`Jmtfw0~bf_CKVuTwtYLv^()q6x`KPFF}>rJ8PY} zw$GVMe8yb+7tM8j#oVBm&82Knxa=)+{Y%Z|ss7~ya~FSNZpb!sgFiPnefo5F8JMOr{-F4SpZoz^D?%sRv_4B)Q=~DOTqmO!9_T-aKy3J27b=Q1p?$JHw zo_+RN_reP=xR+mk*=^jo(QVnX#l7{`TkhR=-*vk`+UhocW$yj=-}iFy>8GE%y}Nh1 z55F_FXU`t@?YG~0S%Iy`#A~n8PtV{;XsXYa@4i&RBW16(M{;T>y98_NF1G$|l8tq9 zEZ;q9%iI>*7}!&Hir{1VX?@LcE@8HO*`<13M({)TXsxum?k2(CCHN(RUnBTUg5Mqi z-%RjH{j|r(kzYC6xmimUj}ZL2J^HS&;I|6?J;8q{_)i7DTk!iL;NzPqE|)A>zgYg% zIK`A^DW>v(d}r-LY*SrlTiZH&x4$!Oifw1Uvpvh4?b}$6fBY$ej}d$u!FLmUhTzrL zp5cO@RM*)}ZJo{Q@9e>`&erCu&t=X&+IRq7&PTuF1b@8XYY4uk;AMaI$ceGg$dW70OewN_x5&Xk~UnThG1+Ovl-x2&rg5OgP zf0o_~(MLTDR}a^#hkMn-GwR`e^{}U|xv$!q+uPsVzOm-M%{TYmGIKv{v~u{<1m8;V ziGsgC@IwSYLGU*S{*Jn)-*zzfNPlzd#+rLI-`qRP%zd`;0DN`9!+}!;f12PM3BH-& z&k}sMy57Lv@In|%)+cLIklbD!h zy~=M$?w+2OmYSNLl9-sBn%OG0dGn@Cdi83Y8NMMkO_%kTrVpv9nQ<++p;xa^_AZ;Ik9_k=2_g(v}sJE@b$Eeq~!GEl++Z#gIVjd;^Ml6apactjP#)NJT0nz zW^3I*4^3hkg&GUVH2P0ZPfJYCJfmKNC|%bLvEdtW{1d~&+3pHlCPK4IMYwgd&iXC@|RoR^;7BR#Vr z9W;olU;j@ho_ONveZi6IytF+-qhvNb=!Q(#!1a_K8ENV189g$h54u6uo454+ch5*o z?~#$3(euoPr-p~n2$?NnTlE$F^q=B~2)LHnHA*OOB>wsS+jhwii!*wpXLuq!jsgFL ze`adiBv44t$jEHivuC|}^+ZJZ^~`gR&rFK%p&`!G^)q|+^i-Z+em$jj)!H3vwds+b znjtRtjP_KX(K9R_iJg;^&px(x)i#Nl8JQVrJu=F$lnE&-X({J)Pl-RSO3m}a7vNP| zs?1gRdS<8iIyH{3Ql)NhVF-#D>4_4g1J{!~v~Sa{#zH^1k5&dhT z(~>(M3@!aQxMUpEx$LX0nYzFBw8U+4F4m|{E33c9pPQ;aDpsq^mz(AiEBJGV_frCY z?t%+0@czh@>|W)*$Ndw#xf_P}aC34~+#}cYa7*u;;ySFCPa!+k`wepg>e?f&t*vtXZM_?7n_a%W!Os)?LxO)n@TKMJ%pZP=*!PE@^8clu($JNah7B7^ zi4@mv!2gC(P2=O^TkPVE*a>9}E|#?71S z-nw<7;;Gf!{SG&cT(nz3hwPlQ6TE#s?KKQUG?El#d= zQp2eFE#pt9UcF|Gs#Q;HQM-1-&3U@Q=A>VLrK8vu2Ie!YjhdM^d6LzWCzA zb5(TnX^JD>qJ6ZjUcK5r`Q#JRdTNTh5TLTs(o)m@+qfPs>2%fls&!Stg?X(Ru6%~aT>F#i8LIUat7x}>|9)i#v@JSv{2Peh zuSy5cp^pw7Iy3`6;pOpv@x>QSF;WwLwqnH!kGEn-_VLFb+lCDrOfdz2A29L}IKN#m z^Or4KHu#4E@+Pi|EHgR zdZy+vMlvx2TGp*w=V?Gb6mK%-KyfB7FW`tyNQZs9dGltYq8rQyE&74(0Q1sIFL@f6 zL+QUU4~o?j!Ywa4{z2z|Hp#d9!yrCz|5x6ba?}ui;WtRAg**Pzm#XoBn zx7Mun8nX*tG+XqM**E+4ZI-T}|3^|h{;O84%5T!7NqhP5vt%z#^Wka0<}e5F6?&Kp zaOX4g|K5A=`O0T_4!^+{S%4PshX2TD;pb+lubU-pGV7ucdGrpX?@FJBtRzlPyPdf&cK$W=)Df`1_A$UpOeTp=3)E$BFMhTNkYL5p5c{;Xf|K2J2HOa6O`-_Vet zpXl!Nav2S6qzj6ZTkL9oJ9hL)ipO8JIA1aT_L_%T=oz-9GCGj=$T`Y`Cuw6O|4ey$ziH07cVd<{OI}Z8zOI^A15X3b2?z`_y>&!tF8iE?oQQ4dXv_#VJ z!w)~$#>eN`y;rB(onsU2wlPT_v5P)7`^$%B{kNHAK*O71eU{904fGkCBz?vvHIp8o z&#fd+==9IZvsFTa$6xb3Q?ZK}+4ULFf<2@SXrM*zk?V?d1oDNfBTJ?K^N8JlWmhX2 zpI~?Ab@Mbpz$KrWT^OdJU#VGN;nZ6+lKHc2+Msccds(Gb0&%(kkV=@n@3 z_{YS=l-qx;@vQT|m=E-q^&prJ^nv3*PrzSx&(_?2nH5j!YWH24V1=S#o@k(C$$kzJ z4FhH4|LX%YXbAKf8UlUBCbfNDe1F=k1vH5Np~ub6mz`31r84|w@4fs>*Jj}lp>qKp z0WHh{>p|q41hmkCKX&rb>B+X_sxG!jG~6p13dWV;pSRoW($7T04zs_C2J{)5lrEbT z=rc3~`V0+$K4X(&S9<*QWY0mR0eZ&zB0sCVLPJn1(h=xNBprN)2FhboJJ~~5ceW*e z>tYW~6b%y+?3+D1?G33jG-Qef<=E`65&DcxLZ6eR3)rNNvPrDZflX?@+N^OI{uS+# z$6x;a%=Ycu$H+&S0Ug*yT4V+~@OfyVg>$U6fnUZo)?0iA$`jK%+43o!Y?)|SEE*Py zh9~bBV|#Z?c#fcaDmq7LPCBwai-y3*Xuhfp_#p1f{$no!`S0Ahb2H{5sOT$rgB!R9 zv|#(O_v~x1o2*r=ZIo5lbhO7s!=qC=+tRDMSczz;sL%hFP0AmYY%{OuZZ`~1v1^B> z*wo8X?dI_p*?VtnFldPMF}yy@CQ&NGU-sV1zwns_?%+iWPT-9`z~^92uYH=R$N?c_@zM&=7aWddZM_jZMZwj)?VAeo}4Zkqzey= zh6ls?j7=&S-_7RbcC$G-iFWJgB)er~vfV5it`iN{h=$3cfl|@O_;2Zk$}j8>_)8YN z{7bH9K+AR4U1udFC7zyu2HHc>0Zqsjusb$BZ0lxpur;D#rD*W_EE=#$fj%#gO}Z;L z(QeO4vVVz&S)$=4(eQW4(e&(8k0>@NIl{+ySbDGW3;PfLf&6#r(xn+VfTx}cDW`#X ziKOFjwEXnbkM{oBJMGz-DYp9Bj(npUT{UW%ckLbXZDN{TRk@8iMmWp%2F*X5R z__gxJGfT>S4D|UQVSTOucUb=i{F(dSy?Zkc_P_%Vc;5@3M)3igFkwPD4Tq!SaB~7a z*rZ3WNx!DgqJdHguPZy%06ytio3*l?*1UOh%goF)J)2?pb$NMtHg4QFn>ll)*K37* zyxqYUs*DzR&)R^k!WY6HWj{bcpI^Oiy#4sS_8UbL{Q4Z}W84we=h@N)N@d6f=M<}` zEnok?bnNkO)vDFGdbDS>eq#GG`O$tJ3JVME{PWK@JzHe|_{Tp?IVHRG)>}P3?C;?> zG_V%1P9RrQ_AtcXB`@w&oy?D=r13rdy0UrZ>#wOi4F$45}b^oQ< z-;GLzPDg$u>-pOEx7TwpCDLKjHPdh2nRLQ9?$f7__j~j_jJKcQ4zA1*Gz67%@Eh4< zpM!75T%Zr=wAW|N1M733&)B3$eg2=v=Hmy>4S0RHwo|80x9XW59|vLVur8aPQ8Yc1 zWQu87ixw?RzO}~%Jn)wyD>MW(po5nFsK`{%H@5ZVd4uiAzb9#ZF7q*1pNl7_*kk`1 zYHz=^-1dF*&1TK_5yv0&ETzquG2?4Jqh!}ybImswU38I+968eSUHeKeOVEH`5cdH$ z=%`2wF$mUSEcos{=Igh{f9OlPyFf9@(yOkz>SyrO zbECeZ(*X@FTedVkyXNPCH3ON)_X+3#U+_ofp_9DE&?1BQCc)an-kbfi z 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 literal 0 HcmV?d00001 diff --git a/venv/Scripts/pyserial-ports.exe b/venv/Scripts/pyserial-ports.exe new file mode 100644 index 0000000000000000000000000000000000000000..0def4e11d9f0117b8464c7441732adbfae74051a GIT binary patch literal 106356 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 literal 0 HcmV?d00001 diff --git a/venv/Scripts/pytest.exe b/venv/Scripts/pytest.exe new file mode 100644 index 0000000000000000000000000000000000000000..e6ed79f9d12c94d28ad4e1310b8b3f6303dcba77 GIT binary patch literal 106355 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 z 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 z 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 <;5K2ASaqIeJ=8hfsvOarxH^C(@{2m#lpx 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 literal 0 HcmV?d00001 diff --git a/venv/pyvenv.cfg b/venv/pyvenv.cfg new file mode 100644 index 0000000..6a19430 --- /dev/null +++ b/venv/pyvenv.cfg @@ -0,0 +1,3 @@ +home = C:\esp\python39 +include-system-site-packages = false +version = 3.9.5

  • $HepCDiwsZFdrJ7rIs|7VD2ips%SeDXGDXM)N!wSX mGkFDcC%BU;ZyXaO3p= literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/attr/__pycache__/_make.cpython-39.pyc b/venv/Lib/site-packages/attr/__pycache__/_make.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c3cbfa02beb5eed4af4b4c8d838d13b7d955d1ef GIT binary patch literal 72712 zcmdSC3wWGYdfzuU3PU;N$SU?QYW_jq_OKZPU@tN6T5Dcwr-oGX*#(yP13|pQm3vP zKdE9%{r%r_zRLihR$e<#o`9V1dcJdc&wJk6dCz<56BDC3{>H!epD+E7Tlw5S(oObn zKR2J@7yapCE*Ip2{90~3znEVyEEd*_i^aU271m0NB|8r-4%xZ9Shn-<;;@}7ixoSM zERNWDba9k(acyjUd~uvSO2N?D#QNmoz~X`RgNp|(-SFD&>vt^Pv3}>`o$Gfk-nD-B;@#`_EZ&pPHTFb*-paan z@!m#t_0Zzspt5*hFtT_g7+t(S7+ZWG7+-v_aa%KYEx$P1s9wu0&V4QyOazl}<$_7> zi;MG(d7gfPr&GZ+Pp2D4d3vnziJ%bd;TIouHDmXMmC0alu;zIB}!T#U?R}Tfh zFE|+7&eg-g9l@P%J{R2mC-T<{i;o8P1o!gvv7j0p;{14UIJl4VCxauw z{hXf&9ta-f{A4g2%yE7ym=8X|`BROVZ{))t362KGxI5me1Pksecqn+7C!eO?j|7iW z?? zr%82+yq|Gb!M6pU;mLEs$>3SepAAk0&v8B-d^R}E`E$YNg6BCuAG{EJp7RUATfw&n zUm(}dH!3&3{oUN+7lJPaXL$3);Cq7?gO_-7CisrvOFVfocsV%B`K92?!7H4avDyd4IOutD!NmplDwvgAcO+6)`D<;L3D+)#i2 zbB&ey=33|ES_nk0zTD`1HVoTgfA8tA-EFj9XhofRYq{Yb?mN@&oNS%!bi(MAYT=&l zdFg8BLc4Xg-F|T`aF1^Pa;x5IwLA4rBT(|@IY45sx_fK2I#1Nh%Z-pm)b5YEd;G(d z`f{foUah^k)qT3k-nZbItRc?agRm)bd+eiVN`Xs}Y5*H9GCqLiOwgFhyUrTWeRV z4LXObhVpNA{PznBRUQjS%NOdc^Nry6@#>?GEIhLCFvXs0x7TLRym@e8jaM%>T9;q_Li60K5omO51BlkoH=lYgf8&|_SDe-w#v&)*7u^?d42K zSqR$8xly%TqrFz6gKO*UU~^5^uLta6986e!QBGotH{W?6L!-E|96TaU? z<49N43p?DSD}mUM=xHvZaSoMysZuJ84^@hlqP;lQ7e^fUaTCpctBd7~6o8$&^1 zrC{SczgTX~G=_sA7+9HS!;F2N1w-_HrN5_kN|fYz=*U;X`UXf_|9!yC2FJ-3gS*o> z&#;R&&-np=q28%(G?y=~HKOW6)#dg^lZ$#()ev_>`s@WpEdMv7Mo?X8ht*cQbu3zI zcOpkLNPnf-xxn}|B4#w~>9gS`6ym4|O8s)X8CV4(_(^@O*@Yrh>(z}m*YqH z`nl(;_dW5*V+Nu1Hgqn4XExU$jH0u1MMK|y?#!|}I{wqO2Hz=uQH4V<-=m{n&V>iL zF^qbz7#`tzaZe4n!&G4(op4i3tUpw%hv%aV^4ZV^t>MR;%b@kTvh{=et{s?RqueDt zW10L;s}?>KSJKtN(EbrUOUs?BgtK}wr^CDspU`21Lw^{-L}W13H~kW964cb&^k7*Y zv{mXBWNzHIkHy5Gw6 z%B0VQVKcW??v#5Io#9SprEskf{{GI$>S%A`2Xb2zy?pqiyvrx=ezY^z%kw7RtAszt zn@aNL=Q^Xk3U4Z%@tz?>Q0SF+#OMwuvp)j-)SspN%d>5$9$_(nafugBx`c=e6Od2JI710tuSOG_&7X%j4#mORYVvKLk?4<`%>3L>daew7%rblORm z5Wc3!BZQ)Uj*47quB}ziHIm|tltLPl!R&j?n#`_gp7K)4)b!u(%ez1V^v8iJiopfffVN& zaE^_QwWfg>iah|~PgfBw8{w5Enp3^Hbe7thK%VxRheI{2lP1Vi_H2fZF9TG=E|o?_ zU6*)Nqw+L_4i!pARVee77S&p7U-7ve7m(t1gKMp;zA4rgkBUjhcx~W#_2sLr4t>{L zrh(_{p zMW#4kMftkW4nVdQbQ#0H1m|E+wB9OloJkb-F_R6^M&KoLh3%5^S;i0<`P zQU*h?v0?qn&_QC&l*6b4pM>(JbGX{LA!hn%wGO-O80mvc8w+nTVA`PuXiydICdB}L zBNCbHUy-okk_-1hyYp-9b2c`DW~AXquH+Lojx}2D)^IY(a|u3H3pZP}q?J*O6CxV) zMyCD07wDaoGk{`@jRZ_0MhWu0T8-5+kN*K0PfH_n=x7l3l9HSab zODc9&s$Oi4hfgx$J&L}T7LX(ON0(#8^rK6y?6k&B&d1I@e@ zG4D83N>Cy!2Cn-Ts$XQ(m^4N2uQ%V2C?xfFURi*pK8I|zttKC@(k~hdtgn5ihVE;o zetv91ogjp|UW#jGh#?f%VD^@~%wS$`${K6ckF{TuATrW1U=#Wy>x)8x^%v@wo1!Yq zJPHI}y>>R*su({R#DF*EvSZ*Rszn2)z}K+drHoZ^9=^NW-V8C61@j(~PPh_S!2f3F zV?)IT!Vdi*W?$78Oa<9!M^RIfy2A--q&6cM$85`xL(*s>Qzy{2#tFU}l`w+kx;2Rm z5;hbVBWNM+2gqOTmdO?wSuDxRFiF4^6q_rOvpw1MBnhZ{xpM$4F>#kh;cVUmlEDt) zGb!+!DO@RqHf=?Dr_GA-CCLqlsq#T1f|r@G2%am@7?95+g*w^7NpNEE2E3gDr;{ql z!rl+^Ny(2CA`REQg-hx|0&PRzbnLoBbVm#ur0)j&q`^tJ>xElicTziH5H=^3^y&bd zK^vO~kz#?=d9K+B>)};&sSQm8n7Iq8VCo$bK3Eh6SbUjDNI)chfgPvSiENM{c-ypq zMo}%!9pTZKxkGs=G{;SdG}UI8)`EF{@IAKhglN!tlZ8VQr;?I4!o~{n5AnVy)Ad@bfI-ZIah)gvD#=rO&=p{p6!hR8L&!bT*>n4?T3{$`y>+ znyOi7hvy#(8kZm1Xlz6}9DA6Q4?VL~^OB138WTLj^PB4;#}v)H%Vp#_XJ$Nyr5qbY z;8<*@d9G>9KdHl)8YpVa`oYm-m~4;%=1=PYCretRVzm=#N6pW)V<}a87L+v6Rw{i2D&OFR86pYN}srZ$cqjCV?RVg3Q7X zwcsiQuPSwRxs6Dxfu7Yt5s0LnT6;`T66&7NZS>3_zFfRg)BMWu>K74bg@YMFRK3`^ zdIg$&%w}L_W32haTw2fFr2m+FR%cC=*DXLG! zts3N1@MJ8lp}!lwl|+9V})T9Ij<#U-ep`ezYK3IP+rd#g|YK+EsMC>O30m{CsuE*M14*qkS9pM=PujHId_qy@4e_Cm zh(b=AUt(}!xDn*jlD6j%clNwQ8k6CcVo^Z?I;0E2CUWj184Q?`#2~+(b8gQ+Y4DS- zB^pCXD*_bGhNeFaBu_3p{&7Gun5+>ZN=nN>4P*YaVZ=@Pi->HRf6}ZN;6gBnSCdH! zv8sqpZnT!KI_c6ZYd~wWb?h8O34??un|^*F1z*y#r0IEnknrv}k0rbJ0Y0M|Oh-w(#LJsEWHSxCJF#4HSU+h-})o5xES^;F|i9DV@|0*?0cwRXk6--F>Jt8`$MKU z_DAewPe#*DpPT64QDZ)^5w7EGVJ;udb2Yy7ADx8+nW;QHtO7<~k;Ta#^eb`o`@^T* z!k-ZmcyzO(6D58_kVIzXWNlF-v_4iXlF85{5Q69e$D&p(dEa zCv|vAhfnEnTnG7=SQEofbJZXAPz^PU9X_MOXLNX0hi}v2qz={OVAGFf$M`PuH2M;U z+;`_@3i-;1n(HEv$ulup0v@G&4%p;zCg6Az-^X_~16}GAS9yCPN1EL2IfK`W1}{Iu zDEN3aB7v8Op97oV9j0IhW?P9 zfL=*J3SXu*;VU|v)#1xJRFi{&Yuub_q)ahu@~j}Wj|90=3A8H}CWp!(7YO)Uqr>6r zJYO#8t^XV5te-({gNq=46D)c+zXfEt0y;tQd||7|bnbHwZqMajyX)1$R%vUfSG-vO zZi3lRPhIH2{b#T%8;ew`#gW;RQy&}*qUn}I9Lhcm|&Xw-3X5q}F zpN4kEVWW+}i8Df~xGXv0QzjG*J;d7VOs~je;_V?-0Jfsp63f-P&O5uw??Y0Gz#LB( zxY^z>UOgpOJdqHaPL5{n>$&4rQOO0hn)!p&YPitNrnE8r(#P^hKlEl&AIab5cPQCwxp zv=;M3f27eu0o7b{zbucLR?y#Dvlb?PfVGW!C)9}QSK>Sz=9X65&DPv7RC+D4s`N*b zT1AUfc5SV)my3Hd4{)qRJfzTkGh8BThtO4B*}-smzr$tw%vH&9`>L?|4wB|ZM4c#bpf8F7;8TauYgc@LNG@F76 z^ERywhe~`Ry@lKm*Uh3h8+$WCQoA6BM9|nwEE1UIT!bqj4Aaf>!|eU!ixr#NF7Q&i z(8M-y#e8`-Hk}(qfVKF$dMt22Qv}F%&N1dKt-o4Cht(Wv++q zS`CJBH?J1QGG!tPQ31HCaGsj=^GxrPC^O1tWG$~@dhwDOp2UAhfR#H@t~_phVZ17&MIb^LZG>FrcL+IC2s!pv z%^}fGsujkXOW`%OAj_tO@d-}+Zv{W3zqk+ zLaWO z;TwEpqbdA8UCGG~jplNDjrW0WirA_8Wz-D}|NSCrE&hBOV*Tmul?hvVJdv;yag(si zv+zy5keo}mHUjx0H(J}VcrV>=5UC{@L_Z@GILybA-Acv%aJA`tp)d}An;a_T%Z$iT zWV!MDWFhWRV_vl#w6(O5Cn8M<8*@rJx*Ac77Q_-xby&Bh;zM1jQmX` zyH%6xKoZbMo$HaL0Fpd#X+LDrb#%FYjOsh;_zjS1goz&7v;d^3zIgny7X z%SKq0hCdN|IkFb{MNdGWgj;XJT3K0;e;Xak+)FAGAU~BGIrsFnMf8r5tr7DE&vG7ziT_k*l6=Nmqn*j#SWtoJ zjX}DmdLzBD6{oeda5SwP%x#T#rhDV)h9ehC)arrGo?tW>yE)U@+Z(^G)lB2T#9I}o z>l|R6-2qzn^?d7|&R)01F0DUnohII5W#BvLKDK0K2AJT`YHge7Dfh2-_Q8nus@=T_ zJ-=3Lt%IH0c(ZTko8UG<7I)Z*0}e{lo&EH|f!_4B!q%Q%kz9X@b$d!d@y%s{o$Ba zoDJ)(NWv%f&9&;%fc*wVPhO9XK`{InTUjm8k7K6!=NYn5!=!6Bxp~y4n`{=?$zPhg zT4UbAPg^wBm9qxA$+12srUE=kzAEv`@^dyk|3Y7^QxW&2Co5n4(u>$rp%B>kcyI2Y zMiAt4q#{Ixm=Z||5jXaMpAdBf!9Mwtb1sx>n^9!^bk~^yDNJLq#P4N|MckpG_r2U} z(_j#~yYs65gM~{7ttml3Au239?3i9*d52UudP24}=^oD%7~8)=p1JOk8Z*AGha8Vx zIAPYH=ov2JOgaqkNRXUKU)C<)uO@g<|Pr^>IO9VBC=li zJ){6)`4)%5a^RX}EvlSwV&yYc8WjFv(sd7iL{og?etuz~CNXA2s>u-!fL#_F@FD@b z$Bi$M!e<_cVO{#>u#UeOsGa~t9u6-CrSbO) zXBcmO?wz6= z9dmiqD?_nA#5ZOPW|)kej5&ONMuMu7)jD^h(GlN9LQe+eJ#kAm^C4*Imuhn5 zhb4q#u5cRDx|^+d`c}oh%Zo3@#dcd~+FI*i4&GO@qACoweMQo%&HTY*5;4|I0VJMY zv71rqdR46^148WY(qK$tNpCjW_T%rCT`kG3MAyccX z!?%?{b{i+vYBbqAkdb{i*&#_c{F)B8>2RI{(+MU+o9bh-shRxTYAc)P`K*$zahTiZ zbhaVbZ`A0IYayGBTog}3lm6r&tMBi@^=vIjX>B819A2*1#Nig1k#g!MW)@j0+>Ntg zKBgiy8W+bl)AiYlBe88I%82MqB^wnW-BRyx-M+g}XDzu~(q}`O?*5a?^+$Bj=<83X z^>RG;*YhBe@H{tB#za%e&lFG*5#Hf{ACyg1oR-O-C*!=483z+7=_Ywrz-&76Vd29< zsqkBc56hK;eN`GNzh531cgxZ1K*ordD%ml|F&jYNc`=8H~lQV_c%o;doZ7X}R_^ajpw&XW= z>!tm{STN2K{}C7Igg7h~+hlPJeQSp2;}*Xpw>W{Ow4eJ)^q+&Ar_gln;5;4N8QjHr zPjGi|59gWS-k{2PZ*V9$%z0mMAINt^6UmwadX|+LNcP=XgWU_*bFlwiX3cI@Yc>Mh z4O*xjUWYKe+XEsQ0Bu^SUv>kwKbe|&WG5n#nSETMF%T8}HW%i8$!xYGI=T=OQ&0!u z3Ap(S2pGm+2XQJXGcFc%TP{c$cH53R?tWO7b4+Ozmd?ymBb&P7<13B&MXc|3PdCOU zVLu9(6VemsW)dU#vM74|CmbE-XG44j#AApvn59W;$>z24tHrIUt!ea9>;pCCU&Eua z39O<6wlH4uti+20eP{v}wUgJ&dNBt5rH9;Dg>wC2yEraE+-_GRCvt(lJpv4SEN7V z;*F&ESgV}|haCRb@Hcl@iIDIX4G8}n2hrSw-keoP0@NeF`TB{P#Ni6%PH9yy~49_`lJ^|5k_p zT8F=?!(Y?kmv#6bbPx$QO4dK%W+#(c#uc2KarDs)bAN{9#LrN?0H$1xUG>KY#@e18 z8H7KnG9=*jM>Djb>;`c7Q+oR>hsBwlV>0~D^j5Nj$r_7p;z!2ls-D^55LXh@9CG|& zIxP|p%n zY$8gI&Cbaqm2=LX0U|h;*Otiu6r`+#*?h{Mq|o! zm;*PnN0|jS@l52$`#P9l;p`g!rM{^qheNvh-#Pq!b@DtH zxf0{Lh~cbI{;*J(`~bxOaeus2VMbsQ=e6>OrJ}|@h;ZK^BJ7G?11K>@_M`lY{34k` zt{1VQi`!Q^xf{jRc!nU)bs>5s(DXagkK7fbS2&hdhv?+$+qoOL)$&b3zXZj13*qCP z%IXLyhfc_T^~$BkJEPe5nRVDI+BajGP0)lW-yToCtpp?Q7J?Cw?Dq$wAYkF2NpGQEa{h*SMopV@4IWK-Trc6qyWy+q52yQh}B=~SK;t`~SZqjWcXI=s@xZ`iB^r6@+;GdD^% z_jdNFPMzCs5U_)mBVYJh>ps5gRlbpr z{xLHEKhUcH_Y+$q-Ra)Q>b)v?Gq*MRm0W8dgSq;R!qw?`IJagV&h=7ReoC80D+<_duD>7C1LuRVRW>_%!Ljp|9+2y#9NFCld*w0_==Pzf-tp;Cz z^n?T$GhA1wLt?m|`JnsBZR2YvNzVv&NiL^DqYDzLGE<8Gl-o0dCV9qRPGsF$EQ6Z0 zcv=FeS_^jg78<=k4Hc<+&)Xbd;~FcQO*B@yc9y|^#?^yrMTv`Yq41&fZr=r#3!kVa z1CUoels>74o4ZgwZg^N{O@~#IhW~;CdHoYz+XEwHQX!91yc33hN8n3lF9xb6q)4hJ zF~C6NXDzIHxhyKUs;EA`%NI*({Nj|32)E8fv>jp`9<@h~FzbU9e&3eGg31R2_4(F8 z)gKQV5+hu1Y=hZzG0ZGPk?=SUVpy9y>@0Y{s5+VeY@+ZlaI3zRa1$ZL|Abzf>F)p3 zl_B5X)YThGSl-;g0NHRdb}E}2|4Za>=W($|dgcYM0-G7mjibkunTBP*Y{r+!nQg{w zygSC+AD4d6T=sVexC)y<2U|#PGAqRnq6&m}8nh!u8^0;|(l* zvhN$}F7Am(bu#DQA9A{SC61crEQ}>tG#4jg;Hj*rU(%^Rgsb+J=oG@4r+H zj_B}fIv5)NeXdHv0s${q!pHH0@}xMfd_K&u2ahfxm(OrS{w|DuQ2MYuEGfJM&z}CE zP(&7&L9dL#ZiwfY0cViYD`o;z4wd2^mC5{caXLRWfw@M1mriWr{d-J!F+p-aX>kzTTf@D^yDirOt+9}+ zrc<#bpgYA*Xc*g{WZg?qkqD8PII;mn}O)o=-y-Xpt>8OQ(V0 z=>*`U*LX-pD=Yhda6%a_1vJwAD zI7G3SMs3vFu>P#FMdlD?Gjx4Y50q?s7YzGMFRNIMtsGb@+_J?FU|jdMT`RF`N2o)h z6C65dUuOG(3p?x2+j*0!@K1WHVroU~I5fFf)D%v3;>OsXi!%rQ{vfFr3-#@O zVBxzwzY%v=Dc^l+5K^}=IHcWoVpsDHos@DhdH8eyR4jPk@Zi*teE(AknAF;n0}=n6 z^bu%jp=7BHRCa|-iE>1=%^Ly8AXmgv#dhP?KPQ=S^b8sw(ldeM8-T;{^Ot^yz%b&S z7?E}X5`)KNwEHLqmOSmuOR2rqRYx`HPY!1G2jV`;mAX&=uBtUaLx(6zaNEnzWBpC2 zW~YE36KgAOK!UCnkfvvxnk#QRlw4HfOZ#uIoCzliLi*`mPRgL!Fg<^U#UtZ2^ut&c z6|hJNWjF5%^OaSe=5Ynhb)x%7fP3Wudui;;n-rC zK1NhHLfMWqWC0kDwjUAzNBKU-5*`dn%q4w`fQ)m5E?H-qQ1MsDI3`r1st7uUug#RM zRHg85ak!>>OmdMMVtEYnTjS+&VREW_|3K>(ZZYr%NEZ2~yhB+A#yx_(=Lp26MIgdw)_@6jmrMZujoNifv1~qe5aBr}&vhli|p4 zVptnia~yy3@05%;6UH&+&4uu9^I7>#~fLebLI@QxBiln3FxsAzM}dO`xR${;Z&Nm`?_v6+?})*1Ht`U!b>VH|Qyge+SSz8}0{Zn1k|` zFlT8F+2vq&QDssxgK-su_Fw%SG;ZTuYKCSZ6mr(cpj zwi{>vXKJjnEnz6=&hKobbBjzp3}fL&pjVtPJ8O1`d&T#%v%16yuIRBw9xa=+L&C$t z!Ybuv`1^=}Y6<_TYGs^>E*LG0e{j!G_rNXcHqZyw5^}XZxWR?cD}?4~3C!ENL(+Qf zs075-!p31C93pBV+$XlZDr@&XC$P%&vPw@}+IJ%teO}*_b{GfY3d|k7!+KRt)s-!T zU*3&4tpn$0EvVEzU+WDMqpml*PoApF=@m26IhG8?;Q~zhHF>AzOlIlKu#4-lFyO2p z3={R*%P)G4Lw8zry`AY!S=}+|?Z>2-yUHpl{p%IBhQ5|-O=8reF0$~;G%1ykc4Fa2 zOrdtMGh~G|H*OL5n2@LJ#){;Oei4n|Eu$zY+6Zf5dGKC-u@coC(MSi9SssSy3O|aZ z(c-_3hTC1(*@<47Nkzj9K+dLSw7GFNWoWGKS!(3w1OgRV)1-#RN43te zykj~hlh1%`De9*hpW+wwICPn-UIi(c{K(UFGjv_yYBjON79P%Z?_m9}t)azJRF-LD_8oobTK$iIh(z~GC|43Kob(q&-R)mAk;L=^*jLY5M;_SDLX2WhM$4MVNVYkT@I4@glrUhdXt+TZj8}nAbsm z_6{+tN}L`J>Gr?k&;v~j^^k^3rG1r86S04+@<3&+Qm#x?4p&N*@yb5_jq>k^ebEi& zExj3OaVTrHmIx$ak%iJ28P=n55Go($9mtSgJ@)0qNGLx-~47#uk;FCXH z>t~3)#s!Nd6xaNS98h${vJ#F=Xy_TNTr||Dc^q1l)5Atz#qL3eiX`CW$5w;~8UBp@lKD06)zBDuBpQixJJDB?ig7_Zvix@XIOBy#K&+ zZP9%KHTs^hHSYlSLCbZ?0e0c^+4qn6>j6WR);nm#Sx-=;-FMpUh4=7YCpw%NP$6(K zm8Bsv`lD%0wjo*ncFf>df|EcX_$2u_AfMD|l64|CbQktXJPDX>EGaxj0r$m1_!P)H z(D9;XNyp16;^TC@H`>9F3`xEq)`U1z*vc_$ma{b~Lo2M4@CdP_xDf?JsjYHroU|}t z(sJjKa=1 z3`)&>HdP%5yL2@CQrczd2l1c8odmxGABPi>{K~Z90pgnBi3r3j{j6uRJ>ia=8ulUT z&}j%dh483zXuWh)Z|6$&AEx%!Tc-xX0$d?hh1+X`y{*<<k$U^mHnnoZz;>OrAEL9Z>Z!wapNn>%7 z5hY_)6%o)cCt*1JGN_omnFzq92dutUNCXA#Xb@0<%yh~B`gy_I;Kp7!TuHz17>@VdeDQw{oJJ8tO_F;2xW+eFN z4>ht#n*4^4(~RVk1h4=ROAtPJ*@P#+k!2%?9v=^ImXlzSpSgSA$cNJ%Ok0o^(+jlD z8yCuHI9%z~!rMb{PxOY~9>HuluD~m{BgZqH^6K!7@gK;&jkCk`{Pn{1;`P$?q3h-A z!`CaFyk%yi$Se{&K)+0h@zFIvNBY!63 zuC3{HIGY5EO|UQyq*4n*7LaPPmP^lIS!b0CFJBxIgsj66VyoyjJ*Xp@;kVeit zMc!IbMvjPBTt2 z@}Cdl#`X+dsFxT^E&M4-grsqE5zd&wv-q3|evVop9;tb&Sd7F9ijG&mq8;p9h+CqQ zD7Kz;mtttDstsZqLt3xYSqPuN(Z$?T4+yCT=7bz6q6(Wkz1oR|>b>s>Lm`0M@1_5+yZ+3`V<0!Omo5jP<5M_nM zw#@@H&M4Ex?4Xm_RG`_B3@%8-vUpM(FOMHJnb-%~j41>$(~}yKAc}~Vq^5wZ)YICP zj*}bEJ6xKB-SywrEQ%3-FyM6Z6ZPL^5u{ zx7}cyjdxTaxsAspr^I8yMc9bgkuMWwBMv#EzJ0mvSm_&em8V(0q+mM2Xp79A>W6d{ zwn&+FE6EHqL&AhQxFN*XjZ5)KsuVaDnOJ2(xz9mGvbh=Qu)r5$T58RbZC-+^5G`~Fva#mOtmL#rXV?Z>?GOT& zWbzVYOywkFiN+^^mQ}A^$8xMwr#j2EjsTw+Rtc^fO5PkLhhkY%6W zO#631DVO39*U3&DKD>3j7k@`&kKp% zYtgJ7vVB|vY*pQz)W!272X>9kM_qPwg-C)~2?r{`LsPX?LMETsD7;OIB131}T(5^0 zjpeWnUg8f?opq56;bu*-UX`!E#|G-~QN>xLe4?$jH*FoUmo?SHNoM3XP*aEEu^cC_ zS1)?SO@P}4wm(4TT}CUk{M;f9+ZWcY-XWBVn6l-u!l}Zh?+`Xv3s}W5yF6a!1QIdgzhIp1>AjeJTwp$qsQP-Dfu|J4oZ% z#=@N&MnbV&W%bZ)ifB8_BpIjZkr5QCUqHf8_c_}a;+yXrq=P2WW8K~*0!ysJB>73@ zvmxq;i8U@+uACup-V0W0hVn54G}%?dB*~2~mnoV8{cbi%S`$i=HaUJWP-MFJbjOB% zA6d4TlrJ;gi!vD>hIdKzQlQ4>M~yh+Y)-Z#sOt}H+dS9$y{NiX}ZJ= z`hy+eL{E&ZMn(icKC>&(4e?yR$XT3ml9MNEPTrvoY;y+d$WSFHT z+X8-eE?HBoeF;-xTZ54vAQXehn99mF#r7~%lJrGp39#M{aB$P=&EYbOzj}}uEUG64 zZnX=HVKZZRV05gr%RzYc7?W$*7!Xt!sxN1=@yP4=oOXk?R%&yv!Qq<#%uG#Q?L5k| z{|+H}k#0<{KHtW}A6QAv^MvUl8+=Fr+U%IZc_kgTn8cw=zY&_5O(m!kmcr%=h7K7I z6lWvOIqi3oUKgAMZJrsqkAv+EIDt%e%yj@r&Lo2R z!S|ymv33HBJ4OcS7f|yUilGK@5T#Md)gJz`bU;KXT5F!aAnO|g3Jt~SlkAsb{pdHc z-`2FpJsD4FpCwM@56LASb)XxVheYmL+uOA(Eg+@AM_(P&R!D3DGf?G)cnhiR*)2Ci z7`)k591m`?(W+ziNF9Sa);dU#Z^G3RJYs7v%vsgL!YZ54viLrSk2=M}DAhp8pM;`p zp7Rtz@?kPq9CRdl`_ZF$b0?Q%*>t?NNc3#S0M^T6$TVnV zg-yhMO-sH^d7Tpo)>C$FGaMPD0%e&3(T?E}N{GhiyxVdc-NZ$MHmMV0Xe+{uaM63J zHjPaB!)U5PEW-<3O5+KJ<+49xy#|@|m7@6>^;SSXrIuQqR%Sf>dC<`eiA?UbDlTd$>8IPw$a8;k#g@pysE1uV}nVBT7c$PttmA~7#z)SW^OK%)!Ja#Di@kf`m-?;`%QkMJN8w#z@ zVj0%GG0h&EhC~EeH2h5S;0SY_qh|S!pMLyN4Icb^y!=mB#8gYehMgvt*`Mwz4nOQ{ zlvGAS2>oM5_*mx2SYE5P6Q4J@BA-FR1~jF#=`kJ!Ce8Q`IHl>fSL&hP^E(46BO#34 zq$1u%o#11@=yN;CZ$~SQ`1yIi>)SlGi6oB!+pwoWI-Mq`6=I2FYLy6%T`jhqCN)at zAibWJSWX#sGW;fGklhmf6z}MLBE5b_J*~0D(ne%7zo)&&nsiiTta#)FBUxX_Ok??Z za+65?th=Z^vF@GlVpF9wM}iB0p&i+pNk$(;CeoP1XvXWMwC3UiAV_xlkJ_9yjj*8| zTSY8vvvPvvvtOQKY51KRsR@ebIf$j(*wNYPQIg4!K+M_tU>zlhoJx{}z)R{4L1@Sx zO>y=|r_~Y)c)a?t_j337fC(Q`Uel$CkXk;ac5>rguet8WqIp!POpubF6hmx!Kf`#PCx zlWdHRV-AU@@{s@1tQ;7;i6PXEU3MMYdp+>dI$=eDLi;Q?aD8xG%0epr|`SYWwF%c0eGFs0?NUOG4UQc5U8bR;nJyn zJ^@T@mP@BVH7eI(J8Nsl+@m0#3QV#~ zfonBCo{r>W1`%Uo-i^u}GIyW+1*_e@orb>vBB@|C;ad*vMel{d*{ zWaUT2f!yI*HJ9SeF$=bxAx1Iti^rPh@a7CQpgvFqQ-AB3!UyTQ0Vl|8`&_?_waUzh zF!k7hj4`C90HE-ER-mhNzi>I|Dp3QV5vb^NhxmVDuDmNA6Pxg#-IX1ihA2 z%NH38rZiH&f&GEypcCiiaU0SVW6ExMs&0wF(%_l3g$+Pr5L$Fg3>xJzpTGvd+EQa1 zz1iBu6|>~?-Z{iMfg~TBBZ{U^vn%+eKFMuMKCPHX1GZ6G#(WwlLA%aLt&>=^!|;JM#_VNFae9A0 z+r;GY238K!(&mhF0J{du5gfU5e!=l}q-n(>5n``wpx4rnnDsedx`Yt&&iM-48gyT?r+qm~&P=wQ?<~>B%PA+Ld&hgGWN};;Ovn1i>`Cb0bWM9ynxfG;-pI zA*t^u*8HB-+&HYC7umf0s6j-+r14(ZBn03E$YT=eb2;BMCon=>n9c-)&|yQ44bz?cTL1b(baipr2B|ewmY$ejaz4=Brk7nm{O@) ziaH4m2!ooPbaLWHT9tGYW4CE|3?A>d#GHh3)Fh1e6F_uwNbP6B;bqzmT1XI+@LhHm zPr%}L{?3~Iwl^wMrGbsK8HuqAb!J>JIFo@+UBD<8q}aL6TD_baA+O+%XuTDs(Vi*8 zyM?%sZZ|gkHS3uuFexaLHa_L{+s;~7lQyPH)3hnJrHnzY9^1tW#(MG=-Y=TsF=|vz z#NegpLBA+>Y+SY&9J8qGC(lCOyFWeFauYt9PvC=H524`^N0 zXx@$%+j}zJ7kpdIwgE&g5Ym2TB?B1}@VZ7qnmRUcGZ zzFF!-bb{wQx$737jI&Qgn2*TXsG?EkEZ^GxL5g34+q5IFFqIYiup&EXVq1hRrki># zZFgH$x94{&s_TQHf1DhgYnUt}Zs>YjR`*z@L(r=*Dmt4qjMy7PAG1MevmFa(mRYuw z{0(!ZP$hf$MZd`#tq3EIRqk3b$gx`NV7wgl#_+~r#h_p(uKW#__HX9ekF#$XJ0`PV zR^djz{cMNTiGl72l^dRA|K+p9NgvXR!VV!$Zp_?ZRoacc9YUSlsN5is!;R@+_?@8} zxf^3QSa^Sh) zx{+(z-MJ8HFE6ci`(;v$DFx4d#Zpa>YLwiFcY1T&-jr@m=zL>D?Ifb-ep2wQuB;_v z7D04>FwVNl(v1;CA?3XymOjhXcyg6F;J)FXObzKVY z*EJy;a^ZtI%<7;BKc~aTRmZg#6kUN`g`I>UF(##{2xVu@s_$){a42}&QM;3x{W~<$ zF0jf@E8DxLzm-Bo8hIH4x_aK$*4rxkbb2};{gU*TpJd@{q_;U+46B%2gr4}Y#4_26 z0#5Aq@86V$w>|$CrTv6}CqT49P3k*cEBGJ5E4gBCnj+z&2}jBGZ?ne?YlZKza2Dm_ z5kgKYutj-@eQQ2sdsFu-jr!-MA(vmds4c%;+R{+hwiJ)jMa60hONJG9Jk(-ITQ%;s zA9_Kz79vb*mBVl6Fn3^aWW9d%ob8P4fOAUSE%3Qz$Lb#kWCNnX-{5E9Oycyr&N``Y zty@2%&(xnTSigGABKEspd`>T_diEJz9nzH|_WzI;gWknO?z{On6UR&>%4dO?orOo& zv=-h{4saRr$R+EP4|5|fd7iw0k4)W+H&#t&lyX_+EpD~glwDc zYCvP1^^ObX$<7Rp>RWpH8oFJ!+Ex-ox=iH$DbJWc3o?rnkEYOA; z>mX6pl|kZZ+B47EYZruv{ii|0!uW58dcLFG1wK2)&k`&8NGEUY%ZHyOIg~Fg^;Ts) zrLvSyZfGE2ikP=B#j;miiH-&SC$M;)xI2X{f{$zaI0`KiTi)XBtP<6R7(B4cfFP7a zt|uJ83bAfhO4>VFQR)dWP=2@2D_<d~o(@J7abR7{cP<0Cg-L0N*{3l6Kee>b1>YI;j zExh^Y)|-!QJ!4Tv&U8!1c+N)2rOnM|&>zzAaktTe^&D$`VH-NwAoxyQy~=@@#*L2G zKNE6jA$V2o#gWZcldZyP%96d8!g_zm_Pp*-`4@2m7yYIe>~nxkOu5@Z$U*psxbj5E zv+UxpKBI0F_A3ejfpUs2xZHTY@LQ!3cCCW8XqPLkuMWQ`RML=%|3oje^uNe264{T# zjou(=JX^7{oh{+yONw1&Lf8<(8p7AxY%jA_4$5yAg5kH>aUhcLQlUS~ts|XcbcVYT z-Ekh(IT*W9IFNfAfi4(-lC5%k<@5Qsi|I=2l_A^6s6zONV)##P6xndO6iju7_7ikm zA{x=6ex4BP(|W?2J^OMB_UO}b7mT}ftr=3y?3!wCnp6q|Tq!1OQ&;(*OsKr2EyZ4x+!C_##=k-PNl%CxiJg-_!jyDmdZtv36h28xUN_#iH?S_wz^DB0zoC#$cR!7n5ywr$tL zn$KeP{rubj4H2+tiBka1azS4aMxsZ53KJAi;szT5sDIBBT#3E}-(EY2$X_JzCt)Uf zh1Ejb`Z<2SiCX-tZoT^unRd-DF(%=2T(f6m_WfdF!+RDQH(b4xydcwvh?A-na-hx3G z2&Z1-pl2v^OxkG=cdb*iAYAVaxxMx#6{nj@u>Um+k)y>C6>BlW7suTcDm(r$SGqVN zgQQ_ph7#Rvv(te?GzV6*_qcDm!e5K)FkE7{z3~qSemq>s?=N&8*$taNI>+7lx zY~JD-{XVW<7o48sc}^jsiN7jnRgeqazfrDC{`8V)xv+1qb{!OLA4j6xhxF*q1VJY} z4ml2tKfyrb?-W0aEo32C7I)exS{wIr<6YCQT|nsx$w+g8VC+y} zRt-Pb1P#H-POv%C?!1s$ke*qy>9I+4g;`f7shP?-4<+(WCK{yxPMTt+46M{v15MOlbU4PaDhWn+jXy=^gamc>NI1Mh2f(3+fAg*>N zGOwz$T?5)&_35X%9bVApPD<`y)E<9)kTlP7eT%5p7>q-;%IkB zkLJ2lI{B)|$l<89h7qxej8GfD`!*ov;!y5RqeYbaL&ncS2_l#Z7^z|8wyUiul9r_O z456+n$3r?)`SvepxTwE!rAd+KiU>5Sx+(YLDEyFQh;a! zUIPP7gG`iBBrAG_gBxg0@K!|s73_10@$cjP``G>#`+qp7CGhOZ{8J@z2ntTW$~i8wrlz>u}tvwHulb3^V#8-K(g| zO{}hXXtAr53+wNc7g}HHK9G?4m$hPPQ&Aig$;7#$Coc!eiCIZR5vE%q@84nZ?-aOp zKk!90YNb0Jz);-I@v0M4{RcVSGNhcz`Tw8LO5KlD=)>+4y8^+ejiP3Cw01rN9I1lZ zgnppTB0)c{PSkd_uu|9_v$*Jt6cQW5lRAhj5d*x4nB~}q4TX1k z`fpTcD<&US=!)=l4)5iiETn52HT}uKdjBhZHOlAcrwkwl$p>tpmIf$J+co&zz+j2# z>t3>PjdQyuYBJ$2V)4S8Y~!ZlDd6L`eDl0Ly+aS}(Tpcc| zT|Q{_ce*y#)x4@+9@0U!WbOLuwg)7XKhdi1+_h0|TJ>9O3=L6Z!gDG;(4nEjiVkUI zWYZq`xRoJt!M9i$h{?yOjBM*OAG0!=SpF6(gQ@6aR7N)bJ3nq^fQ$jvhrV-Yt&ZM4 zX&}fW(b-<3h8Kr;z}{B5+`kmAPjV+_F+Mgv;{MsYT^o9eq|pN$q!EM57Mhqri*&9n zFObvO{^A<=%_hN;S_|Dz8dghQPvq5@k~7nND`7>($hRoQ*pQnQIi!}EkoZ+yRqd?~ zY7Kj3NQ5U`s-WjbFJXSMX&1utq#tNaOfHR;9pUqsEcX&ClHZ`lZCV2nr7c6<*R3sI zRJffwte&k}W97qqrdrEuo9rFNo3+Meg?sbz7rs+?*22Q(TP!f7)e^n64B4_1H}~2B z^-TU^@)+?XdXxj?aXvBOUzCZ^;rvE&Spamn9;_g;RHcJJtpb}(UYDOB}6C55U6sr65Iq55~w z<%l*;*lJm0wJjpM${!E%XgX|ip-EJWUU*qnSp*8jBF9$CvwYY>KGtTELu?3Hz8yLK z(p%VF5GGohCLP93!`%Us?{AK{+$BqQFJObKoF~e8zyziQme-zwISy}CdgbtE*i^EN z?l%tB_zznehr{rv3N)8T()S5G@jFfqGxb5rc_O-yx` z9ACAFfnZU8O!L|@hhW7G{}G?qF3SBvGjQBM$bxPbvC^XB%)j5SM1c<9L8jqhU7dHS zSayB6fssI?AiT)i#R{cq$Jr?SH%c<491%mZim|=SZFZxC zQBCTHJ&!`V-s9XMm2p3Z&kEg+bAdErG1J*EjiJh95>l$Bp=Jt|!en_8Pi<8>xw9xgs#vqEu!HkUEBi9(ovkItKhxwOOAFB!`+1!(=iqVS-_KOcH<;nR_ z&zD6T;y+#McZy$h4+k(arGDt@CSV!$^HeWur5GR-16B&{0&G-HB^qRSa5woopjqv! zy0wGq%vOG)bILMQ%}vF|jxI_UhN?Heuk~r z&C`Z6!O%MhjYQyPWX_fAzedJJufF7rQKDfc=V_ft;$WY3FLRR~hCbeT^K9~sxxwM> z*uKK_)v^Lii9Q-#)bdM0Yufq@d<+er-10$NHYoYu>d|lk@RFcz-k(<{K<3|BBYVmw z;UTxIhX~t>MiQ@5JMP@T&XUxYgW4Jz$BMkJS$9+TjxPJ;LhtMfdi2ect&=>AtfX7z zq-D|iG`n6R3zXxbs*F#rgl1yqC_0`R?cu@-IwF7qVk=(7*&5NMQ3C=Fb+I#L>T2`I z6XsLrI!b0=%lRXqZCO?M1L`W;HT(U0v;>szy@P7(GZ4eZQ1{IHv)btUP`DMV>D~uA zI_8%?l7VZk+X;Lq90L)<=X4OI3dK0wq}6VGz3iMxw}k9wre(I((FiC9hQ)C6Ba!|z znSU!t-$8o~hTnpqCz@HM{uJXWI|OK{se!amF7|S0Wi{o;7!F5qDQ7*I<%Jo7ArLZ{ z7#!;3Omt>HZ2o=6>}_z8KRxYD8osU3X3n&E27^n2o7)F~;?24BBtGm)wI0V>#fFq! zHvvv#KL`Km92Za4dDUk;!-znuS(1!f_;Ewp<4fP=o%iV7TZN}sxfmlr?HaB(KNd))Y`2|;%e z<7w$GD$1-{Tv0DyI_7WLvpc+SN$3->%B_}#Qs_4{iYo}|n!aiVfZb^1< zllu2%IfT!0(NFN&FJmKC`LLKazD6~c zmDIp-zaYbim=oTf;C*W0U}CJK^4y*@zp8iDvP}cTpPbAe=r~I{~-ledsdTR*8f2yj$X!X{nLA_GQhUkjm?xMH%${YK8 zrLD2^xvg>L5+-ckV0CMSN|(W- zgmf3;eDPEn-$9MI16}NjJH8JqnAE zdL`o}DPbQI#9>Z-r!-EnkxIOt|b1t zot@N=W@Yc+=2vnkdcT~K-Co}2;?yrjSEK$gYnnER?HT?*`r1%^OkcmQ+bs^1Z9=%@ z?|y}eY0IcTlB^AJTP~WTnPyW%L%F}J)VDF4S|cvhIfc({CCxfO{{4NeMk9!}wRmw7 zzZfll^xNu%Z>lP9Dp$<}I5WTI0oc>PMOYCda-MEgrLw~zUENOl|5>QL#9eMej(z4} z109&g=Fk3vnh)S9yB~ch>4|6Bff{T7Hupd|`03xj8l4y~XhuLTflT1Kr@vKV>rE@| z)=H9_8`Y54G#RK;=ziV?`j^2Bjd9oSUbNKeD-BH9M!0l#6C2HDE}1uJ0D2kvsjT;B zV#vAF0sUFF6MV0p{(Af0PmwgAgkrvv3M~^1&ei!?fZdAp$f0za75)kw6i%V5$KssZNJd;+3Rr#u5Z5 zcHL^SFSi(aL_b%5`!1%3ki0Sk!}hd0DxVc5{=#*|kW6AcWP+R+d}M6X?6q(wZjA%b z+4sj&rwSmGw|+&#UZSpUw&VMDfeDsGE9{tHfPIu1983^}@cTcq{YOZ*Tl-}h9YB&| z_djq*^1FmX)gr}NR<7H$pe_R(vqEkM%(ia!aOzHH;bcfuu1v#EVWYiaLnx*qF^YVWN-mBO>VUmr zR_ZuPY#%;*@oiKs;+$$5S08;rATUi8vy9wgjFM^qy(1v-Oa26q1PDjD3Gz51U~su< zMj5nEGe;YvX8TX)u7IOJY?AqYGB5;C13f$>!OnXS)D)#*-bqez6%8z=VqIW`8U_a! zq{oZ6%II^fC02B*HMY2ybc^(fZ6>N7elr=eTZb)&?31V8^f1~woKz|H0M&*@E-#zb zR;K3gX6s`CzDden!rM{LB)4rg8@4H0CqRYq40-AkJwUz#KIA(dFybdzbJkf!Alp*L z0mhtKZLclfyCuV!_CNE?GXxgEW;Az4Fw%+m~wn1w) z$wMXS4{LPt47kN`|6SzLEveBvFkB=w(yz+F&G81}oq6yXXQv;)r7}BZ5xyjv)8rrr zRE-%nI+rP%yB{P6wzCV*ZySgQMpY5Ur*GDMkV*vM-Kz%R{**ZIy zAY6ip^pGMvx{y&+-l7{HhZsFry+6}`)*Ex9JNn{d6-r1`vCvJ;v%8oIzEn(0extGb z!v~WNzaN*cH#)Nf0dE_ety!dx(A%M{ks@Fflb&w}h!KAIa`=#Ci zR;IN&(>M1h51avS=RBiQc>bJPg(|tPcRNmD2c1K5@3w0;In_}u%jU9nhra3U>D_T6 z*SgET8Q=XI(v7d~BjxR+Jew)$C*qQV>E1pQU_QE(gK;U_(|;r1vKDu4qXmWV^R%xJ zeLmR3#$@}X8*JT4srzZ$zwS-ZwmSt~rsF2#>!ePF@bS1#eCunYx4k`S%V~w-3X1W* zG!=DtopOue>0qX}-yq~lEue<)UaIhIuS=^M?b;uO@HbT>T=Uf{(cG*f}7#)7Sb1?ck6R>x6r#rXbG^_>V?_%os zU@6zRvvb!<;Txr^%qr`jn>6m-#aDOt?pnR)=DqJ?MB2K$cX#;p-rd1Lj2hL>?W>1Y z4Pm(gHzflT*qH`a{4RGBce24YWGzQ0KxAq3Yh%%GM_XKzOt9_R0?%qB0u0|GG zo_zr=V8q-*E_*reJCXZF33^i^|DUxl?y*!u-2YlH&+%`w+O-t^_2AyEdl~OlJ09A) z*I<2b=SXlkaU~u8#!z@LxbNou!I9wpRQ}s?6z_pvRk*9%g9mTkFVhlcrzyVabPjSn z+j*cj#f178tr+V~2D80Mp8o%xoqLR2^>xSRwX=6;XJ=>E5Ae#QvxWrEVzv>q0Xc^H z9h`&(fkl%^=z6`gYZmOaxwAI)unsOJ5h>87kmQdxX)w?xw5V0nNQ69^N|manuS89x zR3lMEY9h61(pEr`>JYo1@A>`iy>oYGNfTRh_IF>u_v5_JEzPT8;0)k*fAcT%*0;kk+ItYir-!a)pe5yIIV3#K* zSCs$k01hdfDO?=h0#S>Y4Pu=bYazv7#08P}JftX7ya>f<<G#`%w&yq&5!GgizXua7#yAn5hdJlI%Yq=E>a@0^yT0PN-4HM)08~moQRw zEO$!t$6%%rK4~s|K_|MA#**IW+V!3+w*=8jzFfNnlazs-dL?ef*veK&Kwu{k${3E{ zGS%MHvo%J-=3R@#w&k}~u5 z#)$LukVq}28~y%PdsB^nUWHmzwt`)oHA}rcmR=)?cb%>E@DWwxNrB%4bPBSUxsctq zih?j~klH%ci_F~{O+19{T9a9RFUs?te#jc1vCm2r@nF1TV-|l_ZLP&%T&eSb$I)JL za^OChGfseHQP0;<&_^zlv4k>!!xjF2A)kF%jX`?S8KGuziiR-ybx_ zv?T3kybAn-BGu2-9rg8~q0VPY2{*jmXWLSzAZoRqZw$m4bPD>?F!K7wOUJ%> zOeyhRLK7?X;G1`fM@8SCaF*9%c!ci)bV^GoxgqJHP1cQ_5yw)Tcg~-Up?Fwtzf-{2 zL{my_5x7avm z349FD7>O5oBx7rgm^R8ZDkz@G-wMy^edE_3QeDJRYy^yn@irTS;y(LMYm}S`yE?To zVj~!Jr?PDfh>z!FSiem!2rCwjV5+-tgx;nDS`EqiT(-mZHF@9T4wp`WM=dwQZHF6I zU6#q^thO<=F0;W50boe6k-Z@sHwg!d#Zs$HCp?oKm&^r9Ieyv7rCeUBS!Fbg zFz)8Q(vGQxIN&ETA>fW@b47R1CH3!!bDVz1-3j_f$wxuGDc{~LI&*}Jrq+Rb0+nvh zQB_XL)LZ0NG&2Tct+6>%0{2RoG)N_c7F19S>G4KTu`uGjOB|)ums6*TwWaF(DsV;K z@?&r*cQMyjrB)X8nYt-|S58@V?Jv?d#at0_3U+UWqBEq>J=!?NJz9G)=qsRloy(41 zK(#!Zd)-E8ma$3w)1ukQ&*V6BnMSsmY2=#O20D<9LUTh?q-?g)XJjnqA#>OPi(q{O zZLLNyH_(((1&DE@WH!ohFq^~8>LZy(xjE7tO48nYS#fZ#ZRGQCg5gb(Vtlm*NV!@hg{_{+CDlA49e7XvhpbVt1+e)}YMmw&B|1&BI59W^m(_jY5fc1B zSquOl3c(k@TK^?-P!%_N!b)2E#Y9fG^36oMjZCkTAHXEj2;tG0C3*u%kOJ)yxg}N> z6oL}#pJUr>!6fj|rY%(5VH{t-nQt@a3`+s~#BWuwizwuvT|1&>Kb@+3OXtv%?U9(BtW(MmMbSpurI17knbb4D|`@k*qO?i;WOxn;^_jd z?ejSpVJ4T64+B!Tsq!f-6n(1Cc_-j$aT!LJz1P27@u>i^)ZH6cuKEw(PrT()|t+*dB>=vw`s<0zlI z1b6nw1@wpXzdgDu-svMGcNO_YH@NvEuI*ChT;^d;tH`+Q%c{i}1!^&{n!U(nnJ=8Y zJ=@;UU6L1SL+y&Xjs9uk>R{xUa@L2K1jv}G`Y75S5DWmEpNnY@f=uMerk1ol-&@qE_LL3fgpqUb76X#`+ZR zTM>`LwcwAKNUj;r@pxFxcpHffPGE#^SyON*Rk&Cz3;7hPY7M-AYpeflZ+^2Trbb2c zHQocIvmhPBB{a5V%=$#0y@#oBd<-bxKKb@uJFs+m+_`{1_sCy|8}M0_oJ{-gm=j?- z&0U=h3UuRkb$uG%MX{)5lPr;n<7UG4$AT>+REf_uYQSzCF7R z9=L6)GrHP4sk6l1NyWn}1!^&{IiMk$ROi{!0bgwoC0&E*aH=n~l=R+v_?iC!{3g+U z=YK$6O`gu3cMMOoGttz$8281V<0nqztrt4R9ff$s0K=PbXS7@}#BZ86bmxcB%WHX& z^Vyw!bEl3UJvHY7JQ?~Edq`3&HZ%Ixw{dQ!8-M6f+$3@0;)nRv2lqUNw^@o%s8|cp z!Rm}L&($u9yFTxUl;AWo&R@EivostN48KLS!WZZ|v<0r>Pp98EzmvyJ;L3q{ls9hF zz-UXz43=`0T)P^#W72g*YIHPHH9EH8T~1=j39{sHB4G1EPgeepG)pfe(%svS67FDe zfthK2{&X=GC*_r?#oZTYUN;7j`9+#JXcS2024HFMp-E5Xn2bJtsuH$d` zZ2>#FeyCJ3HEmX&*Q?R2H0f+TX}WPk&CMx(5tH30G8ujzk&&|04%3ydm0+3w6A#`U z_iO=D^kREs@9a|=C~AGY6Kv!{-Z#W@#Mc9RvN74|UCMuvp-@Y|z~694K$T%L*06^a z-KZ&LU|gwh0q#_tR7j?ft`C#PH#b!u)g`XaEnLWp8TeNY{EHePq__^7HGErfXk{6n zZO&+37|;DxmkxKJ%ULjt2?wOQZ@5ry%7%uWcu;jsqb}j+xW^;4#j!WEi=Tw|b7BTv zv+#L+Vw?9%ys@&O>XP@Xq0*YoK;t@^o~;?W8D!#(_PVa2h?Y%#U}|b=KfkG|dUmQl z?2Odv%6~%O8UY*dmz5H>5dM$Aa{}`Mb{1(-b@|b>@u}%v0>*XuEZ}(|LdqSk$5`PC z6DT>cl7s(Q`sE;xisM`0Aq)hg!9Y;7?^dmt3VH0|*OJ1HdCj=Wyo_K23o_w;j8`*% zpPebq!V@LuOQYU5g^8aQ$@3-GqrK2U!}WIZ(h9_od8lSM2AeIinl24kBR|gN5e>x{ ziQ-W1H9H^ODQVDLF}@z^z3>p@wyI^?ZLDR=!CE%z7_GJ+BtAhttQ`JOpch+J`M0Tu zu~pYDqsD7i1}6WqTNOTpHGp=WbV;l;BX?{Lu2tZ2Tp-z|{*7?8lV9r|z)EJy9DZ`~ zSb?dV+f3(h5eHiZ{Q!OgK>d_*ztR(xdAQAu{^npKXqKBpFi{4YmF6&iOU-I?gujE$ zezxS%@K%bOkv6lP9|siS!{$kV3Or+(En@9uLt!Rkg|1A^eFE zhNFaq){Sy^`#)*b3uwI|9ti4o|dRZNMRsADT<-#wk*1gv4d0zQlt=lam3M0Ay zUb${l*RQlMo8IFQcbdr3{zQSRtwFZzn=#4j^z`idmEMkI?neIpbA~PMpSjbFczyQR+ z8i@NNAg0woNPHk9J#ZM~g3PoXSK%`hSs$?l)EUIe#1Sm0xSi#JYhE%1ekK-4JXK(| z*xB7{15Wy?7dN2SAX}f-R5=6VO1%vDx<*yEGNTo!7-JCkglKZ7#x$n3bNbl-Q#u|N z)gDZR^9B-l)P^FnSTXFyC`vk$zs%uW`*B;YB185@i_dK`*b&1mlmmm z$iR`n9z45p?#IqkF@1i(`5q2b*aPitcuq(0AULXBf&`Szlxhvdtn!DWA|SvT*`gFaBU$MTp3g> zk6AuX{nG;4$*(1``$}v8!3n$|6Rnu-Zvz*x%>mAIbo^LKW$Uh)zoHE`SMF4(OdZpn zjSf@D#g6i0gEETKEsn$x(Qq1moe;qbhly%yykW;w7ZqzHs}H{uX;D+s%8*OmFgPkW zs>$j=Rwm7jq697~dOW;p4Cobf3%d)4R%qfLgL|R#`E(7b46(~GX~y^>QXx&b1D!}j z#NI5&SH~igCJ(n;Bw=eIa!iG_r%cH&NfE$E;h3O@6oAxlpCq~-Q{AB+$*VFH=~Xk| z^;fv3R#g!XYzkYF~s& zMWcNT12_3cCa@-+2`)eR3;5&dSXT*+!evlHqV{I|`XPr|6~%6yV@X4(%zWqD`wkOFU5&f7*ZG%dD=Fb=dYO~ug!lj3OV($ z`cLItAARg>nIo1$f@C=_WMXbQv6DlXez_8? zDU|>#Nva;hDv7;T`)mcLt^tIv?ynlpnDAAJM!MBj6TY0QLaOsI(JLf6{F26FO$=BR z<2zJ~wJ2BH-Cs{FQdL;u06qhACVPk3qx?pA75o8B*fo6Jd1O`LbrK24QLFeU;+pYB zu!Mz?5B|}ojUPS>Qmm)t_(5qz0;Kyb-M^6eD6+Cf5MCElODzB)C z&*x76b4uYgPd$dL9s^4ikmfw!q55fmQQIVhN#TP`zGj6w8_O$`<21%+=fh{ES@_K!3UZ|k zT%ux~XF9DcGrv3gFGLICon=>S6l6Lz=6%7mL3QRhf$1^$%A5cu!*(xqMWjop-7Z{+ zGdEH%Wjl^!_wwuD2xjl8qqg;sq#YKzJcj)9l)DKae0Hy%Z1Sv|I{60$urLl^S$Ze&g_ zM!_T#YiEFX;Ey8n@e*9B z`-(oi1i-~+K~VwS88WWhp8=eVYrMH&cJ-EkM}7(xAX8{mD(q8 zBd>>FBZYC39lOU!clvCCG}b3ONiu00fjAG^B*IvakiZzNxzO|=JAGPJz6CWE@s}z> zz0yI;?>@z;K~wX9PPN_K%xscMT?M7Ez?9jfFek~T|DW4fly!aKQYl}}7V~AVX^g7e z8z{GlXAp#cLjh3F-%(-WQOqja?temQcJebV*>2iqu5d1cx|k&4sEfHk1f8MjU5m}T zB09f)oozFF5dSL9JygJHg_ZJ504>)U>@B6zAZo$kp0h%YqJKh>Vl-(p7b3brkgCWl z;OEB35xy_txz!m=1ja2df(Rc_Kb|H7hZ_C6QneUZ2Qz_mI!#Z1THR~4kh;RgREt1r z-Ai)1(hEnjb@;a`?e7GVGAyNn!msn-6Dp&~RX|7*+#cv&s_XMS({tfWMz3N-25XGA z*Ce-jS~8^^jE5X%Yp5aMr7?HJ4B75SEddP|uo3SG*tR9N=?;4eH zU5ab;j0VK`l_jkqsQpU}%}j3|vks&Yk6G*OuD==pP$* zy)^TF`p?g$*rUT*b18lSv_XnaOylIa&tdg6 zc8ZKvWazd}B zih@wfAj3KR`0Hp3Qs|8mo-g${5tXN3?qqjGWqgu?rkt6Fm3cpThq}obt7XwCa)>cb zoqmnRT{EFMzX?C1`aLaRXXjphbwJ>}?!BheGfF)x@Eo8s;)Z-WQJLxt+!?9*v+Vyt zZ~mjeHw6Ab-xQrryKD8|>-M;Eeh=V9wO4Y|%y79_V1=WGo|nGQD1K}RcITC{s~2-R zCoh#z_>P{f&lfIoMffPosgCG-l6)j(&K|69G?6o+=luuw>z`6y2Ay-F=y{Zw2rMIs z82E`sH`MTkI%L8>S1qk~KA=m7gWw_zeQA0z+^@%f3g`sKPbjp{@i5d5ET} zcq-P8jzboWK`27jxnL_RH6&mtj(K0xKG!KZrCsQvmSfwF>LpD%7ddZ(Qr}eR-&e}S z<%7C3vy9(YYEBPM3cM;HnSc0zQkMyA0d(>#|L|H}+R6AnrG8D|K7peGrv#P-J|u8X z;9~;6Bk*~FFA01_;Hv_Erh*Ln+M~-Ix)knZ$e0C+bCD6&>w&f{i|^r_KG$`51VA(d z>8OSG>IP~JgfkP6!}4U-E`mC(uaq9j>?;MOjlmUqi-?Rv!3g>ZrJz6fghL{#OhAJ-MN(%~d+VfV4?-Lyi0j5hvl1XRL8l64xkIjYr1@Br zT*)hpFO`cK7ckIUFZl-o^e>ugFAbVod+H^9kF#1SMqTuf8So9sAs-(ff4}!gS612v zJl#M3WBB(q!}uFEu0LDQcn^yFD+p%z1~Zuzn~CLHCf4oPP8{EXy2ISqO&Wef*Bfy& z@q907`K_ewx08($eA}OGvJUG$v;8jb`YYVxUHxz6h3&6$ z9CcZ6H0r>2^|LNpW9!c>e~mTwjo#+DXdg_tPetWSsGL+zlt!v@Wt=IPQ!A;u zXPll0J`S+EvZWGLm(Elc)H-F=JUI){Emb?D;e-cTm8XaBCt+pr$CV47;-c#4PR11v zRkd-%mBgK)nYvQ7v!%_|#yH|J3zVR#9A_foBE&7@vk3*UwE@M3B0mOEn1xl?gCsDMw&1WrAU(j$_M0TY~TU-#Cw zR+UXvl%+du-PnQirU}{AeKh|4D9hqc$@NJ)jP8y6B;T2Y8;*LB^vvvCFWyAhJ!4{iA$WGMSxl*=D5HSfKe_?166P7X*31Y zUjne+>Jpfm4rxYaS6E$W2GAdprBR>^*)+~(Dbq$%DQ5{d?rRRZkNey1U+(1XKG_8) zin9pF2pj$HbF?S6wIP6WPzn18vp5EvT`GdrmrMks`nB(o$(pyxOwm!yNf^Tlpy18n zu*U!W+RMWp@-$+AR{#MS4ueZ}1;KE*(AMvhQ{W;TEoF?eogMP_z5ZMMd)kH&aI}1| z{w*jspvZe5l#v_dTG=g|rB|+$Zn<8r=4RO{tx zr}N5XSrDn8gD!aogkf6$@B8KXO$g9Y%KPuc8ANsYZeQzKMj=#((Z_?LkMYk?DQ#vd z z$9s>r@z`gk7?mc#!?>{ajq@(nnR8)%V(c4^p_=nQnFhYP-@33G#_!vcj38V~mhZ*BU%u-3%OyFS62NDWSF=8!n4qPIiKyHj> zwwPX%sAkOF6aokaze_HG-Y5kQ!&$K~qJX?k0c88H1S5s){pYdxda zboKfNJx{pccHbTB2D^KecgfMJ6$B#+L=J+gbB(H^38Sm_$Cz?;9u;_mJzIPqwX9Ak zgH(3OY~Mo_NUok!(}uvP8qgZNKfvY>K~xTU^CPIqAAvBuReRlRIBm0SItaM09JgUM zEYECPmf3~(eCuU!p@M-xjlC`LJ@|Zsgno&@;tf=sePm{k&T>7Z)SHwK&a8ycE|eFM*p4#9~^t!YqZD3s<13 zIfkT{iFqB|u0k3fRR~2t7=y5&EgwLU1Ox`(+{~@q&Yj%N8+kML@>br?JIs911r}N7 zPc(}jF}S!z5Dj!D#&FYz9CruuZOEtU>vuv-E&9eMA!0%1JAz!7syZ7Ao@A#;2Cemf zrVVi$41|!2VMf>W!PP=T&(#;D9DYlLjdAmpA?{xSV_?r5^wz6p5U?x+cC^N`6x^0U zB*&!WujyUY8v@2lO_0jaG^^Yop(k84V4!AFZ53t%eUN^0kw)wD#MQ1@d&L&-og`zk nn7@lRI=KhJal010<}Jtg%CcX%gR9{EW%t-ISKU{P{O5lFSRN%R literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/attr/__pycache__/_version_info.cpython-39.pyc b/venv/Lib/site-packages/attr/__pycache__/_version_info.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bc6877d822044d5c45bbe0620c026bba206a9285 GIT binary patch literal 2393 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/attr/__pycache__/exceptions.cpython-39.pyc b/venv/Lib/site-packages/attr/__pycache__/exceptions.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ef1f4ab254962cb522bd560e2cce9f6b395779b1 GIT binary patch literal 3222 zcmbtWTW=dh6rT02udx%SNeiVQjmiVHP~DqEAu3g)&_X4YR;2X77_G-U<7C^txHGc} zi6VqlQ27%`@J9a9zVd{S_yIg|&g|M=w@D$e){gg^ot>HQeCNzLtkr4|xSBuwG5D!U z$ZrUWmjmDq-0D|oIN?@6LaS$mrCuqtd$vVBCA`G#?+Les^W5&00Xw`5xGc(mD}XD! z3b<-;6>yE$0oO$h-q!&)_yXVsgByUGyal)=7T|po@FHIVyku|-aGNg!UN(3U@EN`W zcttG1_%>hNBAv6p!){5(PM6qF#X+Kl>xbi5>a@oFU0?ZelrD~?A8B`#M4krh;TOfr z0&oXz^%gXO^ej$#C2sX>Ug|l**|vIRZo^tmx^Pd%2O_#FWh@W=Y*4t?>B#h5@xw6D zY#0dYM@qBE6Ev2TX)XOBY>V!&T|tLJMA#?7u@;=_7<$Y!Va)xJFL<|M;C)R)A6HRS z@8D!n3jC0ULMgVL&CViDRWkGfrp(a6 z`ea*%${=60+oiigqRklRg0HR7>sPzix>wDX4puxCy{D-Xdd6zAQ%P&Ci#u}NwBfqI zBMI=i>AKGn7UXZLuFGQ&y6sSHrw)_b>WIiD*xNLJ-QHO1Ka}wk;c3->Fc#5gaVYw` zBHHbL?hpIQ*W&V+c~60m>Z47sh;KYGMmbe?yf4cz6FRV}KvT1vhGUt&leKQczK?G2&Mk+t7)Zx&%!z<-8yx%al~Lo|BbQz48l4 zW4#KbqDtC^Am3m?fHg%X{dc+a^FE%@#0r2>FCRZ28OBK%-DN?NNp8#oaRI_lKRMYH zA5Bf+O}HQ*e8!xM|LP(%nrQ2gJTLuRI^{tvr|sXDj5rz z)*pcf8e&*N_>r$&S7MEtmd#Ef8#_t_Bhx1_MI2da-F1!Q!%&kHS!dl)?=CMAg{ciQ|SiL@W){dks1 n3~Py=F3)3wD;G>MRVA(6%8J3;Xk&%5t=jFSb}j$a+wl7vQG5ak literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/attr/__pycache__/filters.cpython-39.pyc b/venv/Lib/site-packages/attr/__pycache__/filters.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..44a414a1144377ab6fa657e2e9604e4be8bd4917 GIT binary patch literal 1724 zcmbtUO^*{t5bd6^*W+35vXDh6Q9}9RYyBJK7rMH@ke;OMaUae z&MpU(>#+455S)w%r`(Y=bw*C=j@-00YEi;n-jeNfYqVw0ZP`h^kq753-Z>?sF86pB zetquqZTQ{hy?dm;Gyldv%F{H@<;M#iW?q(#>LpP-V0nM-XgK~!<&Q*c^!Ud@WIyDo7|%pD8{bJLW1SfBZ4t$f zqX$BdF`w^NjUN=pRrlb5$i%Zk9qvJ_cd*I)a-a*DnBeKd$P5l-9!F9i4%UwUC;^*k zOZXfPP{Mv)f}tnQhefC^IP{&$2Lv;1qAV851Cv5l+G1+4i|RQL=kb7vA)4zhgVMs> z{`N(+eJ1a1_()^JNPCa&{i*ih6rwx~)c}-uFm*gd#I!OLg?>Svi zz~7z!W&yna5c(+-ZP*pe;mW|b=3x=3C}n6Gvil&J$w6EB*4PitaUoVNA%+Mod(aA} zW(L-z4aZEcf-CeHvJjAv(L{={<`UM>EIi}>1h&2aVnH8~S9C^-y#;wiX22oa{v~uW z2rNZ|0NB)BGqh^YB3TNswb7{S28=H;iVbxJjFsot7|u>k z4F&`KZogM`f`I375LA8uk1jldA_x?+K;aXn@DTu?I8O_hOq{B!9i-9Y)r8~xSzHyv glf8Q*EE~LoH0PxhhxkBiBwwqw@APQT^?dLAUta{0$N&HU literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/attr/__pycache__/setters.cpython-39.pyc b/venv/Lib/site-packages/attr/__pycache__/setters.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..af5b909e674b3108511f75daae13b590316ca2a6 GIT binary patch literal 1596 zcma)6-EQ1O6rQoYUa#ZqmQW$YpXLV8uEpD% z#C#4hzEw)93?p9-Oxp$PgXvq~ekTixsXq_SX-fgHyIO<{#`jId(xPA!fM|r$6P8w- zDPbB_F>@*|r^~cx1d|K9SESlNT4Ea77}F_R!chR5axVD60sH*U^t0(5`!Yiumjg;f z(B1^oAAoC}bN3JO4f)~Lxz~CvX=&@O-E;S+pmhNyJ$gaLy)MWrZPF?eosTr;^KMXy zr-{YXIcnMY8}z_U{~`}Hfaw2xd3Z28QSwY=M$e9GQGF*%F0_a182ZzT zUg1f*p+-1+16=3f*&Uc^4;<-7fkH~QxI*5r_J?2^Rc^?d{0dbBUYykfOYVshkc@$v zu?;bx24*Ci3sy>=FLJ?eQlleifko|=f-TR0JME=i?78; z@LmtW5kCZ3_|yX*IqGfL`5!Sznk5El11bnoq4rw0b=FW*O95T|@;VBj^P$0@J=y;L zcG|cB>2L9}-M}`U61pkOO{4%f&EV>HuOTCQm5lzy*Iz(Zn+EA44}qiJg`K1E2yU)~ z%PdK{Fo9bQE*1gmLN*jiT%lk(VhhuS;^$1%cdbO#Lw3HLV?)QiqvPcG jG_JRS}(5L?bwV-+5 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/attr/__pycache__/validators.cpython-39.pyc b/venv/Lib/site-packages/attr/__pycache__/validators.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1b76ffbc4d4c9392ebe9371ceb2bdf6033f670af GIT binary patch literal 11368 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/attr/_cmp.py b/venv/Lib/site-packages/attr/_cmp.py new file mode 100644 index 0000000..b747b60 --- /dev/null +++ b/venv/Lib/site-packages/attr/_cmp.py @@ -0,0 +1,152 @@ +from __future__ import absolute_import, division, print_function + +import functools + +from ._compat import new_class +from ._make import _make_ne + + +_operation_names = {"eq": "==", "lt": "<", "le": "<=", "gt": ">", "ge": ">="} + + +def cmp_using( + eq=None, + lt=None, + le=None, + gt=None, + ge=None, + require_same_type=True, + class_name="Comparable", +): + """ + Create a class that can be passed into `attr.ib`'s ``eq``, ``order``, and + ``cmp`` arguments to customize field comparison. + + The resulting class will have a full set of ordering methods if + at least one of ``{lt, le, gt, ge}`` and ``eq`` are provided. + + :param Optional[callable] eq: `callable` used to evaluate equality + of two objects. + :param Optional[callable] lt: `callable` used to evaluate whether + one object is less than another object. + :param Optional[callable] le: `callable` used to evaluate whether + one object is less than or equal to another object. + :param Optional[callable] gt: `callable` used to evaluate whether + one object is greater than another object. + :param Optional[callable] ge: `callable` used to evaluate whether + one object is greater than or equal to another object. + + :param bool require_same_type: When `True`, equality and ordering methods + will return `NotImplemented` if objects are not of the same type. + + :param Optional[str] class_name: Name of class. Defaults to 'Comparable'. + + See `comparison` for more details. + + .. versionadded:: 21.1.0 + """ + + body = { + "__slots__": ["value"], + "__init__": _make_init(), + "_requirements": [], + "_is_comparable_to": _is_comparable_to, + } + + # Add operations. + num_order_functions = 0 + has_eq_function = False + + if eq is not None: + has_eq_function = True + body["__eq__"] = _make_operator("eq", eq) + body["__ne__"] = _make_ne() + + if lt is not None: + num_order_functions += 1 + body["__lt__"] = _make_operator("lt", lt) + + if le is not None: + num_order_functions += 1 + body["__le__"] = _make_operator("le", le) + + if gt is not None: + num_order_functions += 1 + body["__gt__"] = _make_operator("gt", gt) + + if ge is not None: + num_order_functions += 1 + body["__ge__"] = _make_operator("ge", ge) + + type_ = new_class(class_name, (object,), {}, lambda ns: ns.update(body)) + + # Add same type requirement. + if require_same_type: + type_._requirements.append(_check_same_type) + + # Add total ordering if at least one operation was defined. + if 0 < num_order_functions < 4: + if not has_eq_function: + # functools.total_ordering requires __eq__ to be defined, + # so raise early error here to keep a nice stack. + raise ValueError( + "eq must be define is order to complete ordering from " + "lt, le, gt, ge." + ) + type_ = functools.total_ordering(type_) + + return type_ + + +def _make_init(): + """ + Create __init__ method. + """ + + def __init__(self, value): + """ + Initialize object with *value*. + """ + self.value = value + + return __init__ + + +def _make_operator(name, func): + """ + Create operator method. + """ + + def method(self, other): + if not self._is_comparable_to(other): + return NotImplemented + + result = func(self.value, other.value) + if result is NotImplemented: + return NotImplemented + + return result + + method.__name__ = "__%s__" % (name,) + method.__doc__ = "Return a %s b. Computed by attrs." % ( + _operation_names[name], + ) + + return method + + +def _is_comparable_to(self, other): + """ + Check whether `other` is comparable to `self`. + """ + for func in self._requirements: + if not func(self, other): + return False + return True + + +def _check_same_type(self, other): + """ + Return True if *self* and *other* are of the same type, False otherwise. + """ + return other.value.__class__ is self.value.__class__ diff --git a/venv/Lib/site-packages/attr/_cmp.pyi b/venv/Lib/site-packages/attr/_cmp.pyi new file mode 100644 index 0000000..7093550 --- /dev/null +++ b/venv/Lib/site-packages/attr/_cmp.pyi @@ -0,0 +1,14 @@ +from typing import Type + +from . import _CompareWithType + + +def cmp_using( + eq: Optional[_CompareWithType], + lt: Optional[_CompareWithType], + le: Optional[_CompareWithType], + gt: Optional[_CompareWithType], + ge: Optional[_CompareWithType], + require_same_type: bool, + class_name: str, +) -> Type: ... diff --git a/venv/Lib/site-packages/attr/_compat.py b/venv/Lib/site-packages/attr/_compat.py new file mode 100644 index 0000000..6939f33 --- /dev/null +++ b/venv/Lib/site-packages/attr/_compat.py @@ -0,0 +1,242 @@ +from __future__ import absolute_import, division, print_function + +import platform +import sys +import types +import warnings + + +PY2 = sys.version_info[0] == 2 +PYPY = platform.python_implementation() == "PyPy" + + +if PYPY or sys.version_info[:2] >= (3, 6): + ordered_dict = dict +else: + from collections import OrderedDict + + ordered_dict = OrderedDict + + +if PY2: + from collections import Mapping, Sequence + + from UserDict import IterableUserDict + + # We 'bundle' isclass instead of using inspect as importing inspect is + # fairly expensive (order of 10-15 ms for a modern machine in 2016) + def isclass(klass): + return isinstance(klass, (type, types.ClassType)) + + def new_class(name, bases, kwds, exec_body): + """ + A minimal stub of types.new_class that we need for make_class. + """ + ns = {} + exec_body(ns) + + return type(name, bases, ns) + + # TYPE is used in exceptions, repr(int) is different on Python 2 and 3. + TYPE = "type" + + def iteritems(d): + return d.iteritems() + + # Python 2 is bereft of a read-only dict proxy, so we make one! + class ReadOnlyDict(IterableUserDict): + """ + Best-effort read-only dict wrapper. + """ + + def __setitem__(self, key, val): + # We gently pretend we're a Python 3 mappingproxy. + raise TypeError( + "'mappingproxy' object does not support item assignment" + ) + + def update(self, _): + # We gently pretend we're a Python 3 mappingproxy. + raise AttributeError( + "'mappingproxy' object has no attribute 'update'" + ) + + def __delitem__(self, _): + # We gently pretend we're a Python 3 mappingproxy. + raise TypeError( + "'mappingproxy' object does not support item deletion" + ) + + def clear(self): + # We gently pretend we're a Python 3 mappingproxy. + raise AttributeError( + "'mappingproxy' object has no attribute 'clear'" + ) + + def pop(self, key, default=None): + # We gently pretend we're a Python 3 mappingproxy. + raise AttributeError( + "'mappingproxy' object has no attribute 'pop'" + ) + + def popitem(self): + # We gently pretend we're a Python 3 mappingproxy. + raise AttributeError( + "'mappingproxy' object has no attribute 'popitem'" + ) + + def setdefault(self, key, default=None): + # We gently pretend we're a Python 3 mappingproxy. + raise AttributeError( + "'mappingproxy' object has no attribute 'setdefault'" + ) + + def __repr__(self): + # Override to be identical to the Python 3 version. + return "mappingproxy(" + repr(self.data) + ")" + + def metadata_proxy(d): + res = ReadOnlyDict() + res.data.update(d) # We blocked update, so we have to do it like this. + return res + + def just_warn(*args, **kw): # pragma: no cover + """ + We only warn on Python 3 because we are not aware of any concrete + consequences of not setting the cell on Python 2. + """ + + +else: # Python 3 and later. + from collections.abc import Mapping, Sequence # noqa + + def just_warn(*args, **kw): + """ + We only warn on Python 3 because we are not aware of any concrete + consequences of not setting the cell on Python 2. + """ + warnings.warn( + "Running interpreter doesn't sufficiently support code object " + "introspection. Some features like bare super() or accessing " + "__class__ will not work with slotted classes.", + RuntimeWarning, + stacklevel=2, + ) + + def isclass(klass): + return isinstance(klass, type) + + TYPE = "class" + + def iteritems(d): + return d.items() + + new_class = types.new_class + + def metadata_proxy(d): + return types.MappingProxyType(dict(d)) + + +def make_set_closure_cell(): + """Return a function of two arguments (cell, value) which sets + the value stored in the closure cell `cell` to `value`. + """ + # pypy makes this easy. (It also supports the logic below, but + # why not do the easy/fast thing?) + if PYPY: + + def set_closure_cell(cell, value): + cell.__setstate__((value,)) + + return set_closure_cell + + # Otherwise gotta do it the hard way. + + # Create a function that will set its first cellvar to `value`. + def set_first_cellvar_to(value): + x = value + return + + # This function will be eliminated as dead code, but + # not before its reference to `x` forces `x` to be + # represented as a closure cell rather than a local. + def force_x_to_be_a_cell(): # pragma: no cover + return x + + try: + # Extract the code object and make sure our assumptions about + # the closure behavior are correct. + if PY2: + co = set_first_cellvar_to.func_code + else: + co = set_first_cellvar_to.__code__ + if co.co_cellvars != ("x",) or co.co_freevars != (): + raise AssertionError # pragma: no cover + + # Convert this code object to a code object that sets the + # function's first _freevar_ (not cellvar) to the argument. + if sys.version_info >= (3, 8): + # CPython 3.8+ has an incompatible CodeType signature + # (added a posonlyargcount argument) but also added + # CodeType.replace() to do this without counting parameters. + set_first_freevar_code = co.replace( + co_cellvars=co.co_freevars, co_freevars=co.co_cellvars + ) + else: + args = [co.co_argcount] + if not PY2: + args.append(co.co_kwonlyargcount) + args.extend( + [ + co.co_nlocals, + co.co_stacksize, + co.co_flags, + co.co_code, + co.co_consts, + co.co_names, + co.co_varnames, + co.co_filename, + co.co_name, + co.co_firstlineno, + co.co_lnotab, + # These two arguments are reversed: + co.co_cellvars, + co.co_freevars, + ] + ) + set_first_freevar_code = types.CodeType(*args) + + def set_closure_cell(cell, value): + # Create a function using the set_first_freevar_code, + # whose first closure cell is `cell`. Calling it will + # change the value of that cell. + setter = types.FunctionType( + set_first_freevar_code, {}, "setter", (), (cell,) + ) + # And call it to set the cell. + setter(value) + + # Make sure it works on this interpreter: + def make_func_with_cell(): + x = None + + def func(): + return x # pragma: no cover + + return func + + if PY2: + cell = make_func_with_cell().func_closure[0] + else: + cell = make_func_with_cell().__closure__[0] + set_closure_cell(cell, 100) + if cell.cell_contents != 100: + raise AssertionError # pragma: no cover + + except Exception: + return just_warn + else: + return set_closure_cell + + +set_closure_cell = make_set_closure_cell() diff --git a/venv/Lib/site-packages/attr/_config.py b/venv/Lib/site-packages/attr/_config.py new file mode 100644 index 0000000..8ec9209 --- /dev/null +++ b/venv/Lib/site-packages/attr/_config.py @@ -0,0 +1,23 @@ +from __future__ import absolute_import, division, print_function + + +__all__ = ["set_run_validators", "get_run_validators"] + +_run_validators = True + + +def set_run_validators(run): + """ + Set whether or not validators are run. By default, they are run. + """ + if not isinstance(run, bool): + raise TypeError("'run' must be bool.") + global _run_validators + _run_validators = run + + +def get_run_validators(): + """ + Return whether or not validators are run. + """ + return _run_validators diff --git a/venv/Lib/site-packages/attr/_funcs.py b/venv/Lib/site-packages/attr/_funcs.py new file mode 100644 index 0000000..fda508c --- /dev/null +++ b/venv/Lib/site-packages/attr/_funcs.py @@ -0,0 +1,395 @@ +from __future__ import absolute_import, division, print_function + +import copy + +from ._compat import iteritems +from ._make import NOTHING, _obj_setattr, fields +from .exceptions import AttrsAttributeNotFoundError + + +def asdict( + inst, + recurse=True, + filter=None, + dict_factory=dict, + retain_collection_types=False, + value_serializer=None, +): + """ + Return the ``attrs`` attribute values of *inst* as a dict. + + Optionally recurse into other ``attrs``-decorated classes. + + :param inst: Instance of an ``attrs``-decorated class. + :param bool recurse: Recurse into classes that are also + ``attrs``-decorated. + :param callable filter: A callable whose return code determines whether an + attribute or element is included (``True``) or dropped (``False``). Is + called with the `attr.Attribute` as the first argument and the + value as the second argument. + :param callable dict_factory: A callable to produce dictionaries from. For + example, to produce ordered dictionaries instead of normal Python + dictionaries, pass in ``collections.OrderedDict``. + :param bool retain_collection_types: Do not convert to ``list`` when + encountering an attribute whose type is ``tuple`` or ``set``. Only + meaningful if ``recurse`` is ``True``. + :param Optional[callable] value_serializer: A hook that is called for every + attribute or dict key/value. It receives the current instance, field + and value and must return the (updated) value. The hook is run *after* + the optional *filter* has been applied. + + :rtype: return type of *dict_factory* + + :raise attr.exceptions.NotAnAttrsClassError: If *cls* is not an ``attrs`` + class. + + .. versionadded:: 16.0.0 *dict_factory* + .. versionadded:: 16.1.0 *retain_collection_types* + .. versionadded:: 20.3.0 *value_serializer* + """ + attrs = fields(inst.__class__) + rv = dict_factory() + for a in attrs: + v = getattr(inst, a.name) + if filter is not None and not filter(a, v): + continue + + if value_serializer is not None: + v = value_serializer(inst, a, v) + + if recurse is True: + if has(v.__class__): + rv[a.name] = asdict( + v, + True, + filter, + dict_factory, + retain_collection_types, + value_serializer, + ) + elif isinstance(v, (tuple, list, set, frozenset)): + cf = v.__class__ if retain_collection_types is True else list + rv[a.name] = cf( + [ + _asdict_anything( + i, + filter, + dict_factory, + retain_collection_types, + value_serializer, + ) + for i in v + ] + ) + elif isinstance(v, dict): + df = dict_factory + rv[a.name] = df( + ( + _asdict_anything( + kk, + filter, + df, + retain_collection_types, + value_serializer, + ), + _asdict_anything( + vv, + filter, + df, + retain_collection_types, + value_serializer, + ), + ) + for kk, vv in iteritems(v) + ) + else: + rv[a.name] = v + else: + rv[a.name] = v + return rv + + +def _asdict_anything( + val, + filter, + dict_factory, + retain_collection_types, + value_serializer, +): + """ + ``asdict`` only works on attrs instances, this works on anything. + """ + if getattr(val.__class__, "__attrs_attrs__", None) is not None: + # Attrs class. + rv = asdict( + val, + True, + filter, + dict_factory, + retain_collection_types, + value_serializer, + ) + elif isinstance(val, (tuple, list, set, frozenset)): + cf = val.__class__ if retain_collection_types is True else list + rv = cf( + [ + _asdict_anything( + i, + filter, + dict_factory, + retain_collection_types, + value_serializer, + ) + for i in val + ] + ) + elif isinstance(val, dict): + df = dict_factory + rv = df( + ( + _asdict_anything( + kk, filter, df, retain_collection_types, value_serializer + ), + _asdict_anything( + vv, filter, df, retain_collection_types, value_serializer + ), + ) + for kk, vv in iteritems(val) + ) + else: + rv = val + if value_serializer is not None: + rv = value_serializer(None, None, rv) + + return rv + + +def astuple( + inst, + recurse=True, + filter=None, + tuple_factory=tuple, + retain_collection_types=False, +): + """ + Return the ``attrs`` attribute values of *inst* as a tuple. + + Optionally recurse into other ``attrs``-decorated classes. + + :param inst: Instance of an ``attrs``-decorated class. + :param bool recurse: Recurse into classes that are also + ``attrs``-decorated. + :param callable filter: A callable whose return code determines whether an + attribute or element is included (``True``) or dropped (``False``). Is + called with the `attr.Attribute` as the first argument and the + value as the second argument. + :param callable tuple_factory: A callable to produce tuples from. For + example, to produce lists instead of tuples. + :param bool retain_collection_types: Do not convert to ``list`` + or ``dict`` when encountering an attribute which type is + ``tuple``, ``dict`` or ``set``. Only meaningful if ``recurse`` is + ``True``. + + :rtype: return type of *tuple_factory* + + :raise attr.exceptions.NotAnAttrsClassError: If *cls* is not an ``attrs`` + class. + + .. versionadded:: 16.2.0 + """ + attrs = fields(inst.__class__) + rv = [] + retain = retain_collection_types # Very long. :/ + for a in attrs: + v = getattr(inst, a.name) + if filter is not None and not filter(a, v): + continue + if recurse is True: + if has(v.__class__): + rv.append( + astuple( + v, + recurse=True, + filter=filter, + tuple_factory=tuple_factory, + retain_collection_types=retain, + ) + ) + elif isinstance(v, (tuple, list, set, frozenset)): + cf = v.__class__ if retain is True else list + rv.append( + cf( + [ + astuple( + j, + recurse=True, + filter=filter, + tuple_factory=tuple_factory, + retain_collection_types=retain, + ) + if has(j.__class__) + else j + for j in v + ] + ) + ) + elif isinstance(v, dict): + df = v.__class__ if retain is True else dict + rv.append( + df( + ( + astuple( + kk, + tuple_factory=tuple_factory, + retain_collection_types=retain, + ) + if has(kk.__class__) + else kk, + astuple( + vv, + tuple_factory=tuple_factory, + retain_collection_types=retain, + ) + if has(vv.__class__) + else vv, + ) + for kk, vv in iteritems(v) + ) + ) + else: + rv.append(v) + else: + rv.append(v) + + return rv if tuple_factory is list else tuple_factory(rv) + + +def has(cls): + """ + Check whether *cls* is a class with ``attrs`` attributes. + + :param type cls: Class to introspect. + :raise TypeError: If *cls* is not a class. + + :rtype: bool + """ + return getattr(cls, "__attrs_attrs__", None) is not None + + +def assoc(inst, **changes): + """ + Copy *inst* and apply *changes*. + + :param inst: Instance of a class with ``attrs`` attributes. + :param changes: Keyword changes in the new copy. + + :return: A copy of inst with *changes* incorporated. + + :raise attr.exceptions.AttrsAttributeNotFoundError: If *attr_name* couldn't + be found on *cls*. + :raise attr.exceptions.NotAnAttrsClassError: If *cls* is not an ``attrs`` + class. + + .. deprecated:: 17.1.0 + Use `evolve` instead. + """ + import warnings + + warnings.warn( + "assoc is deprecated and will be removed after 2018/01.", + DeprecationWarning, + stacklevel=2, + ) + new = copy.copy(inst) + attrs = fields(inst.__class__) + for k, v in iteritems(changes): + a = getattr(attrs, k, NOTHING) + if a is NOTHING: + raise AttrsAttributeNotFoundError( + "{k} is not an attrs attribute on {cl}.".format( + k=k, cl=new.__class__ + ) + ) + _obj_setattr(new, k, v) + return new + + +def evolve(inst, **changes): + """ + Create a new instance, based on *inst* with *changes* applied. + + :param inst: Instance of a class with ``attrs`` attributes. + :param changes: Keyword changes in the new copy. + + :return: A copy of inst with *changes* incorporated. + + :raise TypeError: If *attr_name* couldn't be found in the class + ``__init__``. + :raise attr.exceptions.NotAnAttrsClassError: If *cls* is not an ``attrs`` + class. + + .. versionadded:: 17.1.0 + """ + cls = inst.__class__ + attrs = fields(cls) + for a in attrs: + if not a.init: + continue + attr_name = a.name # To deal with private attributes. + init_name = attr_name if attr_name[0] != "_" else attr_name[1:] + if init_name not in changes: + changes[init_name] = getattr(inst, attr_name) + + return cls(**changes) + + +def resolve_types(cls, globalns=None, localns=None, attribs=None): + """ + Resolve any strings and forward annotations in type annotations. + + This is only required if you need concrete types in `Attribute`'s *type* + field. In other words, you don't need to resolve your types if you only + use them for static type checking. + + With no arguments, names will be looked up in the module in which the class + was created. If this is not what you want, e.g. if the name only exists + inside a method, you may pass *globalns* or *localns* to specify other + dictionaries in which to look up these names. See the docs of + `typing.get_type_hints` for more details. + + :param type cls: Class to resolve. + :param Optional[dict] globalns: Dictionary containing global variables. + :param Optional[dict] localns: Dictionary containing local variables. + :param Optional[list] attribs: List of attribs for the given class. + This is necessary when calling from inside a ``field_transformer`` + since *cls* is not an ``attrs`` class yet. + + :raise TypeError: If *cls* is not a class. + :raise attr.exceptions.NotAnAttrsClassError: If *cls* is not an ``attrs`` + class and you didn't pass any attribs. + :raise NameError: If types cannot be resolved because of missing variables. + + :returns: *cls* so you can use this function also as a class decorator. + Please note that you have to apply it **after** `attr.s`. That means + the decorator has to come in the line **before** `attr.s`. + + .. versionadded:: 20.1.0 + .. versionadded:: 21.1.0 *attribs* + + """ + try: + # Since calling get_type_hints is expensive we cache whether we've + # done it already. + cls.__attrs_types_resolved__ + except AttributeError: + import typing + + hints = typing.get_type_hints(cls, globalns=globalns, localns=localns) + for field in fields(cls) if attribs is None else attribs: + if field.name in hints: + # Since fields have been frozen we must work around it. + _obj_setattr(field, "type", hints[field.name]) + cls.__attrs_types_resolved__ = True + + # Return the class so you can use it as a decorator too. + return cls diff --git a/venv/Lib/site-packages/attr/_make.py b/venv/Lib/site-packages/attr/_make.py new file mode 100644 index 0000000..a1912b1 --- /dev/null +++ b/venv/Lib/site-packages/attr/_make.py @@ -0,0 +1,3052 @@ +from __future__ import absolute_import, division, print_function + +import copy +import inspect +import linecache +import sys +import threading +import uuid +import warnings + +from operator import itemgetter + +from . import _config, setters +from ._compat import ( + PY2, + PYPY, + isclass, + iteritems, + metadata_proxy, + new_class, + ordered_dict, + set_closure_cell, +) +from .exceptions import ( + DefaultAlreadySetError, + FrozenInstanceError, + NotAnAttrsClassError, + PythonTooOldError, + UnannotatedAttributeError, +) + + +if not PY2: + import typing + + +# This is used at least twice, so cache it here. +_obj_setattr = object.__setattr__ +_init_converter_pat = "__attr_converter_%s" +_init_factory_pat = "__attr_factory_{}" +_tuple_property_pat = ( + " {attr_name} = _attrs_property(_attrs_itemgetter({index}))" +) +_classvar_prefixes = ( + "typing.ClassVar", + "t.ClassVar", + "ClassVar", + "typing_extensions.ClassVar", +) +# we don't use a double-underscore prefix because that triggers +# name mangling when trying to create a slot for the field +# (when slots=True) +_hash_cache_field = "_attrs_cached_hash" + +_empty_metadata_singleton = metadata_proxy({}) + +# Unique object for unequivocal getattr() defaults. +_sentinel = object() + + +class _Nothing(object): + """ + Sentinel class to indicate the lack of a value when ``None`` is ambiguous. + + ``_Nothing`` is a singleton. There is only ever one of it. + + .. versionchanged:: 21.1.0 ``bool(NOTHING)`` is now False. + """ + + _singleton = None + + def __new__(cls): + if _Nothing._singleton is None: + _Nothing._singleton = super(_Nothing, cls).__new__(cls) + return _Nothing._singleton + + def __repr__(self): + return "NOTHING" + + def __bool__(self): + return False + + def __len__(self): + return 0 # __bool__ for Python 2 + + +NOTHING = _Nothing() +""" +Sentinel to indicate the lack of a value when ``None`` is ambiguous. +""" + + +class _CacheHashWrapper(int): + """ + An integer subclass that pickles / copies as None + + This is used for non-slots classes with ``cache_hash=True``, to avoid + serializing a potentially (even likely) invalid hash value. Since ``None`` + is the default value for uncalculated hashes, whenever this is copied, + the copy's value for the hash should automatically reset. + + See GH #613 for more details. + """ + + if PY2: + # For some reason `type(None)` isn't callable in Python 2, but we don't + # actually need a constructor for None objects, we just need any + # available function that returns None. + def __reduce__(self, _none_constructor=getattr, _args=(0, "", None)): + return _none_constructor, _args + + else: + + def __reduce__(self, _none_constructor=type(None), _args=()): + return _none_constructor, _args + + +def attrib( + default=NOTHING, + validator=None, + repr=True, + cmp=None, + hash=None, + init=True, + metadata=None, + type=None, + converter=None, + factory=None, + kw_only=False, + eq=None, + order=None, + on_setattr=None, +): + """ + Create a new attribute on a class. + + .. warning:: + + Does *not* do anything unless the class is also decorated with + `attr.s`! + + :param default: A value that is used if an ``attrs``-generated ``__init__`` + is used and no value is passed while instantiating or the attribute is + excluded using ``init=False``. + + If the value is an instance of `Factory`, its callable will be + used to construct a new value (useful for mutable data types like lists + or dicts). + + If a default is not set (or set manually to `attr.NOTHING`), a value + *must* be supplied when instantiating; otherwise a `TypeError` + will be raised. + + The default can also be set using decorator notation as shown below. + + :type default: Any value + + :param callable factory: Syntactic sugar for + ``default=attr.Factory(factory)``. + + :param validator: `callable` that is called by ``attrs``-generated + ``__init__`` methods after the instance has been initialized. They + receive the initialized instance, the `Attribute`, and the + passed value. + + The return value is *not* inspected so the validator has to throw an + exception itself. + + If a `list` is passed, its items are treated as validators and must + all pass. + + Validators can be globally disabled and re-enabled using + `get_run_validators`. + + The validator can also be set using decorator notation as shown below. + + :type validator: `callable` or a `list` of `callable`\\ s. + + :param repr: Include this attribute in the generated ``__repr__`` + method. If ``True``, include the attribute; if ``False``, omit it. By + default, the built-in ``repr()`` function is used. To override how the + attribute value is formatted, pass a ``callable`` that takes a single + value and returns a string. Note that the resulting string is used + as-is, i.e. it will be used directly *instead* of calling ``repr()`` + (the default). + :type repr: a `bool` or a `callable` to use a custom function. + + :param eq: If ``True`` (default), include this attribute in the + generated ``__eq__`` and ``__ne__`` methods that check two instances + for equality. To override how the attribute value is compared, + pass a ``callable`` that takes a single value and returns the value + to be compared. + :type eq: a `bool` or a `callable`. + + :param order: If ``True`` (default), include this attributes in the + generated ``__lt__``, ``__le__``, ``__gt__`` and ``__ge__`` methods. + To override how the attribute value is ordered, + pass a ``callable`` that takes a single value and returns the value + to be ordered. + :type order: a `bool` or a `callable`. + + :param cmp: Setting *cmp* is equivalent to setting *eq* and *order* to the + same value. Must not be mixed with *eq* or *order*. + :type cmp: a `bool` or a `callable`. + + :param Optional[bool] hash: Include this attribute in the generated + ``__hash__`` method. If ``None`` (default), mirror *eq*'s value. This + is the correct behavior according the Python spec. Setting this value + to anything else than ``None`` is *discouraged*. + :param bool init: Include this attribute in the generated ``__init__`` + method. It is possible to set this to ``False`` and set a default + value. In that case this attributed is unconditionally initialized + with the specified default value or factory. + :param callable converter: `callable` that is called by + ``attrs``-generated ``__init__`` methods to convert attribute's value + to the desired format. It is given the passed-in value, and the + returned value will be used as the new value of the attribute. The + value is converted before being passed to the validator, if any. + :param metadata: An arbitrary mapping, to be used by third-party + components. See `extending_metadata`. + :param type: The type of the attribute. In Python 3.6 or greater, the + preferred method to specify the type is using a variable annotation + (see `PEP 526 `_). + This argument is provided for backward compatibility. + Regardless of the approach used, the type will be stored on + ``Attribute.type``. + + Please note that ``attrs`` doesn't do anything with this metadata by + itself. You can use it as part of your own code or for + `static type checking `. + :param kw_only: Make this attribute keyword-only (Python 3+) + in the generated ``__init__`` (if ``init`` is ``False``, this + parameter is ignored). + :param on_setattr: Allows to overwrite the *on_setattr* setting from + `attr.s`. If left `None`, the *on_setattr* value from `attr.s` is used. + Set to `attr.setters.NO_OP` to run **no** `setattr` hooks for this + attribute -- regardless of the setting in `attr.s`. + :type on_setattr: `callable`, or a list of callables, or `None`, or + `attr.setters.NO_OP` + + .. versionadded:: 15.2.0 *convert* + .. versionadded:: 16.3.0 *metadata* + .. versionchanged:: 17.1.0 *validator* can be a ``list`` now. + .. versionchanged:: 17.1.0 + *hash* is ``None`` and therefore mirrors *eq* by default. + .. versionadded:: 17.3.0 *type* + .. deprecated:: 17.4.0 *convert* + .. versionadded:: 17.4.0 *converter* as a replacement for the deprecated + *convert* to achieve consistency with other noun-based arguments. + .. versionadded:: 18.1.0 + ``factory=f`` is syntactic sugar for ``default=attr.Factory(f)``. + .. versionadded:: 18.2.0 *kw_only* + .. versionchanged:: 19.2.0 *convert* keyword argument removed. + .. versionchanged:: 19.2.0 *repr* also accepts a custom callable. + .. deprecated:: 19.2.0 *cmp* Removal on or after 2021-06-01. + .. versionadded:: 19.2.0 *eq* and *order* + .. versionadded:: 20.1.0 *on_setattr* + .. versionchanged:: 20.3.0 *kw_only* backported to Python 2 + .. versionchanged:: 21.1.0 + *eq*, *order*, and *cmp* also accept a custom callable + .. versionchanged:: 21.1.0 *cmp* undeprecated + """ + eq, eq_key, order, order_key = _determine_attrib_eq_order( + cmp, eq, order, True + ) + + if hash is not None and hash is not True and hash is not False: + raise TypeError( + "Invalid value for hash. Must be True, False, or None." + ) + + if factory is not None: + if default is not NOTHING: + raise ValueError( + "The `default` and `factory` arguments are mutually " + "exclusive." + ) + if not callable(factory): + raise ValueError("The `factory` argument must be a callable.") + default = Factory(factory) + + if metadata is None: + metadata = {} + + # Apply syntactic sugar by auto-wrapping. + if isinstance(on_setattr, (list, tuple)): + on_setattr = setters.pipe(*on_setattr) + + if validator and isinstance(validator, (list, tuple)): + validator = and_(*validator) + + if converter and isinstance(converter, (list, tuple)): + converter = pipe(*converter) + + return _CountingAttr( + default=default, + validator=validator, + repr=repr, + cmp=None, + hash=hash, + init=init, + converter=converter, + metadata=metadata, + type=type, + kw_only=kw_only, + eq=eq, + eq_key=eq_key, + order=order, + order_key=order_key, + on_setattr=on_setattr, + ) + + +def _compile_and_eval(script, globs, locs=None, filename=""): + """ + "Exec" the script with the given global (globs) and local (locs) variables. + """ + bytecode = compile(script, filename, "exec") + eval(bytecode, globs, locs) + + +def _make_method(name, script, filename, globs=None): + """ + Create the method with the script given and return the method object. + """ + locs = {} + if globs is None: + globs = {} + + _compile_and_eval(script, globs, locs, filename) + + # In order of debuggers like PDB being able to step through the code, + # we add a fake linecache entry. + linecache.cache[filename] = ( + len(script), + None, + script.splitlines(True), + filename, + ) + + return locs[name] + + +def _make_attr_tuple_class(cls_name, attr_names): + """ + Create a tuple subclass to hold `Attribute`s for an `attrs` class. + + The subclass is a bare tuple with properties for names. + + class MyClassAttributes(tuple): + __slots__ = () + x = property(itemgetter(0)) + """ + attr_class_name = "{}Attributes".format(cls_name) + attr_class_template = [ + "class {}(tuple):".format(attr_class_name), + " __slots__ = ()", + ] + if attr_names: + for i, attr_name in enumerate(attr_names): + attr_class_template.append( + _tuple_property_pat.format(index=i, attr_name=attr_name) + ) + else: + attr_class_template.append(" pass") + globs = {"_attrs_itemgetter": itemgetter, "_attrs_property": property} + _compile_and_eval("\n".join(attr_class_template), globs) + return globs[attr_class_name] + + +# Tuple class for extracted attributes from a class definition. +# `base_attrs` is a subset of `attrs`. +_Attributes = _make_attr_tuple_class( + "_Attributes", + [ + # all attributes to build dunder methods for + "attrs", + # attributes that have been inherited + "base_attrs", + # map inherited attributes to their originating classes + "base_attrs_map", + ], +) + + +def _is_class_var(annot): + """ + Check whether *annot* is a typing.ClassVar. + + The string comparison hack is used to avoid evaluating all string + annotations which would put attrs-based classes at a performance + disadvantage compared to plain old classes. + """ + annot = str(annot) + + # Annotation can be quoted. + if annot.startswith(("'", '"')) and annot.endswith(("'", '"')): + annot = annot[1:-1] + + return annot.startswith(_classvar_prefixes) + + +def _has_own_attribute(cls, attrib_name): + """ + Check whether *cls* defines *attrib_name* (and doesn't just inherit it). + + Requires Python 3. + """ + attr = getattr(cls, attrib_name, _sentinel) + if attr is _sentinel: + return False + + for base_cls in cls.__mro__[1:]: + a = getattr(base_cls, attrib_name, None) + if attr is a: + return False + + return True + + +def _get_annotations(cls): + """ + Get annotations for *cls*. + """ + if _has_own_attribute(cls, "__annotations__"): + return cls.__annotations__ + + return {} + + +def _counter_getter(e): + """ + Key function for sorting to avoid re-creating a lambda for every class. + """ + return e[1].counter + + +def _collect_base_attrs(cls, taken_attr_names): + """ + Collect attr.ibs from base classes of *cls*, except *taken_attr_names*. + """ + base_attrs = [] + base_attr_map = {} # A dictionary of base attrs to their classes. + + # Traverse the MRO and collect attributes. + for base_cls in reversed(cls.__mro__[1:-1]): + for a in getattr(base_cls, "__attrs_attrs__", []): + if a.inherited or a.name in taken_attr_names: + continue + + a = a.evolve(inherited=True) + base_attrs.append(a) + base_attr_map[a.name] = base_cls + + # For each name, only keep the freshest definition i.e. the furthest at the + # back. base_attr_map is fine because it gets overwritten with every new + # instance. + filtered = [] + seen = set() + for a in reversed(base_attrs): + if a.name in seen: + continue + filtered.insert(0, a) + seen.add(a.name) + + return filtered, base_attr_map + + +def _collect_base_attrs_broken(cls, taken_attr_names): + """ + Collect attr.ibs from base classes of *cls*, except *taken_attr_names*. + + N.B. *taken_attr_names* will be mutated. + + Adhere to the old incorrect behavior. + + Notably it collects from the front and considers inherited attributes which + leads to the buggy behavior reported in #428. + """ + base_attrs = [] + base_attr_map = {} # A dictionary of base attrs to their classes. + + # Traverse the MRO and collect attributes. + for base_cls in cls.__mro__[1:-1]: + for a in getattr(base_cls, "__attrs_attrs__", []): + if a.name in taken_attr_names: + continue + + a = a.evolve(inherited=True) + taken_attr_names.add(a.name) + base_attrs.append(a) + base_attr_map[a.name] = base_cls + + return base_attrs, base_attr_map + + +def _transform_attrs( + cls, these, auto_attribs, kw_only, collect_by_mro, field_transformer +): + """ + Transform all `_CountingAttr`s on a class into `Attribute`s. + + If *these* is passed, use that and don't look for them on the class. + + *collect_by_mro* is True, collect them in the correct MRO order, otherwise + use the old -- incorrect -- order. See #428. + + Return an `_Attributes`. + """ + cd = cls.__dict__ + anns = _get_annotations(cls) + + if these is not None: + ca_list = [(name, ca) for name, ca in iteritems(these)] + + if not isinstance(these, ordered_dict): + ca_list.sort(key=_counter_getter) + elif auto_attribs is True: + ca_names = { + name + for name, attr in cd.items() + if isinstance(attr, _CountingAttr) + } + ca_list = [] + annot_names = set() + for attr_name, type in anns.items(): + if _is_class_var(type): + continue + annot_names.add(attr_name) + a = cd.get(attr_name, NOTHING) + + if not isinstance(a, _CountingAttr): + if a is NOTHING: + a = attrib() + else: + a = attrib(default=a) + ca_list.append((attr_name, a)) + + unannotated = ca_names - annot_names + if len(unannotated) > 0: + raise UnannotatedAttributeError( + "The following `attr.ib`s lack a type annotation: " + + ", ".join( + sorted(unannotated, key=lambda n: cd.get(n).counter) + ) + + "." + ) + else: + ca_list = sorted( + ( + (name, attr) + for name, attr in cd.items() + if isinstance(attr, _CountingAttr) + ), + key=lambda e: e[1].counter, + ) + + own_attrs = [ + Attribute.from_counting_attr( + name=attr_name, ca=ca, type=anns.get(attr_name) + ) + for attr_name, ca in ca_list + ] + + if collect_by_mro: + base_attrs, base_attr_map = _collect_base_attrs( + cls, {a.name for a in own_attrs} + ) + else: + base_attrs, base_attr_map = _collect_base_attrs_broken( + cls, {a.name for a in own_attrs} + ) + + attr_names = [a.name for a in base_attrs + own_attrs] + + AttrsClass = _make_attr_tuple_class(cls.__name__, attr_names) + + if kw_only: + own_attrs = [a.evolve(kw_only=True) for a in own_attrs] + base_attrs = [a.evolve(kw_only=True) for a in base_attrs] + + attrs = AttrsClass(base_attrs + own_attrs) + + # Mandatory vs non-mandatory attr order only matters when they are part of + # the __init__ signature and when they aren't kw_only (which are moved to + # the end and can be mandatory or non-mandatory in any order, as they will + # be specified as keyword args anyway). Check the order of those attrs: + had_default = False + for a in (a for a in attrs if a.init is not False and a.kw_only is False): + if had_default is True and a.default is NOTHING: + raise ValueError( + "No mandatory attributes allowed after an attribute with a " + "default value or factory. Attribute in question: %r" % (a,) + ) + + if had_default is False and a.default is not NOTHING: + had_default = True + + if field_transformer is not None: + attrs = field_transformer(cls, attrs) + return _Attributes((attrs, base_attrs, base_attr_map)) + + +if PYPY: + + def _frozen_setattrs(self, name, value): + """ + Attached to frozen classes as __setattr__. + """ + if isinstance(self, BaseException) and name in ( + "__cause__", + "__context__", + ): + BaseException.__setattr__(self, name, value) + return + + raise FrozenInstanceError() + + +else: + + def _frozen_setattrs(self, name, value): + """ + Attached to frozen classes as __setattr__. + """ + raise FrozenInstanceError() + + +def _frozen_delattrs(self, name): + """ + Attached to frozen classes as __delattr__. + """ + raise FrozenInstanceError() + + +class _ClassBuilder(object): + """ + Iteratively build *one* class. + """ + + __slots__ = ( + "_attr_names", + "_attrs", + "_base_attr_map", + "_base_names", + "_cache_hash", + "_cls", + "_cls_dict", + "_delete_attribs", + "_frozen", + "_has_pre_init", + "_has_post_init", + "_is_exc", + "_on_setattr", + "_slots", + "_weakref_slot", + "_has_own_setattr", + "_has_custom_setattr", + ) + + def __init__( + self, + cls, + these, + slots, + frozen, + weakref_slot, + getstate_setstate, + auto_attribs, + kw_only, + cache_hash, + is_exc, + collect_by_mro, + on_setattr, + has_custom_setattr, + field_transformer, + ): + attrs, base_attrs, base_map = _transform_attrs( + cls, + these, + auto_attribs, + kw_only, + collect_by_mro, + field_transformer, + ) + + self._cls = cls + self._cls_dict = dict(cls.__dict__) if slots else {} + self._attrs = attrs + self._base_names = set(a.name for a in base_attrs) + self._base_attr_map = base_map + self._attr_names = tuple(a.name for a in attrs) + self._slots = slots + self._frozen = frozen + self._weakref_slot = weakref_slot + self._cache_hash = cache_hash + self._has_pre_init = bool(getattr(cls, "__attrs_pre_init__", False)) + self._has_post_init = bool(getattr(cls, "__attrs_post_init__", False)) + self._delete_attribs = not bool(these) + self._is_exc = is_exc + self._on_setattr = on_setattr + + self._has_custom_setattr = has_custom_setattr + self._has_own_setattr = False + + self._cls_dict["__attrs_attrs__"] = self._attrs + + if frozen: + self._cls_dict["__setattr__"] = _frozen_setattrs + self._cls_dict["__delattr__"] = _frozen_delattrs + + self._has_own_setattr = True + + if getstate_setstate: + ( + self._cls_dict["__getstate__"], + self._cls_dict["__setstate__"], + ) = self._make_getstate_setstate() + + def __repr__(self): + return "<_ClassBuilder(cls={cls})>".format(cls=self._cls.__name__) + + def build_class(self): + """ + Finalize class based on the accumulated configuration. + + Builder cannot be used after calling this method. + """ + if self._slots is True: + return self._create_slots_class() + else: + return self._patch_original_class() + + def _patch_original_class(self): + """ + Apply accumulated methods and return the class. + """ + cls = self._cls + base_names = self._base_names + + # Clean class of attribute definitions (`attr.ib()`s). + if self._delete_attribs: + for name in self._attr_names: + if ( + name not in base_names + and getattr(cls, name, _sentinel) is not _sentinel + ): + try: + delattr(cls, name) + except AttributeError: + # This can happen if a base class defines a class + # variable and we want to set an attribute with the + # same name by using only a type annotation. + pass + + # Attach our dunder methods. + for name, value in self._cls_dict.items(): + setattr(cls, name, value) + + # If we've inherited an attrs __setattr__ and don't write our own, + # reset it to object's. + if not self._has_own_setattr and getattr( + cls, "__attrs_own_setattr__", False + ): + cls.__attrs_own_setattr__ = False + + if not self._has_custom_setattr: + cls.__setattr__ = object.__setattr__ + + return cls + + def _create_slots_class(self): + """ + Build and return a new class with a `__slots__` attribute. + """ + cd = { + k: v + for k, v in iteritems(self._cls_dict) + if k not in tuple(self._attr_names) + ("__dict__", "__weakref__") + } + + # If our class doesn't have its own implementation of __setattr__ + # (either from the user or by us), check the bases, if one of them has + # an attrs-made __setattr__, that needs to be reset. We don't walk the + # MRO because we only care about our immediate base classes. + # XXX: This can be confused by subclassing a slotted attrs class with + # XXX: a non-attrs class and subclass the resulting class with an attrs + # XXX: class. See `test_slotted_confused` for details. For now that's + # XXX: OK with us. + if not self._has_own_setattr: + cd["__attrs_own_setattr__"] = False + + if not self._has_custom_setattr: + for base_cls in self._cls.__bases__: + if base_cls.__dict__.get("__attrs_own_setattr__", False): + cd["__setattr__"] = object.__setattr__ + break + + # Traverse the MRO to collect existing slots + # and check for an existing __weakref__. + existing_slots = dict() + weakref_inherited = False + for base_cls in self._cls.__mro__[1:-1]: + if base_cls.__dict__.get("__weakref__", None) is not None: + weakref_inherited = True + existing_slots.update( + { + name: getattr(base_cls, name) + for name in getattr(base_cls, "__slots__", []) + } + ) + + base_names = set(self._base_names) + + names = self._attr_names + if ( + self._weakref_slot + and "__weakref__" not in getattr(self._cls, "__slots__", ()) + and "__weakref__" not in names + and not weakref_inherited + ): + names += ("__weakref__",) + + # We only add the names of attributes that aren't inherited. + # Setting __slots__ to inherited attributes wastes memory. + slot_names = [name for name in names if name not in base_names] + # There are slots for attributes from current class + # that are defined in parent classes. + # As their descriptors may be overriden by a child class, + # we collect them here and update the class dict + reused_slots = { + slot: slot_descriptor + for slot, slot_descriptor in iteritems(existing_slots) + if slot in slot_names + } + slot_names = [name for name in slot_names if name not in reused_slots] + cd.update(reused_slots) + if self._cache_hash: + slot_names.append(_hash_cache_field) + cd["__slots__"] = tuple(slot_names) + + qualname = getattr(self._cls, "__qualname__", None) + if qualname is not None: + cd["__qualname__"] = qualname + + # Create new class based on old class and our methods. + cls = type(self._cls)(self._cls.__name__, self._cls.__bases__, cd) + + # The following is a fix for + # https://github.com/python-attrs/attrs/issues/102. On Python 3, + # if a method mentions `__class__` or uses the no-arg super(), the + # compiler will bake a reference to the class in the method itself + # as `method.__closure__`. Since we replace the class with a + # clone, we rewrite these references so it keeps working. + for item in cls.__dict__.values(): + if isinstance(item, (classmethod, staticmethod)): + # Class- and staticmethods hide their functions inside. + # These might need to be rewritten as well. + closure_cells = getattr(item.__func__, "__closure__", None) + elif isinstance(item, property): + # Workaround for property `super()` shortcut (PY3-only). + # There is no universal way for other descriptors. + closure_cells = getattr(item.fget, "__closure__", None) + else: + closure_cells = getattr(item, "__closure__", None) + + if not closure_cells: # Catch None or the empty list. + continue + for cell in closure_cells: + try: + match = cell.cell_contents is self._cls + except ValueError: # ValueError: Cell is empty + pass + else: + if match: + set_closure_cell(cell, cls) + + return cls + + def add_repr(self, ns): + self._cls_dict["__repr__"] = self._add_method_dunders( + _make_repr(self._attrs, ns=ns) + ) + return self + + def add_str(self): + repr = self._cls_dict.get("__repr__") + if repr is None: + raise ValueError( + "__str__ can only be generated if a __repr__ exists." + ) + + def __str__(self): + return self.__repr__() + + self._cls_dict["__str__"] = self._add_method_dunders(__str__) + return self + + def _make_getstate_setstate(self): + """ + Create custom __setstate__ and __getstate__ methods. + """ + # __weakref__ is not writable. + state_attr_names = tuple( + an for an in self._attr_names if an != "__weakref__" + ) + + def slots_getstate(self): + """ + Automatically created by attrs. + """ + return tuple(getattr(self, name) for name in state_attr_names) + + hash_caching_enabled = self._cache_hash + + def slots_setstate(self, state): + """ + Automatically created by attrs. + """ + __bound_setattr = _obj_setattr.__get__(self, Attribute) + for name, value in zip(state_attr_names, state): + __bound_setattr(name, value) + + # The hash code cache is not included when the object is + # serialized, but it still needs to be initialized to None to + # indicate that the first call to __hash__ should be a cache + # miss. + if hash_caching_enabled: + __bound_setattr(_hash_cache_field, None) + + return slots_getstate, slots_setstate + + def make_unhashable(self): + self._cls_dict["__hash__"] = None + return self + + def add_hash(self): + self._cls_dict["__hash__"] = self._add_method_dunders( + _make_hash( + self._cls, + self._attrs, + frozen=self._frozen, + cache_hash=self._cache_hash, + ) + ) + + return self + + def add_init(self): + self._cls_dict["__init__"] = self._add_method_dunders( + _make_init( + self._cls, + self._attrs, + self._has_pre_init, + self._has_post_init, + self._frozen, + self._slots, + self._cache_hash, + self._base_attr_map, + self._is_exc, + self._on_setattr is not None + and self._on_setattr is not setters.NO_OP, + attrs_init=False, + ) + ) + + return self + + def add_attrs_init(self): + self._cls_dict["__attrs_init__"] = self._add_method_dunders( + _make_init( + self._cls, + self._attrs, + self._has_pre_init, + self._has_post_init, + self._frozen, + self._slots, + self._cache_hash, + self._base_attr_map, + self._is_exc, + self._on_setattr is not None + and self._on_setattr is not setters.NO_OP, + attrs_init=True, + ) + ) + + return self + + def add_eq(self): + cd = self._cls_dict + + cd["__eq__"] = self._add_method_dunders( + _make_eq(self._cls, self._attrs) + ) + cd["__ne__"] = self._add_method_dunders(_make_ne()) + + return self + + def add_order(self): + cd = self._cls_dict + + cd["__lt__"], cd["__le__"], cd["__gt__"], cd["__ge__"] = ( + self._add_method_dunders(meth) + for meth in _make_order(self._cls, self._attrs) + ) + + return self + + def add_setattr(self): + if self._frozen: + return self + + sa_attrs = {} + for a in self._attrs: + on_setattr = a.on_setattr or self._on_setattr + if on_setattr and on_setattr is not setters.NO_OP: + sa_attrs[a.name] = a, on_setattr + + if not sa_attrs: + return self + + if self._has_custom_setattr: + # We need to write a __setattr__ but there already is one! + raise ValueError( + "Can't combine custom __setattr__ with on_setattr hooks." + ) + + # docstring comes from _add_method_dunders + def __setattr__(self, name, val): + try: + a, hook = sa_attrs[name] + except KeyError: + nval = val + else: + nval = hook(self, a, val) + + _obj_setattr(self, name, nval) + + self._cls_dict["__attrs_own_setattr__"] = True + self._cls_dict["__setattr__"] = self._add_method_dunders(__setattr__) + self._has_own_setattr = True + + return self + + def _add_method_dunders(self, method): + """ + Add __module__ and __qualname__ to a *method* if possible. + """ + try: + method.__module__ = self._cls.__module__ + except AttributeError: + pass + + try: + method.__qualname__ = ".".join( + (self._cls.__qualname__, method.__name__) + ) + except AttributeError: + pass + + try: + method.__doc__ = "Method generated by attrs for class %s." % ( + self._cls.__qualname__, + ) + except AttributeError: + pass + + return method + + +_CMP_DEPRECATION = ( + "The usage of `cmp` is deprecated and will be removed on or after " + "2021-06-01. Please use `eq` and `order` instead." +) + + +def _determine_attrs_eq_order(cmp, eq, order, default_eq): + """ + Validate the combination of *cmp*, *eq*, and *order*. Derive the effective + values of eq and order. If *eq* is None, set it to *default_eq*. + """ + if cmp is not None and any((eq is not None, order is not None)): + raise ValueError("Don't mix `cmp` with `eq' and `order`.") + + # cmp takes precedence due to bw-compatibility. + if cmp is not None: + return cmp, cmp + + # If left None, equality is set to the specified default and ordering + # mirrors equality. + if eq is None: + eq = default_eq + + if order is None: + order = eq + + if eq is False and order is True: + raise ValueError("`order` can only be True if `eq` is True too.") + + return eq, order + + +def _determine_attrib_eq_order(cmp, eq, order, default_eq): + """ + Validate the combination of *cmp*, *eq*, and *order*. Derive the effective + values of eq and order. If *eq* is None, set it to *default_eq*. + """ + if cmp is not None and any((eq is not None, order is not None)): + raise ValueError("Don't mix `cmp` with `eq' and `order`.") + + def decide_callable_or_boolean(value): + """ + Decide whether a key function is used. + """ + if callable(value): + value, key = True, value + else: + key = None + return value, key + + # cmp takes precedence due to bw-compatibility. + if cmp is not None: + cmp, cmp_key = decide_callable_or_boolean(cmp) + return cmp, cmp_key, cmp, cmp_key + + # If left None, equality is set to the specified default and ordering + # mirrors equality. + if eq is None: + eq, eq_key = default_eq, None + else: + eq, eq_key = decide_callable_or_boolean(eq) + + if order is None: + order, order_key = eq, eq_key + else: + order, order_key = decide_callable_or_boolean(order) + + if eq is False and order is True: + raise ValueError("`order` can only be True if `eq` is True too.") + + return eq, eq_key, order, order_key + + +def _determine_whether_to_implement( + cls, flag, auto_detect, dunders, default=True +): + """ + Check whether we should implement a set of methods for *cls*. + + *flag* is the argument passed into @attr.s like 'init', *auto_detect* the + same as passed into @attr.s and *dunders* is a tuple of attribute names + whose presence signal that the user has implemented it themselves. + + Return *default* if no reason for either for or against is found. + + auto_detect must be False on Python 2. + """ + if flag is True or flag is False: + return flag + + if flag is None and auto_detect is False: + return default + + # Logically, flag is None and auto_detect is True here. + for dunder in dunders: + if _has_own_attribute(cls, dunder): + return False + + return default + + +def attrs( + maybe_cls=None, + these=None, + repr_ns=None, + repr=None, + cmp=None, + hash=None, + init=None, + slots=False, + frozen=False, + weakref_slot=True, + str=False, + auto_attribs=False, + kw_only=False, + cache_hash=False, + auto_exc=False, + eq=None, + order=None, + auto_detect=False, + collect_by_mro=False, + getstate_setstate=None, + on_setattr=None, + field_transformer=None, +): + r""" + A class decorator that adds `dunder + `_\ -methods according to the + specified attributes using `attr.ib` or the *these* argument. + + :param these: A dictionary of name to `attr.ib` mappings. This is + useful to avoid the definition of your attributes within the class body + because you can't (e.g. if you want to add ``__repr__`` methods to + Django models) or don't want to. + + If *these* is not ``None``, ``attrs`` will *not* search the class body + for attributes and will *not* remove any attributes from it. + + If *these* is an ordered dict (`dict` on Python 3.6+, + `collections.OrderedDict` otherwise), the order is deduced from + the order of the attributes inside *these*. Otherwise the order + of the definition of the attributes is used. + + :type these: `dict` of `str` to `attr.ib` + + :param str repr_ns: When using nested classes, there's no way in Python 2 + to automatically detect that. Therefore it's possible to set the + namespace explicitly for a more meaningful ``repr`` output. + :param bool auto_detect: Instead of setting the *init*, *repr*, *eq*, + *order*, and *hash* arguments explicitly, assume they are set to + ``True`` **unless any** of the involved methods for one of the + arguments is implemented in the *current* class (i.e. it is *not* + inherited from some base class). + + So for example by implementing ``__eq__`` on a class yourself, + ``attrs`` will deduce ``eq=False`` and will create *neither* + ``__eq__`` *nor* ``__ne__`` (but Python classes come with a sensible + ``__ne__`` by default, so it *should* be enough to only implement + ``__eq__`` in most cases). + + .. warning:: + + If you prevent ``attrs`` from creating the ordering methods for you + (``order=False``, e.g. by implementing ``__le__``), it becomes + *your* responsibility to make sure its ordering is sound. The best + way is to use the `functools.total_ordering` decorator. + + + Passing ``True`` or ``False`` to *init*, *repr*, *eq*, *order*, + *cmp*, or *hash* overrides whatever *auto_detect* would determine. + + *auto_detect* requires Python 3. Setting it ``True`` on Python 2 raises + a `PythonTooOldError`. + + :param bool repr: Create a ``__repr__`` method with a human readable + representation of ``attrs`` attributes.. + :param bool str: Create a ``__str__`` method that is identical to + ``__repr__``. This is usually not necessary except for + `Exception`\ s. + :param Optional[bool] eq: If ``True`` or ``None`` (default), add ``__eq__`` + and ``__ne__`` methods that check two instances for equality. + + They compare the instances as if they were tuples of their ``attrs`` + attributes if and only if the types of both classes are *identical*! + :param Optional[bool] order: If ``True``, add ``__lt__``, ``__le__``, + ``__gt__``, and ``__ge__`` methods that behave like *eq* above and + allow instances to be ordered. If ``None`` (default) mirror value of + *eq*. + :param Optional[bool] cmp: Setting *cmp* is equivalent to setting *eq* + and *order* to the same value. Must not be mixed with *eq* or *order*. + :param Optional[bool] hash: If ``None`` (default), the ``__hash__`` method + is generated according how *eq* and *frozen* are set. + + 1. If *both* are True, ``attrs`` will generate a ``__hash__`` for you. + 2. If *eq* is True and *frozen* is False, ``__hash__`` will be set to + None, marking it unhashable (which it is). + 3. If *eq* is False, ``__hash__`` will be left untouched meaning the + ``__hash__`` method of the base class will be used (if base class is + ``object``, this means it will fall back to id-based hashing.). + + Although not recommended, you can decide for yourself and force + ``attrs`` to create one (e.g. if the class is immutable even though you + didn't freeze it programmatically) by passing ``True`` or not. Both of + these cases are rather special and should be used carefully. + + See our documentation on `hashing`, Python's documentation on + `object.__hash__`, and the `GitHub issue that led to the default \ + behavior `_ for more + details. + :param bool init: Create a ``__init__`` method that initializes the + ``attrs`` attributes. Leading underscores are stripped for the argument + name. If a ``__attrs_pre_init__`` method exists on the class, it will + be called before the class is initialized. If a ``__attrs_post_init__`` + method exists on the class, it will be called after the class is fully + initialized. + + If ``init`` is ``False``, an ``__attrs_init__`` method will be + injected instead. This allows you to define a custom ``__init__`` + method that can do pre-init work such as ``super().__init__()``, + and then call ``__attrs_init__()`` and ``__attrs_post_init__()``. + :param bool slots: Create a `slotted class ` that's more + memory-efficient. Slotted classes are generally superior to the default + dict classes, but have some gotchas you should know about, so we + encourage you to read the `glossary entry `. + :param bool frozen: Make instances immutable after initialization. If + someone attempts to modify a frozen instance, + `attr.exceptions.FrozenInstanceError` is raised. + + .. note:: + + 1. This is achieved by installing a custom ``__setattr__`` method + on your class, so you can't implement your own. + + 2. True immutability is impossible in Python. + + 3. This *does* have a minor a runtime performance `impact + ` when initializing new instances. In other words: + ``__init__`` is slightly slower with ``frozen=True``. + + 4. If a class is frozen, you cannot modify ``self`` in + ``__attrs_post_init__`` or a self-written ``__init__``. You can + circumvent that limitation by using + ``object.__setattr__(self, "attribute_name", value)``. + + 5. Subclasses of a frozen class are frozen too. + + :param bool weakref_slot: Make instances weak-referenceable. This has no + effect unless ``slots`` is also enabled. + :param bool auto_attribs: If ``True``, collect `PEP 526`_-annotated + attributes (Python 3.6 and later only) from the class body. + + In this case, you **must** annotate every field. If ``attrs`` + encounters a field that is set to an `attr.ib` but lacks a type + annotation, an `attr.exceptions.UnannotatedAttributeError` is + raised. Use ``field_name: typing.Any = attr.ib(...)`` if you don't + want to set a type. + + If you assign a value to those attributes (e.g. ``x: int = 42``), that + value becomes the default value like if it were passed using + ``attr.ib(default=42)``. Passing an instance of `Factory` also + works as expected in most cases (see warning below). + + Attributes annotated as `typing.ClassVar`, and attributes that are + neither annotated nor set to an `attr.ib` are **ignored**. + + .. warning:: + For features that use the attribute name to create decorators (e.g. + `validators `), you still *must* assign `attr.ib` to + them. Otherwise Python will either not find the name or try to use + the default value to call e.g. ``validator`` on it. + + These errors can be quite confusing and probably the most common bug + report on our bug tracker. + + .. _`PEP 526`: https://www.python.org/dev/peps/pep-0526/ + :param bool kw_only: Make all attributes keyword-only (Python 3+) + in the generated ``__init__`` (if ``init`` is ``False``, this + parameter is ignored). + :param bool cache_hash: Ensure that the object's hash code is computed + only once and stored on the object. If this is set to ``True``, + hashing must be either explicitly or implicitly enabled for this + class. If the hash code is cached, avoid any reassignments of + fields involved in hash code computation or mutations of the objects + those fields point to after object creation. If such changes occur, + the behavior of the object's hash code is undefined. + :param bool auto_exc: If the class subclasses `BaseException` + (which implicitly includes any subclass of any exception), the + following happens to behave like a well-behaved Python exceptions + class: + + - the values for *eq*, *order*, and *hash* are ignored and the + instances compare and hash by the instance's ids (N.B. ``attrs`` will + *not* remove existing implementations of ``__hash__`` or the equality + methods. It just won't add own ones.), + - all attributes that are either passed into ``__init__`` or have a + default value are additionally available as a tuple in the ``args`` + attribute, + - the value of *str* is ignored leaving ``__str__`` to base classes. + :param bool collect_by_mro: Setting this to `True` fixes the way ``attrs`` + collects attributes from base classes. The default behavior is + incorrect in certain cases of multiple inheritance. It should be on by + default but is kept off for backward-compatability. + + See issue `#428 `_ for + more details. + + :param Optional[bool] getstate_setstate: + .. note:: + This is usually only interesting for slotted classes and you should + probably just set *auto_detect* to `True`. + + If `True`, ``__getstate__`` and + ``__setstate__`` are generated and attached to the class. This is + necessary for slotted classes to be pickleable. If left `None`, it's + `True` by default for slotted classes and ``False`` for dict classes. + + If *auto_detect* is `True`, and *getstate_setstate* is left `None`, + and **either** ``__getstate__`` or ``__setstate__`` is detected directly + on the class (i.e. not inherited), it is set to `False` (this is usually + what you want). + + :param on_setattr: A callable that is run whenever the user attempts to set + an attribute (either by assignment like ``i.x = 42`` or by using + `setattr` like ``setattr(i, "x", 42)``). It receives the same arguments + as validators: the instance, the attribute that is being modified, and + the new value. + + If no exception is raised, the attribute is set to the return value of + the callable. + + If a list of callables is passed, they're automatically wrapped in an + `attr.setters.pipe`. + + :param Optional[callable] field_transformer: + A function that is called with the original class object and all + fields right before ``attrs`` finalizes the class. You can use + this, e.g., to automatically add converters or validators to + fields based on their types. See `transform-fields` for more details. + + .. versionadded:: 16.0.0 *slots* + .. versionadded:: 16.1.0 *frozen* + .. versionadded:: 16.3.0 *str* + .. versionadded:: 16.3.0 Support for ``__attrs_post_init__``. + .. versionchanged:: 17.1.0 + *hash* supports ``None`` as value which is also the default now. + .. versionadded:: 17.3.0 *auto_attribs* + .. versionchanged:: 18.1.0 + If *these* is passed, no attributes are deleted from the class body. + .. versionchanged:: 18.1.0 If *these* is ordered, the order is retained. + .. versionadded:: 18.2.0 *weakref_slot* + .. deprecated:: 18.2.0 + ``__lt__``, ``__le__``, ``__gt__``, and ``__ge__`` now raise a + `DeprecationWarning` if the classes compared are subclasses of + each other. ``__eq`` and ``__ne__`` never tried to compared subclasses + to each other. + .. versionchanged:: 19.2.0 + ``__lt__``, ``__le__``, ``__gt__``, and ``__ge__`` now do not consider + subclasses comparable anymore. + .. versionadded:: 18.2.0 *kw_only* + .. versionadded:: 18.2.0 *cache_hash* + .. versionadded:: 19.1.0 *auto_exc* + .. deprecated:: 19.2.0 *cmp* Removal on or after 2021-06-01. + .. versionadded:: 19.2.0 *eq* and *order* + .. versionadded:: 20.1.0 *auto_detect* + .. versionadded:: 20.1.0 *collect_by_mro* + .. versionadded:: 20.1.0 *getstate_setstate* + .. versionadded:: 20.1.0 *on_setattr* + .. versionadded:: 20.3.0 *field_transformer* + .. versionchanged:: 21.1.0 + ``init=False`` injects ``__attrs_init__`` + .. versionchanged:: 21.1.0 Support for ``__attrs_pre_init__`` + .. versionchanged:: 21.1.0 *cmp* undeprecated + """ + if auto_detect and PY2: + raise PythonTooOldError( + "auto_detect only works on Python 3 and later." + ) + + eq_, order_ = _determine_attrs_eq_order(cmp, eq, order, None) + hash_ = hash # work around the lack of nonlocal + + if isinstance(on_setattr, (list, tuple)): + on_setattr = setters.pipe(*on_setattr) + + def wrap(cls): + + if getattr(cls, "__class__", None) is None: + raise TypeError("attrs only works with new-style classes.") + + is_frozen = frozen or _has_frozen_base_class(cls) + is_exc = auto_exc is True and issubclass(cls, BaseException) + has_own_setattr = auto_detect and _has_own_attribute( + cls, "__setattr__" + ) + + if has_own_setattr and is_frozen: + raise ValueError("Can't freeze a class with a custom __setattr__.") + + builder = _ClassBuilder( + cls, + these, + slots, + is_frozen, + weakref_slot, + _determine_whether_to_implement( + cls, + getstate_setstate, + auto_detect, + ("__getstate__", "__setstate__"), + default=slots, + ), + auto_attribs, + kw_only, + cache_hash, + is_exc, + collect_by_mro, + on_setattr, + has_own_setattr, + field_transformer, + ) + if _determine_whether_to_implement( + cls, repr, auto_detect, ("__repr__",) + ): + builder.add_repr(repr_ns) + if str is True: + builder.add_str() + + eq = _determine_whether_to_implement( + cls, eq_, auto_detect, ("__eq__", "__ne__") + ) + if not is_exc and eq is True: + builder.add_eq() + if not is_exc and _determine_whether_to_implement( + cls, order_, auto_detect, ("__lt__", "__le__", "__gt__", "__ge__") + ): + builder.add_order() + + builder.add_setattr() + + if ( + hash_ is None + and auto_detect is True + and _has_own_attribute(cls, "__hash__") + ): + hash = False + else: + hash = hash_ + if hash is not True and hash is not False and hash is not None: + # Can't use `hash in` because 1 == True for example. + raise TypeError( + "Invalid value for hash. Must be True, False, or None." + ) + elif hash is False or (hash is None and eq is False) or is_exc: + # Don't do anything. Should fall back to __object__'s __hash__ + # which is by id. + if cache_hash: + raise TypeError( + "Invalid value for cache_hash. To use hash caching," + " hashing must be either explicitly or implicitly " + "enabled." + ) + elif hash is True or ( + hash is None and eq is True and is_frozen is True + ): + # Build a __hash__ if told so, or if it's safe. + builder.add_hash() + else: + # Raise TypeError on attempts to hash. + if cache_hash: + raise TypeError( + "Invalid value for cache_hash. To use hash caching," + " hashing must be either explicitly or implicitly " + "enabled." + ) + builder.make_unhashable() + + if _determine_whether_to_implement( + cls, init, auto_detect, ("__init__",) + ): + builder.add_init() + else: + builder.add_attrs_init() + if cache_hash: + raise TypeError( + "Invalid value for cache_hash. To use hash caching," + " init must be True." + ) + + return builder.build_class() + + # maybe_cls's type depends on the usage of the decorator. It's a class + # if it's used as `@attrs` but ``None`` if used as `@attrs()`. + if maybe_cls is None: + return wrap + else: + return wrap(maybe_cls) + + +_attrs = attrs +""" +Internal alias so we can use it in functions that take an argument called +*attrs*. +""" + + +if PY2: + + def _has_frozen_base_class(cls): + """ + Check whether *cls* has a frozen ancestor by looking at its + __setattr__. + """ + return ( + getattr(cls.__setattr__, "__module__", None) + == _frozen_setattrs.__module__ + and cls.__setattr__.__name__ == _frozen_setattrs.__name__ + ) + + +else: + + def _has_frozen_base_class(cls): + """ + Check whether *cls* has a frozen ancestor by looking at its + __setattr__. + """ + return cls.__setattr__ == _frozen_setattrs + + +def _generate_unique_filename(cls, func_name): + """ + Create a "filename" suitable for a function being generated. + """ + unique_id = uuid.uuid4() + extra = "" + count = 1 + + while True: + unique_filename = "".format( + func_name, + cls.__module__, + getattr(cls, "__qualname__", cls.__name__), + extra, + ) + # To handle concurrency we essentially "reserve" our spot in + # the linecache with a dummy line. The caller can then + # set this value correctly. + cache_line = (1, None, (str(unique_id),), unique_filename) + if ( + linecache.cache.setdefault(unique_filename, cache_line) + == cache_line + ): + return unique_filename + + # Looks like this spot is taken. Try again. + count += 1 + extra = "-{0}".format(count) + + +def _make_hash(cls, attrs, frozen, cache_hash): + attrs = tuple( + a for a in attrs if a.hash is True or (a.hash is None and a.eq is True) + ) + + tab = " " + + unique_filename = _generate_unique_filename(cls, "hash") + type_hash = hash(unique_filename) + + hash_def = "def __hash__(self" + hash_func = "hash((" + closing_braces = "))" + if not cache_hash: + hash_def += "):" + else: + if not PY2: + hash_def += ", *" + + hash_def += ( + ", _cache_wrapper=" + + "__import__('attr._make')._make._CacheHashWrapper):" + ) + hash_func = "_cache_wrapper(" + hash_func + closing_braces += ")" + + method_lines = [hash_def] + + def append_hash_computation_lines(prefix, indent): + """ + Generate the code for actually computing the hash code. + Below this will either be returned directly or used to compute + a value which is then cached, depending on the value of cache_hash + """ + + method_lines.extend( + [ + indent + prefix + hash_func, + indent + " %d," % (type_hash,), + ] + ) + + for a in attrs: + method_lines.append(indent + " self.%s," % a.name) + + method_lines.append(indent + " " + closing_braces) + + if cache_hash: + method_lines.append(tab + "if self.%s is None:" % _hash_cache_field) + if frozen: + append_hash_computation_lines( + "object.__setattr__(self, '%s', " % _hash_cache_field, tab * 2 + ) + method_lines.append(tab * 2 + ")") # close __setattr__ + else: + append_hash_computation_lines( + "self.%s = " % _hash_cache_field, tab * 2 + ) + method_lines.append(tab + "return self.%s" % _hash_cache_field) + else: + append_hash_computation_lines("return ", tab) + + script = "\n".join(method_lines) + return _make_method("__hash__", script, unique_filename) + + +def _add_hash(cls, attrs): + """ + Add a hash method to *cls*. + """ + cls.__hash__ = _make_hash(cls, attrs, frozen=False, cache_hash=False) + return cls + + +def _make_ne(): + """ + Create __ne__ method. + """ + + def __ne__(self, other): + """ + Check equality and either forward a NotImplemented or + return the result negated. + """ + result = self.__eq__(other) + if result is NotImplemented: + return NotImplemented + + return not result + + return __ne__ + + +def _make_eq(cls, attrs): + """ + Create __eq__ method for *cls* with *attrs*. + """ + attrs = [a for a in attrs if a.eq] + + unique_filename = _generate_unique_filename(cls, "eq") + lines = [ + "def __eq__(self, other):", + " if other.__class__ is not self.__class__:", + " return NotImplemented", + ] + + # We can't just do a big self.x = other.x and... clause due to + # irregularities like nan == nan is false but (nan,) == (nan,) is true. + globs = {} + if attrs: + lines.append(" return (") + others = [" ) == ("] + for a in attrs: + if a.eq_key: + cmp_name = "_%s_key" % (a.name,) + # Add the key function to the global namespace + # of the evaluated function. + globs[cmp_name] = a.eq_key + lines.append( + " %s(self.%s)," + % ( + cmp_name, + a.name, + ) + ) + others.append( + " %s(other.%s)," + % ( + cmp_name, + a.name, + ) + ) + else: + lines.append(" self.%s," % (a.name,)) + others.append(" other.%s," % (a.name,)) + + lines += others + [" )"] + else: + lines.append(" return True") + + script = "\n".join(lines) + + return _make_method("__eq__", script, unique_filename, globs) + + +def _make_order(cls, attrs): + """ + Create ordering methods for *cls* with *attrs*. + """ + attrs = [a for a in attrs if a.order] + + def attrs_to_tuple(obj): + """ + Save us some typing. + """ + return tuple( + key(value) if key else value + for value, key in ( + (getattr(obj, a.name), a.order_key) for a in attrs + ) + ) + + def __lt__(self, other): + """ + Automatically created by attrs. + """ + if other.__class__ is self.__class__: + return attrs_to_tuple(self) < attrs_to_tuple(other) + + return NotImplemented + + def __le__(self, other): + """ + Automatically created by attrs. + """ + if other.__class__ is self.__class__: + return attrs_to_tuple(self) <= attrs_to_tuple(other) + + return NotImplemented + + def __gt__(self, other): + """ + Automatically created by attrs. + """ + if other.__class__ is self.__class__: + return attrs_to_tuple(self) > attrs_to_tuple(other) + + return NotImplemented + + def __ge__(self, other): + """ + Automatically created by attrs. + """ + if other.__class__ is self.__class__: + return attrs_to_tuple(self) >= attrs_to_tuple(other) + + return NotImplemented + + return __lt__, __le__, __gt__, __ge__ + + +def _add_eq(cls, attrs=None): + """ + Add equality methods to *cls* with *attrs*. + """ + if attrs is None: + attrs = cls.__attrs_attrs__ + + cls.__eq__ = _make_eq(cls, attrs) + cls.__ne__ = _make_ne() + + return cls + + +_already_repring = threading.local() + + +def _make_repr(attrs, ns): + """ + Make a repr method that includes relevant *attrs*, adding *ns* to the full + name. + """ + + # Figure out which attributes to include, and which function to use to + # format them. The a.repr value can be either bool or a custom callable. + attr_names_with_reprs = tuple( + (a.name, repr if a.repr is True else a.repr) + for a in attrs + if a.repr is not False + ) + + def __repr__(self): + """ + Automatically created by attrs. + """ + try: + working_set = _already_repring.working_set + except AttributeError: + working_set = set() + _already_repring.working_set = working_set + + if id(self) in working_set: + return "..." + real_cls = self.__class__ + if ns is None: + qualname = getattr(real_cls, "__qualname__", None) + if qualname is not None: + class_name = qualname.rsplit(">.", 1)[-1] + else: + class_name = real_cls.__name__ + else: + class_name = ns + "." + real_cls.__name__ + + # Since 'self' remains on the stack (i.e.: strongly referenced) for the + # duration of this call, it's safe to depend on id(...) stability, and + # not need to track the instance and therefore worry about properties + # like weakref- or hash-ability. + working_set.add(id(self)) + try: + result = [class_name, "("] + first = True + for name, attr_repr in attr_names_with_reprs: + if first: + first = False + else: + result.append(", ") + result.extend( + (name, "=", attr_repr(getattr(self, name, NOTHING))) + ) + return "".join(result) + ")" + finally: + working_set.remove(id(self)) + + return __repr__ + + +def _add_repr(cls, ns=None, attrs=None): + """ + Add a repr method to *cls*. + """ + if attrs is None: + attrs = cls.__attrs_attrs__ + + cls.__repr__ = _make_repr(attrs, ns) + return cls + + +def fields(cls): + """ + Return the tuple of ``attrs`` attributes for a class. + + The tuple also allows accessing the fields by their names (see below for + examples). + + :param type cls: Class to introspect. + + :raise TypeError: If *cls* is not a class. + :raise attr.exceptions.NotAnAttrsClassError: If *cls* is not an ``attrs`` + class. + + :rtype: tuple (with name accessors) of `attr.Attribute` + + .. versionchanged:: 16.2.0 Returned tuple allows accessing the fields + by name. + """ + if not isclass(cls): + raise TypeError("Passed object must be a class.") + attrs = getattr(cls, "__attrs_attrs__", None) + if attrs is None: + raise NotAnAttrsClassError( + "{cls!r} is not an attrs-decorated class.".format(cls=cls) + ) + return attrs + + +def fields_dict(cls): + """ + Return an ordered dictionary of ``attrs`` attributes for a class, whose + keys are the attribute names. + + :param type cls: Class to introspect. + + :raise TypeError: If *cls* is not a class. + :raise attr.exceptions.NotAnAttrsClassError: If *cls* is not an ``attrs`` + class. + + :rtype: an ordered dict where keys are attribute names and values are + `attr.Attribute`\\ s. This will be a `dict` if it's + naturally ordered like on Python 3.6+ or an + :class:`~collections.OrderedDict` otherwise. + + .. versionadded:: 18.1.0 + """ + if not isclass(cls): + raise TypeError("Passed object must be a class.") + attrs = getattr(cls, "__attrs_attrs__", None) + if attrs is None: + raise NotAnAttrsClassError( + "{cls!r} is not an attrs-decorated class.".format(cls=cls) + ) + return ordered_dict(((a.name, a) for a in attrs)) + + +def validate(inst): + """ + Validate all attributes on *inst* that have a validator. + + Leaves all exceptions through. + + :param inst: Instance of a class with ``attrs`` attributes. + """ + if _config._run_validators is False: + return + + for a in fields(inst.__class__): + v = a.validator + if v is not None: + v(inst, a, getattr(inst, a.name)) + + +def _is_slot_cls(cls): + return "__slots__" in cls.__dict__ + + +def _is_slot_attr(a_name, base_attr_map): + """ + Check if the attribute name comes from a slot class. + """ + return a_name in base_attr_map and _is_slot_cls(base_attr_map[a_name]) + + +def _make_init( + cls, + attrs, + pre_init, + post_init, + frozen, + slots, + cache_hash, + base_attr_map, + is_exc, + has_global_on_setattr, + attrs_init, +): + if frozen and has_global_on_setattr: + raise ValueError("Frozen classes can't use on_setattr.") + + needs_cached_setattr = cache_hash or frozen + filtered_attrs = [] + attr_dict = {} + for a in attrs: + if not a.init and a.default is NOTHING: + continue + + filtered_attrs.append(a) + attr_dict[a.name] = a + + if a.on_setattr is not None: + if frozen is True: + raise ValueError("Frozen classes can't use on_setattr.") + + needs_cached_setattr = True + elif ( + has_global_on_setattr and a.on_setattr is not setters.NO_OP + ) or _is_slot_attr(a.name, base_attr_map): + needs_cached_setattr = True + + unique_filename = _generate_unique_filename(cls, "init") + + script, globs, annotations = _attrs_to_init_script( + filtered_attrs, + frozen, + slots, + pre_init, + post_init, + cache_hash, + base_attr_map, + is_exc, + needs_cached_setattr, + has_global_on_setattr, + attrs_init, + ) + if cls.__module__ in sys.modules: + # This makes typing.get_type_hints(CLS.__init__) resolve string types. + globs.update(sys.modules[cls.__module__].__dict__) + + globs.update({"NOTHING": NOTHING, "attr_dict": attr_dict}) + + if needs_cached_setattr: + # Save the lookup overhead in __init__ if we need to circumvent + # setattr hooks. + globs["_cached_setattr"] = _obj_setattr + + init = _make_method( + "__attrs_init__" if attrs_init else "__init__", + script, + unique_filename, + globs, + ) + init.__annotations__ = annotations + + return init + + +def _setattr(attr_name, value_var, has_on_setattr): + """ + Use the cached object.setattr to set *attr_name* to *value_var*. + """ + return "_setattr('%s', %s)" % (attr_name, value_var) + + +def _setattr_with_converter(attr_name, value_var, has_on_setattr): + """ + Use the cached object.setattr to set *attr_name* to *value_var*, but run + its converter first. + """ + return "_setattr('%s', %s(%s))" % ( + attr_name, + _init_converter_pat % (attr_name,), + value_var, + ) + + +def _assign(attr_name, value, has_on_setattr): + """ + Unless *attr_name* has an on_setattr hook, use normal assignment. Otherwise + relegate to _setattr. + """ + if has_on_setattr: + return _setattr(attr_name, value, True) + + return "self.%s = %s" % (attr_name, value) + + +def _assign_with_converter(attr_name, value_var, has_on_setattr): + """ + Unless *attr_name* has an on_setattr hook, use normal assignment after + conversion. Otherwise relegate to _setattr_with_converter. + """ + if has_on_setattr: + return _setattr_with_converter(attr_name, value_var, True) + + return "self.%s = %s(%s)" % ( + attr_name, + _init_converter_pat % (attr_name,), + value_var, + ) + + +if PY2: + + def _unpack_kw_only_py2(attr_name, default=None): + """ + Unpack *attr_name* from _kw_only dict. + """ + if default is not None: + arg_default = ", %s" % default + else: + arg_default = "" + return "%s = _kw_only.pop('%s'%s)" % ( + attr_name, + attr_name, + arg_default, + ) + + def _unpack_kw_only_lines_py2(kw_only_args): + """ + Unpack all *kw_only_args* from _kw_only dict and handle errors. + + Given a list of strings "{attr_name}" and "{attr_name}={default}" + generates list of lines of code that pop attrs from _kw_only dict and + raise TypeError similar to builtin if required attr is missing or + extra key is passed. + + >>> print("\n".join(_unpack_kw_only_lines_py2(["a", "b=42"]))) + try: + a = _kw_only.pop('a') + b = _kw_only.pop('b', 42) + except KeyError as _key_error: + raise TypeError( + ... + if _kw_only: + raise TypeError( + ... + """ + lines = ["try:"] + lines.extend( + " " + _unpack_kw_only_py2(*arg.split("=")) + for arg in kw_only_args + ) + lines += """\ +except KeyError as _key_error: + raise TypeError( + '__init__() missing required keyword-only argument: %s' % _key_error + ) +if _kw_only: + raise TypeError( + '__init__() got an unexpected keyword argument %r' + % next(iter(_kw_only)) + ) +""".split( + "\n" + ) + return lines + + +def _attrs_to_init_script( + attrs, + frozen, + slots, + pre_init, + post_init, + cache_hash, + base_attr_map, + is_exc, + needs_cached_setattr, + has_global_on_setattr, + attrs_init, +): + """ + Return a script of an initializer for *attrs* and a dict of globals. + + The globals are expected by the generated script. + + If *frozen* is True, we cannot set the attributes directly so we use + a cached ``object.__setattr__``. + """ + lines = [] + if pre_init: + lines.append("self.__attrs_pre_init__()") + + if needs_cached_setattr: + lines.append( + # Circumvent the __setattr__ descriptor to save one lookup per + # assignment. + # Note _setattr will be used again below if cache_hash is True + "_setattr = _cached_setattr.__get__(self, self.__class__)" + ) + + if frozen is True: + if slots is True: + fmt_setter = _setattr + fmt_setter_with_converter = _setattr_with_converter + else: + # Dict frozen classes assign directly to __dict__. + # But only if the attribute doesn't come from an ancestor slot + # class. + # Note _inst_dict will be used again below if cache_hash is True + lines.append("_inst_dict = self.__dict__") + + def fmt_setter(attr_name, value_var, has_on_setattr): + if _is_slot_attr(attr_name, base_attr_map): + return _setattr(attr_name, value_var, has_on_setattr) + + return "_inst_dict['%s'] = %s" % (attr_name, value_var) + + def fmt_setter_with_converter( + attr_name, value_var, has_on_setattr + ): + if has_on_setattr or _is_slot_attr(attr_name, base_attr_map): + return _setattr_with_converter( + attr_name, value_var, has_on_setattr + ) + + return "_inst_dict['%s'] = %s(%s)" % ( + attr_name, + _init_converter_pat % (attr_name,), + value_var, + ) + + else: + # Not frozen. + fmt_setter = _assign + fmt_setter_with_converter = _assign_with_converter + + args = [] + kw_only_args = [] + attrs_to_validate = [] + + # This is a dictionary of names to validator and converter callables. + # Injecting this into __init__ globals lets us avoid lookups. + names_for_globals = {} + annotations = {"return": None} + + for a in attrs: + if a.validator: + attrs_to_validate.append(a) + + attr_name = a.name + has_on_setattr = a.on_setattr is not None or ( + a.on_setattr is not setters.NO_OP and has_global_on_setattr + ) + arg_name = a.name.lstrip("_") + + has_factory = isinstance(a.default, Factory) + if has_factory and a.default.takes_self: + maybe_self = "self" + else: + maybe_self = "" + + if a.init is False: + if has_factory: + init_factory_name = _init_factory_pat.format(a.name) + if a.converter is not None: + lines.append( + fmt_setter_with_converter( + attr_name, + init_factory_name + "(%s)" % (maybe_self,), + has_on_setattr, + ) + ) + conv_name = _init_converter_pat % (a.name,) + names_for_globals[conv_name] = a.converter + else: + lines.append( + fmt_setter( + attr_name, + init_factory_name + "(%s)" % (maybe_self,), + has_on_setattr, + ) + ) + names_for_globals[init_factory_name] = a.default.factory + else: + if a.converter is not None: + lines.append( + fmt_setter_with_converter( + attr_name, + "attr_dict['%s'].default" % (attr_name,), + has_on_setattr, + ) + ) + conv_name = _init_converter_pat % (a.name,) + names_for_globals[conv_name] = a.converter + else: + lines.append( + fmt_setter( + attr_name, + "attr_dict['%s'].default" % (attr_name,), + has_on_setattr, + ) + ) + elif a.default is not NOTHING and not has_factory: + arg = "%s=attr_dict['%s'].default" % (arg_name, attr_name) + if a.kw_only: + kw_only_args.append(arg) + else: + args.append(arg) + + if a.converter is not None: + lines.append( + fmt_setter_with_converter( + attr_name, arg_name, has_on_setattr + ) + ) + names_for_globals[ + _init_converter_pat % (a.name,) + ] = a.converter + else: + lines.append(fmt_setter(attr_name, arg_name, has_on_setattr)) + + elif has_factory: + arg = "%s=NOTHING" % (arg_name,) + if a.kw_only: + kw_only_args.append(arg) + else: + args.append(arg) + lines.append("if %s is not NOTHING:" % (arg_name,)) + + init_factory_name = _init_factory_pat.format(a.name) + if a.converter is not None: + lines.append( + " " + + fmt_setter_with_converter( + attr_name, arg_name, has_on_setattr + ) + ) + lines.append("else:") + lines.append( + " " + + fmt_setter_with_converter( + attr_name, + init_factory_name + "(" + maybe_self + ")", + has_on_setattr, + ) + ) + names_for_globals[ + _init_converter_pat % (a.name,) + ] = a.converter + else: + lines.append( + " " + fmt_setter(attr_name, arg_name, has_on_setattr) + ) + lines.append("else:") + lines.append( + " " + + fmt_setter( + attr_name, + init_factory_name + "(" + maybe_self + ")", + has_on_setattr, + ) + ) + names_for_globals[init_factory_name] = a.default.factory + else: + if a.kw_only: + kw_only_args.append(arg_name) + else: + args.append(arg_name) + + if a.converter is not None: + lines.append( + fmt_setter_with_converter( + attr_name, arg_name, has_on_setattr + ) + ) + names_for_globals[ + _init_converter_pat % (a.name,) + ] = a.converter + else: + lines.append(fmt_setter(attr_name, arg_name, has_on_setattr)) + + if a.init is True: + if a.type is not None and a.converter is None: + annotations[arg_name] = a.type + elif a.converter is not None and not PY2: + # Try to get the type from the converter. + sig = None + try: + sig = inspect.signature(a.converter) + except (ValueError, TypeError): # inspect failed + pass + if sig: + sig_params = list(sig.parameters.values()) + if ( + sig_params + and sig_params[0].annotation + is not inspect.Parameter.empty + ): + annotations[arg_name] = sig_params[0].annotation + + if attrs_to_validate: # we can skip this if there are no validators. + names_for_globals["_config"] = _config + lines.append("if _config._run_validators is True:") + for a in attrs_to_validate: + val_name = "__attr_validator_" + a.name + attr_name = "__attr_" + a.name + lines.append( + " %s(self, %s, self.%s)" % (val_name, attr_name, a.name) + ) + names_for_globals[val_name] = a.validator + names_for_globals[attr_name] = a + + if post_init: + lines.append("self.__attrs_post_init__()") + + # because this is set only after __attrs_post_init is called, a crash + # will result if post-init tries to access the hash code. This seemed + # preferable to setting this beforehand, in which case alteration to + # field values during post-init combined with post-init accessing the + # hash code would result in silent bugs. + if cache_hash: + if frozen: + if slots: + # if frozen and slots, then _setattr defined above + init_hash_cache = "_setattr('%s', %s)" + else: + # if frozen and not slots, then _inst_dict defined above + init_hash_cache = "_inst_dict['%s'] = %s" + else: + init_hash_cache = "self.%s = %s" + lines.append(init_hash_cache % (_hash_cache_field, "None")) + + # For exceptions we rely on BaseException.__init__ for proper + # initialization. + if is_exc: + vals = ",".join("self." + a.name for a in attrs if a.init) + + lines.append("BaseException.__init__(self, %s)" % (vals,)) + + args = ", ".join(args) + if kw_only_args: + if PY2: + lines = _unpack_kw_only_lines_py2(kw_only_args) + lines + + args += "%s**_kw_only" % (", " if args else "",) # leading comma + else: + args += "%s*, %s" % ( + ", " if args else "", # leading comma + ", ".join(kw_only_args), # kw_only args + ) + return ( + """\ +def {init_name}(self, {args}): + {lines} +""".format( + init_name=("__attrs_init__" if attrs_init else "__init__"), + args=args, + lines="\n ".join(lines) if lines else "pass", + ), + names_for_globals, + annotations, + ) + + +class Attribute(object): + """ + *Read-only* representation of an attribute. + + Instances of this class are frequently used for introspection purposes + like: + + - `fields` returns a tuple of them. + - Validators get them passed as the first argument. + - The *field transformer* hook receives a list of them. + + :attribute name: The name of the attribute. + :attribute inherited: Whether or not that attribute has been inherited from + a base class. + + Plus *all* arguments of `attr.ib` (except for ``factory`` + which is only syntactic sugar for ``default=Factory(...)``. + + .. versionadded:: 20.1.0 *inherited* + .. versionadded:: 20.1.0 *on_setattr* + .. versionchanged:: 20.2.0 *inherited* is not taken into account for + equality checks and hashing anymore. + .. versionadded:: 21.1.0 *eq_key* and *order_key* + + For the full version history of the fields, see `attr.ib`. + """ + + __slots__ = ( + "name", + "default", + "validator", + "repr", + "eq", + "eq_key", + "order", + "order_key", + "hash", + "init", + "metadata", + "type", + "converter", + "kw_only", + "inherited", + "on_setattr", + ) + + def __init__( + self, + name, + default, + validator, + repr, + cmp, # XXX: unused, remove along with other cmp code. + hash, + init, + inherited, + metadata=None, + type=None, + converter=None, + kw_only=False, + eq=None, + eq_key=None, + order=None, + order_key=None, + on_setattr=None, + ): + eq, eq_key, order, order_key = _determine_attrib_eq_order( + cmp, eq_key or eq, order_key or order, True + ) + + # Cache this descriptor here to speed things up later. + bound_setattr = _obj_setattr.__get__(self, Attribute) + + # Despite the big red warning, people *do* instantiate `Attribute` + # themselves. + bound_setattr("name", name) + bound_setattr("default", default) + bound_setattr("validator", validator) + bound_setattr("repr", repr) + bound_setattr("eq", eq) + bound_setattr("eq_key", eq_key) + bound_setattr("order", order) + bound_setattr("order_key", order_key) + bound_setattr("hash", hash) + bound_setattr("init", init) + bound_setattr("converter", converter) + bound_setattr( + "metadata", + ( + metadata_proxy(metadata) + if metadata + else _empty_metadata_singleton + ), + ) + bound_setattr("type", type) + bound_setattr("kw_only", kw_only) + bound_setattr("inherited", inherited) + bound_setattr("on_setattr", on_setattr) + + def __setattr__(self, name, value): + raise FrozenInstanceError() + + @classmethod + def from_counting_attr(cls, name, ca, type=None): + # type holds the annotated value. deal with conflicts: + if type is None: + type = ca.type + elif ca.type is not None: + raise ValueError( + "Type annotation and type argument cannot both be present" + ) + inst_dict = { + k: getattr(ca, k) + for k in Attribute.__slots__ + if k + not in ( + "name", + "validator", + "default", + "type", + "inherited", + ) # exclude methods and deprecated alias + } + return cls( + name=name, + validator=ca._validator, + default=ca._default, + type=type, + cmp=None, + inherited=False, + **inst_dict + ) + + @property + def cmp(self): + """ + Simulate the presence of a cmp attribute and warn. + """ + warnings.warn(_CMP_DEPRECATION, DeprecationWarning, stacklevel=2) + + return self.eq and self.order + + # Don't use attr.evolve since fields(Attribute) doesn't work + def evolve(self, **changes): + """ + Copy *self* and apply *changes*. + + This works similarly to `attr.evolve` but that function does not work + with ``Attribute``. + + It is mainly meant to be used for `transform-fields`. + + .. versionadded:: 20.3.0 + """ + new = copy.copy(self) + + new._setattrs(changes.items()) + + return new + + # Don't use _add_pickle since fields(Attribute) doesn't work + def __getstate__(self): + """ + Play nice with pickle. + """ + return tuple( + getattr(self, name) if name != "metadata" else dict(self.metadata) + for name in self.__slots__ + ) + + def __setstate__(self, state): + """ + Play nice with pickle. + """ + self._setattrs(zip(self.__slots__, state)) + + def _setattrs(self, name_values_pairs): + bound_setattr = _obj_setattr.__get__(self, Attribute) + for name, value in name_values_pairs: + if name != "metadata": + bound_setattr(name, value) + else: + bound_setattr( + name, + metadata_proxy(value) + if value + else _empty_metadata_singleton, + ) + + +_a = [ + Attribute( + name=name, + default=NOTHING, + validator=None, + repr=True, + cmp=None, + eq=True, + order=False, + hash=(name != "metadata"), + init=True, + inherited=False, + ) + for name in Attribute.__slots__ +] + +Attribute = _add_hash( + _add_eq( + _add_repr(Attribute, attrs=_a), + attrs=[a for a in _a if a.name != "inherited"], + ), + attrs=[a for a in _a if a.hash and a.name != "inherited"], +) + + +class _CountingAttr(object): + """ + Intermediate representation of attributes that uses a counter to preserve + the order in which the attributes have been defined. + + *Internal* data structure of the attrs library. Running into is most + likely the result of a bug like a forgotten `@attr.s` decorator. + """ + + __slots__ = ( + "counter", + "_default", + "repr", + "eq", + "eq_key", + "order", + "order_key", + "hash", + "init", + "metadata", + "_validator", + "converter", + "type", + "kw_only", + "on_setattr", + ) + __attrs_attrs__ = tuple( + Attribute( + name=name, + default=NOTHING, + validator=None, + repr=True, + cmp=None, + hash=True, + init=True, + kw_only=False, + eq=True, + eq_key=None, + order=False, + order_key=None, + inherited=False, + on_setattr=None, + ) + for name in ( + "counter", + "_default", + "repr", + "eq", + "order", + "hash", + "init", + "on_setattr", + ) + ) + ( + Attribute( + name="metadata", + default=None, + validator=None, + repr=True, + cmp=None, + hash=False, + init=True, + kw_only=False, + eq=True, + eq_key=None, + order=False, + order_key=None, + inherited=False, + on_setattr=None, + ), + ) + cls_counter = 0 + + def __init__( + self, + default, + validator, + repr, + cmp, + hash, + init, + converter, + metadata, + type, + kw_only, + eq, + eq_key, + order, + order_key, + on_setattr, + ): + _CountingAttr.cls_counter += 1 + self.counter = _CountingAttr.cls_counter + self._default = default + self._validator = validator + self.converter = converter + self.repr = repr + self.eq = eq + self.eq_key = eq_key + self.order = order + self.order_key = order_key + self.hash = hash + self.init = init + self.metadata = metadata + self.type = type + self.kw_only = kw_only + self.on_setattr = on_setattr + + def validator(self, meth): + """ + Decorator that adds *meth* to the list of validators. + + Returns *meth* unchanged. + + .. versionadded:: 17.1.0 + """ + if self._validator is None: + self._validator = meth + else: + self._validator = and_(self._validator, meth) + return meth + + def default(self, meth): + """ + Decorator that allows to set the default for an attribute. + + Returns *meth* unchanged. + + :raises DefaultAlreadySetError: If default has been set before. + + .. versionadded:: 17.1.0 + """ + if self._default is not NOTHING: + raise DefaultAlreadySetError() + + self._default = Factory(meth, takes_self=True) + + return meth + + +_CountingAttr = _add_eq(_add_repr(_CountingAttr)) + + +class Factory(object): + """ + Stores a factory callable. + + If passed as the default value to `attr.ib`, the factory is used to + generate a new value. + + :param callable factory: A callable that takes either none or exactly one + mandatory positional argument depending on *takes_self*. + :param bool takes_self: Pass the partially initialized instance that is + being initialized as a positional argument. + + .. versionadded:: 17.1.0 *takes_self* + """ + + __slots__ = ("factory", "takes_self") + + def __init__(self, factory, takes_self=False): + """ + `Factory` is part of the default machinery so if we want a default + value here, we have to implement it ourselves. + """ + self.factory = factory + self.takes_self = takes_self + + def __getstate__(self): + """ + Play nice with pickle. + """ + return tuple(getattr(self, name) for name in self.__slots__) + + def __setstate__(self, state): + """ + Play nice with pickle. + """ + for name, value in zip(self.__slots__, state): + setattr(self, name, value) + + +_f = [ + Attribute( + name=name, + default=NOTHING, + validator=None, + repr=True, + cmp=None, + eq=True, + order=False, + hash=True, + init=True, + inherited=False, + ) + for name in Factory.__slots__ +] + +Factory = _add_hash(_add_eq(_add_repr(Factory, attrs=_f), attrs=_f), attrs=_f) + + +def make_class(name, attrs, bases=(object,), **attributes_arguments): + """ + A quick way to create a new class called *name* with *attrs*. + + :param str name: The name for the new class. + + :param attrs: A list of names or a dictionary of mappings of names to + attributes. + + If *attrs* is a list or an ordered dict (`dict` on Python 3.6+, + `collections.OrderedDict` otherwise), the order is deduced from + the order of the names or attributes inside *attrs*. Otherwise the + order of the definition of the attributes is used. + :type attrs: `list` or `dict` + + :param tuple bases: Classes that the new class will subclass. + + :param attributes_arguments: Passed unmodified to `attr.s`. + + :return: A new class with *attrs*. + :rtype: type + + .. versionadded:: 17.1.0 *bases* + .. versionchanged:: 18.1.0 If *attrs* is ordered, the order is retained. + """ + if isinstance(attrs, dict): + cls_dict = attrs + elif isinstance(attrs, (list, tuple)): + cls_dict = dict((a, attrib()) for a in attrs) + else: + raise TypeError("attrs argument must be a dict or a list.") + + pre_init = cls_dict.pop("__attrs_pre_init__", None) + post_init = cls_dict.pop("__attrs_post_init__", None) + user_init = cls_dict.pop("__init__", None) + + body = {} + if pre_init is not None: + body["__attrs_pre_init__"] = pre_init + if post_init is not None: + body["__attrs_post_init__"] = post_init + if user_init is not None: + body["__init__"] = user_init + + type_ = new_class(name, bases, {}, lambda ns: ns.update(body)) + + # For pickling to work, the __module__ variable needs to be set to the + # frame where the class is created. Bypass this step in environments where + # sys._getframe is not defined (Jython for example) or sys._getframe is not + # defined for arguments greater than 0 (IronPython). + try: + type_.__module__ = sys._getframe(1).f_globals.get( + "__name__", "__main__" + ) + except (AttributeError, ValueError): + pass + + # We do it here for proper warnings with meaningful stacklevel. + cmp = attributes_arguments.pop("cmp", None) + ( + attributes_arguments["eq"], + attributes_arguments["order"], + ) = _determine_attrs_eq_order( + cmp, + attributes_arguments.get("eq"), + attributes_arguments.get("order"), + True, + ) + + return _attrs(these=cls_dict, **attributes_arguments)(type_) + + +# These are required by within this module so we define them here and merely +# import into .validators / .converters. + + +@attrs(slots=True, hash=True) +class _AndValidator(object): + """ + Compose many validators to a single one. + """ + + _validators = attrib() + + def __call__(self, inst, attr, value): + for v in self._validators: + v(inst, attr, value) + + +def and_(*validators): + """ + A validator that composes multiple validators into one. + + When called on a value, it runs all wrapped validators. + + :param callables validators: Arbitrary number of validators. + + .. versionadded:: 17.1.0 + """ + vals = [] + for validator in validators: + vals.extend( + validator._validators + if isinstance(validator, _AndValidator) + else [validator] + ) + + return _AndValidator(tuple(vals)) + + +def pipe(*converters): + """ + A converter that composes multiple converters into one. + + When called on a value, it runs all wrapped converters, returning the + *last* value. + + Type annotations will be inferred from the wrapped converters', if + they have any. + + :param callables converters: Arbitrary number of converters. + + .. versionadded:: 20.1.0 + """ + + def pipe_converter(val): + for converter in converters: + val = converter(val) + + return val + + if not PY2: + if not converters: + # If the converter list is empty, pipe_converter is the identity. + A = typing.TypeVar("A") + pipe_converter.__annotations__ = {"val": A, "return": A} + else: + # Get parameter type. + sig = None + try: + sig = inspect.signature(converters[0]) + except (ValueError, TypeError): # inspect failed + pass + if sig: + params = list(sig.parameters.values()) + if ( + params + and params[0].annotation is not inspect.Parameter.empty + ): + pipe_converter.__annotations__["val"] = params[ + 0 + ].annotation + # Get return type. + sig = None + try: + sig = inspect.signature(converters[-1]) + except (ValueError, TypeError): # inspect failed + pass + if sig and sig.return_annotation is not inspect.Signature().empty: + pipe_converter.__annotations__[ + "return" + ] = sig.return_annotation + + return pipe_converter diff --git a/venv/Lib/site-packages/attr/_next_gen.py b/venv/Lib/site-packages/attr/_next_gen.py new file mode 100644 index 0000000..fab0af9 --- /dev/null +++ b/venv/Lib/site-packages/attr/_next_gen.py @@ -0,0 +1,158 @@ +""" +These are Python 3.6+-only and keyword-only APIs that call `attr.s` and +`attr.ib` with different default values. +""" + +from functools import partial + +from attr.exceptions import UnannotatedAttributeError + +from . import setters +from ._make import NOTHING, _frozen_setattrs, attrib, attrs + + +def define( + maybe_cls=None, + *, + these=None, + repr=None, + hash=None, + init=None, + slots=True, + frozen=False, + weakref_slot=True, + str=False, + auto_attribs=None, + kw_only=False, + cache_hash=False, + auto_exc=True, + eq=None, + order=False, + auto_detect=True, + getstate_setstate=None, + on_setattr=None, + field_transformer=None, +): + r""" + The only behavioral differences are the handling of the *auto_attribs* + option: + + :param Optional[bool] auto_attribs: If set to `True` or `False`, it behaves + exactly like `attr.s`. If left `None`, `attr.s` will try to guess: + + 1. If any attributes are annotated and no unannotated `attr.ib`\ s + are found, it assumes *auto_attribs=True*. + 2. Otherwise it assumes *auto_attribs=False* and tries to collect + `attr.ib`\ s. + + and that mutable classes (``frozen=False``) validate on ``__setattr__``. + + .. versionadded:: 20.1.0 + """ + + def do_it(cls, auto_attribs): + return attrs( + maybe_cls=cls, + these=these, + repr=repr, + hash=hash, + init=init, + slots=slots, + frozen=frozen, + weakref_slot=weakref_slot, + str=str, + auto_attribs=auto_attribs, + kw_only=kw_only, + cache_hash=cache_hash, + auto_exc=auto_exc, + eq=eq, + order=order, + auto_detect=auto_detect, + collect_by_mro=True, + getstate_setstate=getstate_setstate, + on_setattr=on_setattr, + field_transformer=field_transformer, + ) + + def wrap(cls): + """ + Making this a wrapper ensures this code runs during class creation. + + We also ensure that frozen-ness of classes is inherited. + """ + nonlocal frozen, on_setattr + + had_on_setattr = on_setattr not in (None, setters.NO_OP) + + # By default, mutable classes validate on setattr. + if frozen is False and on_setattr is None: + on_setattr = setters.validate + + # However, if we subclass a frozen class, we inherit the immutability + # and disable on_setattr. + for base_cls in cls.__bases__: + if base_cls.__setattr__ is _frozen_setattrs: + if had_on_setattr: + raise ValueError( + "Frozen classes can't use on_setattr " + "(frozen-ness was inherited)." + ) + + on_setattr = setters.NO_OP + break + + if auto_attribs is not None: + return do_it(cls, auto_attribs) + + try: + return do_it(cls, True) + except UnannotatedAttributeError: + return do_it(cls, False) + + # maybe_cls's type depends on the usage of the decorator. It's a class + # if it's used as `@attrs` but ``None`` if used as `@attrs()`. + if maybe_cls is None: + return wrap + else: + return wrap(maybe_cls) + + +mutable = define +frozen = partial(define, frozen=True, on_setattr=None) + + +def field( + *, + default=NOTHING, + validator=None, + repr=True, + hash=None, + init=True, + metadata=None, + converter=None, + factory=None, + kw_only=False, + eq=None, + order=None, + on_setattr=None, +): + """ + Identical to `attr.ib`, except keyword-only and with some arguments + removed. + + .. versionadded:: 20.1.0 + """ + return attrib( + default=default, + validator=validator, + repr=repr, + hash=hash, + init=init, + metadata=metadata, + converter=converter, + factory=factory, + kw_only=kw_only, + eq=eq, + order=order, + on_setattr=on_setattr, + ) diff --git a/venv/Lib/site-packages/attr/_version_info.py b/venv/Lib/site-packages/attr/_version_info.py new file mode 100644 index 0000000..014e78a --- /dev/null +++ b/venv/Lib/site-packages/attr/_version_info.py @@ -0,0 +1,85 @@ +from __future__ import absolute_import, division, print_function + +from functools import total_ordering + +from ._funcs import astuple +from ._make import attrib, attrs + + +@total_ordering +@attrs(eq=False, order=False, slots=True, frozen=True) +class VersionInfo(object): + """ + A version object that can be compared to tuple of length 1--4: + + >>> attr.VersionInfo(19, 1, 0, "final") <= (19, 2) + True + >>> attr.VersionInfo(19, 1, 0, "final") < (19, 1, 1) + True + >>> vi = attr.VersionInfo(19, 2, 0, "final") + >>> vi < (19, 1, 1) + False + >>> vi < (19,) + False + >>> vi == (19, 2,) + True + >>> vi == (19, 2, 1) + False + + .. versionadded:: 19.2 + """ + + year = attrib(type=int) + minor = attrib(type=int) + micro = attrib(type=int) + releaselevel = attrib(type=str) + + @classmethod + def _from_version_string(cls, s): + """ + Parse *s* and return a _VersionInfo. + """ + v = s.split(".") + if len(v) == 3: + v.append("final") + + return cls( + year=int(v[0]), minor=int(v[1]), micro=int(v[2]), releaselevel=v[3] + ) + + def _ensure_tuple(self, other): + """ + Ensure *other* is a tuple of a valid length. + + Returns a possibly transformed *other* and ourselves as a tuple of + the same length as *other*. + """ + + if self.__class__ is other.__class__: + other = astuple(other) + + if not isinstance(other, tuple): + raise NotImplementedError + + if not (1 <= len(other) <= 4): + raise NotImplementedError + + return astuple(self)[: len(other)], other + + def __eq__(self, other): + try: + us, them = self._ensure_tuple(other) + except NotImplementedError: + return NotImplemented + + return us == them + + def __lt__(self, other): + try: + us, them = self._ensure_tuple(other) + except NotImplementedError: + return NotImplemented + + # Since alphabetically "dev0" < "final" < "post1" < "post2", we don't + # have to do anything special with releaselevel for now. + return us < them diff --git a/venv/Lib/site-packages/attr/_version_info.pyi b/venv/Lib/site-packages/attr/_version_info.pyi new file mode 100644 index 0000000..45ced08 --- /dev/null +++ b/venv/Lib/site-packages/attr/_version_info.pyi @@ -0,0 +1,9 @@ +class VersionInfo: + @property + def year(self) -> int: ... + @property + def minor(self) -> int: ... + @property + def micro(self) -> int: ... + @property + def releaselevel(self) -> str: ... diff --git a/venv/Lib/site-packages/attr/converters.py b/venv/Lib/site-packages/attr/converters.py new file mode 100644 index 0000000..2777db6 --- /dev/null +++ b/venv/Lib/site-packages/attr/converters.py @@ -0,0 +1,111 @@ +""" +Commonly useful converters. +""" + +from __future__ import absolute_import, division, print_function + +from ._compat import PY2 +from ._make import NOTHING, Factory, pipe + + +if not PY2: + import inspect + import typing + + +__all__ = [ + "pipe", + "optional", + "default_if_none", +] + + +def optional(converter): + """ + A converter that allows an attribute to be optional. An optional attribute + is one which can be set to ``None``. + + Type annotations will be inferred from the wrapped converter's, if it + has any. + + :param callable converter: the converter that is used for non-``None`` + values. + + .. versionadded:: 17.1.0 + """ + + def optional_converter(val): + if val is None: + return None + return converter(val) + + if not PY2: + sig = None + try: + sig = inspect.signature(converter) + except (ValueError, TypeError): # inspect failed + pass + if sig: + params = list(sig.parameters.values()) + if params and params[0].annotation is not inspect.Parameter.empty: + optional_converter.__annotations__["val"] = typing.Optional[ + params[0].annotation + ] + if sig.return_annotation is not inspect.Signature.empty: + optional_converter.__annotations__["return"] = typing.Optional[ + sig.return_annotation + ] + + return optional_converter + + +def default_if_none(default=NOTHING, factory=None): + """ + A converter that allows to replace ``None`` values by *default* or the + result of *factory*. + + :param default: Value to be used if ``None`` is passed. Passing an instance + of `attr.Factory` is supported, however the ``takes_self`` option + is *not*. + :param callable factory: A callable that takes no parameters whose result + is used if ``None`` is passed. + + :raises TypeError: If **neither** *default* or *factory* is passed. + :raises TypeError: If **both** *default* and *factory* are passed. + :raises ValueError: If an instance of `attr.Factory` is passed with + ``takes_self=True``. + + .. versionadded:: 18.2.0 + """ + if default is NOTHING and factory is None: + raise TypeError("Must pass either `default` or `factory`.") + + if default is not NOTHING and factory is not None: + raise TypeError( + "Must pass either `default` or `factory` but not both." + ) + + if factory is not None: + default = Factory(factory) + + if isinstance(default, Factory): + if default.takes_self: + raise ValueError( + "`takes_self` is not supported by default_if_none." + ) + + def default_if_none_converter(val): + if val is not None: + return val + + return default.factory() + + else: + + def default_if_none_converter(val): + if val is not None: + return val + + return default + + return default_if_none_converter diff --git a/venv/Lib/site-packages/attr/converters.pyi b/venv/Lib/site-packages/attr/converters.pyi new file mode 100644 index 0000000..84a5759 --- /dev/null +++ b/venv/Lib/site-packages/attr/converters.pyi @@ -0,0 +1,13 @@ +from typing import Callable, Optional, TypeVar, overload + +from . import _ConverterType + + +_T = TypeVar("_T") + +def pipe(*validators: _ConverterType) -> _ConverterType: ... +def optional(converter: _ConverterType) -> _ConverterType: ... +@overload +def default_if_none(default: _T) -> _ConverterType: ... +@overload +def default_if_none(*, factory: Callable[[], _T]) -> _ConverterType: ... diff --git a/venv/Lib/site-packages/attr/exceptions.py b/venv/Lib/site-packages/attr/exceptions.py new file mode 100644 index 0000000..f6f9861 --- /dev/null +++ b/venv/Lib/site-packages/attr/exceptions.py @@ -0,0 +1,92 @@ +from __future__ import absolute_import, division, print_function + + +class FrozenError(AttributeError): + """ + A frozen/immutable instance or attribute have been attempted to be + modified. + + It mirrors the behavior of ``namedtuples`` by using the same error message + and subclassing `AttributeError`. + + .. versionadded:: 20.1.0 + """ + + msg = "can't set attribute" + args = [msg] + + +class FrozenInstanceError(FrozenError): + """ + A frozen instance has been attempted to be modified. + + .. versionadded:: 16.1.0 + """ + + +class FrozenAttributeError(FrozenError): + """ + A frozen attribute has been attempted to be modified. + + .. versionadded:: 20.1.0 + """ + + +class AttrsAttributeNotFoundError(ValueError): + """ + An ``attrs`` function couldn't find an attribute that the user asked for. + + .. versionadded:: 16.2.0 + """ + + +class NotAnAttrsClassError(ValueError): + """ + A non-``attrs`` class has been passed into an ``attrs`` function. + + .. versionadded:: 16.2.0 + """ + + +class DefaultAlreadySetError(RuntimeError): + """ + A default has been set using ``attr.ib()`` and is attempted to be reset + using the decorator. + + .. versionadded:: 17.1.0 + """ + + +class UnannotatedAttributeError(RuntimeError): + """ + A class with ``auto_attribs=True`` has an ``attr.ib()`` without a type + annotation. + + .. versionadded:: 17.3.0 + """ + + +class PythonTooOldError(RuntimeError): + """ + It was attempted to use an ``attrs`` feature that requires a newer Python + version. + + .. versionadded:: 18.2.0 + """ + + +class NotCallableError(TypeError): + """ + A ``attr.ib()`` requiring a callable has been set with a value + that is not callable. + + .. versionadded:: 19.2.0 + """ + + def __init__(self, msg, value): + super(TypeError, self).__init__(msg, value) + self.msg = msg + self.value = value + + def __str__(self): + return str(self.msg) diff --git a/venv/Lib/site-packages/attr/exceptions.pyi b/venv/Lib/site-packages/attr/exceptions.pyi new file mode 100644 index 0000000..a800fb2 --- /dev/null +++ b/venv/Lib/site-packages/attr/exceptions.pyi @@ -0,0 +1,18 @@ +from typing import Any + + +class FrozenError(AttributeError): + msg: str = ... + +class FrozenInstanceError(FrozenError): ... +class FrozenAttributeError(FrozenError): ... +class AttrsAttributeNotFoundError(ValueError): ... +class NotAnAttrsClassError(ValueError): ... +class DefaultAlreadySetError(RuntimeError): ... +class UnannotatedAttributeError(RuntimeError): ... +class PythonTooOldError(RuntimeError): ... + +class NotCallableError(TypeError): + msg: str = ... + value: Any = ... + def __init__(self, msg: str, value: Any) -> None: ... diff --git a/venv/Lib/site-packages/attr/filters.py b/venv/Lib/site-packages/attr/filters.py new file mode 100644 index 0000000..dc47e8f --- /dev/null +++ b/venv/Lib/site-packages/attr/filters.py @@ -0,0 +1,52 @@ +""" +Commonly useful filters for `attr.asdict`. +""" + +from __future__ import absolute_import, division, print_function + +from ._compat import isclass +from ._make import Attribute + + +def _split_what(what): + """ + Returns a tuple of `frozenset`s of classes and attributes. + """ + return ( + frozenset(cls for cls in what if isclass(cls)), + frozenset(cls for cls in what if isinstance(cls, Attribute)), + ) + + +def include(*what): + """ + Whitelist *what*. + + :param what: What to whitelist. + :type what: `list` of `type` or `attr.Attribute`\\ s + + :rtype: `callable` + """ + cls, attrs = _split_what(what) + + def include_(attribute, value): + return value.__class__ in cls or attribute in attrs + + return include_ + + +def exclude(*what): + """ + Blacklist *what*. + + :param what: What to blacklist. + :type what: `list` of classes or `attr.Attribute`\\ s. + + :rtype: `callable` + """ + cls, attrs = _split_what(what) + + def exclude_(attribute, value): + return value.__class__ not in cls and attribute not in attrs + + return exclude_ diff --git a/venv/Lib/site-packages/attr/filters.pyi b/venv/Lib/site-packages/attr/filters.pyi new file mode 100644 index 0000000..f7b63f1 --- /dev/null +++ b/venv/Lib/site-packages/attr/filters.pyi @@ -0,0 +1,7 @@ +from typing import Any, Union + +from . import Attribute, _FilterType + + +def include(*what: Union[type, Attribute[Any]]) -> _FilterType[Any]: ... +def exclude(*what: Union[type, Attribute[Any]]) -> _FilterType[Any]: ... diff --git a/venv/Lib/site-packages/attr/py.typed b/venv/Lib/site-packages/attr/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/venv/Lib/site-packages/attr/setters.py b/venv/Lib/site-packages/attr/setters.py new file mode 100644 index 0000000..240014b --- /dev/null +++ b/venv/Lib/site-packages/attr/setters.py @@ -0,0 +1,77 @@ +""" +Commonly used hooks for on_setattr. +""" + +from __future__ import absolute_import, division, print_function + +from . import _config +from .exceptions import FrozenAttributeError + + +def pipe(*setters): + """ + Run all *setters* and return the return value of the last one. + + .. versionadded:: 20.1.0 + """ + + def wrapped_pipe(instance, attrib, new_value): + rv = new_value + + for setter in setters: + rv = setter(instance, attrib, rv) + + return rv + + return wrapped_pipe + + +def frozen(_, __, ___): + """ + Prevent an attribute to be modified. + + .. versionadded:: 20.1.0 + """ + raise FrozenAttributeError() + + +def validate(instance, attrib, new_value): + """ + Run *attrib*'s validator on *new_value* if it has one. + + .. versionadded:: 20.1.0 + """ + if _config._run_validators is False: + return new_value + + v = attrib.validator + if not v: + return new_value + + v(instance, attrib, new_value) + + return new_value + + +def convert(instance, attrib, new_value): + """ + Run *attrib*'s converter -- if it has one -- on *new_value* and return the + result. + + .. versionadded:: 20.1.0 + """ + c = attrib.converter + if c: + return c(new_value) + + return new_value + + +NO_OP = object() +""" +Sentinel for disabling class-wide *on_setattr* hooks for certain attributes. + +Does not work in `pipe` or within lists. + +.. versionadded:: 20.1.0 +""" diff --git a/venv/Lib/site-packages/attr/setters.pyi b/venv/Lib/site-packages/attr/setters.pyi new file mode 100644 index 0000000..a921e07 --- /dev/null +++ b/venv/Lib/site-packages/attr/setters.pyi @@ -0,0 +1,20 @@ +from typing import Any, NewType, NoReturn, TypeVar, cast + +from . import Attribute, _OnSetAttrType + + +_T = TypeVar("_T") + +def frozen( + instance: Any, attribute: Attribute[Any], new_value: Any +) -> NoReturn: ... +def pipe(*setters: _OnSetAttrType) -> _OnSetAttrType: ... +def validate(instance: Any, attribute: Attribute[_T], new_value: _T) -> _T: ... + +# convert is allowed to return Any, because they can be chained using pipe. +def convert( + instance: Any, attribute: Attribute[Any], new_value: Any +) -> Any: ... + +_NoOpType = NewType("_NoOpType", object) +NO_OP: _NoOpType diff --git a/venv/Lib/site-packages/attr/validators.py b/venv/Lib/site-packages/attr/validators.py new file mode 100644 index 0000000..b9a7305 --- /dev/null +++ b/venv/Lib/site-packages/attr/validators.py @@ -0,0 +1,379 @@ +""" +Commonly useful validators. +""" + +from __future__ import absolute_import, division, print_function + +import re + +from ._make import _AndValidator, and_, attrib, attrs +from .exceptions import NotCallableError + + +__all__ = [ + "and_", + "deep_iterable", + "deep_mapping", + "in_", + "instance_of", + "is_callable", + "matches_re", + "optional", + "provides", +] + + +@attrs(repr=False, slots=True, hash=True) +class _InstanceOfValidator(object): + type = attrib() + + def __call__(self, inst, attr, value): + """ + We use a callable class to be able to change the ``__repr__``. + """ + if not isinstance(value, self.type): + raise TypeError( + "'{name}' must be {type!r} (got {value!r} that is a " + "{actual!r}).".format( + name=attr.name, + type=self.type, + actual=value.__class__, + value=value, + ), + attr, + self.type, + value, + ) + + def __repr__(self): + return "".format( + type=self.type + ) + + +def instance_of(type): + """ + A validator that raises a `TypeError` if the initializer is called + with a wrong type for this particular attribute (checks are performed using + `isinstance` therefore it's also valid to pass a tuple of types). + + :param type: The type to check for. + :type type: type or tuple of types + + :raises TypeError: With a human readable error message, the attribute + (of type `attr.Attribute`), the expected type, and the value it + got. + """ + return _InstanceOfValidator(type) + + +@attrs(repr=False, frozen=True, slots=True) +class _MatchesReValidator(object): + regex = attrib() + flags = attrib() + match_func = attrib() + + def __call__(self, inst, attr, value): + """ + We use a callable class to be able to change the ``__repr__``. + """ + if not self.match_func(value): + raise ValueError( + "'{name}' must match regex {regex!r}" + " ({value!r} doesn't)".format( + name=attr.name, regex=self.regex.pattern, value=value + ), + attr, + self.regex, + value, + ) + + def __repr__(self): + return "".format( + regex=self.regex + ) + + +def matches_re(regex, flags=0, func=None): + r""" + A validator that raises `ValueError` if the initializer is called + with a string that doesn't match *regex*. + + :param str regex: a regex string to match against + :param int flags: flags that will be passed to the underlying re function + (default 0) + :param callable func: which underlying `re` function to call (options + are `re.fullmatch`, `re.search`, `re.match`, default + is ``None`` which means either `re.fullmatch` or an emulation of + it on Python 2). For performance reasons, they won't be used directly + but on a pre-`re.compile`\ ed pattern. + + .. versionadded:: 19.2.0 + """ + fullmatch = getattr(re, "fullmatch", None) + valid_funcs = (fullmatch, None, re.search, re.match) + if func not in valid_funcs: + raise ValueError( + "'func' must be one of %s." + % ( + ", ".join( + sorted( + e and e.__name__ or "None" for e in set(valid_funcs) + ) + ), + ) + ) + + pattern = re.compile(regex, flags) + if func is re.match: + match_func = pattern.match + elif func is re.search: + match_func = pattern.search + else: + if fullmatch: + match_func = pattern.fullmatch + else: + pattern = re.compile(r"(?:{})\Z".format(regex), flags) + match_func = pattern.match + + return _MatchesReValidator(pattern, flags, match_func) + + +@attrs(repr=False, slots=True, hash=True) +class _ProvidesValidator(object): + interface = attrib() + + def __call__(self, inst, attr, value): + """ + We use a callable class to be able to change the ``__repr__``. + """ + if not self.interface.providedBy(value): + raise TypeError( + "'{name}' must provide {interface!r} which {value!r} " + "doesn't.".format( + name=attr.name, interface=self.interface, value=value + ), + attr, + self.interface, + value, + ) + + def __repr__(self): + return "".format( + interface=self.interface + ) + + +def provides(interface): + """ + A validator that raises a `TypeError` if the initializer is called + with an object that does not provide the requested *interface* (checks are + performed using ``interface.providedBy(value)`` (see `zope.interface + `_). + + :param interface: The interface to check for. + :type interface: ``zope.interface.Interface`` + + :raises TypeError: With a human readable error message, the attribute + (of type `attr.Attribute`), the expected interface, and the + value it got. + """ + return _ProvidesValidator(interface) + + +@attrs(repr=False, slots=True, hash=True) +class _OptionalValidator(object): + validator = attrib() + + def __call__(self, inst, attr, value): + if value is None: + return + + self.validator(inst, attr, value) + + def __repr__(self): + return "".format( + what=repr(self.validator) + ) + + +def optional(validator): + """ + A validator that makes an attribute optional. An optional attribute is one + which can be set to ``None`` in addition to satisfying the requirements of + the sub-validator. + + :param validator: A validator (or a list of validators) that is used for + non-``None`` values. + :type validator: callable or `list` of callables. + + .. versionadded:: 15.1.0 + .. versionchanged:: 17.1.0 *validator* can be a list of validators. + """ + if isinstance(validator, list): + return _OptionalValidator(_AndValidator(validator)) + return _OptionalValidator(validator) + + +@attrs(repr=False, slots=True, hash=True) +class _InValidator(object): + options = attrib() + + def __call__(self, inst, attr, value): + try: + in_options = value in self.options + except TypeError: # e.g. `1 in "abc"` + in_options = False + + if not in_options: + raise ValueError( + "'{name}' must be in {options!r} (got {value!r})".format( + name=attr.name, options=self.options, value=value + ) + ) + + def __repr__(self): + return "".format( + options=self.options + ) + + +def in_(options): + """ + A validator that raises a `ValueError` if the initializer is called + with a value that does not belong in the options provided. The check is + performed using ``value in options``. + + :param options: Allowed options. + :type options: list, tuple, `enum.Enum`, ... + + :raises ValueError: With a human readable error message, the attribute (of + type `attr.Attribute`), the expected options, and the value it + got. + + .. versionadded:: 17.1.0 + """ + return _InValidator(options) + + +@attrs(repr=False, slots=False, hash=True) +class _IsCallableValidator(object): + def __call__(self, inst, attr, value): + """ + We use a callable class to be able to change the ``__repr__``. + """ + if not callable(value): + message = ( + "'{name}' must be callable " + "(got {value!r} that is a {actual!r})." + ) + raise NotCallableError( + msg=message.format( + name=attr.name, value=value, actual=value.__class__ + ), + value=value, + ) + + def __repr__(self): + return "" + + +def is_callable(): + """ + A validator that raises a `attr.exceptions.NotCallableError` if the + initializer is called with a value for this particular attribute + that is not callable. + + .. versionadded:: 19.1.0 + + :raises `attr.exceptions.NotCallableError`: With a human readable error + message containing the attribute (`attr.Attribute`) name, + and the value it got. + """ + return _IsCallableValidator() + + +@attrs(repr=False, slots=True, hash=True) +class _DeepIterable(object): + member_validator = attrib(validator=is_callable()) + iterable_validator = attrib( + default=None, validator=optional(is_callable()) + ) + + def __call__(self, inst, attr, value): + """ + We use a callable class to be able to change the ``__repr__``. + """ + if self.iterable_validator is not None: + self.iterable_validator(inst, attr, value) + + for member in value: + self.member_validator(inst, attr, member) + + def __repr__(self): + iterable_identifier = ( + "" + if self.iterable_validator is None + else " {iterable!r}".format(iterable=self.iterable_validator) + ) + return ( + "" + ).format( + iterable_identifier=iterable_identifier, + member=self.member_validator, + ) + + +def deep_iterable(member_validator, iterable_validator=None): + """ + A validator that performs deep validation of an iterable. + + :param member_validator: Validator to apply to iterable members + :param iterable_validator: Validator to apply to iterable itself + (optional) + + .. versionadded:: 19.1.0 + + :raises TypeError: if any sub-validators fail + """ + return _DeepIterable(member_validator, iterable_validator) + + +@attrs(repr=False, slots=True, hash=True) +class _DeepMapping(object): + key_validator = attrib(validator=is_callable()) + value_validator = attrib(validator=is_callable()) + mapping_validator = attrib(default=None, validator=optional(is_callable())) + + def __call__(self, inst, attr, value): + """ + We use a callable class to be able to change the ``__repr__``. + """ + if self.mapping_validator is not None: + self.mapping_validator(inst, attr, value) + + for key in value: + self.key_validator(inst, attr, key) + self.value_validator(inst, attr, value[key]) + + def __repr__(self): + return ( + "" + ).format(key=self.key_validator, value=self.value_validator) + + +def deep_mapping(key_validator, value_validator, mapping_validator=None): + """ + A validator that performs deep validation of a dictionary. + + :param key_validator: Validator to apply to dictionary keys + :param value_validator: Validator to apply to dictionary values + :param mapping_validator: Validator to apply to top-level mapping + attribute (optional) + + .. versionadded:: 19.1.0 + + :raises TypeError: if any sub-validators fail + """ + return _DeepMapping(key_validator, value_validator, mapping_validator) diff --git a/venv/Lib/site-packages/attr/validators.pyi b/venv/Lib/site-packages/attr/validators.pyi new file mode 100644 index 0000000..fe92aac --- /dev/null +++ b/venv/Lib/site-packages/attr/validators.pyi @@ -0,0 +1,68 @@ +from typing import ( + Any, + AnyStr, + Callable, + Container, + Iterable, + List, + Mapping, + Match, + Optional, + Tuple, + Type, + TypeVar, + Union, + overload, +) + +from . import _ValidatorType + + +_T = TypeVar("_T") +_T1 = TypeVar("_T1") +_T2 = TypeVar("_T2") +_T3 = TypeVar("_T3") +_I = TypeVar("_I", bound=Iterable) +_K = TypeVar("_K") +_V = TypeVar("_V") +_M = TypeVar("_M", bound=Mapping) + +# To be more precise on instance_of use some overloads. +# If there are more than 3 items in the tuple then we fall back to Any +@overload +def instance_of(type: Type[_T]) -> _ValidatorType[_T]: ... +@overload +def instance_of(type: Tuple[Type[_T]]) -> _ValidatorType[_T]: ... +@overload +def instance_of( + type: Tuple[Type[_T1], Type[_T2]] +) -> _ValidatorType[Union[_T1, _T2]]: ... +@overload +def instance_of( + type: Tuple[Type[_T1], Type[_T2], Type[_T3]] +) -> _ValidatorType[Union[_T1, _T2, _T3]]: ... +@overload +def instance_of(type: Tuple[type, ...]) -> _ValidatorType[Any]: ... +def provides(interface: Any) -> _ValidatorType[Any]: ... +def optional( + validator: Union[_ValidatorType[_T], List[_ValidatorType[_T]]] +) -> _ValidatorType[Optional[_T]]: ... +def in_(options: Container[_T]) -> _ValidatorType[_T]: ... +def and_(*validators: _ValidatorType[_T]) -> _ValidatorType[_T]: ... +def matches_re( + regex: AnyStr, + flags: int = ..., + func: Optional[ + Callable[[AnyStr, AnyStr, int], Optional[Match[AnyStr]]] + ] = ..., +) -> _ValidatorType[AnyStr]: ... +def deep_iterable( + member_validator: _ValidatorType[_T], + iterable_validator: Optional[_ValidatorType[_I]] = ..., +) -> _ValidatorType[_I]: ... +def deep_mapping( + key_validator: _ValidatorType[_K], + value_validator: _ValidatorType[_V], + mapping_validator: Optional[_ValidatorType[_M]] = ..., +) -> _ValidatorType[_M]: ... +def is_callable() -> _ValidatorType[_T]: ... diff --git a/venv/Lib/site-packages/attrs-21.2.0.dist-info/AUTHORS.rst b/venv/Lib/site-packages/attrs-21.2.0.dist-info/AUTHORS.rst new file mode 100644 index 0000000..f14ef6c --- /dev/null +++ b/venv/Lib/site-packages/attrs-21.2.0.dist-info/AUTHORS.rst @@ -0,0 +1,11 @@ +Credits +======= + +``attrs`` is written and maintained by `Hynek Schlawack `_. + +The development is kindly supported by `Variomedia AG `_. + +A full list of contributors can be found in `GitHub's overview `_. + +It’s the spiritual successor of `characteristic `_ and aspires to fix some of it clunkiness and unfortunate decisions. +Both were inspired by Twisted’s `FancyEqMixin `_ but both are implemented using class decorators because `subclassing is bad for you `_, m’kay? diff --git a/venv/Lib/site-packages/attrs-21.2.0.dist-info/INSTALLER b/venv/Lib/site-packages/attrs-21.2.0.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/venv/Lib/site-packages/attrs-21.2.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/Lib/site-packages/attrs-21.2.0.dist-info/LICENSE b/venv/Lib/site-packages/attrs-21.2.0.dist-info/LICENSE new file mode 100644 index 0000000..7ae3df9 --- /dev/null +++ b/venv/Lib/site-packages/attrs-21.2.0.dist-info/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015 Hynek Schlawack + +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/attrs-21.2.0.dist-info/METADATA b/venv/Lib/site-packages/attrs-21.2.0.dist-info/METADATA new file mode 100644 index 0000000..ceca5b9 --- /dev/null +++ b/venv/Lib/site-packages/attrs-21.2.0.dist-info/METADATA @@ -0,0 +1,211 @@ +Metadata-Version: 2.1 +Name: attrs +Version: 21.2.0 +Summary: Classes Without Boilerplate +Home-page: https://www.attrs.org/ +Author: Hynek Schlawack +Author-email: hs@ox.cx +Maintainer: Hynek Schlawack +Maintainer-email: hs@ox.cx +License: MIT +Project-URL: Documentation, https://www.attrs.org/ +Project-URL: Changelog, https://www.attrs.org/en/stable/changelog.html +Project-URL: Bug Tracker, https://github.com/python-attrs/attrs/issues +Project-URL: Source Code, https://github.com/python-attrs/attrs +Project-URL: Funding, https://github.com/sponsors/hynek +Project-URL: Tidelift, https://tidelift.com/subscription/pkg/pypi-attrs?utm_source=pypi-attrs&utm_medium=pypi +Project-URL: Ko-fi, https://ko-fi.com/the_hynek +Keywords: class,attribute,boilerplate +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: Natural Language :: English +Classifier: License :: OSI Approved :: MIT License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.* +Description-Content-Type: text/x-rst +Provides-Extra: dev +Requires-Dist: coverage[toml] (>=5.0.2) ; extra == 'dev' +Requires-Dist: hypothesis ; extra == 'dev' +Requires-Dist: pympler ; extra == 'dev' +Requires-Dist: pytest (>=4.3.0) ; extra == 'dev' +Requires-Dist: six ; extra == 'dev' +Requires-Dist: mypy ; extra == 'dev' +Requires-Dist: pytest-mypy-plugins ; extra == 'dev' +Requires-Dist: zope.interface ; extra == 'dev' +Requires-Dist: furo ; extra == 'dev' +Requires-Dist: sphinx ; extra == 'dev' +Requires-Dist: sphinx-notfound-page ; extra == 'dev' +Requires-Dist: pre-commit ; extra == 'dev' +Provides-Extra: docs +Requires-Dist: furo ; extra == 'docs' +Requires-Dist: sphinx ; extra == 'docs' +Requires-Dist: zope.interface ; extra == 'docs' +Requires-Dist: sphinx-notfound-page ; extra == 'docs' +Provides-Extra: tests +Requires-Dist: coverage[toml] (>=5.0.2) ; extra == 'tests' +Requires-Dist: hypothesis ; extra == 'tests' +Requires-Dist: pympler ; extra == 'tests' +Requires-Dist: pytest (>=4.3.0) ; extra == 'tests' +Requires-Dist: six ; extra == 'tests' +Requires-Dist: mypy ; extra == 'tests' +Requires-Dist: pytest-mypy-plugins ; extra == 'tests' +Requires-Dist: zope.interface ; extra == 'tests' +Provides-Extra: tests_no_zope +Requires-Dist: coverage[toml] (>=5.0.2) ; extra == 'tests_no_zope' +Requires-Dist: hypothesis ; extra == 'tests_no_zope' +Requires-Dist: pympler ; extra == 'tests_no_zope' +Requires-Dist: pytest (>=4.3.0) ; extra == 'tests_no_zope' +Requires-Dist: six ; extra == 'tests_no_zope' +Requires-Dist: mypy ; extra == 'tests_no_zope' +Requires-Dist: pytest-mypy-plugins ; extra == 'tests_no_zope' + +====================================== +``attrs``: Classes Without Boilerplate +====================================== + + +``attrs`` is the Python package that will bring back the **joy** of **writing classes** by relieving you from the drudgery of implementing object protocols (aka `dunder `_ methods). +`Trusted by NASA `_ for Mars missions since 2020! + +Its main goal is to help you to write **concise** and **correct** software without slowing down your code. + +.. teaser-end + +For that, it gives you a class decorator and a way to declaratively define the attributes on that class: + +.. -code-begin- + +.. code-block:: pycon + + >>> import attr + + >>> @attr.s + ... class SomeClass(object): + ... a_number = attr.ib(default=42) + ... list_of_numbers = attr.ib(factory=list) + ... + ... def hard_math(self, another_number): + ... return self.a_number + sum(self.list_of_numbers) * another_number + + + >>> sc = SomeClass(1, [1, 2, 3]) + >>> sc + SomeClass(a_number=1, list_of_numbers=[1, 2, 3]) + + >>> sc.hard_math(3) + 19 + >>> sc == SomeClass(1, [1, 2, 3]) + True + >>> sc != SomeClass(2, [3, 2, 1]) + True + + >>> attr.asdict(sc) + {'a_number': 1, 'list_of_numbers': [1, 2, 3]} + + >>> SomeClass() + SomeClass(a_number=42, list_of_numbers=[]) + + >>> C = attr.make_class("C", ["a", "b"]) + >>> C("foo", "bar") + C(a='foo', b='bar') + + +After *declaring* your attributes ``attrs`` gives you: + +- a concise and explicit overview of the class's attributes, +- a nice human-readable ``__repr__``, +- a complete set of comparison methods (equality and ordering), +- an initializer, +- and much more, + +*without* writing dull boilerplate code again and again and *without* runtime performance penalties. + +On Python 3.6 and later, you can often even drop the calls to ``attr.ib()`` by using `type annotations `_. + +This gives you the power to use actual classes with actual types in your code instead of confusing ``tuple``\ s or `confusingly behaving `_ ``namedtuple``\ s. +Which in turn encourages you to write *small classes* that do `one thing well `_. +Never again violate the `single responsibility principle `_ just because implementing ``__init__`` et al is a painful drag. + + +.. -getting-help- + +Getting Help +============ + +Please use the ``python-attrs`` tag on `StackOverflow `_ to get help. + +Answering questions of your fellow developers is also a great way to help the project! + + +.. -project-information- + +Project Information +=================== + +``attrs`` is released under the `MIT `_ license, +its documentation lives at `Read the Docs `_, +the code on `GitHub `_, +and the latest release on `PyPI `_. +It’s rigorously tested on Python 2.7, 3.5+, and PyPy. + +We collect information on **third-party extensions** in our `wiki `_. +Feel free to browse and add your own! + +If you'd like to contribute to ``attrs`` you're most welcome and we've written `a little guide `_ to get you started! + + +``attrs`` for Enterprise +------------------------ + +Available as part of the Tidelift Subscription. + +The maintainers of ``attrs`` and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source packages you use to build your applications. +Save time, reduce risk, and improve code health, while paying the maintainers of the exact packages you use. +`Learn more. `_ + + +Release Information +=================== + +21.2.0 (2021-05-07) +------------------- + +Backward-incompatible Changes +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +- We had to revert the recursive feature for ``attr.evolve()`` because it broke some use-cases -- sorry! + `#806 `_ +- Python 3.4 is now blocked using packaging metadata because ``attrs`` can't be imported on it anymore. + To ensure that 3.4 users can keep installing ``attrs`` easily, we will `yank `_ 21.1.0 from PyPI. + This has **no** consequences if you pin ``attrs`` to 21.1.0. + `#807 `_ + +`Full changelog `_. + +Credits +======= + +``attrs`` is written and maintained by `Hynek Schlawack `_. + +The development is kindly supported by `Variomedia AG `_. + +A full list of contributors can be found in `GitHub's overview `_. + +It’s the spiritual successor of `characteristic `_ and aspires to fix some of it clunkiness and unfortunate decisions. +Both were inspired by Twisted’s `FancyEqMixin `_ but both are implemented using class decorators because `subclassing is bad for you `_, m’kay? + + diff --git a/venv/Lib/site-packages/attrs-21.2.0.dist-info/RECORD b/venv/Lib/site-packages/attrs-21.2.0.dist-info/RECORD new file mode 100644 index 0000000..0090425 --- /dev/null +++ b/venv/Lib/site-packages/attrs-21.2.0.dist-info/RECORD @@ -0,0 +1,42 @@ +attr/__init__.py,sha256=OlF8DYZfrT1KFU6VKHkW4ia76ntgvvaqBbsNf8j6Woc,1613 +attr/__init__.pyi,sha256=bNx5qLa3MBtgaf9P5OP2uwuv0xbS7HUrEfLlkrAsrr8,14837 +attr/__pycache__/__init__.cpython-39.pyc,, +attr/__pycache__/_cmp.cpython-39.pyc,, +attr/__pycache__/_compat.cpython-39.pyc,, +attr/__pycache__/_config.cpython-39.pyc,, +attr/__pycache__/_funcs.cpython-39.pyc,, +attr/__pycache__/_make.cpython-39.pyc,, +attr/__pycache__/_next_gen.cpython-39.pyc,, +attr/__pycache__/_version_info.cpython-39.pyc,, +attr/__pycache__/converters.cpython-39.pyc,, +attr/__pycache__/exceptions.cpython-39.pyc,, +attr/__pycache__/filters.cpython-39.pyc,, +attr/__pycache__/setters.cpython-39.pyc,, +attr/__pycache__/validators.cpython-39.pyc,, +attr/_cmp.py,sha256=CB01fdAcVk9Uwho7qdhrpK1ss9lilIeKoY-WJ-EaZYA,4133 +attr/_cmp.pyi,sha256=APRWqmFwHtTrapyy-vNKovjF9dA-HPi-AqqidjgvLpQ,318 +attr/_compat.py,sha256=hYZsXQOKJzAIAPPEzo-Y4aF0DMjhCXEp-nr1gSxVVG4,7562 +attr/_config.py,sha256=_KvW0mQdH2PYjHc0YfIUaV_o2pVfM7ziMEYTxwmEhOA,514 +attr/_funcs.py,sha256=azJeF9YIMg3lP2qeQyuGhrrcJkfTjm7OLm2u4MhPTqs,13398 +attr/_make.py,sha256=xrK0rSAYDINJF-yGgb_Qb2DHuEaKRmrs102mkO0LI5c,97743 +attr/_next_gen.py,sha256=aZEIlr2XlPVzJ_SWSNRAEx07jgqbtHWQm3PnaOXTMyw,4072 +attr/_version_info.py,sha256=azMi1lNelb3cJvvYUMXsXVbUANkRzbD5IEiaXVpeVr4,2162 +attr/_version_info.pyi,sha256=x_M3L3WuB7r_ULXAWjx959udKQ4HLB8l-hsc1FDGNvk,209 +attr/converters.py,sha256=mn8pLVYzzl-WvmlNe52HM2ukSkuO4a12mrTaHpQjX9c,3039 +attr/converters.pyi,sha256=L7eN2rEXCNVOkh1hYP-GVbWtyO3e6eKOBvJR-hK_h1M,382 +attr/exceptions.py,sha256=6dC-9b6_nTG066z9sw0TP_Tx4vJaIC5RImMONTkDM6Q,1949 +attr/exceptions.pyi,sha256=Ydjpt9xbNLM8HUEhayegA3c0xIBc75kpRgtiv0qsLCs,540 +attr/filters.py,sha256=weDxwATsa69T_0bPVjiM1fGsciAMQmwhY5G8Jm5BxuI,1098 +attr/filters.pyi,sha256=jUFN1Nqx2x5ayyLLHzsW5hHObjd6RudZjnj-ENAJdWk,216 +attr/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +attr/setters.py,sha256=0ElzHwdVK3dsYcQi2CXkFvhx8fNxUI5OVhw8SWeaKmA,1434 +attr/setters.pyi,sha256=kTxNSnrItMgRpFDyIwvtFd6xYtqnOWwi4UVks4dskRY,574 +attr/validators.py,sha256=6DBx1jt4oZxx1ppvx6JWqm9-UAsYpXC4HTwxJilCeRg,11497 +attr/validators.pyi,sha256=qN6dsUdWh2UkLaX46JJ86lzYlhy4sh8z66fTXgJQO60,1870 +attrs-21.2.0.dist-info/AUTHORS.rst,sha256=wsqCNbGz_mklcJrt54APIZHZpoTIJLkXqEhhn4Nd8hc,752 +attrs-21.2.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +attrs-21.2.0.dist-info/LICENSE,sha256=v2WaKLSSQGAvVrvfSQy-LsUJsVuY-Z17GaUsdA4yeGM,1082 +attrs-21.2.0.dist-info/METADATA,sha256=oaarWZ5r9x96ZwIcBvpmzpyt6ADyZP2QYjYVZrJrrEQ,9097 +attrs-21.2.0.dist-info/RECORD,, +attrs-21.2.0.dist-info/WHEEL,sha256=Z-nyYpwrcSqxfdux5Mbn_DQ525iP7J2DG3JgGvOYyTQ,110 +attrs-21.2.0.dist-info/top_level.txt,sha256=tlRYMddkRlKPqJ96wP2_j9uEsmcNHgD2SbuWd4CzGVU,5 diff --git a/venv/Lib/site-packages/attrs-21.2.0.dist-info/WHEEL b/venv/Lib/site-packages/attrs-21.2.0.dist-info/WHEEL new file mode 100644 index 0000000..01b8fc7 --- /dev/null +++ b/venv/Lib/site-packages/attrs-21.2.0.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.36.2) +Root-Is-Purelib: true +Tag: py2-none-any +Tag: py3-none-any + diff --git a/venv/Lib/site-packages/attrs-21.2.0.dist-info/top_level.txt b/venv/Lib/site-packages/attrs-21.2.0.dist-info/top_level.txt new file mode 100644 index 0000000..66a062d --- /dev/null +++ b/venv/Lib/site-packages/attrs-21.2.0.dist-info/top_level.txt @@ -0,0 +1 @@ +attr diff --git a/venv/Lib/site-packages/blinker-1.4-py3.9.egg-info/PKG-INFO b/venv/Lib/site-packages/blinker-1.4-py3.9.egg-info/PKG-INFO new file mode 100644 index 0000000..98d32f5 --- /dev/null +++ b/venv/Lib/site-packages/blinker-1.4-py3.9.egg-info/PKG-INFO @@ -0,0 +1,105 @@ +Metadata-Version: 2.1 +Name: blinker +Version: 1.4 +Summary: Fast, simple object-to-object and broadcast signaling +Home-page: http://pythonhosted.org/blinker/ +Author: Jason Kirtland +Author-email: jek@discorporate.us +License: MIT License +Keywords: signal emit events broadcast +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: MIT License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 2.4 +Classifier: Programming Language :: Python :: 2.5 +Classifier: Programming Language :: Python :: 2.6 +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.0 +Classifier: Programming Language :: Python :: 3.1 +Classifier: Programming Language :: Python :: 3.2 +Classifier: Programming Language :: Python :: 3.3 +Classifier: Programming Language :: Python :: 3.4 +Classifier: Topic :: Software Development :: Libraries +Classifier: Topic :: Utilities +License-File: LICENSE +License-File: AUTHORS + +[![Build Status](https://travis-ci.org/jek/blinker.svg?branch=master)](https://travis-ci.org/jek/blinker) + + +# Blinker + +Blinker provides a fast dispatching system that allows any number of +interested parties to subscribe to events, or "signals". + +Signal receivers can subscribe to specific senders or receive signals +sent by any sender. + + >>> from blinker import signal + >>> started = signal('round-started') + >>> def each(round): + ... print "Round %s!" % round + ... + >>> started.connect(each) + + >>> def round_two(round): + ... print "This is round two." + ... + >>> started.connect(round_two, sender=2) + + >>> for round in range(1, 4): + ... started.send(round) + ... + Round 1! + Round 2! + This is round two. + Round 3! + +See the [Blinker documentation](https://pythonhosted.org/blinker/) for more information. + +## Requirements + +Blinker requires Python 2.4 or higher, Python 3.0 or higher, or Jython 2.5 or higher. + +## Changelog Summary + +1.3 (July 3, 2013) + + - The global signal stash behind blinker.signal() is now backed by a + regular name-to-Signal dictionary. Previously, weak references were + held in the mapping and ephemeral usage in code like + ``signal('foo').connect(...)`` could have surprising program behavior + depending on import order of modules. + - blinker.Namespace is now built on a regular dict. Use + blinker.WeakNamespace for the older, weak-referencing behavior. + - Signal.connect('text-sender') uses an alternate hashing strategy to + avoid sharp edges in text identity. + +1.2 (October 26, 2011) + + - Added Signal.receiver_connected and Signal.receiver_disconnected + per-Signal signals. + - Deprecated the global 'receiver_connected' signal. + - Verified Python 3.2 support (no changes needed!) + +1.1 (July 21, 2010) + + - Added ``@signal.connect_via(sender)`` decorator + - Added ``signal.connected_to`` shorthand name for the + ``temporarily_connected_to`` context manager. + +1.0 (March 28, 2010) + + - Python 3.x compatibility + +0.9 (February 26, 2010) + + - Sphinx docs, project website + - Added ``with a_signal.temporarily_connected_to(receiver): ...`` support + + diff --git a/venv/Lib/site-packages/blinker-1.4-py3.9.egg-info/SOURCES.txt b/venv/Lib/site-packages/blinker-1.4-py3.9.egg-info/SOURCES.txt new file mode 100644 index 0000000..8a2dace --- /dev/null +++ b/venv/Lib/site-packages/blinker-1.4-py3.9.egg-info/SOURCES.txt @@ -0,0 +1,49 @@ +AUTHORS +CHANGES +LICENSE +MANIFEST.in +README.md +setup.cfg +setup.py +blinker/__init__.py +blinker/_saferef.py +blinker/_utilities.py +blinker/base.py +blinker.egg-info/PKG-INFO +blinker.egg-info/SOURCES.txt +blinker.egg-info/dependency_links.txt +blinker.egg-info/top_level.txt +docs/html/genindex.html +docs/html/index.html +docs/html/objects.inv +docs/html/search.html +docs/html/searchindex.js +docs/html/_sources/index.txt +docs/html/_static/basic.css +docs/html/_static/blinker-named.png +docs/html/_static/blinker64.png +docs/html/_static/comment-bright.png +docs/html/_static/comment-close.png +docs/html/_static/comment.png +docs/html/_static/doctools.js +docs/html/_static/down-pressed.png +docs/html/_static/down.png +docs/html/_static/file.png +docs/html/_static/flasky.css +docs/html/_static/jquery.js +docs/html/_static/minus.png +docs/html/_static/plus.png +docs/html/_static/pygments.css +docs/html/_static/searchtools.js +docs/html/_static/underscore.js +docs/html/_static/up-pressed.png +docs/html/_static/up.png +docs/html/_static/websupport.js +docs/source/conf.py +docs/source/index.rst +docs/source/_themes/flask_theme_support.py +docs/text/index.txt +tests/test_context.py +tests/test_saferef.py +tests/test_signals.py +tests/test_utilities.py \ No newline at end of file diff --git a/venv/Lib/site-packages/blinker-1.4-py3.9.egg-info/dependency_links.txt b/venv/Lib/site-packages/blinker-1.4-py3.9.egg-info/dependency_links.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/venv/Lib/site-packages/blinker-1.4-py3.9.egg-info/dependency_links.txt @@ -0,0 +1 @@ + diff --git a/venv/Lib/site-packages/blinker-1.4-py3.9.egg-info/installed-files.txt b/venv/Lib/site-packages/blinker-1.4-py3.9.egg-info/installed-files.txt new file mode 100644 index 0000000..562e16a --- /dev/null +++ b/venv/Lib/site-packages/blinker-1.4-py3.9.egg-info/installed-files.txt @@ -0,0 +1,12 @@ +..\blinker\__init__.py +..\blinker\__pycache__\__init__.cpython-39.pyc +..\blinker\__pycache__\_saferef.cpython-39.pyc +..\blinker\__pycache__\_utilities.cpython-39.pyc +..\blinker\__pycache__\base.cpython-39.pyc +..\blinker\_saferef.py +..\blinker\_utilities.py +..\blinker\base.py +PKG-INFO +SOURCES.txt +dependency_links.txt +top_level.txt diff --git a/venv/Lib/site-packages/blinker-1.4-py3.9.egg-info/top_level.txt b/venv/Lib/site-packages/blinker-1.4-py3.9.egg-info/top_level.txt new file mode 100644 index 0000000..1ff4ca5 --- /dev/null +++ b/venv/Lib/site-packages/blinker-1.4-py3.9.egg-info/top_level.txt @@ -0,0 +1 @@ +blinker diff --git a/venv/Lib/site-packages/blinker/__init__.py b/venv/Lib/site-packages/blinker/__init__.py new file mode 100644 index 0000000..3ea239c --- /dev/null +++ b/venv/Lib/site-packages/blinker/__init__.py @@ -0,0 +1,22 @@ +from blinker.base import ( + ANY, + NamedSignal, + Namespace, + Signal, + WeakNamespace, + receiver_connected, + signal, +) + +__all__ = [ + 'ANY', + 'NamedSignal', + 'Namespace', + 'Signal', + 'WeakNamespace', + 'receiver_connected', + 'signal', + ] + + +__version__ = '1.4' diff --git a/venv/Lib/site-packages/blinker/_saferef.py b/venv/Lib/site-packages/blinker/_saferef.py new file mode 100644 index 0000000..269e362 --- /dev/null +++ b/venv/Lib/site-packages/blinker/_saferef.py @@ -0,0 +1,234 @@ +# extracted from Louie, http://pylouie.org/ +# updated for Python 3 +# +# Copyright (c) 2006 Patrick K. O'Brien, Mike C. Fletcher, +# Matthew R. Scott +# +# 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 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 +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +"""Refactored 'safe reference from dispatcher.py""" + +import operator +import sys +import traceback +import weakref + + +try: + callable +except NameError: + def callable(object): + return hasattr(object, '__call__') + + +if sys.version_info < (3,): + get_self = operator.attrgetter('im_self') + get_func = operator.attrgetter('im_func') +else: + get_self = operator.attrgetter('__self__') + get_func = operator.attrgetter('__func__') + + +def safe_ref(target, on_delete=None): + """Return a *safe* weak reference to a callable target. + + - ``target``: The object to be weakly referenced, if it's a bound + method reference, will create a BoundMethodWeakref, otherwise + creates a simple weakref. + + - ``on_delete``: If provided, will have a hard reference stored to + the callable to be called after the safe reference goes out of + scope with the reference object, (either a weakref or a + BoundMethodWeakref) as argument. + """ + try: + im_self = get_self(target) + except AttributeError: + if callable(on_delete): + return weakref.ref(target, on_delete) + else: + return weakref.ref(target) + else: + if im_self is not None: + # Turn a bound method into a BoundMethodWeakref instance. + # Keep track of these instances for lookup by disconnect(). + assert hasattr(target, 'im_func') or hasattr(target, '__func__'), ( + "safe_ref target %r has im_self, but no im_func, " + "don't know how to create reference" % target) + reference = BoundMethodWeakref(target=target, on_delete=on_delete) + return reference + + +class BoundMethodWeakref(object): + """'Safe' and reusable weak references to instance methods. + + BoundMethodWeakref objects provide a mechanism for referencing a + bound method without requiring that the method object itself + (which is normally a transient object) is kept alive. Instead, + the BoundMethodWeakref object keeps weak references to both the + object and the function which together define the instance method. + + Attributes: + + - ``key``: The identity key for the reference, calculated by the + class's calculate_key method applied to the target instance method. + + - ``deletion_methods``: Sequence of callable objects taking single + argument, a reference to this object which will be called when + *either* the target object or target function is garbage + collected (i.e. when this object becomes invalid). These are + specified as the on_delete parameters of safe_ref calls. + + - ``weak_self``: Weak reference to the target object. + + - ``weak_func``: Weak reference to the target function. + + Class Attributes: + + - ``_all_instances``: Class attribute pointing to all live + BoundMethodWeakref objects indexed by the class's + calculate_key(target) method applied to the target objects. + This weak value dictionary is used to short-circuit creation so + that multiple references to the same (object, function) pair + produce the same BoundMethodWeakref instance. + """ + + _all_instances = weakref.WeakValueDictionary() + + def __new__(cls, target, on_delete=None, *arguments, **named): + """Create new instance or return current instance. + + Basically this method of construction allows us to + short-circuit creation of references to already- referenced + instance methods. The key corresponding to the target is + calculated, and if there is already an existing reference, + that is returned, with its deletion_methods attribute updated. + Otherwise the new instance is created and registered in the + table of already-referenced methods. + """ + key = cls.calculate_key(target) + current = cls._all_instances.get(key) + if current is not None: + current.deletion_methods.append(on_delete) + return current + else: + base = super(BoundMethodWeakref, cls).__new__(cls) + cls._all_instances[key] = base + base.__init__(target, on_delete, *arguments, **named) + return base + + def __init__(self, target, on_delete=None): + """Return a weak-reference-like instance for a bound method. + + - ``target``: The instance-method target for the weak reference, + must have im_self and im_func attributes and be + reconstructable via the following, which is true of built-in + instance methods:: + + target.im_func.__get__( target.im_self ) + + - ``on_delete``: Optional callback which will be called when + this weak reference ceases to be valid (i.e. either the + object or the function is garbage collected). Should take a + single argument, which will be passed a pointer to this + object. + """ + def remove(weak, self=self): + """Set self.isDead to True when method or instance is destroyed.""" + methods = self.deletion_methods[:] + del self.deletion_methods[:] + try: + del self.__class__._all_instances[self.key] + except KeyError: + pass + for function in methods: + try: + if callable(function): + function(self) + except Exception: + try: + traceback.print_exc() + except AttributeError: + e = sys.exc_info()[1] + print ('Exception during saferef %s ' + 'cleanup function %s: %s' % (self, function, e)) + self.deletion_methods = [on_delete] + self.key = self.calculate_key(target) + im_self = get_self(target) + im_func = get_func(target) + self.weak_self = weakref.ref(im_self, remove) + self.weak_func = weakref.ref(im_func, remove) + self.self_name = str(im_self) + self.func_name = str(im_func.__name__) + + def calculate_key(cls, target): + """Calculate the reference key for this reference. + + Currently this is a two-tuple of the id()'s of the target + object and the target function respectively. + """ + return (id(get_self(target)), id(get_func(target))) + calculate_key = classmethod(calculate_key) + + def __str__(self): + """Give a friendly representation of the object.""" + return "%s(%s.%s)" % ( + self.__class__.__name__, + self.self_name, + self.func_name, + ) + + __repr__ = __str__ + + def __nonzero__(self): + """Whether we are still a valid reference.""" + return self() is not None + + def __cmp__(self, other): + """Compare with another reference.""" + if not isinstance(other, self.__class__): + return cmp(self.__class__, type(other)) + return cmp(self.key, other.key) + + def __call__(self): + """Return a strong reference to the bound method. + + If the target cannot be retrieved, then will return None, + otherwise returns a bound instance method for our object and + function. + + Note: You may call this method any number of times, as it does + not invalidate the reference. + """ + target = self.weak_self() + if target is not None: + function = self.weak_func() + if function is not None: + return function.__get__(target) + return None diff --git a/venv/Lib/site-packages/blinker/_utilities.py b/venv/Lib/site-packages/blinker/_utilities.py new file mode 100644 index 0000000..056270d --- /dev/null +++ b/venv/Lib/site-packages/blinker/_utilities.py @@ -0,0 +1,163 @@ +from weakref import ref + +from blinker._saferef import BoundMethodWeakref + + +try: + callable +except NameError: + def callable(object): + return hasattr(object, '__call__') + + +try: + from collections import defaultdict +except: + class defaultdict(dict): + + def __init__(self, default_factory=None, *a, **kw): + if (default_factory is not None and + not hasattr(default_factory, '__call__')): + raise TypeError('first argument must be callable') + dict.__init__(self, *a, **kw) + self.default_factory = default_factory + + def __getitem__(self, key): + try: + return dict.__getitem__(self, key) + except KeyError: + return self.__missing__(key) + + def __missing__(self, key): + if self.default_factory is None: + raise KeyError(key) + self[key] = value = self.default_factory() + return value + + def __reduce__(self): + if self.default_factory is None: + args = tuple() + else: + args = self.default_factory, + return type(self), args, None, None, self.items() + + def copy(self): + return self.__copy__() + + def __copy__(self): + return type(self)(self.default_factory, self) + + def __deepcopy__(self, memo): + import copy + return type(self)(self.default_factory, + copy.deepcopy(self.items())) + + def __repr__(self): + return 'defaultdict(%s, %s)' % (self.default_factory, + dict.__repr__(self)) + + +try: + from contextlib import contextmanager +except ImportError: + def contextmanager(fn): + def oops(*args, **kw): + raise RuntimeError("Python 2.5 or above is required to use " + "context managers.") + oops.__name__ = fn.__name__ + return oops + +class _symbol(object): + + def __init__(self, name): + """Construct a new named symbol.""" + self.__name__ = self.name = name + + def __reduce__(self): + return symbol, (self.name,) + + def __repr__(self): + return self.name +_symbol.__name__ = 'symbol' + + +class symbol(object): + """A constant symbol. + + >>> symbol('foo') is symbol('foo') + True + >>> symbol('foo') + foo + + A slight refinement of the MAGICCOOKIE=object() pattern. The primary + advantage of symbol() is its repr(). They are also singletons. + + Repeated calls of symbol('name') will all return the same instance. + + """ + symbols = {} + + def __new__(cls, name): + try: + return cls.symbols[name] + except KeyError: + return cls.symbols.setdefault(name, _symbol(name)) + + +try: + text = (str, unicode) +except NameError: + text = str + + +def hashable_identity(obj): + if hasattr(obj, '__func__'): + return (id(obj.__func__), id(obj.__self__)) + elif hasattr(obj, 'im_func'): + return (id(obj.im_func), id(obj.im_self)) + elif isinstance(obj, text): + return obj + else: + return id(obj) + + +WeakTypes = (ref, BoundMethodWeakref) + + +class annotatable_weakref(ref): + """A weakref.ref that supports custom instance attributes.""" + + +def reference(object, callback=None, **annotations): + """Return an annotated weak ref.""" + if callable(object): + weak = callable_reference(object, callback) + else: + weak = annotatable_weakref(object, callback) + for key, value in annotations.items(): + setattr(weak, key, value) + return weak + + +def callable_reference(object, callback=None): + """Return an annotated weak ref, supporting bound instance methods.""" + if hasattr(object, 'im_self') and object.im_self is not None: + return BoundMethodWeakref(target=object, on_delete=callback) + elif hasattr(object, '__self__') and object.__self__ is not None: + return BoundMethodWeakref(target=object, on_delete=callback) + return annotatable_weakref(object, callback) + + +class lazy_property(object): + """A @property that is only evaluated once.""" + + def __init__(self, deferred): + self._deferred = deferred + self.__doc__ = deferred.__doc__ + + def __get__(self, obj, cls): + if obj is None: + return self + value = self._deferred(obj) + setattr(obj, self._deferred.__name__, value) + return value diff --git a/venv/Lib/site-packages/blinker/base.py b/venv/Lib/site-packages/blinker/base.py new file mode 100644 index 0000000..cc5880e --- /dev/null +++ b/venv/Lib/site-packages/blinker/base.py @@ -0,0 +1,455 @@ +# -*- coding: utf-8; fill-column: 76 -*- +"""Signals and events. + +A small implementation of signals, inspired by a snippet of Django signal +API client code seen in a blog post. Signals are first-class objects and +each manages its own receivers and message emission. + +The :func:`signal` function provides singleton behavior for named signals. + +""" +from warnings import warn +from weakref import WeakValueDictionary + +from blinker._utilities import ( + WeakTypes, + contextmanager, + defaultdict, + hashable_identity, + lazy_property, + reference, + symbol, + ) + + +ANY = symbol('ANY') +ANY.__doc__ = 'Token for "any sender".' +ANY_ID = 0 + + +class Signal(object): + """A notification emitter.""" + + #: An :obj:`ANY` convenience synonym, allows ``Signal.ANY`` + #: without an additional import. + ANY = ANY + + @lazy_property + def receiver_connected(self): + """Emitted after each :meth:`connect`. + + The signal sender is the signal instance, and the :meth:`connect` + arguments are passed through: *receiver*, *sender*, and *weak*. + + .. versionadded:: 1.2 + + """ + return Signal(doc="Emitted after a receiver connects.") + + @lazy_property + def receiver_disconnected(self): + """Emitted after :meth:`disconnect`. + + The sender is the signal instance, and the :meth:`disconnect` arguments + are passed through: *receiver* and *sender*. + + Note, this signal is emitted **only** when :meth:`disconnect` is + called explicitly. + + The disconnect signal can not be emitted by an automatic disconnect + (due to a weakly referenced receiver or sender going out of scope), + as the receiver and/or sender instances are no longer available for + use at the time this signal would be emitted. + + An alternative approach is available by subscribing to + :attr:`receiver_connected` and setting up a custom weakref cleanup + callback on weak receivers and senders. + + .. versionadded:: 1.2 + + """ + return Signal(doc="Emitted after a receiver disconnects.") + + def __init__(self, doc=None): + """ + :param doc: optional. If provided, will be assigned to the signal's + __doc__ attribute. + + """ + if doc: + self.__doc__ = doc + #: A mapping of connected receivers. + #: + #: The values of this mapping are not meaningful outside of the + #: internal :class:`Signal` implementation, however the boolean value + #: of the mapping is useful as an extremely efficient check to see if + #: any receivers are connected to the signal. + self.receivers = {} + self._by_receiver = defaultdict(set) + self._by_sender = defaultdict(set) + self._weak_senders = {} + + def connect(self, receiver, sender=ANY, weak=True): + """Connect *receiver* to signal events sent by *sender*. + + :param receiver: A callable. Will be invoked by :meth:`send` with + `sender=` as a single positional argument and any \*\*kwargs that + were provided to a call to :meth:`send`. + + :param sender: Any object or :obj:`ANY`, defaults to ``ANY``. + Restricts notifications delivered to *receiver* to only those + :meth:`send` emissions sent by *sender*. If ``ANY``, the receiver + will always be notified. A *receiver* may be connected to + multiple *sender* values on the same Signal through multiple calls + to :meth:`connect`. + + :param weak: If true, the Signal will hold a weakref to *receiver* + and automatically disconnect when *receiver* goes out of scope or + is garbage collected. Defaults to True. + + """ + receiver_id = hashable_identity(receiver) + if weak: + receiver_ref = reference(receiver, self._cleanup_receiver) + receiver_ref.receiver_id = receiver_id + else: + receiver_ref = receiver + if sender is ANY: + sender_id = ANY_ID + else: + sender_id = hashable_identity(sender) + + self.receivers.setdefault(receiver_id, receiver_ref) + self._by_sender[sender_id].add(receiver_id) + self._by_receiver[receiver_id].add(sender_id) + del receiver_ref + + if sender is not ANY and sender_id not in self._weak_senders: + # wire together a cleanup for weakref-able senders + try: + sender_ref = reference(sender, self._cleanup_sender) + sender_ref.sender_id = sender_id + except TypeError: + pass + else: + self._weak_senders.setdefault(sender_id, sender_ref) + del sender_ref + + # broadcast this connection. if receivers raise, disconnect. + if ('receiver_connected' in self.__dict__ and + self.receiver_connected.receivers): + try: + self.receiver_connected.send(self, + receiver=receiver, + sender=sender, + weak=weak) + except: + self.disconnect(receiver, sender) + raise + if receiver_connected.receivers and self is not receiver_connected: + try: + receiver_connected.send(self, + receiver_arg=receiver, + sender_arg=sender, + weak_arg=weak) + except: + self.disconnect(receiver, sender) + raise + return receiver + + def connect_via(self, sender, weak=False): + """Connect the decorated function as a receiver for *sender*. + + :param sender: Any object or :obj:`ANY`. The decorated function + will only receive :meth:`send` emissions sent by *sender*. If + ``ANY``, the receiver will always be notified. A function may be + decorated multiple times with differing *sender* values. + + :param weak: If true, the Signal will hold a weakref to the + decorated function and automatically disconnect when *receiver* + goes out of scope or is garbage collected. Unlike + :meth:`connect`, this defaults to False. + + The decorated function will be invoked by :meth:`send` with + `sender=` as a single positional argument and any \*\*kwargs that + were provided to the call to :meth:`send`. + + + .. versionadded:: 1.1 + + """ + def decorator(fn): + self.connect(fn, sender, weak) + return fn + return decorator + + @contextmanager + def connected_to(self, receiver, sender=ANY): + """Execute a block with the signal temporarily connected to *receiver*. + + :param receiver: a receiver callable + :param sender: optional, a sender to filter on + + This is a context manager for use in the ``with`` statement. It can + be useful in unit tests. *receiver* is connected to the signal for + the duration of the ``with`` block, and will be disconnected + automatically when exiting the block: + + .. testsetup:: + + from __future__ import with_statement + from blinker import Signal + on_ready = Signal() + receiver = lambda sender: None + + .. testcode:: + + with on_ready.connected_to(receiver): + # do stuff + on_ready.send(123) + + .. versionadded:: 1.1 + + """ + self.connect(receiver, sender=sender, weak=False) + try: + yield None + except: + self.disconnect(receiver) + raise + else: + self.disconnect(receiver) + + def temporarily_connected_to(self, receiver, sender=ANY): + """An alias for :meth:`connected_to`. + + :param receiver: a receiver callable + :param sender: optional, a sender to filter on + + .. versionadded:: 0.9 + + .. versionchanged:: 1.1 + Renamed to :meth:`connected_to`. ``temporarily_connected_to`` was + deprecated in 1.2 and will be removed in a subsequent version. + + """ + warn("temporarily_connected_to is deprecated; " + "use connected_to instead.", + DeprecationWarning) + return self.connected_to(receiver, sender) + + def send(self, *sender, **kwargs): + """Emit this signal on behalf of *sender*, passing on \*\*kwargs. + + Returns a list of 2-tuples, pairing receivers with their return + value. The ordering of receiver notification is undefined. + + :param \*sender: Any object or ``None``. If omitted, synonymous + with ``None``. Only accepts one positional argument. + + :param \*\*kwargs: Data to be sent to receivers. + + """ + # Using '*sender' rather than 'sender=None' allows 'sender' to be + # used as a keyword argument- i.e. it's an invisible name in the + # function signature. + if len(sender) == 0: + sender = None + elif len(sender) > 1: + raise TypeError('send() accepts only one positional argument, ' + '%s given' % len(sender)) + else: + sender = sender[0] + if not self.receivers: + return [] + else: + return [(receiver, receiver(sender, **kwargs)) + for receiver in self.receivers_for(sender)] + + def has_receivers_for(self, sender): + """True if there is probably a receiver for *sender*. + + Performs an optimistic check only. Does not guarantee that all + weakly referenced receivers are still alive. See + :meth:`receivers_for` for a stronger search. + + """ + if not self.receivers: + return False + if self._by_sender[ANY_ID]: + return True + if sender is ANY: + return False + return hashable_identity(sender) in self._by_sender + + def receivers_for(self, sender): + """Iterate all live receivers listening for *sender*.""" + # TODO: test receivers_for(ANY) + if self.receivers: + sender_id = hashable_identity(sender) + if sender_id in self._by_sender: + ids = (self._by_sender[ANY_ID] | + self._by_sender[sender_id]) + else: + ids = self._by_sender[ANY_ID].copy() + for receiver_id in ids: + receiver = self.receivers.get(receiver_id) + if receiver is None: + continue + if isinstance(receiver, WeakTypes): + strong = receiver() + if strong is None: + self._disconnect(receiver_id, ANY_ID) + continue + receiver = strong + yield receiver + + def disconnect(self, receiver, sender=ANY): + """Disconnect *receiver* from this signal's events. + + :param receiver: a previously :meth:`connected` callable + + :param sender: a specific sender to disconnect from, or :obj:`ANY` + to disconnect from all senders. Defaults to ``ANY``. + + """ + if sender is ANY: + sender_id = ANY_ID + else: + sender_id = hashable_identity(sender) + receiver_id = hashable_identity(receiver) + self._disconnect(receiver_id, sender_id) + + if ('receiver_disconnected' in self.__dict__ and + self.receiver_disconnected.receivers): + self.receiver_disconnected.send(self, + receiver=receiver, + sender=sender) + + def _disconnect(self, receiver_id, sender_id): + if sender_id == ANY_ID: + if self._by_receiver.pop(receiver_id, False): + for bucket in self._by_sender.values(): + bucket.discard(receiver_id) + self.receivers.pop(receiver_id, None) + else: + self._by_sender[sender_id].discard(receiver_id) + self._by_receiver[receiver_id].discard(sender_id) + + def _cleanup_receiver(self, receiver_ref): + """Disconnect a receiver from all senders.""" + self._disconnect(receiver_ref.receiver_id, ANY_ID) + + def _cleanup_sender(self, sender_ref): + """Disconnect all receivers from a sender.""" + sender_id = sender_ref.sender_id + assert sender_id != ANY_ID + self._weak_senders.pop(sender_id, None) + for receiver_id in self._by_sender.pop(sender_id, ()): + self._by_receiver[receiver_id].discard(sender_id) + + def _cleanup_bookkeeping(self): + """Prune unused sender/receiver bookeeping. Not threadsafe. + + Connecting & disconnecting leave behind a small amount of bookeeping + for the receiver and sender values. Typical workloads using Blinker, + for example in most web apps, Flask, CLI scripts, etc., are not + adversely affected by this bookkeeping. + + With a long-running Python process performing dynamic signal routing + with high volume- e.g. connecting to function closures, "senders" are + all unique object instances, and doing all of this over and over- you + may see memory usage will grow due to extraneous bookeeping. (An empty + set() for each stale sender/receiver pair.) + + This method will prune that bookeeping away, with the caveat that such + pruning is not threadsafe. The risk is that cleanup of a fully + disconnected receiver/sender pair occurs while another thread is + connecting that same pair. If you are in the highly dynamic, unique + receiver/sender situation that has lead you to this method, that + failure mode is perhaps not a big deal for you. + """ + for mapping in (self._by_sender, self._by_receiver): + for _id, bucket in list(mapping.items()): + if not bucket: + mapping.pop(_id, None) + + def _clear_state(self): + """Throw away all signal state. Useful for unit tests.""" + self._weak_senders.clear() + self.receivers.clear() + self._by_sender.clear() + self._by_receiver.clear() + + +receiver_connected = Signal("""\ +Sent by a :class:`Signal` after a receiver connects. + +:argument: the Signal that was connected to +:keyword receiver_arg: the connected receiver +:keyword sender_arg: the sender to connect to +:keyword weak_arg: true if the connection to receiver_arg is a weak reference + +.. deprecated:: 1.2 + +As of 1.2, individual signals have their own private +:attr:`~Signal.receiver_connected` and +:attr:`~Signal.receiver_disconnected` signals with a slightly simplified +call signature. This global signal is planned to be removed in 1.6. + +""") + + +class NamedSignal(Signal): + """A named generic notification emitter.""" + + def __init__(self, name, doc=None): + Signal.__init__(self, doc) + + #: The name of this signal. + self.name = name + + def __repr__(self): + base = Signal.__repr__(self) + return "%s; %r>" % (base[:-1], self.name) + + +class Namespace(dict): + """A mapping of signal names to signals.""" + + def signal(self, name, doc=None): + """Return the :class:`NamedSignal` *name*, creating it if required. + + Repeated calls to this function will return the same signal object. + + """ + try: + return self[name] + except KeyError: + return self.setdefault(name, NamedSignal(name, doc)) + + +class WeakNamespace(WeakValueDictionary): + """A weak mapping of signal names to signals. + + Automatically cleans up unused Signals when the last reference goes out + of scope. This namespace implementation exists for a measure of legacy + compatibility with Blinker <= 1.2, and may be dropped in the future. + + .. versionadded:: 1.3 + + """ + + def signal(self, name, doc=None): + """Return the :class:`NamedSignal` *name*, creating it if required. + + Repeated calls to this function will return the same signal object. + + """ + try: + return self[name] + except KeyError: + return self.setdefault(name, NamedSignal(name, doc)) + + +signal = Namespace().signal diff --git a/venv/Lib/site-packages/click-8.0.1.dist-info/INSTALLER b/venv/Lib/site-packages/click-8.0.1.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/venv/Lib/site-packages/click-8.0.1.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/Lib/site-packages/click-8.0.1.dist-info/LICENSE.rst b/venv/Lib/site-packages/click-8.0.1.dist-info/LICENSE.rst new file mode 100644 index 0000000..d12a849 --- /dev/null +++ b/venv/Lib/site-packages/click-8.0.1.dist-info/LICENSE.rst @@ -0,0 +1,28 @@ +Copyright 2014 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/click-8.0.1.dist-info/METADATA b/venv/Lib/site-packages/click-8.0.1.dist-info/METADATA new file mode 100644 index 0000000..7be0832 --- /dev/null +++ b/venv/Lib/site-packages/click-8.0.1.dist-info/METADATA @@ -0,0 +1,110 @@ +Metadata-Version: 2.1 +Name: click +Version: 8.0.1 +Summary: Composable command line interface toolkit +Home-page: https://palletsprojects.com/p/click/ +Author: Armin Ronacher +Author-email: armin.ronacher@active-4.com +Maintainer: Pallets +Maintainer-email: contact@palletsprojects.com +License: BSD-3-Clause +Project-URL: Donate, https://palletsprojects.com/donate +Project-URL: Documentation, https://click.palletsprojects.com/ +Project-URL: Changes, https://click.palletsprojects.com/changes/ +Project-URL: Source Code, https://github.com/pallets/click/ +Project-URL: Issue Tracker, https://github.com/pallets/click/issues/ +Project-URL: Twitter, https://twitter.com/PalletsTeam +Project-URL: Chat, https://discord.gg/pallets +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Requires-Python: >=3.6 +Description-Content-Type: text/x-rst +Requires-Dist: colorama ; platform_system == "Windows" +Requires-Dist: importlib-metadata ; python_version < "3.8" + +\$ click\_ +========== + +Click is a Python package for creating beautiful command line interfaces +in a composable way with as little code as necessary. It's the "Command +Line Interface Creation Kit". It's highly configurable but comes with +sensible defaults out of the box. + +It aims to make the process of writing command line tools quick and fun +while also preventing any frustration caused by the inability to +implement an intended CLI API. + +Click in three points: + +- Arbitrary nesting of commands +- Automatic help page generation +- Supports lazy loading of subcommands at runtime + + +Installing +---------- + +Install and update using `pip`_: + +.. code-block:: text + + $ pip install -U click + +.. _pip: https://pip.pypa.io/en/stable/quickstart/ + + +A Simple Example +---------------- + +.. code-block:: python + + import click + + @click.command() + @click.option("--count", default=1, help="Number of greetings.") + @click.option("--name", prompt="Your name", help="The person to greet.") + def hello(count, name): + """Simple program that greets NAME for a total of COUNT times.""" + for _ in range(count): + click.echo(f"Hello, {name}!") + + if __name__ == '__main__': + hello() + +.. code-block:: text + + $ python hello.py --count=3 + Your name: Click + Hello, Click! + Hello, Click! + Hello, Click! + + +Donate +------ + +The Pallets organization develops and supports Click and other popular +packages. In order to grow the community of contributors and users, and +allow the maintainers to devote more time to the projects, `please +donate today`_. + +.. _please donate today: https://palletsprojects.com/donate + + +Links +----- + +- Documentation: https://click.palletsprojects.com/ +- Changes: https://click.palletsprojects.com/changes/ +- PyPI Releases: https://pypi.org/project/click/ +- Source Code: https://github.com/pallets/click +- Issue Tracker: https://github.com/pallets/click/issues +- Website: https://palletsprojects.com/p/click +- Twitter: https://twitter.com/PalletsTeam +- Chat: https://discord.gg/pallets + + diff --git a/venv/Lib/site-packages/click-8.0.1.dist-info/RECORD b/venv/Lib/site-packages/click-8.0.1.dist-info/RECORD new file mode 100644 index 0000000..42afba8 --- /dev/null +++ b/venv/Lib/site-packages/click-8.0.1.dist-info/RECORD @@ -0,0 +1,41 @@ +click-8.0.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +click-8.0.1.dist-info/LICENSE.rst,sha256=morRBqOU6FO_4h9C9OctWSgZoigF2ZG18ydQKSkrZY0,1475 +click-8.0.1.dist-info/METADATA,sha256=Q_8tjC_Ps-9OmIDcovMWvqzrNlmYNwJ7yZxyeJ-SIsk,3216 +click-8.0.1.dist-info/RECORD,, +click-8.0.1.dist-info/WHEEL,sha256=OqRkF0eY5GHssMorFjlbTIq072vpHpF60fIQA6lS9xA,92 +click-8.0.1.dist-info/top_level.txt,sha256=J1ZQogalYS4pphY_lPECoNMfw0HzTSrZglC4Yfwo4xA,6 +click/__init__.py,sha256=TweMqq3qEdmxSl3M_O0H1crtKtd7_oS7PDd0WlLote0,3243 +click/__pycache__/__init__.cpython-39.pyc,, +click/__pycache__/_compat.cpython-39.pyc,, +click/__pycache__/_termui_impl.cpython-39.pyc,, +click/__pycache__/_textwrap.cpython-39.pyc,, +click/__pycache__/_unicodefun.cpython-39.pyc,, +click/__pycache__/_winconsole.cpython-39.pyc,, +click/__pycache__/core.cpython-39.pyc,, +click/__pycache__/decorators.cpython-39.pyc,, +click/__pycache__/exceptions.cpython-39.pyc,, +click/__pycache__/formatting.cpython-39.pyc,, +click/__pycache__/globals.cpython-39.pyc,, +click/__pycache__/parser.cpython-39.pyc,, +click/__pycache__/shell_completion.cpython-39.pyc,, +click/__pycache__/termui.cpython-39.pyc,, +click/__pycache__/testing.cpython-39.pyc,, +click/__pycache__/types.cpython-39.pyc,, +click/__pycache__/utils.cpython-39.pyc,, +click/_compat.py,sha256=P15KQumAZC2F2MFe_JSRbvVOJcNosQfMDrdZq0ReCLM,18814 +click/_termui_impl.py,sha256=3IBc-wR8art7cOIN3y4vQ3ftyCs4GNLMjDcrSalUD9c,23423 +click/_textwrap.py,sha256=10fQ64OcBUMuK7mFvh8363_uoOxPlRItZBmKzRJDgoY,1353 +click/_unicodefun.py,sha256=JKSh1oSwG_zbjAu4TBCa9tQde2P9FiYcf4MBfy5NdT8,3201 +click/_winconsole.py,sha256=5ju3jQkcZD0W27WEMGqmEP4y_crUVzPCqsX_FYb7BO0,7860 +click/core.py,sha256=xYDxID7ShkgY2Lbw7vKOMjP5ImT1NLCTqMJphUicAQ0,111335 +click/decorators.py,sha256=u_Ehdo3PA2nzCoud9z6fGhxwtMI8vVNG_SL8Bl9lsnY,14871 +click/exceptions.py,sha256=7gDaLGuFZBeCNwY9ERMsF2-Z3R9Fvq09Zc6IZSKjseo,9167 +click/formatting.py,sha256=Frf0-5W33-loyY_i9qrwXR8-STnW3m5gvyxLVUdyxyk,9706 +click/globals.py,sha256=9pcmaNfGS1bJV5DoFYhfv51BPeHP8dWaya7rP3kcrY4,1973 +click/parser.py,sha256=x5DbnBV9O8kXiMdJAdtpdTO2eRUXw2ab5PRMLxo0po4,19043 +click/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +click/shell_completion.py,sha256=F0CHHFOP4ulDsYoqTMm9FXih_OVKsg3mzD-XBzMN79c,17881 +click/termui.py,sha256=MJNkEntRiNZvwa0z9SVK0d6X9BvUcFhvxKky5M-kBGY,28809 +click/testing.py,sha256=kLR5Qcny1OlgxaGB3gweTr0gQe1yVlmgQRn2esA2Fz4,16020 +click/types.py,sha256=ngn3qOaHcDvyeMF2UT5QJNNpJAAVrA9BRj4t8x1xOZM,35375 +click/utils.py,sha256=q7xUTlebAnIENo2Uv-REArW_erqGFm_8yMW241mMjzQ,18752 diff --git a/venv/Lib/site-packages/click-8.0.1.dist-info/WHEEL b/venv/Lib/site-packages/click-8.0.1.dist-info/WHEEL new file mode 100644 index 0000000..385faab --- /dev/null +++ b/venv/Lib/site-packages/click-8.0.1.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.36.2) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/venv/Lib/site-packages/click-8.0.1.dist-info/top_level.txt b/venv/Lib/site-packages/click-8.0.1.dist-info/top_level.txt new file mode 100644 index 0000000..dca9a90 --- /dev/null +++ b/venv/Lib/site-packages/click-8.0.1.dist-info/top_level.txt @@ -0,0 +1 @@ +click diff --git a/venv/Lib/site-packages/click/__init__.py b/venv/Lib/site-packages/click/__init__.py new file mode 100644 index 0000000..9e0afb2 --- /dev/null +++ b/venv/Lib/site-packages/click/__init__.py @@ -0,0 +1,75 @@ +""" +Click is a simple Python module inspired by the stdlib optparse to make +writing command line scripts fun. Unlike other modules, it's based +around a simple API that does not come with too much magic and is +composable. +""" +from .core import Argument as Argument +from .core import BaseCommand as BaseCommand +from .core import Command as Command +from .core import CommandCollection as CommandCollection +from .core import Context as Context +from .core import Group as Group +from .core import MultiCommand as MultiCommand +from .core import Option as Option +from .core import Parameter as Parameter +from .decorators import argument as argument +from .decorators import command as command +from .decorators import confirmation_option as confirmation_option +from .decorators import group as group +from .decorators import help_option as help_option +from .decorators import make_pass_decorator as make_pass_decorator +from .decorators import option as option +from .decorators import pass_context as pass_context +from .decorators import pass_obj as pass_obj +from .decorators import password_option as password_option +from .decorators import version_option as version_option +from .exceptions import Abort as Abort +from .exceptions import BadArgumentUsage as BadArgumentUsage +from .exceptions import BadOptionUsage as BadOptionUsage +from .exceptions import BadParameter as BadParameter +from .exceptions import ClickException as ClickException +from .exceptions import FileError as FileError +from .exceptions import MissingParameter as MissingParameter +from .exceptions import NoSuchOption as NoSuchOption +from .exceptions import UsageError as UsageError +from .formatting import HelpFormatter as HelpFormatter +from .formatting import wrap_text as wrap_text +from .globals import get_current_context as get_current_context +from .parser import OptionParser as OptionParser +from .termui import clear as clear +from .termui import confirm as confirm +from .termui import echo_via_pager as echo_via_pager +from .termui import edit as edit +from .termui import get_terminal_size as get_terminal_size +from .termui import getchar as getchar +from .termui import launch as launch +from .termui import pause as pause +from .termui import progressbar as progressbar +from .termui import prompt as prompt +from .termui import secho as secho +from .termui import style as style +from .termui import unstyle as unstyle +from .types import BOOL as BOOL +from .types import Choice as Choice +from .types import DateTime as DateTime +from .types import File as File +from .types import FLOAT as FLOAT +from .types import FloatRange as FloatRange +from .types import INT as INT +from .types import IntRange as IntRange +from .types import ParamType as ParamType +from .types import Path as Path +from .types import STRING as STRING +from .types import Tuple as Tuple +from .types import UNPROCESSED as UNPROCESSED +from .types import UUID as UUID +from .utils import echo as echo +from .utils import format_filename as format_filename +from .utils import get_app_dir as get_app_dir +from .utils import get_binary_stream as get_binary_stream +from .utils import get_os_args as get_os_args +from .utils import get_text_stream as get_text_stream +from .utils import open_file as open_file + +__version__ = "8.0.1" diff --git a/venv/Lib/site-packages/click/__pycache__/__init__.cpython-39.pyc b/venv/Lib/site-packages/click/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..67c30152bffb3b2b033594b76de0a1c0750f5f5e GIT binary patch literal 2670 zcmd7U*;3m`6b4`ew(-WkgdGJ!2%Fi%9u~7EY!%?<6jd5oZtM=)s#}JT`>C2&nVL89 z?OaXG8_d<5bBrPL1_Q;<*Da}AI%-R{nwnA({7L@$_b-2BB9T9-@$*kTHg@n&&ox9M zg$P6nb%lDT&aH>~I&uTL0UFp*bQGfO7&-!47v%L*iGnWXl6H~TcCyAf^LOYb}PCK+SqOAc4%j}qdTC3-GT0ePIf1{ z3%b}{=x*p{cRO#~9_V4eLH9y0y9eC|ee7OzKlHQv&;u~Q?ne*8AbS8k1VijW^e_yw zhtLKX>|y7vI|3tY13e0(?6=N4cMQhZBhGtw9LCwB&Ifk_CfM)LlQ791Lr=jJ`#pLZ zrrG1@8JJ;zKxZM#olcE zcLi41^XOGrWiO!DV2!<#oD?6Eh|ImofM(EG5@{^T6Ehj7T=Mjycudk1|C$LwA737oL^(4XNmJBR)P zU)cNTQ#eH*z**t&Dl&2Oqms%w(!MvOHY`I+H*kb;`4pCX&v5-h8C%lRfmEVkhy@wO!s%MDJ##gpIGv_;wutVv4)$Lv= zzSEJhBRshPkvS_np?p0sei1zHoxHS^<%&=!+$~|f&e~|d%-CW3-knsgMaP&}#Og$` z;`8X~l5m38{thaw8CY7I1!4Qj3Vj+p{&HZ3x8b1Ar0k-Lnd{O5JFyD6p>?`>K~$>&alx&zITE%je*`EozsNJd}<&P>N5q^-OAw ztK#QFGUxsk-nf^trFiVx5lzQffJ2`uR4rukQCWfce0d#35t?>cDZHKL>s4o}gW)Wi ziyyNcVNs#5@?37Z7Gals^Fdlz&LZy>6$MFG-bRDsWUllq$JFu%U3w#S+9e(&?pS5d zE@5XZu*#aYrUT^{mC!n0-2mmo{xo>|3F@ zmM%F;g`*=6PcL?_5vfDRx5BTM$M>S)X@fkpjf0>qFAQ*NU=h( zO0h<0cM z{~JCg_3Z`T;$wU&-#!TM;r3MKZ?z1?Okmmf_%YMBHoabEO;dU@G|g=AR7q@B-z@l& R#P4mtVMODZy8pp%{lC>J!HfU^ literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/click/__pycache__/_compat.cpython-39.pyc b/venv/Lib/site-packages/click/__pycache__/_compat.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2accdfbfdc64e6569133872c38e688922fda43a8 GIT binary patch literal 16019 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 Cm!&rV literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/click/__pycache__/_termui_impl.cpython-39.pyc b/venv/Lib/site-packages/click/__pycache__/_termui_impl.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..204be8979592380fb7482c053a7df2d4e28c3c85 GIT binary patch literal 15945 zcmb7r3v681ncjWPlf&VNqA2QN>zbBrjUrJGJC3WWiXN66wGQn{j9t&VJ08xtB!?Q# zjLyA7iIZWoFqL%MuHztEWVea59VY4Sda>wZu`s&bZTFET-2yF81VvF?6m8l?(bX13 z7cH6sT4&Sm`|q9MkfPCc$T{~v?{m-j-~XA)=xEl!C-cSs`py@A!Z7}Tnc+_onTz=P z$1KB8M#)g7vg&5Zl&4j)VUCfm!441Blo0tvUa8lrw_Me{ z(>US!O_bFd3);Kg#+darx9xk%U1&F|9KKSo1#522&$jB7D#~jO47jzZN<+EL20BGE zph3lV8=j{;HIscvBA6|jVQSF}0`G3{&_n_96J_NsRND2R%+j(SsAfArdGg9zue^Dk zJ@%kSdEWDbve&3KRjske>Y85(f;B)R&6d|dKkZdkkj(24FiH zE-!C<^={Q`@ug7Vc-5t5Xuc6zjo^_v8k*ToYV^=geERGs>z$N5m$Fr^fA}+w%td_t z9$v&VK)?ow_XQAygKxr19x#C7%%$*5$A1|q$)F^H?B%SKp!K3DQ2KzeU=8{rC#ClC zNDQ%W$|D=d-Y=*Tl>@rwl%Ym}fO$0rbQ@I#wdbBu%A@BP*CBP|YA@;vUQ%)sY9DfY zFbexks{P1~dwabJ$&(a`$omE*2h|}dnH-cHR!>ODelIP3o>WIL##Ee(NbNwJBe^-H zgK_StdIq^eagGG%ln%!$Ii`xpJt6OR90c?UbprV()hF@J+)KWVsZN4j%&1d%^CNM; z)9MV`x!d0Ota=`0Pbm`w^kO*jx^6CN&-X7^^z0+^=|`sfDD}*-`HC+3k1W@nyQXa% z=ffwF6boUx2F$6<*S#{$$!!puUgUN2XdDmu)1E3IHHf^!%Fs0%u*1RlCI7Mf+> zYlUflsd=aD1(h%-P|MR*&{SOBYWiUwQ>>Jwk`xvu>Xmt~9y*xE3lr65y{W@OyQMI0 zd8I}a_ga3KxKmSDO;US7TQ{l&EVuvJ10aF#6@2~UK+CS(HOfZUL27mrNUd%XsohN> zb-HP!iEajIvYSPk>W;KtR3@Nw7bw*>^k;(1hOwUO=1`XHjv^iD=8@)0D`1uLqo?bX(T%F@=~o4Jp3^}ohSR);DWzK zwcvv;`D9Id>RRzReGr?j4>38+dDT37T3LrDkreO7kybfvNA zg$`dqA7F`1?6WJYi=jg)5GMSfqS3F8-h#0ipl$h912Q2@FVq?}f5}r}W~Fks94#UR zME3CBVXoTN8ksUtS*K)0H6I(Nl-CHeWf`$vYk0*yq2qh?1wDl}vWow|4cop~vMaIy z9~k-)MtpSX%DJ~e;LBb$@ZWw70^u_dWpA%~jn%hbtR|^n(1vNSu1VkET#@>_%~^0;}(UXmU*)? zF~omn`cr*i>gO;|m5A5>5Qc6TQ8A^AP$clRXKosxCt|BLJ6E*yc~pjD*nj2eaw}$U z81f>@Wm}@;GMi<}ke+zsa`_!(eX5}b7MM0W`?fDEZdE0U_CJINOUfaG#TZ-+N#_HT zLV7e_MuaX!M-~yLy}Q+N02B=~gn-d)FSLUBovXv%dw03_9tQV0mGRh)MGZcFt4M4h zZo|(9P^QUDuu3c1vYx#eZQG-nx0-EHO5C7H?Z)lzn+rY;eX8MB+&i^k$(^q^tG8zG zBC^Z58|?;ZMMzCw#Z+Nt(QCw{cn$eoUWpV}E-#_dPa}!;8#}%e6{Oi?FJ`01753$Z z2S$L`*}!Yv1Xo5u(F&ag6ej&VGWuf{zy{@VgM-MPCU;I9HTd|cNebLko#jabGJ95F zZ<<6yz)l|rq9rBt1ullP3NfYe`eEkMwYjS|%9k(Q2onqSw!gI7QuTH8SjBg+O^%r} zI|qgla~pa+wqPQt?A0*<@q)2a+_PmY7^%}bs31*RuCfk+qBf{i+q3@#&GGr;PmAm? zCiFSvLL1Unzd#6UZdQVbU+uQXcB>k6(J-PXD+xBx85>?U>OX$5)9A^?`dR8IvRE8( z5G_~^`w;2Nnq5Rg`2Zb12_Urn_KJQ>AV{O#_B0$Px|Sg<$eSTCpz6Mh4#O*ba(JbW zH-Bv51B`qY199Py;ijGjWco}Yo0nN`PZ<+fIA2P`1~#y2${pEU{gZ^axx22~hUEEa zRL09JnBzM(6!qKIl|W+>mz@5W0B{ZZ-RR^u%yko*hB+B&9OhwTkhfVL=PlGb$XjvV zzUAnpuK5kv*IiiH-*BKo*zwci^Fa#4uzO7c?~U4GdiEh1$3u~KWa%ACxuJFX>?8A> zCYBa${b%q9ZOD3EWsV2hGmp@Of`_+_sCOtAw z6>Z>VprZrkp+ihv#3L%8s2-9@{l!Bad4QUj`mTrrCFp)Om-vj!6K`{x6r09oOavGoy1PA zM4d=*^VRF`F<=F>iHv(w! zQOnlb(JQZ$)1{$}+xlzL)(*@d(}QlmJi?j@V-wyZ@IPn2&?)CWZ>U5s*9BAO+}Nb# ztZa-Z!+pkcF$R9{=?Hg6BZA)AIdX&{jZWC8Q?CIPNbjKo99;_SYSWMSs9 zl%)j~8(B7B*gi0_J{Hx{;ukTde-w!!xRQm|l{E9#gqck`e`1-IIb}LOw)mIK?v6(47S~yVizCCI5 z_QVxlNB;a6{1J<90 zM9o8{<^kt%)H(39tnHu<9PURv;*PruPun)=wqwpLbyGfdMf)SLNz1$`b&6zS_ z11DgG+qVq;^FV=o01qk@DL{&JTOf5JPdmLJDeQBM410FDAjt6Rgpl9rD|4-u2qD5tlFh_s%fUtk&B176Mx;v?YO_qj|` zB~`me3}_jEBtHq;@&XnFSUbOJyI4w%}PP7 zQ9^^Z`UJDWR_IXIYe=A&RqjUaK&UhjjFo!XuXVgI1!qZnrQz#4Y?Zxy_1bGUu2SjL zf0liTs`?Ei#Y`lOKF?_ z&Y@xS-eKv%1W3hRp~#A)?CyVT0Q_FT*Z(?_E_@`=&dg82l7mmE&(w|slH9F?J}x*6 z^^@ij%7L?xC5FC8<-cDFzY%)qg?@fHF~zpVAXNCco*Ou+i@0jS={o( zZw53s{7E!4z(^o1tZ~z^vWcWQY2~d+Yr7FV=!F?Lc*F#s161Xf_CMRU_!>&#%re2p z^Wvn!t75~!0A#}{D@0ykn=gY!kl;-rN0A&LLNJ~(csT^HU3n5G`8jd?i8H^?&uF5$ z^J{KJtyUUU_`%(Z>$kjWZJ`Edd!xNF?`gR7vu^wmhv5lV{PN=KuGHb3_dPiO5h8f! zonhzAJMYA?1Gu$-_=7Q^a@A-%%wz{e=UNqAS#e|3JLk^!Cw65*8rIPYy_ubThG#tI zz7{XarI=thE)6k*ohuuR!uNvUkmW~?QRVKkz?m6$)zdy!4MhUJFLmym`_jzmZNprG zhf3|f56%9vfTxM7$xzxjwjxw70(NN)@)nWJMZjt_~KxK zCHSqEz^S(OGv>L82sJ+e%2Zn-Z4uE%+a8UvjRzdBEC4HyZ^Is$4=62uD_|d^ z1O67m=viz{gf)@flM%<3ehtNuBo;@K9Fd?Ckw|}$$(NACW?X3L)g4S$zmDn`d1zvL z=HOGL)&_x=OhBjs&cQvpoxu+Fs+gbEdpUjxQH>VsIo;~@fi6aq0$?c{xC@N##9FVv!Lz|1B|Uk zqBbgvYZnE$eW3hq8gD~2Ur(a-csD_M@5}}97*K`BWt3bu35U?=1K465P0Bf5p5wiV zH|*{0VkIV3rNNu;C?B*r4PFH1IMzA)GQ_v?z%wd=yW#~)O$epPF-<|&sw|Q=-wkF;iPtZ^a`ncn=+~hG_v&Yh7Ve2a3YQYzN~P+T?52OVlU!+P&pi)ydku1V zr89o4j_7C6J$vDl@>WkZ+V#39?xDl-xcT%Wa~jq75eKAkmqpb(p{F{;C^ zSed6o3xOmAgH~(0*$8dK>DdV3cRj*hd5McMoEf#wmRq$}45t|{r4h)0J)%!Oo$g_3 zslu?n)?a0R1n*Z`5)DnQ+~Pa5;2NkmeZ*wj4d~yuim5G3#e2%tfl@{$D_2*P{w7DU z78IjcP}RXZhKP-H7ABN;xX?Cg&$ zMt=U(vh)Y&2Md@q0~ZITrL7Zbe-v{TRyO0B_{or%EC*T zf`?}Wy7`0x3a=XePpMHp^*xq$?d3GQc_Vmc&^G^`18Xwd8ws+@xo&EC6w%jokng5p zU1mBbx(3AN^m?{y`QHo1x`?9!PQwk6g$0?xjUZT&5SAalX*4DsBNz|%E?8^-BjQ|d z8I9~|1Fa`0Mmre2w~r9oPN41Hub69x`z>#OblUiedHd+?kAzGLo!hSi-L{bd_232E zEg^Z-pwL3+Y&4-wfoAAZsI1Jm?)AoM^OmT|B5UQSw+4}$E49U?!1dcLxe_BG_gH2M zY0T;x4lNQx^h@ZUMG`u%UcGr!s87V!BkSaixzI!a_6EG43B*c4SU<;xHYg)ZX-}@p z6eoe{?Rl)O3UUh_RjUU2Z*fpsXY{1BaJ>fo`Ox2Bg}7J}uk(IO=)8XY^{XP?^l7$F zUxxXs*C4hym8Qa^iCub*wMn@t1H<^Wn^ACh&tLG?=9?9*t}_Uz+pR!Xk)5G^^1&ru%nt5KCq_N<^3>q%Zvo zNZJGHa?)1TnSfn;6x78?Y5`W*1X4;WiJ@9kw2H0t2&fB|TGEmcwwYag%K;tzGBSNS z>e`Huf|Wb#Q0ICOKJaP4TjC!_1SknyO$H7H5ug-&r=XY(7;FcO_g#2S(!F#y*@GWt zQ_QkA!Jr-Jl^Zq`#=;rHUpgI2;J1jqM@b_RA%I5*6qtMfAw^t9c`gzmjocO-v-?!% z{H}Oh8{lald2x@*t$^M0suoV!CHtC*QIbyi(7H2^ODog*Zz5A16-KAO1vv6`2qFkd ze2rzl$Ry2#$f19OiC`YNto}Pl;205+zl8z9I?b}?2J7Eu@=uV2=EBYset_a%;t7j& zjS~h}l+&euV+Q9sWofo4I^@v2q_xGfA7_%(KH1etYrb?>}t~6z&HhbTa>t9YOxee~zRd^4g_pCz^4p?+vy) z6*K8?o;TO(QKP}NMs@<$Wj*o&^#1xYJm`=lt8e% ze$%);5jgrB<&dJ$I8Axo1MleCp!dbO0i`ttR; z*KU+j5K+*7RMGB?`P^LGFFP3>LCIdNt(2Swq*x~zS&*HAyQQi@ZF}}iq_0`4wPG^b z&Vl@h1O`t0Tr`tJHgP^#fYPIcOER zwZCf`>nU*TX=w#LhON3Om3a?F%Dd*9##_LK_0(TUH4ud1^RxkOjdL$%V{7?$@dCnU zdF6&OQU5(8ovF`9JGXw$<<3yeSb&i9RMC{p?mY2v4bHir_tr5Mk0dlbux}B!|AIry z_=8i$g|236EwaKtWc6<`;e8POdq~1$#OCxrLI!@*i>#0%kpbn%sa)uPj09YRH*9c$ z*3}{)yG7 z{{cEjI?A^QQBH`q0&G`Qg(D3YE8*2er^Uc>8cYc{T#zTULP-BB_7h1%1t)YOEgJfV z3X=+#E0oud1_{1?QySr}ZAJf*fMP9vhhT=|Vf8Dmh?VXj9EY3nL(%xq1JGxngD)qP~M*%=VAoTw4 zF?vkzGfuLBwZXLRf{v9>eu+->!N)@^(wAvH5O{^z^kx1bO{f>eHKhuppL&7O! zrTvk>l?*jLGz-k>ejR5)?_+9jJkNy8vv0R~I=EkewoAdt)*PGY-BN0D-Dsqv{lSQI z5$x|8iwy0;$VZKI92h#^^_tAU{Th`(p){Ul>yE0UzMU z1J{R7ExxhCbCK(=iFj=MdZgXB-G&m?Nl{XrJ9X+|3gf}<&H*FqwfPyuY4W9gTI$t@~`cnvz^-rW{1_rq6l3U{J$W%N~STvola(u*T2hGFlRdHyK3KvQ?CDfAJO>Wlhov+f5B$}RC?}dCXNWh+`S7Z{?EkYoni5)& zxfaq?P$T6u3#Lkk+_;s7BaSMo1EVvFVIhc)c1c_XVxBihvQS}=2Ui1gmfAdO;gUd3 z{}7`?oLSYS$5EGPj3q>@>_q@TQ4{KQO^>PRc#`;gV%pv-&tnhGyL{e@<8K$ZdJvxX_%JyJ1-R zM@T_oa&tozK2Fj9*Z_9yn$86Xd#{^~B%b(P2VCd03M^&9A9)<+Bl5iO27e8hGS~v0 zR{0_Y zuGWQ-W8or(m9}}6U*nF;b|apzLZXn}qaTFVv6mU;I2_o#Yfe7ZbD*)AxB@{#QeYSq zMyC@bVAzOR*|`v;;ARrp)s_8+K9+#f3Ht0ZoT{5}Hla5p3LT?BE8YP6CPp4*zH#FJ z+P2qkdm8**+^UJ?{MtY4yHy)jtmj#?owRT%{(^2yezy_vg`dN_2}^*o1^(J-hAGJQ zl^WdqnV^b|qjNsA(XEt3upW59azHFEW#+Ejy!7&oOV?*3m(v>@jT|LRfB`l&&gMHT zqJmY*@#YPB(sJ5)*G~}ZS9m_h(9~cY5!4HF6ckJ)o88YN8>k;- ze5BR)U#Fc9&mb;Zz-+Ml{y>JMXr1cwK?+9!D-jT$I2y4>5nPJ#@lBxgmZ5OdfM@XS zVTD`>%AS4bVn)$q;aeq@ME zoY6c8g_E&X21~7L*NV@Fsd8C0tBjZO2cvM$LPF{mXAs8zHA3WPVG_U5p!z6kB?&FE zFH`c9BwT=};MO7nWZ|-0?r!8XBR`2aSr&{b`dOtfW@<<^9lv4{l1%ZpN^d~tmw4m+u8UV11%pmmF00t7)2AO)Hffm*ahQxIymG_-f`}&_ho(F{djY@k6 zP@cf+z6PO)q6JA-Uz#w`S;40w5tMvO6j$PsD51$XxZp{k=uadLt|5*LW9kQ4X)1kM zO;eK|Xq%zEJwi~P!0Ubmp-Dm&Ntotip@UuW;T!5uQN`W{O*WBqbfg1V?JBxURPdIm zP<1ZF1xY^Lon4*V^vkF|AL$?R}$CeBCM+{z+1 z+IO0nHaHOrWc7lofe4 z`BGw|PXOb+*_pp14^yKGNb+BOnAvTCAe{jVX+-<*^xzTn{F7FD_HIPR%!j#A+Eg}z z@QmRm>w(8>jPP|of&lEvf?)?1yoM%?NlhORMkK6&M%NlWzo*DAgp!w}25(peZ%NId zt$npZpAMmdL2mLYPYb!K#NToAeW#D+P}SNL3wsL+u=hYDTVM7XlDOCUZq6!hQ}K2aPR79lQaqzaf7zWarUs#g<^d!fVfm7Fp_P zFAjZIoB34Rw9-hoBCqgrIeQ-*><)@ebQR+=H*vTE?qjqtd+d3xPJx&Z(17`Wgf>@k4^afDA*4_#Y9rc;b``XEJ+r$`ydHOE zyh#@OPy!M{s?-}oLTL9$kG*g~;{TW{C&YiyRw{37ZyRc9zTTVn-tT>lz4>|9fUo|; zzu!D+7{=fFaPq6b;W`Y_f{7Reg+{wrQ5(V849%h0t{~u$%CCr6r24(pwg|doH0^&t z#<co9qoy<+anF~f{m6jSuzm=BeGOMs8=Z}r7N*bA+T9Z># z<1^#yYgsL=P2QOrnUgw`(^F$|hRjb9f0kA!3#l_j$SCOK)X3n^$UB++5P0HcYEI4q z+SA8K--)iLHXyxJQqL4reIg4vAD5i7$7pYnEKZi>atfYK5m|biYamuu2cOSP&XeUS zDtG6msAmGoE18?RpnsIQZ!i|;te(2WN)VjxBjX8r_!Er#X}zR9E$yDyc7b}KuqH2% zi<70)oT6j&9Yhz>{_gndRwNQm1(pL(!bCBRMgeEhkVX;*0{0msxW_oY7X>~ew8bIm z22{r)6iUo#k8(~4jer+KeO2{@9|X6?e{F_=zmE@sFvMP$afy2g2lX7&L+U3oU=i+8 z-%A9=J4bQ=#{*AbpGCc(4}b`k3~T1boz0s!F|cMPaxiZJZQ)G@euNSaq>RP->Z-5J zw_-00sT471d(@Yr<+I^xQuwhVk~jujtGT{mAcMY_qY{$FqNS=a6TzXfL*{!S9h)nj zs&N{Jo==;WvV|lpk@*S%P|ggUvf;?MP{xg>IksDg?5(Vgt9o721V7)6Yx%mec6a>C z1|eXaMP7)52m(FKVXR|_h#Wuy#1SAG;)r391k&q<6yLjhV`U8&$OJy1oMI9PpN(Kg zTKKLO8!{nr80^#V2y^-{33QTSuy6!e~S;16`MXq7{#9HL=7ULW*+uI+Ph}@H$ja#43=;KExeF+00A+T&X3uWn?HaUxG z-VcBh`(A`&UCTYrhB~+uDrN@obxLMpZzW?Le_L|0v3VIcQM*>2m0ciL);8cA|55bI zn>0e4p&>PZ!cpi5?|+HM-K1#PLgB1Ve3pa+sznx{lQ_qX1D%DU-C#=?6obA)4L`;5<%*;W?dek?RCR~NG*Z^i&7CbZrz=VKswb(c-M+J>+e%dgl^_#` zfeeF)f{c(nh*cE|hURTZ#6chjs^Sr%Y%d0duoi8ltUVS)(6dl$l&D%+U8({vnQDBQ zL^2rC+)i^Y_o8k0LJLv9;K+=`i^Y^_z6}$O!w%RG* zu3i$g;v==tfrbxyN1Zr-KmqB<2F>C}m<-cI4RZlCkZU@~s+h=pWxlfN4dejFLN*Kw z0riDro6nt^z6Uf@^H0IPfo!w90dlsZ@0U8925|J=vKlX~SC(U$m(ViEx*+G4_pGY< x0(f=40$7?=ei|0Oq$g8Pi+a+mJQgRW%Rp1kM?*%Ekba_nHA4I3AO|%n{{e&u#Owe7 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/click/__pycache__/_winconsole.cpython-39.pyc b/venv/Lib/site-packages/click/__pycache__/_winconsole.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7e265ba25027c323d7d3ec3ab08a23cfd2522693 GIT binary patch literal 7763 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