From 177252f42e78c3d5d5867d1b89b0612871b21b4b Mon Sep 17 00:00:00 2001 From: grgr Date: Mon, 8 May 2023 12:52:50 +0200 Subject: [PATCH] 1 draft --- .venv/bin/Activate.ps1 | 241 + .venv/bin/activate | 66 + .venv/bin/activate.csh | 25 + .venv/bin/activate.fish | 64 + .venv/bin/easy_install | 8 + .venv/bin/easy_install-3.9 | 8 + .venv/bin/flask | 8 + .venv/bin/pip | 8 + .venv/bin/pip3 | 8 + .venv/bin/pip3.9 | 8 + .venv/bin/python | 1 + .venv/bin/python3 | 1 + .venv/bin/python3.9 | 1 + .../Flask-2.3.2.dist-info/INSTALLER | 1 + .../Flask-2.3.2.dist-info/LICENSE.rst | 28 + .../Flask-2.3.2.dist-info/METADATA | 118 + .../Flask-2.3.2.dist-info/RECORD | 54 + .../Flask-2.3.2.dist-info/REQUESTED | 0 .../site-packages/Flask-2.3.2.dist-info/WHEEL | 5 + .../Flask-2.3.2.dist-info/entry_points.txt | 2 + .../Flask-2.3.2.dist-info/top_level.txt | 1 + .../Jinja2-3.1.2.dist-info/INSTALLER | 1 + .../Jinja2-3.1.2.dist-info/LICENSE.rst | 28 + .../Jinja2-3.1.2.dist-info/METADATA | 113 + .../Jinja2-3.1.2.dist-info/RECORD | 58 + .../Jinja2-3.1.2.dist-info/WHEEL | 5 + .../Jinja2-3.1.2.dist-info/entry_points.txt | 2 + .../Jinja2-3.1.2.dist-info/top_level.txt | 1 + .../MarkupSafe-2.1.2.dist-info/INSTALLER | 1 + .../MarkupSafe-2.1.2.dist-info/LICENSE.rst | 28 + .../MarkupSafe-2.1.2.dist-info/METADATA | 98 + .../MarkupSafe-2.1.2.dist-info/RECORD | 14 + .../MarkupSafe-2.1.2.dist-info/WHEEL | 6 + .../MarkupSafe-2.1.2.dist-info/top_level.txt | 1 + .../Werkzeug-2.3.3.dist-info/INSTALLER | 1 + .../Werkzeug-2.3.3.dist-info/LICENSE.rst | 28 + .../Werkzeug-2.3.3.dist-info/METADATA | 120 + .../Werkzeug-2.3.3.dist-info/RECORD | 126 + .../Werkzeug-2.3.3.dist-info/WHEEL | 5 + .../Werkzeug-2.3.3.dist-info/top_level.txt | 1 + .../__pycache__/easy_install.cpython-39.pyc | Bin 0 -> 316 bytes .../blinker-1.6.2.dist-info/INSTALLER | 1 + .../blinker-1.6.2.dist-info/LICENSE.rst | 20 + .../blinker-1.6.2.dist-info/METADATA | 63 + .../blinker-1.6.2.dist-info/RECORD | 15 + .../blinker-1.6.2.dist-info/WHEEL | 5 + .../blinker-1.6.2.dist-info/top_level.txt | 1 + .../site-packages/blinker/__init__.py | 19 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 501 bytes .../__pycache__/_saferef.cpython-39.pyc | Bin 0 -> 7179 bytes .../__pycache__/_utilities.cpython-39.pyc | Bin 0 -> 4193 bytes .../blinker/__pycache__/base.cpython-39.pyc | Bin 0 -> 17272 bytes .../site-packages/blinker/_saferef.py | 230 + .../site-packages/blinker/_utilities.py | 142 + .../python3.9/site-packages/blinker/base.py | 551 ++ .../python3.9/site-packages/blinker/py.typed | 0 .../click-8.1.3.dist-info/INSTALLER | 1 + .../click-8.1.3.dist-info/LICENSE.rst | 28 + .../click-8.1.3.dist-info/METADATA | 111 + .../click-8.1.3.dist-info/RECORD | 39 + .../site-packages/click-8.1.3.dist-info/WHEEL | 5 + .../click-8.1.3.dist-info/top_level.txt | 1 + .../python3.9/site-packages/click/__init__.py | 73 + .../click/__pycache__/__init__.cpython-39.pyc | Bin 0 -> 2620 bytes .../click/__pycache__/_compat.cpython-39.pyc | Bin 0 -> 16059 bytes .../__pycache__/_termui_impl.cpython-39.pyc | Bin 0 -> 16006 bytes .../__pycache__/_textwrap.cpython-39.pyc | Bin 0 -> 1536 bytes .../__pycache__/_winconsole.cpython-39.pyc | Bin 0 -> 7787 bytes .../click/__pycache__/core.cpython-39.pyc | Bin 0 -> 89662 bytes .../__pycache__/decorators.cpython-39.pyc | Bin 0 -> 15574 bytes .../__pycache__/exceptions.cpython-39.pyc | Bin 0 -> 10135 bytes .../__pycache__/formatting.cpython-39.pyc | Bin 0 -> 9415 bytes .../click/__pycache__/globals.cpython-39.pyc | Bin 0 -> 2427 bytes .../click/__pycache__/parser.cpython-39.pyc | Bin 0 -> 13570 bytes .../shell_completion.cpython-39.pyc | Bin 0 -> 16731 bytes .../click/__pycache__/termui.cpython-39.pyc | Bin 0 -> 25949 bytes .../click/__pycache__/testing.cpython-39.pyc | Bin 0 -> 15141 bytes .../click/__pycache__/types.cpython-39.pyc | Bin 0 -> 32958 bytes .../click/__pycache__/utils.cpython-39.pyc | Bin 0 -> 17577 bytes .../python3.9/site-packages/click/_compat.py | 626 ++ .../site-packages/click/_termui_impl.py | 717 ++ .../site-packages/click/_textwrap.py | 49 + .../site-packages/click/_winconsole.py | 279 + .../lib/python3.9/site-packages/click/core.py | 2998 +++++++++ .../site-packages/click/decorators.py | 497 ++ .../site-packages/click/exceptions.py | 287 + .../site-packages/click/formatting.py | 301 + .../python3.9/site-packages/click/globals.py | 68 + .../python3.9/site-packages/click/parser.py | 529 ++ .../python3.9/site-packages/click/py.typed | 0 .../site-packages/click/shell_completion.py | 580 ++ .../python3.9/site-packages/click/termui.py | 787 +++ .../python3.9/site-packages/click/testing.py | 479 ++ .../python3.9/site-packages/click/types.py | 1073 +++ .../python3.9/site-packages/click/utils.py | 580 ++ .../python3.9/site-packages/easy_install.py | 5 + .../python3.9/site-packages/flask/__init__.py | 102 + .../python3.9/site-packages/flask/__main__.py | 3 + .../flask/__pycache__/__init__.cpython-39.pyc | Bin 0 -> 2841 bytes .../flask/__pycache__/__main__.cpython-39.pyc | Bin 0 -> 216 bytes .../flask/__pycache__/app.cpython-39.pyc | Bin 0 -> 67463 bytes .../__pycache__/blueprints.cpython-39.pyc | Bin 0 -> 22422 bytes .../flask/__pycache__/cli.cpython-39.pyc | Bin 0 -> 26718 bytes .../flask/__pycache__/config.cpython-39.pyc | Bin 0 -> 12647 bytes .../flask/__pycache__/ctx.cpython-39.pyc | Bin 0 -> 14518 bytes .../__pycache__/debughelpers.cpython-39.pyc | Bin 0 -> 5990 bytes .../flask/__pycache__/globals.cpython-39.pyc | Bin 0 -> 2924 bytes .../flask/__pycache__/helpers.cpython-39.pyc | Bin 0 -> 23721 bytes .../flask/__pycache__/logging.cpython-39.pyc | Bin 0 -> 2502 bytes .../flask/__pycache__/scaffold.cpython-39.pyc | Bin 0 -> 27131 bytes .../flask/__pycache__/sessions.cpython-39.pyc | Bin 0 -> 13101 bytes .../flask/__pycache__/signals.cpython-39.pyc | Bin 0 -> 1245 bytes .../__pycache__/templating.cpython-39.pyc | Bin 0 -> 6951 bytes .../flask/__pycache__/testing.cpython-39.pyc | Bin 0 -> 9212 bytes .../flask/__pycache__/typing.cpython-39.pyc | Bin 0 -> 1725 bytes .../flask/__pycache__/views.cpython-39.pyc | Bin 0 -> 5380 bytes .../flask/__pycache__/wrappers.cpython-39.pyc | Bin 0 -> 5153 bytes .../lib/python3.9/site-packages/flask/app.py | 2213 +++++++ .../site-packages/flask/blueprints.py | 626 ++ .../lib/python3.9/site-packages/flask/cli.py | 1067 +++ .../python3.9/site-packages/flask/config.py | 345 + .../lib/python3.9/site-packages/flask/ctx.py | 440 ++ .../site-packages/flask/debughelpers.py | 160 + .../python3.9/site-packages/flask/globals.py | 96 + .../python3.9/site-packages/flask/helpers.py | 693 ++ .../site-packages/flask/json/__init__.py | 170 + .../json/__pycache__/__init__.cpython-39.pyc | Bin 0 -> 5970 bytes .../json/__pycache__/provider.cpython-39.pyc | Bin 0 -> 7553 bytes .../flask/json/__pycache__/tag.cpython-39.pyc | Bin 0 -> 11420 bytes .../site-packages/flask/json/provider.py | 216 + .../python3.9/site-packages/flask/json/tag.py | 314 + .../python3.9/site-packages/flask/logging.py | 76 + .../python3.9/site-packages/flask/py.typed | 0 .../python3.9/site-packages/flask/scaffold.py | 923 +++ .../python3.9/site-packages/flask/sessions.py | 367 ++ .../python3.9/site-packages/flask/signals.py | 33 + .../site-packages/flask/templating.py | 220 + .../python3.9/site-packages/flask/testing.py | 282 + .../python3.9/site-packages/flask/typing.py | 82 + .../python3.9/site-packages/flask/views.py | 190 + .../python3.9/site-packages/flask/wrappers.py | 173 + .../INSTALLER | 1 + .../LICENSE | 202 + .../METADATA | 135 + .../importlib_metadata-6.6.0.dist-info/RECORD | 25 + .../importlib_metadata-6.6.0.dist-info/WHEEL | 5 + .../top_level.txt | 1 + .../importlib_metadata/__init__.py | 987 +++ .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 36366 bytes .../__pycache__/_adapters.cpython-39.pyc | Bin 0 -> 2961 bytes .../__pycache__/_collections.cpython-39.pyc | Bin 0 -> 1559 bytes .../__pycache__/_compat.cpython-39.pyc | Bin 0 -> 2201 bytes .../__pycache__/_functools.cpython-39.pyc | Bin 0 -> 3149 bytes .../__pycache__/_itertools.cpython-39.pyc | Bin 0 -> 2026 bytes .../__pycache__/_meta.cpython-39.pyc | Bin 0 -> 2891 bytes .../__pycache__/_py39compat.cpython-39.pyc | Bin 0 -> 1195 bytes .../__pycache__/_text.cpython-39.pyc | Bin 0 -> 3092 bytes .../importlib_metadata/_adapters.py | 90 + .../importlib_metadata/_collections.py | 30 + .../importlib_metadata/_compat.py | 82 + .../importlib_metadata/_functools.py | 104 + .../importlib_metadata/_itertools.py | 73 + .../site-packages/importlib_metadata/_meta.py | 63 + .../importlib_metadata/_py39compat.py | 35 + .../site-packages/importlib_metadata/_text.py | 99 + .../site-packages/importlib_metadata/py.typed | 0 .../itsdangerous-2.1.2.dist-info/INSTALLER | 1 + .../itsdangerous-2.1.2.dist-info/LICENSE.rst | 28 + .../itsdangerous-2.1.2.dist-info/METADATA | 97 + .../itsdangerous-2.1.2.dist-info/RECORD | 23 + .../itsdangerous-2.1.2.dist-info/WHEEL | 5 + .../top_level.txt | 1 + .../site-packages/itsdangerous/__init__.py | 19 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 869 bytes .../__pycache__/_json.cpython-39.pyc | Bin 0 -> 925 bytes .../__pycache__/encoding.cpython-39.pyc | Bin 0 -> 1882 bytes .../__pycache__/exc.cpython-39.pyc | Bin 0 -> 3438 bytes .../__pycache__/serializer.cpython-39.pyc | Bin 0 -> 9746 bytes .../__pycache__/signer.cpython-39.pyc | Bin 0 -> 8437 bytes .../__pycache__/timed.cpython-39.pyc | Bin 0 -> 6483 bytes .../__pycache__/url_safe.cpython-39.pyc | Bin 0 -> 2718 bytes .../site-packages/itsdangerous/_json.py | 16 + .../site-packages/itsdangerous/encoding.py | 54 + .../site-packages/itsdangerous/exc.py | 107 + .../site-packages/itsdangerous/py.typed | 0 .../site-packages/itsdangerous/serializer.py | 295 + .../site-packages/itsdangerous/signer.py | 257 + .../site-packages/itsdangerous/timed.py | 234 + .../site-packages/itsdangerous/url_safe.py | 80 + .../site-packages/jinja2/__init__.py | 37 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 1605 bytes .../__pycache__/_identifier.cpython-39.pyc | Bin 0 -> 2080 bytes .../__pycache__/async_utils.cpython-39.pyc | Bin 0 -> 2664 bytes .../jinja2/__pycache__/bccache.cpython-39.pyc | Bin 0 -> 13901 bytes .../__pycache__/compiler.cpython-39.pyc | Bin 0 -> 54181 bytes .../__pycache__/constants.cpython-39.pyc | Bin 0 -> 1541 bytes .../jinja2/__pycache__/debug.cpython-39.pyc | Bin 0 -> 3979 bytes .../__pycache__/defaults.cpython-39.pyc | Bin 0 -> 1341 bytes .../__pycache__/environment.cpython-39.pyc | Bin 0 -> 53109 bytes .../__pycache__/exceptions.cpython-39.pyc | Bin 0 -> 5582 bytes .../jinja2/__pycache__/ext.cpython-39.pyc | Bin 0 -> 25537 bytes .../jinja2/__pycache__/filters.cpython-39.pyc | Bin 0 -> 50404 bytes .../__pycache__/idtracking.cpython-39.pyc | Bin 0 -> 11091 bytes .../jinja2/__pycache__/lexer.cpython-39.pyc | Bin 0 -> 20267 bytes .../jinja2/__pycache__/loaders.cpython-39.pyc | Bin 0 -> 20436 bytes .../jinja2/__pycache__/meta.cpython-39.pyc | Bin 0 -> 3799 bytes .../__pycache__/nativetypes.cpython-39.pyc | Bin 0 -> 4963 bytes .../jinja2/__pycache__/nodes.cpython-39.pyc | Bin 0 -> 40876 bytes .../__pycache__/optimizer.cpython-39.pyc | Bin 0 -> 1930 bytes .../jinja2/__pycache__/parser.cpython-39.pyc | Bin 0 -> 27578 bytes .../jinja2/__pycache__/runtime.cpython-39.pyc | Bin 0 -> 32168 bytes .../jinja2/__pycache__/sandbox.cpython-39.pyc | Bin 0 -> 11921 bytes .../jinja2/__pycache__/tests.cpython-39.pyc | Bin 0 -> 6573 bytes .../jinja2/__pycache__/utils.cpython-39.pyc | Bin 0 -> 24541 bytes .../jinja2/__pycache__/visitor.cpython-39.pyc | Bin 0 -> 3924 bytes .../site-packages/jinja2/_identifier.py | 6 + .../site-packages/jinja2/async_utils.py | 84 + .../python3.9/site-packages/jinja2/bccache.py | 406 ++ .../site-packages/jinja2/compiler.py | 1957 ++++++ .../site-packages/jinja2/constants.py | 20 + .../python3.9/site-packages/jinja2/debug.py | 191 + .../site-packages/jinja2/defaults.py | 48 + .../site-packages/jinja2/environment.py | 1667 +++++ .../site-packages/jinja2/exceptions.py | 166 + .../lib/python3.9/site-packages/jinja2/ext.py | 859 +++ .../python3.9/site-packages/jinja2/filters.py | 1840 ++++++ .../site-packages/jinja2/idtracking.py | 318 + .../python3.9/site-packages/jinja2/lexer.py | 866 +++ .../python3.9/site-packages/jinja2/loaders.py | 661 ++ .../python3.9/site-packages/jinja2/meta.py | 111 + .../site-packages/jinja2/nativetypes.py | 130 + .../python3.9/site-packages/jinja2/nodes.py | 1204 ++++ .../site-packages/jinja2/optimizer.py | 47 + .../python3.9/site-packages/jinja2/parser.py | 1032 +++ .../python3.9/site-packages/jinja2/py.typed | 0 .../python3.9/site-packages/jinja2/runtime.py | 1053 +++ .../python3.9/site-packages/jinja2/sandbox.py | 428 ++ .../python3.9/site-packages/jinja2/tests.py | 255 + .../python3.9/site-packages/jinja2/utils.py | 755 +++ .../python3.9/site-packages/jinja2/visitor.py | 92 + .../site-packages/markupsafe/__init__.py | 295 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 10785 bytes .../__pycache__/_native.cpython-39.pyc | Bin 0 -> 2011 bytes .../site-packages/markupsafe/_native.py | 63 + .../site-packages/markupsafe/_speedups.c | 320 + .../_speedups.cpython-39-x86_64-linux-gnu.so | Bin 0 -> 44024 bytes .../site-packages/markupsafe/_speedups.pyi | 9 + .../site-packages/markupsafe/py.typed | 0 .../pip-20.3.4.dist-info/INSTALLER | 1 + .../pip-20.3.4.dist-info/LICENSE.txt | 20 + .../pip-20.3.4.dist-info/METADATA | 94 + .../site-packages/pip-20.3.4.dist-info/RECORD | 284 + .../pip-20.3.4.dist-info/REQUESTED | 0 .../site-packages/pip-20.3.4.dist-info/WHEEL | 6 + .../pip-20.3.4.dist-info/entry_points.txt | 5 + .../pip-20.3.4.dist-info/top_level.txt | 1 + .../python3.9/site-packages/pip/__init__.py | 18 + .../python3.9/site-packages/pip/__main__.py | 26 + .../pip/__pycache__/__init__.cpython-39.pyc | Bin 0 -> 664 bytes .../pip/__pycache__/__main__.cpython-39.pyc | Bin 0 -> 508 bytes .../site-packages/pip/_internal/__init__.py | 17 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 713 bytes .../__pycache__/build_env.cpython-39.pyc | Bin 0 -> 7548 bytes .../__pycache__/cache.cpython-39.pyc | Bin 0 -> 9105 bytes .../__pycache__/configuration.cpython-39.pyc | Bin 0 -> 10848 bytes .../__pycache__/exceptions.cpython-39.pyc | Bin 0 -> 14937 bytes .../__pycache__/locations.cpython-39.pyc | Bin 0 -> 4571 bytes .../_internal/__pycache__/main.cpython-39.pyc | Bin 0 -> 650 bytes .../__pycache__/pyproject.cpython-39.pyc | Bin 0 -> 3753 bytes .../self_outdated_check.cpython-39.pyc | Bin 0 -> 4584 bytes .../__pycache__/wheel_builder.cpython-39.pyc | Bin 0 -> 8626 bytes .../site-packages/pip/_internal/build_env.py | 242 + .../site-packages/pip/_internal/cache.py | 346 + .../pip/_internal/cli/__init__.py | 4 + .../cli/__pycache__/__init__.cpython-39.pyc | Bin 0 -> 271 bytes .../__pycache__/autocompletion.cpython-39.pyc | Bin 0 -> 4950 bytes .../__pycache__/base_command.cpython-39.pyc | Bin 0 -> 6858 bytes .../cli/__pycache__/cmdoptions.cpython-39.pyc | Bin 0 -> 20787 bytes .../command_context.cpython-39.pyc | Bin 0 -> 1351 bytes .../cli/__pycache__/main.cpython-39.pyc | Bin 0 -> 1455 bytes .../__pycache__/main_parser.cpython-39.pyc | Bin 0 -> 2242 bytes .../cli/__pycache__/parser.cpython-39.pyc | Bin 0 -> 9344 bytes .../__pycache__/progress_bars.cpython-39.pyc | Bin 0 -> 7706 bytes .../__pycache__/req_command.cpython-39.pyc | Bin 0 -> 10557 bytes .../cli/__pycache__/spinners.cpython-39.pyc | Bin 0 -> 4800 bytes .../__pycache__/status_codes.cpython-39.pyc | Bin 0 -> 400 bytes .../pip/_internal/cli/autocompletion.py | 164 + .../pip/_internal/cli/base_command.py | 260 + .../pip/_internal/cli/cmdoptions.py | 971 +++ .../pip/_internal/cli/command_context.py | 36 + .../site-packages/pip/_internal/cli/main.py | 75 + .../pip/_internal/cli/main_parser.py | 96 + .../site-packages/pip/_internal/cli/parser.py | 285 + .../pip/_internal/cli/progress_bars.py | 280 + .../pip/_internal/cli/req_command.py | 436 ++ .../pip/_internal/cli/spinners.py | 173 + .../pip/_internal/cli/status_codes.py | 8 + .../pip/_internal/commands/__init__.py | 123 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 2971 bytes .../commands/__pycache__/cache.cpython-39.pyc | Bin 0 -> 5870 bytes .../commands/__pycache__/check.cpython-39.pyc | Bin 0 -> 1587 bytes .../__pycache__/completion.cpython-39.pyc | Bin 0 -> 3193 bytes .../__pycache__/configuration.cpython-39.pyc | Bin 0 -> 8143 bytes .../commands/__pycache__/debug.cpython-39.pyc | Bin 0 -> 7467 bytes .../__pycache__/download.cpython-39.pyc | Bin 0 -> 3975 bytes .../__pycache__/freeze.cpython-39.pyc | Bin 0 -> 3310 bytes .../commands/__pycache__/hash.cpython-39.pyc | Bin 0 -> 2154 bytes .../commands/__pycache__/help.cpython-39.pyc | Bin 0 -> 1380 bytes .../__pycache__/install.cpython-39.pyc | Bin 0 -> 17327 bytes .../commands/__pycache__/list.cpython-39.pyc | Bin 0 -> 9018 bytes .../__pycache__/search.cpython-39.pyc | Bin 0 -> 5102 bytes .../commands/__pycache__/show.cpython-39.pyc | Bin 0 -> 6429 bytes .../__pycache__/uninstall.cpython-39.pyc | Bin 0 -> 2965 bytes .../commands/__pycache__/wheel.cpython-39.pyc | Bin 0 -> 5191 bytes .../pip/_internal/commands/cache.py | 234 + .../pip/_internal/commands/check.py | 51 + .../pip/_internal/commands/completion.py | 98 + .../pip/_internal/commands/configuration.py | 280 + .../pip/_internal/commands/debug.py | 251 + .../pip/_internal/commands/download.py | 143 + .../pip/_internal/commands/freeze.py | 116 + .../pip/_internal/commands/hash.py | 63 + .../pip/_internal/commands/help.py | 46 + .../pip/_internal/commands/install.py | 763 +++ .../pip/_internal/commands/list.py | 328 + .../pip/_internal/commands/search.py | 169 + .../pip/_internal/commands/show.py | 186 + .../pip/_internal/commands/uninstall.py | 95 + .../pip/_internal/commands/wheel.py | 198 + .../pip/_internal/configuration.py | 407 ++ .../pip/_internal/distributions/__init__.py | 24 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 847 bytes .../__pycache__/base.cpython-39.pyc | Bin 0 -> 1963 bytes .../__pycache__/installed.cpython-39.pyc | Bin 0 -> 1243 bytes .../__pycache__/sdist.cpython-39.pyc | Bin 0 -> 3522 bytes .../__pycache__/wheel.cpython-39.pyc | Bin 0 -> 1587 bytes .../pip/_internal/distributions/base.py | 46 + .../pip/_internal/distributions/installed.py | 25 + .../pip/_internal/distributions/sdist.py | 105 + .../pip/_internal/distributions/wheel.py | 37 + .../site-packages/pip/_internal/exceptions.py | 391 ++ .../pip/_internal/index/__init__.py | 2 + .../index/__pycache__/__init__.cpython-39.pyc | Bin 0 -> 225 bytes .../__pycache__/collector.cpython-39.pyc | Bin 0 -> 17750 bytes .../__pycache__/package_finder.cpython-39.pyc | Bin 0 -> 26078 bytes .../pip/_internal/index/collector.py | 667 ++ .../pip/_internal/index/package_finder.py | 1015 +++ .../site-packages/pip/_internal/locations.py | 199 + .../site-packages/pip/_internal/main.py | 16 + .../pip/_internal/models/__init__.py | 2 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 259 bytes .../__pycache__/candidate.cpython-39.pyc | Bin 0 -> 1486 bytes .../__pycache__/direct_url.cpython-39.pyc | Bin 0 -> 6510 bytes .../__pycache__/format_control.cpython-39.pyc | Bin 0 -> 2744 bytes .../models/__pycache__/index.cpython-39.pyc | Bin 0 -> 1225 bytes .../models/__pycache__/link.cpython-39.pyc | Bin 0 -> 7155 bytes .../models/__pycache__/scheme.cpython-39.pyc | Bin 0 -> 947 bytes .../__pycache__/search_scope.cpython-39.pyc | Bin 0 -> 3440 bytes .../selection_prefs.cpython-39.pyc | Bin 0 -> 1657 bytes .../__pycache__/target_python.cpython-39.pyc | Bin 0 -> 3363 bytes .../models/__pycache__/wheel.cpython-39.pyc | Bin 0 -> 3211 bytes .../pip/_internal/models/candidate.py | 39 + .../pip/_internal/models/direct_url.py | 243 + .../pip/_internal/models/format_control.py | 92 + .../pip/_internal/models/index.py | 34 + .../pip/_internal/models/link.py | 246 + .../pip/_internal/models/scheme.py | 31 + .../pip/_internal/models/search_scope.py | 135 + .../pip/_internal/models/selection_prefs.py | 50 + .../pip/_internal/models/target_python.py | 117 + .../pip/_internal/models/wheel.py | 78 + .../pip/_internal/network/__init__.py | 2 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 247 bytes .../network/__pycache__/auth.cpython-39.pyc | Bin 0 -> 7099 bytes .../network/__pycache__/cache.cpython-39.pyc | Bin 0 -> 2823 bytes .../__pycache__/download.cpython-39.pyc | Bin 0 -> 5274 bytes .../__pycache__/lazy_wheel.cpython-39.pyc | Bin 0 -> 8063 bytes .../__pycache__/session.cpython-39.pyc | Bin 0 -> 9508 bytes .../network/__pycache__/utils.cpython-39.pyc | Bin 0 -> 1400 bytes .../network/__pycache__/xmlrpc.cpython-39.pyc | Bin 0 -> 1862 bytes .../pip/_internal/network/auth.py | 310 + .../pip/_internal/network/cache.py | 79 + .../pip/_internal/network/download.py | 202 + .../pip/_internal/network/lazy_wheel.py | 231 + .../pip/_internal/network/session.py | 428 ++ .../pip/_internal/network/utils.py | 97 + .../pip/_internal/network/xmlrpc.py | 53 + .../pip/_internal/operations/__init__.py | 0 .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 195 bytes .../__pycache__/check.cpython-39.pyc | Bin 0 -> 3617 bytes .../__pycache__/freeze.cpython-39.pyc | Bin 0 -> 5937 bytes .../__pycache__/prepare.cpython-39.pyc | Bin 0 -> 13663 bytes .../_internal/operations/build/__init__.py | 0 .../build/__pycache__/__init__.cpython-39.pyc | Bin 0 -> 201 bytes .../build/__pycache__/metadata.cpython-39.pyc | Bin 0 -> 1222 bytes .../metadata_legacy.cpython-39.pyc | Bin 0 -> 1998 bytes .../build/__pycache__/wheel.cpython-39.pyc | Bin 0 -> 1339 bytes .../__pycache__/wheel_legacy.cpython-39.pyc | Bin 0 -> 2621 bytes .../_internal/operations/build/metadata.py | 38 + .../operations/build/metadata_legacy.py | 77 + .../pip/_internal/operations/build/wheel.py | 47 + .../operations/build/wheel_legacy.py | 113 + .../pip/_internal/operations/check.py | 155 + .../pip/_internal/operations/freeze.py | 277 + .../_internal/operations/install/__init__.py | 2 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 259 bytes .../editable_legacy.cpython-39.pyc | Bin 0 -> 1377 bytes .../install/__pycache__/legacy.cpython-39.pyc | Bin 0 -> 3267 bytes .../install/__pycache__/wheel.cpython-39.pyc | Bin 0 -> 21251 bytes .../operations/install/editable_legacy.py | 52 + .../_internal/operations/install/legacy.py | 130 + .../pip/_internal/operations/install/wheel.py | 846 +++ .../pip/_internal/operations/prepare.py | 608 ++ .../site-packages/pip/_internal/pyproject.py | 196 + .../pip/_internal/req/__init__.py | 103 + .../req/__pycache__/__init__.cpython-39.pyc | Bin 0 -> 2496 bytes .../__pycache__/constructors.cpython-39.pyc | Bin 0 -> 11026 bytes .../req/__pycache__/req_file.cpython-39.pyc | Bin 0 -> 12712 bytes .../__pycache__/req_install.cpython-39.pyc | Bin 0 -> 21463 bytes .../req/__pycache__/req_set.cpython-39.pyc | Bin 0 -> 5815 bytes .../__pycache__/req_tracker.cpython-39.pyc | Bin 0 -> 4237 bytes .../__pycache__/req_uninstall.cpython-39.pyc | Bin 0 -> 17565 bytes .../pip/_internal/req/constructors.py | 476 ++ .../pip/_internal/req/req_file.py | 574 ++ .../pip/_internal/req/req_install.py | 915 +++ .../pip/_internal/req/req_set.py | 204 + .../pip/_internal/req/req_tracker.py | 151 + .../pip/_internal/req/req_uninstall.py | 657 ++ .../pip/_internal/resolution/__init__.py | 0 .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 195 bytes .../__pycache__/base.cpython-39.pyc | Bin 0 -> 1027 bytes .../pip/_internal/resolution/base.py | 21 + .../_internal/resolution/legacy/__init__.py | 0 .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 202 bytes .../__pycache__/resolver.cpython-39.pyc | Bin 0 -> 11582 bytes .../_internal/resolution/legacy/resolver.py | 473 ++ .../resolution/resolvelib/__init__.py | 0 .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 206 bytes .../__pycache__/base.cpython-39.pyc | Bin 0 -> 5785 bytes .../__pycache__/candidates.cpython-39.pyc | Bin 0 -> 18010 bytes .../__pycache__/factory.cpython-39.pyc | Bin 0 -> 11555 bytes .../found_candidates.cpython-39.pyc | Bin 0 -> 3464 bytes .../__pycache__/provider.cpython-39.pyc | Bin 0 -> 6327 bytes .../__pycache__/reporter.cpython-39.pyc | Bin 0 -> 3213 bytes .../__pycache__/requirements.cpython-39.pyc | Bin 0 -> 7051 bytes .../__pycache__/resolver.cpython-39.pyc | Bin 0 -> 7900 bytes .../_internal/resolution/resolvelib/base.py | 156 + .../resolution/resolvelib/candidates.py | 604 ++ .../resolution/resolvelib/factory.py | 504 ++ .../resolution/resolvelib/found_candidates.py | 101 + .../resolution/resolvelib/provider.py | 174 + .../resolution/resolvelib/reporter.py | 84 + .../resolution/resolvelib/requirements.py | 201 + .../resolution/resolvelib/resolver.py | 297 + .../pip/_internal/self_outdated_check.py | 197 + .../pip/_internal/utils/__init__.py | 0 .../utils/__pycache__/__init__.cpython-39.pyc | Bin 0 -> 190 bytes .../utils/__pycache__/appdirs.cpython-39.pyc | Bin 0 -> 1380 bytes .../utils/__pycache__/compat.cpython-39.pyc | Bin 0 -> 6705 bytes .../compatibility_tags.cpython-39.pyc | Bin 0 -> 3939 bytes .../utils/__pycache__/datetime.cpython-39.pyc | Bin 0 -> 511 bytes .../__pycache__/deprecation.cpython-39.pyc | Bin 0 -> 2837 bytes .../direct_url_helpers.cpython-39.pyc | Bin 0 -> 2658 bytes .../__pycache__/distutils_args.cpython-39.pyc | Bin 0 -> 1130 bytes .../utils/__pycache__/encoding.cpython-39.pyc | Bin 0 -> 1310 bytes .../__pycache__/entrypoints.cpython-39.pyc | Bin 0 -> 1334 bytes .../__pycache__/filesystem.cpython-39.pyc | Bin 0 -> 5671 bytes .../__pycache__/filetypes.cpython-39.pyc | Bin 0 -> 879 bytes .../utils/__pycache__/glibc.cpython-39.pyc | Bin 0 -> 1737 bytes .../utils/__pycache__/hashes.cpython-39.pyc | Bin 0 -> 5257 bytes .../inject_securetransport.cpython-39.pyc | Bin 0 -> 963 bytes .../utils/__pycache__/logging.cpython-39.pyc | Bin 0 -> 9230 bytes .../utils/__pycache__/misc.cpython-39.pyc | Bin 0 -> 25515 bytes .../utils/__pycache__/models.cpython-39.pyc | Bin 0 -> 1990 bytes .../__pycache__/packaging.cpython-39.pyc | Bin 0 -> 2643 bytes .../utils/__pycache__/parallel.cpython-39.pyc | Bin 0 -> 3208 bytes .../__pycache__/pkg_resources.cpython-39.pyc | Bin 0 -> 1859 bytes .../setuptools_build.cpython-39.pyc | Bin 0 -> 2936 bytes .../__pycache__/subprocess.cpython-39.pyc | Bin 0 -> 6073 bytes .../utils/__pycache__/temp_dir.cpython-39.pyc | Bin 0 -> 7218 bytes .../utils/__pycache__/typing.cpython-39.pyc | Bin 0 -> 1472 bytes .../__pycache__/unpacking.cpython-39.pyc | Bin 0 -> 6634 bytes .../utils/__pycache__/urls.cpython-39.pyc | Bin 0 -> 1534 bytes .../__pycache__/virtualenv.cpython-39.pyc | Bin 0 -> 3367 bytes .../utils/__pycache__/wheel.cpython-39.pyc | Bin 0 -> 6346 bytes .../pip/_internal/utils/appdirs.py | 44 + .../pip/_internal/utils/compat.py | 293 + .../pip/_internal/utils/compatibility_tags.py | 178 + .../pip/_internal/utils/datetime.py | 14 + .../pip/_internal/utils/deprecation.py | 104 + .../pip/_internal/utils/direct_url_helpers.py | 126 + .../pip/_internal/utils/distutils_args.py | 48 + .../pip/_internal/utils/encoding.py | 41 + .../pip/_internal/utils/entrypoints.py | 31 + .../pip/_internal/utils/filesystem.py | 224 + .../pip/_internal/utils/filetypes.py | 26 + .../pip/_internal/utils/glibc.py | 98 + .../pip/_internal/utils/hashes.py | 169 + .../_internal/utils/inject_securetransport.py | 36 + .../pip/_internal/utils/logging.py | 399 ++ .../site-packages/pip/_internal/utils/misc.py | 977 +++ .../pip/_internal/utils/models.py | 44 + .../pip/_internal/utils/packaging.py | 95 + .../pip/_internal/utils/parallel.py | 107 + .../pip/_internal/utils/pkg_resources.py | 44 + .../pip/_internal/utils/setuptools_build.py | 181 + .../pip/_internal/utils/subprocess.py | 299 + .../pip/_internal/utils/temp_dir.py | 284 + .../pip/_internal/utils/typing.py | 38 + .../pip/_internal/utils/unpacking.py | 281 + .../site-packages/pip/_internal/utils/urls.py | 55 + .../pip/_internal/utils/virtualenv.py | 119 + .../pip/_internal/utils/wheel.py | 225 + .../pip/_internal/vcs/__init__.py | 15 + .../vcs/__pycache__/__init__.cpython-39.pyc | Bin 0 -> 483 bytes .../vcs/__pycache__/bazaar.cpython-39.pyc | Bin 0 -> 3805 bytes .../vcs/__pycache__/git.cpython-39.pyc | Bin 0 -> 10618 bytes .../vcs/__pycache__/mercurial.cpython-39.pyc | Bin 0 -> 5180 bytes .../vcs/__pycache__/subversion.cpython-39.pyc | Bin 0 -> 8605 bytes .../__pycache__/versioncontrol.cpython-39.pyc | Bin 0 -> 19693 bytes .../site-packages/pip/_internal/vcs/bazaar.py | 123 + .../site-packages/pip/_internal/vcs/git.py | 460 ++ .../pip/_internal/vcs/mercurial.py | 172 + .../pip/_internal/vcs/subversion.py | 340 + .../pip/_internal/vcs/versioncontrol.py | 735 +++ .../pip/_internal/wheel_builder.py | 363 ++ .../site-packages/pip/_vendor/__init__.py | 123 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 3310 bytes .../site-packages/pip/_vendor/vendor.txt | 24 + .../pkg_resources-0.0.0.dist-info/AUTHORS.txt | 590 ++ .../pkg_resources-0.0.0.dist-info/INSTALLER | 1 + .../pkg_resources-0.0.0.dist-info/LICENSE.txt | 20 + .../pkg_resources-0.0.0.dist-info/METADATA | 13 + .../pkg_resources-0.0.0.dist-info/RECORD | 39 + .../pkg_resources-0.0.0.dist-info/REQUESTED | 0 .../pkg_resources-0.0.0.dist-info/WHEEL | 6 + .../site-packages/pkg_resources/__init__.py | 3296 ++++++++++ .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 100372 bytes .../__pycache__/py31compat.cpython-39.pyc | Bin 0 -> 637 bytes .../pkg_resources/_vendor/__init__.py | 0 .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 192 bytes .../__pycache__/appdirs.cpython-39.pyc | Bin 0 -> 20509 bytes .../__pycache__/pyparsing.cpython-39.pyc | Bin 0 -> 201345 bytes .../_vendor/__pycache__/six.cpython-39.pyc | Bin 0 -> 24475 bytes .../pkg_resources/_vendor/appdirs.py | 608 ++ .../_vendor/packaging/__about__.py | 21 + .../_vendor/packaging/__init__.py | 14 + .../__pycache__/__about__.cpython-39.pyc | Bin 0 -> 716 bytes .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 554 bytes .../__pycache__/_compat.cpython-39.pyc | Bin 0 -> 1016 bytes .../__pycache__/_structures.cpython-39.pyc | Bin 0 -> 2798 bytes .../__pycache__/markers.cpython-39.pyc | Bin 0 -> 8926 bytes .../__pycache__/requirements.cpython-39.pyc | Bin 0 -> 3913 bytes .../__pycache__/specifiers.cpython-39.pyc | Bin 0 -> 19802 bytes .../__pycache__/utils.cpython-39.pyc | Bin 0 -> 501 bytes .../__pycache__/version.cpython-39.pyc | Bin 0 -> 10637 bytes .../_vendor/packaging/_compat.py | 30 + .../_vendor/packaging/_structures.py | 68 + .../_vendor/packaging/markers.py | 301 + .../_vendor/packaging/requirements.py | 127 + .../_vendor/packaging/specifiers.py | 774 +++ .../pkg_resources/_vendor/packaging/utils.py | 14 + .../_vendor/packaging/version.py | 393 ++ .../pkg_resources/_vendor/pyparsing.py | 5742 +++++++++++++++++ .../pkg_resources/_vendor/six.py | 868 +++ .../pkg_resources/extern/__init__.py | 73 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 2443 bytes .../site-packages/pkg_resources/py31compat.py | 23 + .../setuptools-44.1.1.dist-info/AUTHORS.txt | 590 ++ .../setuptools-44.1.1.dist-info/INSTALLER | 1 + .../setuptools-44.1.1.dist-info/LICENSE.txt | 20 + .../setuptools-44.1.1.dist-info/METADATA | 82 + .../setuptools-44.1.1.dist-info/RECORD | 164 + .../setuptools-44.1.1.dist-info/REQUESTED | 0 .../setuptools-44.1.1.dist-info/WHEEL | 6 + .../dependency_links.txt | 2 + .../entry_points.txt | 68 + .../setuptools-44.1.1.dist-info/top_level.txt | 3 + .../setuptools-44.1.1.dist-info/zip-safe | 1 + .../site-packages/setuptools/__init__.py | 245 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 8633 bytes .../_deprecation_warning.cpython-39.pyc | Bin 0 -> 551 bytes .../__pycache__/_imp.cpython-39.pyc | Bin 0 -> 1921 bytes .../__pycache__/archive_util.cpython-39.pyc | Bin 0 -> 5223 bytes .../__pycache__/build_meta.cpython-39.pyc | Bin 0 -> 8638 bytes .../__pycache__/config.cpython-39.pyc | Bin 0 -> 17944 bytes .../__pycache__/dep_util.cpython-39.pyc | Bin 0 -> 854 bytes .../__pycache__/depends.cpython-39.pyc | Bin 0 -> 5272 bytes .../__pycache__/dist.cpython-39.pyc | Bin 0 -> 42478 bytes .../__pycache__/errors.cpython-39.pyc | Bin 0 -> 851 bytes .../__pycache__/extension.cpython-39.pyc | Bin 0 -> 2000 bytes .../__pycache__/glob.cpython-39.pyc | Bin 0 -> 3758 bytes .../__pycache__/installer.cpython-39.pyc | Bin 0 -> 4105 bytes .../__pycache__/launch.cpython-39.pyc | Bin 0 -> 859 bytes .../__pycache__/lib2to3_ex.cpython-39.pyc | Bin 0 -> 2452 bytes .../__pycache__/monkey.cpython-39.pyc | Bin 0 -> 4673 bytes .../__pycache__/msvc.cpython-39.pyc | Bin 0 -> 39661 bytes .../__pycache__/namespaces.cpython-39.pyc | Bin 0 -> 3671 bytes .../__pycache__/package_index.cpython-39.pyc | Bin 0 -> 33109 bytes .../__pycache__/py27compat.cpython-39.pyc | Bin 0 -> 1784 bytes .../__pycache__/py31compat.cpython-39.pyc | Bin 0 -> 1226 bytes .../__pycache__/py33compat.cpython-39.pyc | Bin 0 -> 1441 bytes .../__pycache__/py34compat.cpython-39.pyc | Bin 0 -> 481 bytes .../__pycache__/sandbox.cpython-39.pyc | Bin 0 -> 15911 bytes .../__pycache__/site-patch.cpython-39.pyc | Bin 0 -> 1511 bytes .../__pycache__/ssl_support.cpython-39.pyc | Bin 0 -> 6879 bytes .../__pycache__/unicode_utils.cpython-39.pyc | Bin 0 -> 1180 bytes .../__pycache__/version.cpython-39.pyc | Bin 0 -> 325 bytes .../__pycache__/wheel.cpython-39.pyc | Bin 0 -> 7464 bytes .../windows_support.cpython-39.pyc | Bin 0 -> 1024 bytes .../setuptools/_deprecation_warning.py | 7 + .../site-packages/setuptools/_imp.py | 73 + .../setuptools/_vendor/__init__.py | 0 .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 189 bytes .../__pycache__/ordered_set.cpython-39.pyc | Bin 0 -> 16383 bytes .../__pycache__/pyparsing.cpython-39.pyc | Bin 0 -> 201342 bytes .../_vendor/__pycache__/six.cpython-39.pyc | Bin 0 -> 24472 bytes .../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 -> 713 bytes .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 551 bytes .../__pycache__/_compat.cpython-39.pyc | Bin 0 -> 1013 bytes .../__pycache__/_structures.cpython-39.pyc | Bin 0 -> 2795 bytes .../__pycache__/markers.cpython-39.pyc | Bin 0 -> 8934 bytes .../__pycache__/requirements.cpython-39.pyc | Bin 0 -> 4026 bytes .../__pycache__/specifiers.cpython-39.pyc | Bin 0 -> 19747 bytes .../packaging/__pycache__/tags.cpython-39.pyc | Bin 0 -> 10825 bytes .../__pycache__/utils.cpython-39.pyc | Bin 0 -> 1460 bytes .../__pycache__/version.cpython-39.pyc | Bin 0 -> 12080 bytes .../setuptools/_vendor/packaging/_compat.py | 31 + .../_vendor/packaging/_structures.py | 68 + .../setuptools/_vendor/packaging/markers.py | 296 + .../_vendor/packaging/requirements.py | 138 + .../_vendor/packaging/specifiers.py | 749 +++ .../setuptools/_vendor/packaging/tags.py | 404 ++ .../setuptools/_vendor/packaging/utils.py | 57 + .../setuptools/_vendor/packaging/version.py | 420 ++ .../setuptools/_vendor/pyparsing.py | 5742 +++++++++++++++++ .../site-packages/setuptools/_vendor/six.py | 868 +++ .../site-packages/setuptools/archive_util.py | 173 + .../site-packages/setuptools/build_meta.py | 264 + .../site-packages/setuptools/cli-32.exe | Bin 0 -> 65536 bytes .../site-packages/setuptools/cli-64.exe | Bin 0 -> 74752 bytes .../site-packages/setuptools/cli.exe | Bin 0 -> 65536 bytes .../setuptools/command/__init__.py | 17 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 669 bytes .../command/__pycache__/alias.cpython-39.pyc | Bin 0 -> 2427 bytes .../__pycache__/bdist_egg.cpython-39.pyc | Bin 0 -> 14215 bytes .../__pycache__/bdist_rpm.cpython-39.pyc | Bin 0 -> 1817 bytes .../__pycache__/bdist_wininst.cpython-39.pyc | Bin 0 -> 993 bytes .../__pycache__/build_clib.cpython-39.pyc | Bin 0 -> 2472 bytes .../__pycache__/build_ext.cpython-39.pyc | Bin 0 -> 9862 bytes .../__pycache__/build_py.cpython-39.pyc | Bin 0 -> 8693 bytes .../__pycache__/develop.cpython-39.pyc | Bin 0 -> 6572 bytes .../__pycache__/dist_info.cpython-39.pyc | Bin 0 -> 1396 bytes .../__pycache__/easy_install.cpython-39.pyc | Bin 0 -> 67006 bytes .../__pycache__/egg_info.cpython-39.pyc | Bin 0 -> 21823 bytes .../__pycache__/install.cpython-39.pyc | Bin 0 -> 4037 bytes .../install_egg_info.cpython-39.pyc | Bin 0 -> 2925 bytes .../__pycache__/install_lib.cpython-39.pyc | Bin 0 -> 5090 bytes .../install_scripts.cpython-39.pyc | Bin 0 -> 2304 bytes .../__pycache__/py36compat.cpython-39.pyc | Bin 0 -> 4645 bytes .../__pycache__/register.cpython-39.pyc | Bin 0 -> 846 bytes .../command/__pycache__/rotate.cpython-39.pyc | Bin 0 -> 2541 bytes .../__pycache__/saveopts.cpython-39.pyc | Bin 0 -> 924 bytes .../command/__pycache__/sdist.cpython-39.pyc | Bin 0 -> 7934 bytes .../command/__pycache__/setopt.cpython-39.pyc | Bin 0 -> 4576 bytes .../command/__pycache__/test.cpython-39.pyc | Bin 0 -> 8674 bytes .../command/__pycache__/upload.cpython-39.pyc | Bin 0 -> 819 bytes .../__pycache__/upload_docs.cpython-39.pyc | Bin 0 -> 6213 bytes .../site-packages/setuptools/command/alias.py | 80 + .../setuptools/command/bdist_egg.py | 502 ++ .../setuptools/command/bdist_rpm.py | 43 + .../setuptools/command/bdist_wininst.py | 21 + .../setuptools/command/build_clib.py | 98 + .../setuptools/command/build_ext.py | 327 + .../setuptools/command/build_py.py | 270 + .../setuptools/command/develop.py | 221 + .../setuptools/command/dist_info.py | 36 + .../setuptools/command/easy_install.py | 2402 +++++++ .../setuptools/command/egg_info.py | 717 ++ .../setuptools/command/install.py | 125 + .../setuptools/command/install_egg_info.py | 82 + .../setuptools/command/install_lib.py | 147 + .../setuptools/command/install_scripts.py | 65 + .../setuptools/command/launcher manifest.xml | 15 + .../setuptools/command/py36compat.py | 136 + .../setuptools/command/register.py | 18 + .../setuptools/command/rotate.py | 66 + .../setuptools/command/saveopts.py | 22 + .../site-packages/setuptools/command/sdist.py | 252 + .../setuptools/command/setopt.py | 149 + .../site-packages/setuptools/command/test.py | 279 + .../setuptools/command/upload.py | 17 + .../setuptools/command/upload_docs.py | 206 + .../site-packages/setuptools/config.py | 659 ++ .../site-packages/setuptools/dep_util.py | 23 + .../site-packages/setuptools/depends.py | 176 + .../site-packages/setuptools/dist.py | 1274 ++++ .../site-packages/setuptools/errors.py | 16 + .../site-packages/setuptools/extension.py | 57 + .../setuptools/extern/__init__.py | 73 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 2457 bytes .../site-packages/setuptools/glob.py | 174 + .../site-packages/setuptools/gui-32.exe | Bin 0 -> 65536 bytes .../site-packages/setuptools/gui-64.exe | Bin 0 -> 75264 bytes .../site-packages/setuptools/gui.exe | Bin 0 -> 65536 bytes .../site-packages/setuptools/installer.py | 150 + .../site-packages/setuptools/launch.py | 35 + .../site-packages/setuptools/lib2to3_ex.py | 62 + .../site-packages/setuptools/monkey.py | 179 + .../site-packages/setuptools/msvc.py | 1679 +++++ .../site-packages/setuptools/namespaces.py | 107 + .../site-packages/setuptools/package_index.py | 1136 ++++ .../site-packages/setuptools/py27compat.py | 60 + .../site-packages/setuptools/py31compat.py | 32 + .../site-packages/setuptools/py33compat.py | 59 + .../site-packages/setuptools/py34compat.py | 13 + .../site-packages/setuptools/sandbox.py | 491 ++ .../setuptools/script (dev).tmpl | 6 + .../site-packages/setuptools/script.tmpl | 3 + .../site-packages/setuptools/site-patch.py | 74 + .../site-packages/setuptools/ssl_support.py | 260 + .../site-packages/setuptools/unicode_utils.py | 44 + .../site-packages/setuptools/version.py | 6 + .../site-packages/setuptools/wheel.py | 220 + .../setuptools/windows_support.py | 29 + .../site-packages/werkzeug/__init__.py | 6 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 353 bytes .../__pycache__/_internal.cpython-39.pyc | Bin 0 -> 9005 bytes .../__pycache__/_reloader.cpython-39.pyc | Bin 0 -> 12406 bytes .../__pycache__/exceptions.cpython-39.pyc | Bin 0 -> 27681 bytes .../__pycache__/formparser.cpython-39.pyc | Bin 0 -> 14319 bytes .../werkzeug/__pycache__/http.cpython-39.pyc | Bin 0 -> 41637 bytes .../werkzeug/__pycache__/local.cpython-39.pyc | Bin 0 -> 20497 bytes .../__pycache__/security.cpython-39.pyc | Bin 0 -> 5647 bytes .../__pycache__/serving.cpython-39.pyc | Bin 0 -> 29592 bytes .../werkzeug/__pycache__/test.cpython-39.pyc | Bin 0 -> 44284 bytes .../__pycache__/testapp.cpython-39.pyc | Bin 0 -> 6361 bytes .../werkzeug/__pycache__/urls.cpython-39.pyc | Bin 0 -> 39418 bytes .../__pycache__/user_agent.cpython-39.pyc | Bin 0 -> 1838 bytes .../werkzeug/__pycache__/utils.cpython-39.pyc | Bin 0 -> 22019 bytes .../werkzeug/__pycache__/wsgi.cpython-39.pyc | Bin 0 -> 25890 bytes .../site-packages/werkzeug/_internal.py | 311 + .../site-packages/werkzeug/_reloader.py | 458 ++ .../werkzeug/datastructures/__init__.py | 34 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 1559 bytes .../__pycache__/accept.cpython-39.pyc | Bin 0 -> 10614 bytes .../__pycache__/auth.cpython-39.pyc | Bin 0 -> 15533 bytes .../__pycache__/cache_control.cpython-39.pyc | Bin 0 -> 6576 bytes .../__pycache__/csp.cpython-39.pyc | Bin 0 -> 4144 bytes .../__pycache__/etag.cpython-39.pyc | Bin 0 -> 3945 bytes .../__pycache__/file_storage.cpython-39.pyc | Bin 0 -> 5997 bytes .../__pycache__/headers.cpython-39.pyc | Bin 0 -> 18722 bytes .../__pycache__/mixins.cpython-39.pyc | Bin 0 -> 9574 bytes .../__pycache__/range.cpython-39.pyc | Bin 0 -> 6080 bytes .../__pycache__/structures.cpython-39.pyc | Bin 0 -> 36236 bytes .../werkzeug/datastructures/accept.py | 326 + .../werkzeug/datastructures/accept.pyi | 54 + .../werkzeug/datastructures/auth.py | 509 ++ .../werkzeug/datastructures/cache_control.py | 175 + .../werkzeug/datastructures/cache_control.pyi | 109 + .../werkzeug/datastructures/csp.py | 94 + .../werkzeug/datastructures/csp.pyi | 169 + .../werkzeug/datastructures/etag.py | 95 + .../werkzeug/datastructures/etag.pyi | 30 + .../werkzeug/datastructures/file_storage.py | 192 + .../werkzeug/datastructures/file_storage.pyi | 47 + .../werkzeug/datastructures/headers.py | 566 ++ .../werkzeug/datastructures/headers.pyi | 109 + .../werkzeug/datastructures/mixins.py | 242 + .../werkzeug/datastructures/mixins.pyi | 97 + .../werkzeug/datastructures/range.py | 180 + .../werkzeug/datastructures/range.pyi | 57 + .../werkzeug/datastructures/structures.py | 1006 +++ .../werkzeug/datastructures/structures.pyi | 208 + .../site-packages/werkzeug/debug/__init__.py | 534 ++ .../debug/__pycache__/__init__.cpython-39.pyc | Bin 0 -> 14073 bytes .../debug/__pycache__/console.cpython-39.pyc | Bin 0 -> 8095 bytes .../debug/__pycache__/repr.cpython-39.pyc | Bin 0 -> 8845 bytes .../debug/__pycache__/tbtools.cpython-39.pyc | Bin 0 -> 11500 bytes .../site-packages/werkzeug/debug/console.py | 219 + .../site-packages/werkzeug/debug/repr.py | 283 + .../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/style.css | 150 + .../site-packages/werkzeug/debug/tbtools.py | 437 ++ .../site-packages/werkzeug/exceptions.py | 879 +++ .../site-packages/werkzeug/formparser.py | 549 ++ .../python3.9/site-packages/werkzeug/http.py | 1534 +++++ .../python3.9/site-packages/werkzeug/local.py | 643 ++ .../werkzeug/middleware/__init__.py | 22 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 698 bytes .../__pycache__/dispatcher.cpython-39.pyc | Bin 0 -> 2786 bytes .../__pycache__/http_proxy.cpython-39.pyc | Bin 0 -> 6809 bytes .../__pycache__/lint.cpython-39.pyc | Bin 0 -> 12706 bytes .../__pycache__/profiler.cpython-39.pyc | Bin 0 -> 4987 bytes .../__pycache__/proxy_fix.cpython-39.pyc | Bin 0 -> 5942 bytes .../__pycache__/shared_data.cpython-39.pyc | Bin 0 -> 9059 bytes .../werkzeug/middleware/dispatcher.py | 80 + .../werkzeug/middleware/http_proxy.py | 235 + .../site-packages/werkzeug/middleware/lint.py | 420 ++ .../werkzeug/middleware/profiler.py | 141 + .../werkzeug/middleware/proxy_fix.py | 182 + .../werkzeug/middleware/shared_data.py | 282 + .../python3.9/site-packages/werkzeug/py.typed | 0 .../werkzeug/routing/__init__.py | 133 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 4628 bytes .../__pycache__/converters.cpython-39.pyc | Bin 0 -> 9136 bytes .../__pycache__/exceptions.cpython-39.pyc | Bin 0 -> 5484 bytes .../routing/__pycache__/map.cpython-39.pyc | Bin 0 -> 30868 bytes .../__pycache__/matcher.cpython-39.pyc | Bin 0 -> 5066 bytes .../routing/__pycache__/rules.cpython-39.pyc | Bin 0 -> 27350 bytes .../werkzeug/routing/converters.py | 270 + .../werkzeug/routing/exceptions.py | 148 + .../site-packages/werkzeug/routing/map.py | 974 +++ .../site-packages/werkzeug/routing/matcher.py | 202 + .../site-packages/werkzeug/routing/rules.py | 913 +++ .../site-packages/werkzeug/sansio/__init__.py | 0 .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 186 bytes .../sansio/__pycache__/http.cpython-39.pyc | Bin 0 -> 4561 bytes .../__pycache__/multipart.cpython-39.pyc | Bin 0 -> 7400 bytes .../sansio/__pycache__/request.cpython-39.pyc | Bin 0 -> 19723 bytes .../__pycache__/response.cpython-39.pyc | Bin 0 -> 25055 bytes .../sansio/__pycache__/utils.cpython-39.pyc | Bin 0 -> 4459 bytes .../site-packages/werkzeug/sansio/http.py | 202 + .../werkzeug/sansio/multipart.py | 313 + .../site-packages/werkzeug/sansio/request.py | 659 ++ .../site-packages/werkzeug/sansio/response.py | 789 +++ .../site-packages/werkzeug/sansio/utils.py | 158 + .../site-packages/werkzeug/security.py | 172 + .../site-packages/werkzeug/serving.py | 1107 ++++ .../python3.9/site-packages/werkzeug/test.py | 1544 +++++ .../site-packages/werkzeug/testapp.py | 181 + .../python3.9/site-packages/werkzeug/urls.py | 1370 ++++ .../site-packages/werkzeug/user_agent.py | 47 + .../python3.9/site-packages/werkzeug/utils.py | 694 ++ .../werkzeug/wrappers/__init__.py | 3 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 305 bytes .../__pycache__/request.cpython-39.pyc | Bin 0 -> 21399 bytes .../__pycache__/response.cpython-39.pyc | Bin 0 -> 28044 bytes .../werkzeug/wrappers/request.py | 657 ++ .../werkzeug/wrappers/response.py | 834 +++ .../python3.9/site-packages/werkzeug/wsgi.py | 851 +++ .../zipp-3.15.0.dist-info/INSTALLER | 1 + .../zipp-3.15.0.dist-info/LICENSE | 19 + .../zipp-3.15.0.dist-info/METADATA | 106 + .../zipp-3.15.0.dist-info/RECORD | 10 + .../site-packages/zipp-3.15.0.dist-info/WHEEL | 5 + .../zipp-3.15.0.dist-info/top_level.txt | 1 + .../python3.9/site-packages/zipp/__init__.py | 402 ++ .../zipp/__pycache__/__init__.cpython-39.pyc | Bin 0 -> 13249 bytes .../__pycache__/py310compat.cpython-39.pyc | Bin 0 -> 435 bytes .../site-packages/zipp/py310compat.py | 12 + .venv/lib64 | 1 + .venv/pyvenv.cfg | 3 + .../CacheControl-0.12.6-py2.py3-none-any.whl | Bin 0 -> 23441 bytes .../appdirs-1.4.4-py2.py3-none-any.whl | Bin 0 -> 14285 bytes .../certifi-2020.6.20-py2.py3-none-any.whl | Bin 0 -> 161344 bytes .../chardet-4.0.0-py2.py3-none-any.whl | Bin 0 -> 174749 bytes .../colorama-0.4.4-py2.py3-none-any.whl | Bin 0 -> 20722 bytes ...ntextlib2-0.6.0.post1-py2.py3-none-any.whl | Bin 0 -> 12692 bytes .../distlib-0.3.1-py2.py3-none-any.whl | Bin 0 -> 147633 bytes .../distro-1.5.0-py2.py3-none-any.whl | Bin 0 -> 19426 bytes .../html5lib-1.1-py2.py3-none-any.whl | Bin 0 -> 116071 bytes .../idna-2.10-py2.py3-none-any.whl | Bin 0 -> 63344 bytes .../ipaddr-2.2.0-py2.py3-none-any.whl | Bin 0 -> 19706 bytes .../msgpack-1.0.0-py2.py3-none-any.whl | Bin 0 -> 75866 bytes .../packaging-20.9-py2.py3-none-any.whl | Bin 0 -> 41435 bytes .../pep517-0.9.1-py2.py3-none-any.whl | Bin 0 -> 22249 bytes .../pip-20.3.4-py2.py3-none-any.whl | Bin 0 -> 311123 bytes .../pkg_resources-0.0.0-py2.py3-none-any.whl | Bin 0 -> 122731 bytes .../progress-1.5-py2.py3-none-any.whl | Bin 0 -> 12965 bytes .../pyparsing-2.4.7-py2.py3-none-any.whl | Bin 0 -> 72626 bytes .../requests-2.25.1-py2.py3-none-any.whl | Bin 0 -> 62975 bytes .../resolvelib-0.5.4-py2.py3-none-any.whl | Bin 0 -> 17707 bytes .../retrying-1.3.3-py2.py3-none-any.whl | Bin 0 -> 11776 bytes .../setuptools-44.1.1-py2.py3-none-any.whl | Bin 0 -> 473123 bytes .../six-1.16.0-py2.py3-none-any.whl | Bin 0 -> 15791 bytes .../toml-0.10.1-py2.py3-none-any.whl | Bin 0 -> 21108 bytes .../urllib3-1.26.5-py2.py3-none-any.whl | Bin 0 -> 134200 bytes .../webencodings-0.5.1-py2.py3-none-any.whl | Bin 0 -> 15904 bytes .../wheel-0.34.2-py2.py3-none-any.whl | Bin 0 -> 31030 bytes __pycache__/app.cpython-39.pyc | Bin 0 -> 1452 bytes app.py | 43 + copy.html | 186 + index.html | 236 + templates/copy1.html | 186 + templates/copy2.html | 186 + templates/report.html | 65 + 894 files changed, 130232 insertions(+) create mode 100644 .venv/bin/Activate.ps1 create mode 100644 .venv/bin/activate create mode 100644 .venv/bin/activate.csh create mode 100644 .venv/bin/activate.fish create mode 100755 .venv/bin/easy_install create mode 100755 .venv/bin/easy_install-3.9 create mode 100755 .venv/bin/flask create mode 100755 .venv/bin/pip create mode 100755 .venv/bin/pip3 create mode 100755 .venv/bin/pip3.9 create mode 120000 .venv/bin/python create mode 120000 .venv/bin/python3 create mode 120000 .venv/bin/python3.9 create mode 100644 .venv/lib/python3.9/site-packages/Flask-2.3.2.dist-info/INSTALLER create mode 100644 .venv/lib/python3.9/site-packages/Flask-2.3.2.dist-info/LICENSE.rst create mode 100644 .venv/lib/python3.9/site-packages/Flask-2.3.2.dist-info/METADATA create mode 100644 .venv/lib/python3.9/site-packages/Flask-2.3.2.dist-info/RECORD create mode 100644 .venv/lib/python3.9/site-packages/Flask-2.3.2.dist-info/REQUESTED create mode 100644 .venv/lib/python3.9/site-packages/Flask-2.3.2.dist-info/WHEEL create mode 100644 .venv/lib/python3.9/site-packages/Flask-2.3.2.dist-info/entry_points.txt create mode 100644 .venv/lib/python3.9/site-packages/Flask-2.3.2.dist-info/top_level.txt create mode 100644 .venv/lib/python3.9/site-packages/Jinja2-3.1.2.dist-info/INSTALLER create mode 100644 .venv/lib/python3.9/site-packages/Jinja2-3.1.2.dist-info/LICENSE.rst create mode 100644 .venv/lib/python3.9/site-packages/Jinja2-3.1.2.dist-info/METADATA create mode 100644 .venv/lib/python3.9/site-packages/Jinja2-3.1.2.dist-info/RECORD create mode 100644 .venv/lib/python3.9/site-packages/Jinja2-3.1.2.dist-info/WHEEL create mode 100644 .venv/lib/python3.9/site-packages/Jinja2-3.1.2.dist-info/entry_points.txt create mode 100644 .venv/lib/python3.9/site-packages/Jinja2-3.1.2.dist-info/top_level.txt create mode 100644 .venv/lib/python3.9/site-packages/MarkupSafe-2.1.2.dist-info/INSTALLER create mode 100644 .venv/lib/python3.9/site-packages/MarkupSafe-2.1.2.dist-info/LICENSE.rst create mode 100644 .venv/lib/python3.9/site-packages/MarkupSafe-2.1.2.dist-info/METADATA create mode 100644 .venv/lib/python3.9/site-packages/MarkupSafe-2.1.2.dist-info/RECORD create mode 100644 .venv/lib/python3.9/site-packages/MarkupSafe-2.1.2.dist-info/WHEEL create mode 100644 .venv/lib/python3.9/site-packages/MarkupSafe-2.1.2.dist-info/top_level.txt create mode 100644 .venv/lib/python3.9/site-packages/Werkzeug-2.3.3.dist-info/INSTALLER create mode 100644 .venv/lib/python3.9/site-packages/Werkzeug-2.3.3.dist-info/LICENSE.rst create mode 100644 .venv/lib/python3.9/site-packages/Werkzeug-2.3.3.dist-info/METADATA create mode 100644 .venv/lib/python3.9/site-packages/Werkzeug-2.3.3.dist-info/RECORD create mode 100644 .venv/lib/python3.9/site-packages/Werkzeug-2.3.3.dist-info/WHEEL create mode 100644 .venv/lib/python3.9/site-packages/Werkzeug-2.3.3.dist-info/top_level.txt create mode 100644 .venv/lib/python3.9/site-packages/__pycache__/easy_install.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/blinker-1.6.2.dist-info/INSTALLER create mode 100644 .venv/lib/python3.9/site-packages/blinker-1.6.2.dist-info/LICENSE.rst create mode 100644 .venv/lib/python3.9/site-packages/blinker-1.6.2.dist-info/METADATA create mode 100644 .venv/lib/python3.9/site-packages/blinker-1.6.2.dist-info/RECORD create mode 100644 .venv/lib/python3.9/site-packages/blinker-1.6.2.dist-info/WHEEL create mode 100644 .venv/lib/python3.9/site-packages/blinker-1.6.2.dist-info/top_level.txt create mode 100644 .venv/lib/python3.9/site-packages/blinker/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/blinker/__pycache__/__init__.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/blinker/__pycache__/_saferef.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/blinker/__pycache__/_utilities.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/blinker/__pycache__/base.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/blinker/_saferef.py create mode 100644 .venv/lib/python3.9/site-packages/blinker/_utilities.py create mode 100644 .venv/lib/python3.9/site-packages/blinker/base.py create mode 100644 .venv/lib/python3.9/site-packages/blinker/py.typed create mode 100644 .venv/lib/python3.9/site-packages/click-8.1.3.dist-info/INSTALLER create mode 100644 .venv/lib/python3.9/site-packages/click-8.1.3.dist-info/LICENSE.rst create mode 100644 .venv/lib/python3.9/site-packages/click-8.1.3.dist-info/METADATA create mode 100644 .venv/lib/python3.9/site-packages/click-8.1.3.dist-info/RECORD create mode 100644 .venv/lib/python3.9/site-packages/click-8.1.3.dist-info/WHEEL create mode 100644 .venv/lib/python3.9/site-packages/click-8.1.3.dist-info/top_level.txt create mode 100644 .venv/lib/python3.9/site-packages/click/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/click/__pycache__/__init__.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/click/__pycache__/_compat.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/click/__pycache__/_termui_impl.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/click/__pycache__/_textwrap.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/click/__pycache__/_winconsole.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/click/__pycache__/core.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/click/__pycache__/decorators.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/click/__pycache__/exceptions.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/click/__pycache__/formatting.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/click/__pycache__/globals.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/click/__pycache__/parser.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/click/__pycache__/shell_completion.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/click/__pycache__/termui.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/click/__pycache__/testing.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/click/__pycache__/types.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/click/__pycache__/utils.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/click/_compat.py create mode 100644 .venv/lib/python3.9/site-packages/click/_termui_impl.py create mode 100644 .venv/lib/python3.9/site-packages/click/_textwrap.py create mode 100644 .venv/lib/python3.9/site-packages/click/_winconsole.py create mode 100644 .venv/lib/python3.9/site-packages/click/core.py create mode 100644 .venv/lib/python3.9/site-packages/click/decorators.py create mode 100644 .venv/lib/python3.9/site-packages/click/exceptions.py create mode 100644 .venv/lib/python3.9/site-packages/click/formatting.py create mode 100644 .venv/lib/python3.9/site-packages/click/globals.py create mode 100644 .venv/lib/python3.9/site-packages/click/parser.py create mode 100644 .venv/lib/python3.9/site-packages/click/py.typed create mode 100644 .venv/lib/python3.9/site-packages/click/shell_completion.py create mode 100644 .venv/lib/python3.9/site-packages/click/termui.py create mode 100644 .venv/lib/python3.9/site-packages/click/testing.py create mode 100644 .venv/lib/python3.9/site-packages/click/types.py create mode 100644 .venv/lib/python3.9/site-packages/click/utils.py create mode 100644 .venv/lib/python3.9/site-packages/easy_install.py create mode 100644 .venv/lib/python3.9/site-packages/flask/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/flask/__main__.py create mode 100644 .venv/lib/python3.9/site-packages/flask/__pycache__/__init__.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/flask/__pycache__/__main__.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/flask/__pycache__/app.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/flask/__pycache__/blueprints.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/flask/__pycache__/cli.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/flask/__pycache__/config.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/flask/__pycache__/ctx.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/flask/__pycache__/debughelpers.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/flask/__pycache__/globals.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/flask/__pycache__/helpers.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/flask/__pycache__/logging.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/flask/__pycache__/scaffold.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/flask/__pycache__/sessions.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/flask/__pycache__/signals.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/flask/__pycache__/templating.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/flask/__pycache__/testing.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/flask/__pycache__/typing.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/flask/__pycache__/views.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/flask/__pycache__/wrappers.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/flask/app.py create mode 100644 .venv/lib/python3.9/site-packages/flask/blueprints.py create mode 100644 .venv/lib/python3.9/site-packages/flask/cli.py create mode 100644 .venv/lib/python3.9/site-packages/flask/config.py create mode 100644 .venv/lib/python3.9/site-packages/flask/ctx.py create mode 100644 .venv/lib/python3.9/site-packages/flask/debughelpers.py create mode 100644 .venv/lib/python3.9/site-packages/flask/globals.py create mode 100644 .venv/lib/python3.9/site-packages/flask/helpers.py create mode 100644 .venv/lib/python3.9/site-packages/flask/json/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/flask/json/__pycache__/__init__.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/flask/json/__pycache__/provider.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/flask/json/__pycache__/tag.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/flask/json/provider.py create mode 100644 .venv/lib/python3.9/site-packages/flask/json/tag.py create mode 100644 .venv/lib/python3.9/site-packages/flask/logging.py create mode 100644 .venv/lib/python3.9/site-packages/flask/py.typed create mode 100644 .venv/lib/python3.9/site-packages/flask/scaffold.py create mode 100644 .venv/lib/python3.9/site-packages/flask/sessions.py create mode 100644 .venv/lib/python3.9/site-packages/flask/signals.py create mode 100644 .venv/lib/python3.9/site-packages/flask/templating.py create mode 100644 .venv/lib/python3.9/site-packages/flask/testing.py create mode 100644 .venv/lib/python3.9/site-packages/flask/typing.py create mode 100644 .venv/lib/python3.9/site-packages/flask/views.py create mode 100644 .venv/lib/python3.9/site-packages/flask/wrappers.py create mode 100644 .venv/lib/python3.9/site-packages/importlib_metadata-6.6.0.dist-info/INSTALLER create mode 100644 .venv/lib/python3.9/site-packages/importlib_metadata-6.6.0.dist-info/LICENSE create mode 100644 .venv/lib/python3.9/site-packages/importlib_metadata-6.6.0.dist-info/METADATA create mode 100644 .venv/lib/python3.9/site-packages/importlib_metadata-6.6.0.dist-info/RECORD create mode 100644 .venv/lib/python3.9/site-packages/importlib_metadata-6.6.0.dist-info/WHEEL create mode 100644 .venv/lib/python3.9/site-packages/importlib_metadata-6.6.0.dist-info/top_level.txt create mode 100644 .venv/lib/python3.9/site-packages/importlib_metadata/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/importlib_metadata/__pycache__/__init__.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/importlib_metadata/__pycache__/_adapters.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/importlib_metadata/__pycache__/_collections.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/importlib_metadata/__pycache__/_compat.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/importlib_metadata/__pycache__/_functools.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/importlib_metadata/__pycache__/_itertools.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/importlib_metadata/__pycache__/_meta.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/importlib_metadata/__pycache__/_py39compat.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/importlib_metadata/__pycache__/_text.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/importlib_metadata/_adapters.py create mode 100644 .venv/lib/python3.9/site-packages/importlib_metadata/_collections.py create mode 100644 .venv/lib/python3.9/site-packages/importlib_metadata/_compat.py create mode 100644 .venv/lib/python3.9/site-packages/importlib_metadata/_functools.py create mode 100644 .venv/lib/python3.9/site-packages/importlib_metadata/_itertools.py create mode 100644 .venv/lib/python3.9/site-packages/importlib_metadata/_meta.py create mode 100644 .venv/lib/python3.9/site-packages/importlib_metadata/_py39compat.py create mode 100644 .venv/lib/python3.9/site-packages/importlib_metadata/_text.py create mode 100644 .venv/lib/python3.9/site-packages/importlib_metadata/py.typed create mode 100644 .venv/lib/python3.9/site-packages/itsdangerous-2.1.2.dist-info/INSTALLER create mode 100644 .venv/lib/python3.9/site-packages/itsdangerous-2.1.2.dist-info/LICENSE.rst create mode 100644 .venv/lib/python3.9/site-packages/itsdangerous-2.1.2.dist-info/METADATA create mode 100644 .venv/lib/python3.9/site-packages/itsdangerous-2.1.2.dist-info/RECORD create mode 100644 .venv/lib/python3.9/site-packages/itsdangerous-2.1.2.dist-info/WHEEL create mode 100644 .venv/lib/python3.9/site-packages/itsdangerous-2.1.2.dist-info/top_level.txt create mode 100644 .venv/lib/python3.9/site-packages/itsdangerous/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/itsdangerous/__pycache__/__init__.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/itsdangerous/__pycache__/_json.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/itsdangerous/__pycache__/encoding.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/itsdangerous/__pycache__/exc.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/itsdangerous/__pycache__/serializer.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/itsdangerous/__pycache__/signer.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/itsdangerous/__pycache__/timed.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/itsdangerous/__pycache__/url_safe.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/itsdangerous/_json.py create mode 100644 .venv/lib/python3.9/site-packages/itsdangerous/encoding.py create mode 100644 .venv/lib/python3.9/site-packages/itsdangerous/exc.py create mode 100644 .venv/lib/python3.9/site-packages/itsdangerous/py.typed create mode 100644 .venv/lib/python3.9/site-packages/itsdangerous/serializer.py create mode 100644 .venv/lib/python3.9/site-packages/itsdangerous/signer.py create mode 100644 .venv/lib/python3.9/site-packages/itsdangerous/timed.py create mode 100644 .venv/lib/python3.9/site-packages/itsdangerous/url_safe.py create mode 100644 .venv/lib/python3.9/site-packages/jinja2/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/jinja2/__pycache__/__init__.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/jinja2/__pycache__/_identifier.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/jinja2/__pycache__/async_utils.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/jinja2/__pycache__/bccache.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/jinja2/__pycache__/compiler.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/jinja2/__pycache__/constants.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/jinja2/__pycache__/debug.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/jinja2/__pycache__/defaults.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/jinja2/__pycache__/environment.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/jinja2/__pycache__/exceptions.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/jinja2/__pycache__/ext.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/jinja2/__pycache__/filters.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/jinja2/__pycache__/idtracking.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/jinja2/__pycache__/lexer.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/jinja2/__pycache__/loaders.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/jinja2/__pycache__/meta.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/jinja2/__pycache__/nativetypes.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/jinja2/__pycache__/nodes.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/jinja2/__pycache__/optimizer.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/jinja2/__pycache__/parser.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/jinja2/__pycache__/runtime.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/jinja2/__pycache__/sandbox.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/jinja2/__pycache__/tests.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/jinja2/__pycache__/utils.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/jinja2/__pycache__/visitor.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/jinja2/_identifier.py create mode 100644 .venv/lib/python3.9/site-packages/jinja2/async_utils.py create mode 100644 .venv/lib/python3.9/site-packages/jinja2/bccache.py create mode 100644 .venv/lib/python3.9/site-packages/jinja2/compiler.py create mode 100644 .venv/lib/python3.9/site-packages/jinja2/constants.py create mode 100644 .venv/lib/python3.9/site-packages/jinja2/debug.py create mode 100644 .venv/lib/python3.9/site-packages/jinja2/defaults.py create mode 100644 .venv/lib/python3.9/site-packages/jinja2/environment.py create mode 100644 .venv/lib/python3.9/site-packages/jinja2/exceptions.py create mode 100644 .venv/lib/python3.9/site-packages/jinja2/ext.py create mode 100644 .venv/lib/python3.9/site-packages/jinja2/filters.py create mode 100644 .venv/lib/python3.9/site-packages/jinja2/idtracking.py create mode 100644 .venv/lib/python3.9/site-packages/jinja2/lexer.py create mode 100644 .venv/lib/python3.9/site-packages/jinja2/loaders.py create mode 100644 .venv/lib/python3.9/site-packages/jinja2/meta.py create mode 100644 .venv/lib/python3.9/site-packages/jinja2/nativetypes.py create mode 100644 .venv/lib/python3.9/site-packages/jinja2/nodes.py create mode 100644 .venv/lib/python3.9/site-packages/jinja2/optimizer.py create mode 100644 .venv/lib/python3.9/site-packages/jinja2/parser.py create mode 100644 .venv/lib/python3.9/site-packages/jinja2/py.typed create mode 100644 .venv/lib/python3.9/site-packages/jinja2/runtime.py create mode 100644 .venv/lib/python3.9/site-packages/jinja2/sandbox.py create mode 100644 .venv/lib/python3.9/site-packages/jinja2/tests.py create mode 100644 .venv/lib/python3.9/site-packages/jinja2/utils.py create mode 100644 .venv/lib/python3.9/site-packages/jinja2/visitor.py create mode 100644 .venv/lib/python3.9/site-packages/markupsafe/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/markupsafe/__pycache__/__init__.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/markupsafe/__pycache__/_native.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/markupsafe/_native.py create mode 100644 .venv/lib/python3.9/site-packages/markupsafe/_speedups.c create mode 100755 .venv/lib/python3.9/site-packages/markupsafe/_speedups.cpython-39-x86_64-linux-gnu.so create mode 100644 .venv/lib/python3.9/site-packages/markupsafe/_speedups.pyi create mode 100644 .venv/lib/python3.9/site-packages/markupsafe/py.typed create mode 100644 .venv/lib/python3.9/site-packages/pip-20.3.4.dist-info/INSTALLER create mode 100644 .venv/lib/python3.9/site-packages/pip-20.3.4.dist-info/LICENSE.txt create mode 100644 .venv/lib/python3.9/site-packages/pip-20.3.4.dist-info/METADATA create mode 100644 .venv/lib/python3.9/site-packages/pip-20.3.4.dist-info/RECORD create mode 100644 .venv/lib/python3.9/site-packages/pip-20.3.4.dist-info/REQUESTED create mode 100644 .venv/lib/python3.9/site-packages/pip-20.3.4.dist-info/WHEEL create mode 100644 .venv/lib/python3.9/site-packages/pip-20.3.4.dist-info/entry_points.txt create mode 100644 .venv/lib/python3.9/site-packages/pip-20.3.4.dist-info/top_level.txt create mode 100644 .venv/lib/python3.9/site-packages/pip/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/pip/__main__.py create mode 100644 .venv/lib/python3.9/site-packages/pip/__pycache__/__init__.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/__pycache__/__main__.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/__pycache__/__init__.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/__pycache__/build_env.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/__pycache__/cache.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/__pycache__/configuration.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/__pycache__/exceptions.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/__pycache__/locations.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/__pycache__/main.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/__pycache__/pyproject.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/__pycache__/self_outdated_check.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/__pycache__/wheel_builder.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/build_env.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/cache.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/cli/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/cli/__pycache__/__init__.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/cli/__pycache__/autocompletion.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/cli/__pycache__/base_command.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/cli/__pycache__/cmdoptions.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/cli/__pycache__/command_context.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/cli/__pycache__/main.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/cli/__pycache__/main_parser.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/cli/__pycache__/parser.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/cli/__pycache__/progress_bars.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/cli/__pycache__/req_command.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/cli/__pycache__/spinners.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/cli/__pycache__/status_codes.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/cli/autocompletion.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/cli/base_command.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/cli/cmdoptions.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/cli/command_context.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/cli/main.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/cli/main_parser.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/cli/parser.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/cli/progress_bars.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/cli/req_command.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/cli/spinners.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/cli/status_codes.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/commands/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/commands/__pycache__/__init__.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/commands/__pycache__/cache.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/commands/__pycache__/check.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/commands/__pycache__/completion.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/commands/__pycache__/configuration.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/commands/__pycache__/debug.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/commands/__pycache__/download.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/commands/__pycache__/freeze.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/commands/__pycache__/hash.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/commands/__pycache__/help.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/commands/__pycache__/install.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/commands/__pycache__/list.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/commands/__pycache__/search.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/commands/__pycache__/show.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/commands/__pycache__/uninstall.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/commands/__pycache__/wheel.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/commands/cache.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/commands/check.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/commands/completion.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/commands/configuration.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/commands/debug.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/commands/download.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/commands/freeze.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/commands/hash.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/commands/help.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/commands/install.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/commands/list.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/commands/search.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/commands/show.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/commands/uninstall.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/commands/wheel.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/configuration.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/distributions/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/distributions/__pycache__/__init__.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/distributions/__pycache__/base.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/distributions/__pycache__/installed.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/distributions/__pycache__/sdist.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/distributions/__pycache__/wheel.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/distributions/base.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/distributions/installed.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/distributions/sdist.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/distributions/wheel.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/exceptions.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/index/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/index/__pycache__/__init__.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/index/__pycache__/collector.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/index/__pycache__/package_finder.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/index/collector.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/index/package_finder.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/locations.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/main.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/models/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/models/__pycache__/__init__.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/models/__pycache__/candidate.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/models/__pycache__/direct_url.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/models/__pycache__/format_control.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/models/__pycache__/index.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/models/__pycache__/link.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/models/__pycache__/scheme.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/models/__pycache__/search_scope.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/models/__pycache__/selection_prefs.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/models/__pycache__/target_python.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/models/__pycache__/wheel.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/models/candidate.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/models/direct_url.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/models/format_control.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/models/index.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/models/link.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/models/scheme.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/models/search_scope.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/models/selection_prefs.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/models/target_python.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/models/wheel.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/network/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/network/__pycache__/__init__.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/network/__pycache__/auth.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/network/__pycache__/cache.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/network/__pycache__/download.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/network/__pycache__/lazy_wheel.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/network/__pycache__/session.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/network/__pycache__/utils.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/network/__pycache__/xmlrpc.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/network/auth.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/network/cache.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/network/download.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/network/lazy_wheel.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/network/session.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/network/utils.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/network/xmlrpc.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/operations/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/operations/__pycache__/__init__.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/operations/__pycache__/check.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/operations/__pycache__/freeze.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/operations/__pycache__/prepare.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/operations/build/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/operations/build/__pycache__/__init__.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/operations/build/__pycache__/metadata.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/operations/build/__pycache__/metadata_legacy.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/operations/build/__pycache__/wheel.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/operations/build/__pycache__/wheel_legacy.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/operations/build/metadata.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/operations/build/metadata_legacy.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/operations/build/wheel.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/operations/build/wheel_legacy.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/operations/check.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/operations/freeze.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/operations/install/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/operations/install/__pycache__/__init__.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/operations/install/__pycache__/editable_legacy.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/operations/install/__pycache__/legacy.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/operations/install/__pycache__/wheel.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/operations/install/editable_legacy.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/operations/install/legacy.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/operations/install/wheel.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/operations/prepare.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/pyproject.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/req/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/req/__pycache__/__init__.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/req/__pycache__/constructors.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/req/__pycache__/req_file.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/req/__pycache__/req_install.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/req/__pycache__/req_set.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/req/__pycache__/req_tracker.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/req/__pycache__/req_uninstall.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/req/constructors.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/req/req_file.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/req/req_install.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/req/req_set.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/req/req_tracker.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/req/req_uninstall.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/resolution/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/resolution/__pycache__/__init__.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/resolution/__pycache__/base.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/resolution/base.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/resolution/legacy/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/resolution/legacy/__pycache__/__init__.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/resolution/legacy/__pycache__/resolver.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/resolution/legacy/resolver.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/__pycache__/__init__.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/__pycache__/base.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/__pycache__/candidates.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/__pycache__/factory.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/__pycache__/found_candidates.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/__pycache__/provider.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/__pycache__/reporter.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/__pycache__/requirements.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/__pycache__/resolver.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/base.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/candidates.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/factory.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/found_candidates.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/provider.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/reporter.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/requirements.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/resolver.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/self_outdated_check.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/utils/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/__init__.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/appdirs.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/compat.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/compatibility_tags.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/datetime.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/deprecation.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/direct_url_helpers.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/distutils_args.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/encoding.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/entrypoints.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/filesystem.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/filetypes.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/glibc.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/hashes.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/inject_securetransport.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/logging.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/misc.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/models.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/packaging.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/parallel.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/pkg_resources.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/setuptools_build.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/subprocess.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/temp_dir.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/typing.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/unpacking.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/urls.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/virtualenv.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/wheel.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/utils/appdirs.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/utils/compat.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/utils/compatibility_tags.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/utils/datetime.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/utils/deprecation.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/utils/direct_url_helpers.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/utils/distutils_args.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/utils/encoding.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/utils/entrypoints.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/utils/filesystem.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/utils/filetypes.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/utils/glibc.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/utils/hashes.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/utils/inject_securetransport.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/utils/logging.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/utils/misc.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/utils/models.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/utils/packaging.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/utils/parallel.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/utils/pkg_resources.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/utils/setuptools_build.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/utils/subprocess.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/utils/temp_dir.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/utils/typing.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/utils/unpacking.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/utils/urls.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/utils/virtualenv.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/utils/wheel.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/vcs/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/vcs/__pycache__/__init__.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/vcs/__pycache__/bazaar.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/vcs/__pycache__/git.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/vcs/__pycache__/mercurial.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/vcs/__pycache__/subversion.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/vcs/__pycache__/versioncontrol.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/vcs/bazaar.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/vcs/git.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/vcs/mercurial.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/vcs/subversion.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/vcs/versioncontrol.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/wheel_builder.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/__pycache__/__init__.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/vendor.txt create mode 100644 .venv/lib/python3.9/site-packages/pkg_resources-0.0.0.dist-info/AUTHORS.txt create mode 100644 .venv/lib/python3.9/site-packages/pkg_resources-0.0.0.dist-info/INSTALLER create mode 100644 .venv/lib/python3.9/site-packages/pkg_resources-0.0.0.dist-info/LICENSE.txt create mode 100644 .venv/lib/python3.9/site-packages/pkg_resources-0.0.0.dist-info/METADATA create mode 100644 .venv/lib/python3.9/site-packages/pkg_resources-0.0.0.dist-info/RECORD create mode 100644 .venv/lib/python3.9/site-packages/pkg_resources-0.0.0.dist-info/REQUESTED create mode 100644 .venv/lib/python3.9/site-packages/pkg_resources-0.0.0.dist-info/WHEEL create mode 100644 .venv/lib/python3.9/site-packages/pkg_resources/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/pkg_resources/__pycache__/__init__.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pkg_resources/__pycache__/py31compat.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pkg_resources/_vendor/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/pkg_resources/_vendor/__pycache__/__init__.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pkg_resources/_vendor/__pycache__/appdirs.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pkg_resources/_vendor/__pycache__/pyparsing.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pkg_resources/_vendor/__pycache__/six.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pkg_resources/_vendor/appdirs.py create mode 100644 .venv/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/__about__.py create mode 100644 .venv/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/__pycache__/__about__.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/__pycache__/__init__.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/__pycache__/_compat.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/__pycache__/_structures.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/__pycache__/markers.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/__pycache__/requirements.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/__pycache__/specifiers.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/__pycache__/utils.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/__pycache__/version.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/_compat.py create mode 100644 .venv/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/_structures.py create mode 100644 .venv/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/markers.py create mode 100644 .venv/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/requirements.py create mode 100644 .venv/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/specifiers.py create mode 100644 .venv/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/utils.py create mode 100644 .venv/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/version.py create mode 100644 .venv/lib/python3.9/site-packages/pkg_resources/_vendor/pyparsing.py create mode 100644 .venv/lib/python3.9/site-packages/pkg_resources/_vendor/six.py create mode 100644 .venv/lib/python3.9/site-packages/pkg_resources/extern/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/pkg_resources/extern/__pycache__/__init__.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/pkg_resources/py31compat.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools-44.1.1.dist-info/AUTHORS.txt create mode 100644 .venv/lib/python3.9/site-packages/setuptools-44.1.1.dist-info/INSTALLER create mode 100644 .venv/lib/python3.9/site-packages/setuptools-44.1.1.dist-info/LICENSE.txt create mode 100644 .venv/lib/python3.9/site-packages/setuptools-44.1.1.dist-info/METADATA create mode 100644 .venv/lib/python3.9/site-packages/setuptools-44.1.1.dist-info/RECORD create mode 100644 .venv/lib/python3.9/site-packages/setuptools-44.1.1.dist-info/REQUESTED create mode 100644 .venv/lib/python3.9/site-packages/setuptools-44.1.1.dist-info/WHEEL create mode 100644 .venv/lib/python3.9/site-packages/setuptools-44.1.1.dist-info/dependency_links.txt create mode 100644 .venv/lib/python3.9/site-packages/setuptools-44.1.1.dist-info/entry_points.txt create mode 100644 .venv/lib/python3.9/site-packages/setuptools-44.1.1.dist-info/top_level.txt create mode 100644 .venv/lib/python3.9/site-packages/setuptools-44.1.1.dist-info/zip-safe create mode 100644 .venv/lib/python3.9/site-packages/setuptools/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/__pycache__/__init__.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/setuptools/__pycache__/_deprecation_warning.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/setuptools/__pycache__/_imp.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/setuptools/__pycache__/archive_util.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/setuptools/__pycache__/build_meta.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/setuptools/__pycache__/config.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/setuptools/__pycache__/dep_util.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/setuptools/__pycache__/depends.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/setuptools/__pycache__/dist.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/setuptools/__pycache__/errors.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/setuptools/__pycache__/extension.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/setuptools/__pycache__/glob.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/setuptools/__pycache__/installer.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/setuptools/__pycache__/launch.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/setuptools/__pycache__/lib2to3_ex.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/setuptools/__pycache__/monkey.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/setuptools/__pycache__/msvc.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/setuptools/__pycache__/namespaces.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/setuptools/__pycache__/package_index.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/setuptools/__pycache__/py27compat.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/setuptools/__pycache__/py31compat.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/setuptools/__pycache__/py33compat.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/setuptools/__pycache__/py34compat.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/setuptools/__pycache__/sandbox.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/setuptools/__pycache__/site-patch.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/setuptools/__pycache__/ssl_support.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/setuptools/__pycache__/unicode_utils.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/setuptools/__pycache__/version.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/setuptools/__pycache__/wheel.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/setuptools/__pycache__/windows_support.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/setuptools/_deprecation_warning.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/_imp.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/_vendor/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/_vendor/__pycache__/__init__.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/setuptools/_vendor/__pycache__/ordered_set.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/setuptools/_vendor/__pycache__/pyparsing.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/setuptools/_vendor/__pycache__/six.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/setuptools/_vendor/ordered_set.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/_vendor/packaging/__about__.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/_vendor/packaging/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/_vendor/packaging/__pycache__/__about__.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/setuptools/_vendor/packaging/__pycache__/__init__.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/setuptools/_vendor/packaging/__pycache__/_compat.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/setuptools/_vendor/packaging/__pycache__/_structures.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/setuptools/_vendor/packaging/__pycache__/markers.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/setuptools/_vendor/packaging/__pycache__/requirements.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/setuptools/_vendor/packaging/__pycache__/specifiers.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/setuptools/_vendor/packaging/__pycache__/tags.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/setuptools/_vendor/packaging/__pycache__/utils.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/setuptools/_vendor/packaging/__pycache__/version.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/setuptools/_vendor/packaging/_compat.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/_vendor/packaging/_structures.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/_vendor/packaging/markers.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/_vendor/packaging/requirements.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/_vendor/packaging/specifiers.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/_vendor/packaging/tags.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/_vendor/packaging/utils.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/_vendor/packaging/version.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/_vendor/pyparsing.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/_vendor/six.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/archive_util.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/build_meta.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/cli-32.exe create mode 100644 .venv/lib/python3.9/site-packages/setuptools/cli-64.exe create mode 100644 .venv/lib/python3.9/site-packages/setuptools/cli.exe create mode 100644 .venv/lib/python3.9/site-packages/setuptools/command/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/command/__pycache__/__init__.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/setuptools/command/__pycache__/alias.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/setuptools/command/__pycache__/bdist_egg.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/setuptools/command/__pycache__/bdist_rpm.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/setuptools/command/__pycache__/bdist_wininst.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/setuptools/command/__pycache__/build_clib.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/setuptools/command/__pycache__/build_ext.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/setuptools/command/__pycache__/build_py.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/setuptools/command/__pycache__/develop.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/setuptools/command/__pycache__/dist_info.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/setuptools/command/__pycache__/easy_install.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/setuptools/command/__pycache__/egg_info.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/setuptools/command/__pycache__/install.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/setuptools/command/__pycache__/install_egg_info.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/setuptools/command/__pycache__/install_lib.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/setuptools/command/__pycache__/install_scripts.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/setuptools/command/__pycache__/py36compat.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/setuptools/command/__pycache__/register.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/setuptools/command/__pycache__/rotate.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/setuptools/command/__pycache__/saveopts.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/setuptools/command/__pycache__/sdist.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/setuptools/command/__pycache__/setopt.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/setuptools/command/__pycache__/test.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/setuptools/command/__pycache__/upload.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/setuptools/command/__pycache__/upload_docs.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/setuptools/command/alias.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/command/bdist_egg.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/command/bdist_rpm.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/command/bdist_wininst.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/command/build_clib.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/command/build_ext.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/command/build_py.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/command/develop.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/command/dist_info.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/command/easy_install.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/command/egg_info.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/command/install.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/command/install_egg_info.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/command/install_lib.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/command/install_scripts.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/command/launcher manifest.xml create mode 100644 .venv/lib/python3.9/site-packages/setuptools/command/py36compat.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/command/register.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/command/rotate.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/command/saveopts.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/command/sdist.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/command/setopt.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/command/test.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/command/upload.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/command/upload_docs.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/config.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/dep_util.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/depends.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/dist.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/errors.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/extension.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/extern/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/extern/__pycache__/__init__.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/setuptools/glob.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/gui-32.exe create mode 100644 .venv/lib/python3.9/site-packages/setuptools/gui-64.exe create mode 100644 .venv/lib/python3.9/site-packages/setuptools/gui.exe create mode 100644 .venv/lib/python3.9/site-packages/setuptools/installer.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/launch.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/lib2to3_ex.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/monkey.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/msvc.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/namespaces.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/package_index.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/py27compat.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/py31compat.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/py33compat.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/py34compat.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/sandbox.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/script (dev).tmpl create mode 100644 .venv/lib/python3.9/site-packages/setuptools/script.tmpl create mode 100644 .venv/lib/python3.9/site-packages/setuptools/site-patch.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/ssl_support.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/unicode_utils.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/version.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/wheel.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/windows_support.py create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/__pycache__/__init__.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/__pycache__/_internal.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/__pycache__/_reloader.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/__pycache__/exceptions.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/__pycache__/formparser.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/__pycache__/http.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/__pycache__/local.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/__pycache__/security.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/__pycache__/serving.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/__pycache__/test.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/__pycache__/testapp.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/__pycache__/urls.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/__pycache__/user_agent.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/__pycache__/utils.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/__pycache__/wsgi.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/_internal.py create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/_reloader.py create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/datastructures/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/datastructures/__pycache__/__init__.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/datastructures/__pycache__/accept.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/datastructures/__pycache__/auth.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/datastructures/__pycache__/cache_control.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/datastructures/__pycache__/csp.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/datastructures/__pycache__/etag.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/datastructures/__pycache__/file_storage.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/datastructures/__pycache__/headers.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/datastructures/__pycache__/mixins.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/datastructures/__pycache__/range.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/datastructures/__pycache__/structures.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/datastructures/accept.py create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/datastructures/accept.pyi create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/datastructures/auth.py create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/datastructures/cache_control.py create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/datastructures/cache_control.pyi create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/datastructures/csp.py create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/datastructures/csp.pyi create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/datastructures/etag.py create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/datastructures/etag.pyi create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/datastructures/file_storage.py create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/datastructures/file_storage.pyi create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/datastructures/headers.py create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/datastructures/headers.pyi create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/datastructures/mixins.py create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/datastructures/mixins.pyi create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/datastructures/range.py create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/datastructures/range.pyi create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/datastructures/structures.py create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/datastructures/structures.pyi create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/debug/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/debug/__pycache__/__init__.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/debug/__pycache__/console.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/debug/__pycache__/repr.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/debug/__pycache__/tbtools.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/debug/console.py create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/debug/repr.py create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/debug/shared/ICON_LICENSE.md create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/debug/shared/console.png create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/debug/shared/debugger.js create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/debug/shared/less.png create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/debug/shared/more.png create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/debug/shared/style.css create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/debug/tbtools.py create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/exceptions.py create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/formparser.py create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/http.py create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/local.py create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/middleware/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/middleware/__pycache__/__init__.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/middleware/__pycache__/dispatcher.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/middleware/__pycache__/http_proxy.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/middleware/__pycache__/lint.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/middleware/__pycache__/profiler.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/middleware/__pycache__/proxy_fix.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/middleware/__pycache__/shared_data.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/middleware/dispatcher.py create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/middleware/http_proxy.py create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/middleware/lint.py create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/middleware/profiler.py create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/middleware/proxy_fix.py create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/middleware/shared_data.py create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/py.typed create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/routing/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/routing/__pycache__/__init__.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/routing/__pycache__/converters.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/routing/__pycache__/exceptions.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/routing/__pycache__/map.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/routing/__pycache__/matcher.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/routing/__pycache__/rules.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/routing/converters.py create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/routing/exceptions.py create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/routing/map.py create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/routing/matcher.py create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/routing/rules.py create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/sansio/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/sansio/__pycache__/__init__.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/sansio/__pycache__/http.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/sansio/__pycache__/multipart.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/sansio/__pycache__/request.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/sansio/__pycache__/response.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/sansio/__pycache__/utils.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/sansio/http.py create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/sansio/multipart.py create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/sansio/request.py create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/sansio/response.py create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/sansio/utils.py create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/security.py create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/serving.py create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/test.py create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/testapp.py create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/urls.py create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/user_agent.py create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/utils.py create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/wrappers/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/wrappers/__pycache__/__init__.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/wrappers/__pycache__/request.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/wrappers/__pycache__/response.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/wrappers/request.py create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/wrappers/response.py create mode 100644 .venv/lib/python3.9/site-packages/werkzeug/wsgi.py create mode 100644 .venv/lib/python3.9/site-packages/zipp-3.15.0.dist-info/INSTALLER create mode 100644 .venv/lib/python3.9/site-packages/zipp-3.15.0.dist-info/LICENSE create mode 100644 .venv/lib/python3.9/site-packages/zipp-3.15.0.dist-info/METADATA create mode 100644 .venv/lib/python3.9/site-packages/zipp-3.15.0.dist-info/RECORD create mode 100644 .venv/lib/python3.9/site-packages/zipp-3.15.0.dist-info/WHEEL create mode 100644 .venv/lib/python3.9/site-packages/zipp-3.15.0.dist-info/top_level.txt create mode 100644 .venv/lib/python3.9/site-packages/zipp/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/zipp/__pycache__/__init__.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/zipp/__pycache__/py310compat.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/zipp/py310compat.py create mode 120000 .venv/lib64 create mode 100644 .venv/pyvenv.cfg create mode 100644 .venv/share/python-wheels/CacheControl-0.12.6-py2.py3-none-any.whl create mode 100644 .venv/share/python-wheels/appdirs-1.4.4-py2.py3-none-any.whl create mode 100644 .venv/share/python-wheels/certifi-2020.6.20-py2.py3-none-any.whl create mode 100644 .venv/share/python-wheels/chardet-4.0.0-py2.py3-none-any.whl create mode 100644 .venv/share/python-wheels/colorama-0.4.4-py2.py3-none-any.whl create mode 100644 .venv/share/python-wheels/contextlib2-0.6.0.post1-py2.py3-none-any.whl create mode 100644 .venv/share/python-wheels/distlib-0.3.1-py2.py3-none-any.whl create mode 100644 .venv/share/python-wheels/distro-1.5.0-py2.py3-none-any.whl create mode 100644 .venv/share/python-wheels/html5lib-1.1-py2.py3-none-any.whl create mode 100644 .venv/share/python-wheels/idna-2.10-py2.py3-none-any.whl create mode 100644 .venv/share/python-wheels/ipaddr-2.2.0-py2.py3-none-any.whl create mode 100644 .venv/share/python-wheels/msgpack-1.0.0-py2.py3-none-any.whl create mode 100644 .venv/share/python-wheels/packaging-20.9-py2.py3-none-any.whl create mode 100644 .venv/share/python-wheels/pep517-0.9.1-py2.py3-none-any.whl create mode 100644 .venv/share/python-wheels/pip-20.3.4-py2.py3-none-any.whl create mode 100644 .venv/share/python-wheels/pkg_resources-0.0.0-py2.py3-none-any.whl create mode 100644 .venv/share/python-wheels/progress-1.5-py2.py3-none-any.whl create mode 100644 .venv/share/python-wheels/pyparsing-2.4.7-py2.py3-none-any.whl create mode 100644 .venv/share/python-wheels/requests-2.25.1-py2.py3-none-any.whl create mode 100644 .venv/share/python-wheels/resolvelib-0.5.4-py2.py3-none-any.whl create mode 100644 .venv/share/python-wheels/retrying-1.3.3-py2.py3-none-any.whl create mode 100644 .venv/share/python-wheels/setuptools-44.1.1-py2.py3-none-any.whl create mode 100644 .venv/share/python-wheels/six-1.16.0-py2.py3-none-any.whl create mode 100644 .venv/share/python-wheels/toml-0.10.1-py2.py3-none-any.whl create mode 100644 .venv/share/python-wheels/urllib3-1.26.5-py2.py3-none-any.whl create mode 100644 .venv/share/python-wheels/webencodings-0.5.1-py2.py3-none-any.whl create mode 100644 .venv/share/python-wheels/wheel-0.34.2-py2.py3-none-any.whl create mode 100644 __pycache__/app.cpython-39.pyc create mode 100644 app.py create mode 100644 copy.html create mode 100644 index.html create mode 100644 templates/copy1.html create mode 100644 templates/copy2.html create mode 100644 templates/report.html diff --git a/.venv/bin/Activate.ps1 b/.venv/bin/Activate.ps1 new file mode 100644 index 0000000..2fb3852 --- /dev/null +++ b/.venv/bin/Activate.ps1 @@ -0,0 +1,241 @@ +<# +.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" diff --git a/.venv/bin/activate b/.venv/bin/activate new file mode 100644 index 0000000..ec0d110 --- /dev/null +++ b/.venv/bin/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="/home/grgr/xpub/boiler-inspection/.venv" +export VIRTUAL_ENV + +_OLD_VIRTUAL_PATH="$PATH" +PATH="$VIRTUAL_ENV/bin:$PATH" +export PATH + +# unset PYTHONHOME if set +# this will fail if PYTHONHOME is set to the empty string (which is bad anyway) +# could use `if (set -u; : $PYTHONHOME) ;` in bash +if [ -n "${PYTHONHOME:-}" ] ; then + _OLD_VIRTUAL_PYTHONHOME="${PYTHONHOME:-}" + unset PYTHONHOME +fi + +if [ -z "${VIRTUAL_ENV_DISABLE_PROMPT:-}" ] ; then + _OLD_VIRTUAL_PS1="${PS1:-}" + PS1="(.venv) ${PS1:-}" + export PS1 +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/bin/activate.csh b/.venv/bin/activate.csh new file mode 100644 index 0000000..fa7e563 --- /dev/null +++ b/.venv/bin/activate.csh @@ -0,0 +1,25 @@ +# This file must be used with "source bin/activate.csh" *from csh*. +# You cannot run it directly. +# Created by Davide Di Blasi . +# Ported to Python 3.3 venv by Andrew Svetlov + +alias deactivate 'test $?_OLD_VIRTUAL_PATH != 0 && setenv PATH "$_OLD_VIRTUAL_PATH" && unset _OLD_VIRTUAL_PATH; rehash; test $?_OLD_VIRTUAL_PROMPT != 0 && set prompt="$_OLD_VIRTUAL_PROMPT" && unset _OLD_VIRTUAL_PROMPT; unsetenv VIRTUAL_ENV; test "\!:*" != "nondestructive" && unalias deactivate' + +# Unset irrelevant variables. +deactivate nondestructive + +setenv VIRTUAL_ENV "/home/grgr/xpub/boiler-inspection/.venv" + +set _OLD_VIRTUAL_PATH="$PATH" +setenv PATH "$VIRTUAL_ENV/bin:$PATH" + + +set _OLD_VIRTUAL_PROMPT="$prompt" + +if (! "$?VIRTUAL_ENV_DISABLE_PROMPT") then + set prompt = "(.venv) $prompt" +endif + +alias pydoc python -m pydoc + +rehash diff --git a/.venv/bin/activate.fish b/.venv/bin/activate.fish new file mode 100644 index 0000000..81b61eb --- /dev/null +++ b/.venv/bin/activate.fish @@ -0,0 +1,64 @@ +# This file must be used with "source /bin/activate.fish" *from fish* +# (https://fishshell.com/); you cannot run it directly. + +function deactivate -d "Exit virtual environment and return to normal shell environment" + # reset old environment variables + if test -n "$_OLD_VIRTUAL_PATH" + set -gx PATH $_OLD_VIRTUAL_PATH + set -e _OLD_VIRTUAL_PATH + end + if test -n "$_OLD_VIRTUAL_PYTHONHOME" + set -gx PYTHONHOME $_OLD_VIRTUAL_PYTHONHOME + set -e _OLD_VIRTUAL_PYTHONHOME + end + + if test -n "$_OLD_FISH_PROMPT_OVERRIDE" + functions -e fish_prompt + set -e _OLD_FISH_PROMPT_OVERRIDE + functions -c _old_fish_prompt fish_prompt + functions -e _old_fish_prompt + end + + set -e VIRTUAL_ENV + if test "$argv[1]" != "nondestructive" + # Self-destruct! + functions -e deactivate + end +end + +# Unset irrelevant variables. +deactivate nondestructive + +set -gx VIRTUAL_ENV "/home/grgr/xpub/boiler-inspection/.venv" + +set -gx _OLD_VIRTUAL_PATH $PATH +set -gx PATH "$VIRTUAL_ENV/bin" $PATH + +# Unset PYTHONHOME if set. +if set -q PYTHONHOME + set -gx _OLD_VIRTUAL_PYTHONHOME $PYTHONHOME + set -e PYTHONHOME +end + +if test -z "$VIRTUAL_ENV_DISABLE_PROMPT" + # fish uses a function instead of an env var to generate the prompt. + + # Save the current fish_prompt function as the function _old_fish_prompt. + functions -c fish_prompt _old_fish_prompt + + # With the original prompt function renamed, we can override with our own. + function fish_prompt + # Save the return status of the last command. + set -l old_status $status + + # Output the venv prompt; color taken from the blue of the Python logo. + printf "%s%s%s" (set_color 4B8BBE) "(.venv) " (set_color normal) + + # Restore the return status of the previous command. + echo "exit $old_status" | . + # Output the original/"old" prompt. + _old_fish_prompt + end + + set -gx _OLD_FISH_PROMPT_OVERRIDE "$VIRTUAL_ENV" +end diff --git a/.venv/bin/easy_install b/.venv/bin/easy_install new file mode 100755 index 0000000..55dec9b --- /dev/null +++ b/.venv/bin/easy_install @@ -0,0 +1,8 @@ +#!/home/grgr/xpub/boiler-inspection/.venv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys +from setuptools.command.easy_install import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/.venv/bin/easy_install-3.9 b/.venv/bin/easy_install-3.9 new file mode 100755 index 0000000..55dec9b --- /dev/null +++ b/.venv/bin/easy_install-3.9 @@ -0,0 +1,8 @@ +#!/home/grgr/xpub/boiler-inspection/.venv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys +from setuptools.command.easy_install import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/.venv/bin/flask b/.venv/bin/flask new file mode 100755 index 0000000..540867f --- /dev/null +++ b/.venv/bin/flask @@ -0,0 +1,8 @@ +#!/home/grgr/xpub/boiler-inspection/.venv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys +from flask.cli import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/.venv/bin/pip b/.venv/bin/pip new file mode 100755 index 0000000..657f4bf --- /dev/null +++ b/.venv/bin/pip @@ -0,0 +1,8 @@ +#!/home/grgr/xpub/boiler-inspection/.venv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys +from pip._internal.cli.main import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/.venv/bin/pip3 b/.venv/bin/pip3 new file mode 100755 index 0000000..657f4bf --- /dev/null +++ b/.venv/bin/pip3 @@ -0,0 +1,8 @@ +#!/home/grgr/xpub/boiler-inspection/.venv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys +from pip._internal.cli.main import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/.venv/bin/pip3.9 b/.venv/bin/pip3.9 new file mode 100755 index 0000000..657f4bf --- /dev/null +++ b/.venv/bin/pip3.9 @@ -0,0 +1,8 @@ +#!/home/grgr/xpub/boiler-inspection/.venv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys +from pip._internal.cli.main import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/.venv/bin/python b/.venv/bin/python new file mode 120000 index 0000000..b8a0adb --- /dev/null +++ b/.venv/bin/python @@ -0,0 +1 @@ +python3 \ No newline at end of file diff --git a/.venv/bin/python3 b/.venv/bin/python3 new file mode 120000 index 0000000..ae65fda --- /dev/null +++ b/.venv/bin/python3 @@ -0,0 +1 @@ +/usr/bin/python3 \ No newline at end of file diff --git a/.venv/bin/python3.9 b/.venv/bin/python3.9 new file mode 120000 index 0000000..b8a0adb --- /dev/null +++ b/.venv/bin/python3.9 @@ -0,0 +1 @@ +python3 \ No newline at end of file diff --git a/.venv/lib/python3.9/site-packages/Flask-2.3.2.dist-info/INSTALLER b/.venv/lib/python3.9/site-packages/Flask-2.3.2.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/.venv/lib/python3.9/site-packages/Flask-2.3.2.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.venv/lib/python3.9/site-packages/Flask-2.3.2.dist-info/LICENSE.rst b/.venv/lib/python3.9/site-packages/Flask-2.3.2.dist-info/LICENSE.rst new file mode 100644 index 0000000..9d227a0 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/Flask-2.3.2.dist-info/LICENSE.rst @@ -0,0 +1,28 @@ +Copyright 2010 Pallets + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/.venv/lib/python3.9/site-packages/Flask-2.3.2.dist-info/METADATA b/.venv/lib/python3.9/site-packages/Flask-2.3.2.dist-info/METADATA new file mode 100644 index 0000000..a99e52f --- /dev/null +++ b/.venv/lib/python3.9/site-packages/Flask-2.3.2.dist-info/METADATA @@ -0,0 +1,118 @@ +Metadata-Version: 2.1 +Name: Flask +Version: 2.3.2 +Summary: A simple framework for building complex web applications. +Author-email: Armin Ronacher +Maintainer-email: Pallets +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: Chat, https://discord.gg/pallets +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.8 +Description-Content-Type: text/x-rst +License-File: LICENSE.rst +Requires-Dist: Werkzeug (>=2.3.3) +Requires-Dist: Jinja2 (>=3.1.2) +Requires-Dist: itsdangerous (>=2.1.2) +Requires-Dist: click (>=8.1.3) +Requires-Dist: blinker (>=1.6.2) +Requires-Dist: importlib-metadata (>=3.6.0) ; python_version < "3.10" +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/getting-started/ + + +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/ +- Chat: https://discord.gg/pallets diff --git a/.venv/lib/python3.9/site-packages/Flask-2.3.2.dist-info/RECORD b/.venv/lib/python3.9/site-packages/Flask-2.3.2.dist-info/RECORD new file mode 100644 index 0000000..45a3678 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/Flask-2.3.2.dist-info/RECORD @@ -0,0 +1,54 @@ +../../../bin/flask,sha256=bLhmD6ElpMGRWF2YYcQsuuZjZBIThH7HRuzFz59kY9E,243 +Flask-2.3.2.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +Flask-2.3.2.dist-info/LICENSE.rst,sha256=SJqOEQhQntmKN7uYPhHg9-HTHwvY-Zp5yESOf_N9B-o,1475 +Flask-2.3.2.dist-info/METADATA,sha256=o20FsyHfhQR8TMWB_QrtQN2PHyzacLRUAgol_quBBvA,3716 +Flask-2.3.2.dist-info/RECORD,, +Flask-2.3.2.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +Flask-2.3.2.dist-info/WHEEL,sha256=pkctZYzUS4AYVn6dJ-7367OJZivF2e8RA9b_ZBjif18,92 +Flask-2.3.2.dist-info/entry_points.txt,sha256=s3MqQpduU25y4dq3ftBYD6bMVdVnbMpZP-sUNw0zw0k,41 +Flask-2.3.2.dist-info/top_level.txt,sha256=dvi65F6AeGWVU0TBpYiC04yM60-FX1gJFkK31IKQr5c,6 +flask/__init__.py,sha256=yeirfdSGPoM3Ylc9FWWJfy2gEQlHfiZCKrxBiPefACM,3731 +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=ht3Qx9U9z0I1qUfLoS7bYhJcubdpk-i54eHq37LDlN8,87620 +flask/blueprints.py,sha256=ZpVrwa8UY-YnVDsX_1K10XQjDwCUp7Qn2hmKln5icEQ,24332 +flask/cli.py,sha256=wRxX61jRDKQM4iZsYaVwcgGbpN2_2DmntLMWjVeiAx4,33720 +flask/config.py,sha256=yqdiN7TLOs2EChJ0uhTz3SICA3-QBG6l5wHTIUnANpc,12800 +flask/ctx.py,sha256=x2kGzUXtPzVyi2YSKrU_PV1AvtxTmh2iRdriJRTSPGM,14841 +flask/debughelpers.py,sha256=BR0xkd-sAyFuFW07D6NfrqNwSZxk1IrkG5n8zem-3sw,5547 +flask/globals.py,sha256=KUzVvSPh8v28kUasVDi_aQKB9hI2jZSYQHqaDU2P414,2945 +flask/helpers.py,sha256=QDxFmBW9GGXQDLuXrcxQRL0Ldo-_q11zEt3ZVgfINlI,24957 +flask/json/__init__.py,sha256=pdtpoK2b0b1u7Sxbx3feM7VWhsI20l1yGAvbYWxaxvc,5572 +flask/json/__pycache__/__init__.cpython-39.pyc,, +flask/json/__pycache__/provider.cpython-39.pyc,, +flask/json/__pycache__/tag.cpython-39.pyc,, +flask/json/provider.py,sha256=Os0frb8oGfyWKL-TDxb0Uy-MY6gDhPdJkRaUl5xAOXI,7637 +flask/json/tag.py,sha256=ihb7QWrNEr0YC3KD4TolZbftgSPCuLk7FAvK49huYC0,8871 +flask/logging.py,sha256=lArx2Bq9oTtUJ-DnZL9t88xU2zytzp4UWSM9Bd72NDQ,2327 +flask/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +flask/scaffold.py,sha256=0tYQN98sC93YkIEw9g8BiIwceFZ27tNqBtBtFhFy5tY,35231 +flask/sessions.py,sha256=rFH2QKXG24dEazkKGxAHqUpAUh_30hDHrddhVYgAcY0,14169 +flask/signals.py,sha256=s1H4yKjf3c5dgVr41V6sJpE9dLJvmTJMYuK0rkqx3sw,1146 +flask/templating.py,sha256=XdP2hMFnZ5FCZOG7HUaLjC2VC-b4uHSWlDjwv_1p3qc,7503 +flask/testing.py,sha256=52-m5GecDcA-F2dFEYe8eDwApusxdg6S1suBaSC85N0,9768 +flask/typing.py,sha256=4Lj-YTxUoYvPYofC9GKu-1o0Ht8lyjp9z3I336J13_o,3005 +flask/views.py,sha256=V5hOGZLx0Bn99QGcM6mh5x_uM-MypVT0-RysEFU84jc,6789 +flask/wrappers.py,sha256=PhMp3teK3SnEmIdog59cO_DHiZ9Btn0qI1EifrTdwP8,5709 diff --git a/.venv/lib/python3.9/site-packages/Flask-2.3.2.dist-info/REQUESTED b/.venv/lib/python3.9/site-packages/Flask-2.3.2.dist-info/REQUESTED new file mode 100644 index 0000000..e69de29 diff --git a/.venv/lib/python3.9/site-packages/Flask-2.3.2.dist-info/WHEEL b/.venv/lib/python3.9/site-packages/Flask-2.3.2.dist-info/WHEEL new file mode 100644 index 0000000..1f37c02 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/Flask-2.3.2.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.40.0) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/.venv/lib/python3.9/site-packages/Flask-2.3.2.dist-info/entry_points.txt b/.venv/lib/python3.9/site-packages/Flask-2.3.2.dist-info/entry_points.txt new file mode 100644 index 0000000..137232d --- /dev/null +++ b/.venv/lib/python3.9/site-packages/Flask-2.3.2.dist-info/entry_points.txt @@ -0,0 +1,2 @@ +[console_scripts] +flask = flask.cli:main diff --git a/.venv/lib/python3.9/site-packages/Flask-2.3.2.dist-info/top_level.txt b/.venv/lib/python3.9/site-packages/Flask-2.3.2.dist-info/top_level.txt new file mode 100644 index 0000000..7e10602 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/Flask-2.3.2.dist-info/top_level.txt @@ -0,0 +1 @@ +flask diff --git a/.venv/lib/python3.9/site-packages/Jinja2-3.1.2.dist-info/INSTALLER b/.venv/lib/python3.9/site-packages/Jinja2-3.1.2.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/.venv/lib/python3.9/site-packages/Jinja2-3.1.2.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.venv/lib/python3.9/site-packages/Jinja2-3.1.2.dist-info/LICENSE.rst b/.venv/lib/python3.9/site-packages/Jinja2-3.1.2.dist-info/LICENSE.rst new file mode 100644 index 0000000..c37cae4 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/Jinja2-3.1.2.dist-info/LICENSE.rst @@ -0,0 +1,28 @@ +Copyright 2007 Pallets + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/.venv/lib/python3.9/site-packages/Jinja2-3.1.2.dist-info/METADATA b/.venv/lib/python3.9/site-packages/Jinja2-3.1.2.dist-info/METADATA new file mode 100644 index 0000000..f54bb5c --- /dev/null +++ b/.venv/lib/python3.9/site-packages/Jinja2-3.1.2.dist-info/METADATA @@ -0,0 +1,113 @@ +Metadata-Version: 2.1 +Name: Jinja2 +Version: 3.1.2 +Summary: A very fast and expressive template engine. +Home-page: https://palletsprojects.com/p/jinja/ +Author: Armin Ronacher +Author-email: armin.ronacher@active-4.com +Maintainer: Pallets +Maintainer-email: contact@palletsprojects.com +License: BSD-3-Clause +Project-URL: Donate, https://palletsprojects.com/donate +Project-URL: Documentation, https://jinja.palletsprojects.com/ +Project-URL: Changes, https://jinja.palletsprojects.com/changes/ +Project-URL: Source Code, https://github.com/pallets/jinja/ +Project-URL: Issue Tracker, https://github.com/pallets/jinja/issues/ +Project-URL: Twitter, https://twitter.com/PalletsTeam +Project-URL: Chat, https://discord.gg/pallets +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Web Environment +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content +Classifier: Topic :: Text Processing :: Markup :: HTML +Requires-Python: >=3.7 +Description-Content-Type: text/x-rst +License-File: LICENSE.rst +Requires-Dist: MarkupSafe (>=2.0) +Provides-Extra: i18n +Requires-Dist: Babel (>=2.7) ; extra == 'i18n' + +Jinja +===== + +Jinja is a fast, expressive, extensible templating engine. Special +placeholders in the template allow writing code similar to Python +syntax. Then the template is passed data to render the final document. + +It includes: + +- Template inheritance and inclusion. +- Define and import macros within templates. +- HTML templates can use autoescaping to prevent XSS from untrusted + user input. +- A sandboxed environment can safely render untrusted templates. +- AsyncIO support for generating templates and calling async + functions. +- I18N support with Babel. +- Templates are compiled to optimized Python code just-in-time and + cached, or can be compiled ahead-of-time. +- Exceptions point to the correct line in templates to make debugging + easier. +- Extensible filters, tests, functions, and even syntax. + +Jinja's philosophy is that while application logic belongs in Python if +possible, it shouldn't make the template designer's job difficult by +restricting functionality too much. + + +Installing +---------- + +Install and update using `pip`_: + +.. code-block:: text + + $ pip install -U Jinja2 + +.. _pip: https://pip.pypa.io/en/stable/getting-started/ + + +In A Nutshell +------------- + +.. code-block:: jinja + + {% extends "base.html" %} + {% block title %}Members{% endblock %} + {% block content %} + + {% endblock %} + + +Donate +------ + +The Pallets organization develops and supports Jinja and other popular +packages. In order to grow the community of contributors and users, and +allow the maintainers to devote more time to the projects, `please +donate today`_. + +.. _please donate today: https://palletsprojects.com/donate + + +Links +----- + +- Documentation: https://jinja.palletsprojects.com/ +- Changes: https://jinja.palletsprojects.com/changes/ +- PyPI Releases: https://pypi.org/project/Jinja2/ +- Source Code: https://github.com/pallets/jinja/ +- Issue Tracker: https://github.com/pallets/jinja/issues/ +- Website: https://palletsprojects.com/p/jinja/ +- Twitter: https://twitter.com/PalletsTeam +- Chat: https://discord.gg/pallets + + diff --git a/.venv/lib/python3.9/site-packages/Jinja2-3.1.2.dist-info/RECORD b/.venv/lib/python3.9/site-packages/Jinja2-3.1.2.dist-info/RECORD new file mode 100644 index 0000000..24b49f1 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/Jinja2-3.1.2.dist-info/RECORD @@ -0,0 +1,58 @@ +Jinja2-3.1.2.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +Jinja2-3.1.2.dist-info/LICENSE.rst,sha256=O0nc7kEF6ze6wQ-vG-JgQI_oXSUrjp3y4JefweCUQ3s,1475 +Jinja2-3.1.2.dist-info/METADATA,sha256=PZ6v2SIidMNixR7MRUX9f7ZWsPwtXanknqiZUmRbh4U,3539 +Jinja2-3.1.2.dist-info/RECORD,, +Jinja2-3.1.2.dist-info/WHEEL,sha256=G16H4A3IeoQmnOrYV4ueZGKSjhipXx8zc8nu9FGlvMA,92 +Jinja2-3.1.2.dist-info/entry_points.txt,sha256=zRd62fbqIyfUpsRtU7EVIFyiu1tPwfgO7EvPErnxgTE,59 +Jinja2-3.1.2.dist-info/top_level.txt,sha256=PkeVWtLb3-CqjWi1fO29OCbj55EhX_chhKrCdrVe_zs,7 +jinja2/__init__.py,sha256=8vGduD8ytwgD6GDSqpYc2m3aU-T7PKOAddvVXgGr_Fs,1927 +jinja2/__pycache__/__init__.cpython-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=_zYctNKzRqlk_murTNlzrju1FFJL7Va_Ijqqd7ii2lU,1958 +jinja2/async_utils.py,sha256=dHlbTeaxFPtAOQEYOGYh_PHcDT0rsDaUJAFDl_0XtTg,2472 +jinja2/bccache.py,sha256=mhz5xtLxCcHRAa56azOhphIAe19u1we0ojifNMClDio,14061 +jinja2/compiler.py,sha256=Gs-N8ThJ7OWK4-reKoO8Wh1ZXz95MVphBKNVf75qBr8,72172 +jinja2/constants.py,sha256=GMoFydBF_kdpaRKPoM5cl5MviquVRLVyZtfp5-16jg0,1433 +jinja2/debug.py,sha256=iWJ432RadxJNnaMOPrjIDInz50UEgni3_HKuFXi2vuQ,6299 +jinja2/defaults.py,sha256=boBcSw78h-lp20YbaXSJsqkAI2uN_mD_TtCydpeq5wU,1267 +jinja2/environment.py,sha256=6uHIcc7ZblqOMdx_uYNKqRnnwAF0_nzbyeMP9FFtuh4,61349 +jinja2/exceptions.py,sha256=ioHeHrWwCWNaXX1inHmHVblvc4haO7AXsjCp3GfWvx0,5071 +jinja2/ext.py,sha256=ivr3P7LKbddiXDVez20EflcO3q2aHQwz9P_PgWGHVqE,31502 +jinja2/filters.py,sha256=9js1V-h2RlyW90IhLiBGLM2U-k6SCy2F4BUUMgB3K9Q,53509 +jinja2/idtracking.py,sha256=GfNmadir4oDALVxzn3DL9YInhJDr69ebXeA2ygfuCGA,10704 +jinja2/lexer.py,sha256=DW2nX9zk-6MWp65YR2bqqj0xqCvLtD-u9NWT8AnFRxQ,29726 +jinja2/loaders.py,sha256=BfptfvTVpClUd-leMkHczdyPNYFzp_n7PKOJ98iyHOg,23207 +jinja2/meta.py,sha256=GNPEvifmSaU3CMxlbheBOZjeZ277HThOPUTf1RkppKQ,4396 +jinja2/nativetypes.py,sha256=DXgORDPRmVWgy034H0xL8eF7qYoK3DrMxs-935d0Fzk,4226 +jinja2/nodes.py,sha256=i34GPRAZexXMT6bwuf5SEyvdmS-bRCy9KMjwN5O6pjk,34550 +jinja2/optimizer.py,sha256=tHkMwXxfZkbfA1KmLcqmBMSaz7RLIvvItrJcPoXTyD8,1650 +jinja2/parser.py,sha256=nHd-DFHbiygvfaPtm9rcQXJChZG7DPsWfiEsqfwKerY,39595 +jinja2/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +jinja2/runtime.py,sha256=5CmD5BjbEJxSiDNTFBeKCaq8qU4aYD2v6q2EluyExms,33476 +jinja2/sandbox.py,sha256=Y0xZeXQnH6EX5VjaV2YixESxoepnRbW_3UeQosaBU3M,14584 +jinja2/tests.py,sha256=Am5Z6Lmfr2XaH_npIfJJ8MdXtWsbLjMULZJulTAj30E,5905 +jinja2/utils.py,sha256=u9jXESxGn8ATZNVolwmkjUVu4SA-tLgV0W7PcSfPfdQ,23965 +jinja2/visitor.py,sha256=MH14C6yq24G_KVtWzjwaI7Wg14PCJIYlWW1kpkxYak0,3568 diff --git a/.venv/lib/python3.9/site-packages/Jinja2-3.1.2.dist-info/WHEEL b/.venv/lib/python3.9/site-packages/Jinja2-3.1.2.dist-info/WHEEL new file mode 100644 index 0000000..becc9a6 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/Jinja2-3.1.2.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.37.1) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/.venv/lib/python3.9/site-packages/Jinja2-3.1.2.dist-info/entry_points.txt b/.venv/lib/python3.9/site-packages/Jinja2-3.1.2.dist-info/entry_points.txt new file mode 100644 index 0000000..7b9666c --- /dev/null +++ b/.venv/lib/python3.9/site-packages/Jinja2-3.1.2.dist-info/entry_points.txt @@ -0,0 +1,2 @@ +[babel.extractors] +jinja2 = jinja2.ext:babel_extract[i18n] diff --git a/.venv/lib/python3.9/site-packages/Jinja2-3.1.2.dist-info/top_level.txt b/.venv/lib/python3.9/site-packages/Jinja2-3.1.2.dist-info/top_level.txt new file mode 100644 index 0000000..7f7afbf --- /dev/null +++ b/.venv/lib/python3.9/site-packages/Jinja2-3.1.2.dist-info/top_level.txt @@ -0,0 +1 @@ +jinja2 diff --git a/.venv/lib/python3.9/site-packages/MarkupSafe-2.1.2.dist-info/INSTALLER b/.venv/lib/python3.9/site-packages/MarkupSafe-2.1.2.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/.venv/lib/python3.9/site-packages/MarkupSafe-2.1.2.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.venv/lib/python3.9/site-packages/MarkupSafe-2.1.2.dist-info/LICENSE.rst b/.venv/lib/python3.9/site-packages/MarkupSafe-2.1.2.dist-info/LICENSE.rst new file mode 100644 index 0000000..9d227a0 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/MarkupSafe-2.1.2.dist-info/LICENSE.rst @@ -0,0 +1,28 @@ +Copyright 2010 Pallets + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/.venv/lib/python3.9/site-packages/MarkupSafe-2.1.2.dist-info/METADATA b/.venv/lib/python3.9/site-packages/MarkupSafe-2.1.2.dist-info/METADATA new file mode 100644 index 0000000..4a34999 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/MarkupSafe-2.1.2.dist-info/METADATA @@ -0,0 +1,98 @@ +Metadata-Version: 2.1 +Name: MarkupSafe +Version: 2.1.2 +Summary: Safely add untrusted strings to HTML/XML markup. +Home-page: https://palletsprojects.com/p/markupsafe/ +Author: Armin Ronacher +Author-email: armin.ronacher@active-4.com +Maintainer: Pallets +Maintainer-email: contact@palletsprojects.com +License: BSD-3-Clause +Project-URL: Donate, https://palletsprojects.com/donate +Project-URL: Documentation, https://markupsafe.palletsprojects.com/ +Project-URL: Changes, https://markupsafe.palletsprojects.com/changes/ +Project-URL: Source Code, https://github.com/pallets/markupsafe/ +Project-URL: Issue Tracker, https://github.com/pallets/markupsafe/issues/ +Project-URL: Twitter, https://twitter.com/PalletsTeam +Project-URL: Chat, https://discord.gg/pallets +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Web Environment +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content +Classifier: Topic :: Text Processing :: Markup :: HTML +Requires-Python: >=3.7 +Description-Content-Type: text/x-rst +License-File: LICENSE.rst + +MarkupSafe +========== + +MarkupSafe implements a text object that escapes characters so it is +safe to use in HTML and XML. Characters that have special meanings are +replaced so that they display as the actual characters. This mitigates +injection attacks, meaning untrusted user input can safely be displayed +on a page. + + +Installing +---------- + +Install and update using `pip`_: + +.. code-block:: text + + pip install -U MarkupSafe + +.. _pip: https://pip.pypa.io/en/stable/getting-started/ + + +Examples +-------- + +.. code-block:: pycon + + >>> from markupsafe import Markup, escape + + >>> # escape replaces special characters and wraps in Markup + >>> escape("") + Markup('<script>alert(document.cookie);</script>') + + >>> # wrap in Markup to mark text "safe" and prevent escaping + >>> Markup("Hello") + Markup('hello') + + >>> escape(Markup("Hello")) + Markup('hello') + + >>> # Markup is a str subclass + >>> # methods and operators escape their arguments + >>> template = Markup("Hello {name}") + >>> template.format(name='"World"') + Markup('Hello "World"') + + +Donate +------ + +The Pallets organization develops and supports MarkupSafe and other +popular packages. In order to grow the community of contributors and +users, and allow the maintainers to devote more time to the projects, +`please donate today`_. + +.. _please donate today: https://palletsprojects.com/donate + + +Links +----- + +- Documentation: https://markupsafe.palletsprojects.com/ +- Changes: https://markupsafe.palletsprojects.com/changes/ +- PyPI Releases: https://pypi.org/project/MarkupSafe/ +- Source Code: https://github.com/pallets/markupsafe/ +- Issue Tracker: https://github.com/pallets/markupsafe/issues/ +- Website: https://palletsprojects.com/p/markupsafe/ +- Twitter: https://twitter.com/PalletsTeam +- Chat: https://discord.gg/pallets diff --git a/.venv/lib/python3.9/site-packages/MarkupSafe-2.1.2.dist-info/RECORD b/.venv/lib/python3.9/site-packages/MarkupSafe-2.1.2.dist-info/RECORD new file mode 100644 index 0000000..93bf428 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/MarkupSafe-2.1.2.dist-info/RECORD @@ -0,0 +1,14 @@ +MarkupSafe-2.1.2.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +MarkupSafe-2.1.2.dist-info/LICENSE.rst,sha256=SJqOEQhQntmKN7uYPhHg9-HTHwvY-Zp5yESOf_N9B-o,1475 +MarkupSafe-2.1.2.dist-info/METADATA,sha256=jPw4iOiZg6adxZ5bdvjZapeNmPMINMGG2q2v2rI4SqA,3222 +MarkupSafe-2.1.2.dist-info/RECORD,, +MarkupSafe-2.1.2.dist-info/WHEEL,sha256=D79CBkaJekx7HMK1NaBe_KU1GVla4HlolbFXT3at6fg,148 +MarkupSafe-2.1.2.dist-info/top_level.txt,sha256=qy0Plje5IJuvsCBjejJyhDCjEAdcDLK_2agVcex8Z6U,11 +markupsafe/__init__.py,sha256=LtjnhQ6AHmAgHl37cev2oQBXjr4xOF-QhdXgsCAL3-0,9306 +markupsafe/__pycache__/__init__.cpython-39.pyc,, +markupsafe/__pycache__/_native.cpython-39.pyc,, +markupsafe/_native.py,sha256=GR86Qvo_GcgKmKreA1WmYN9ud17OFwkww8E-fiW-57s,1713 +markupsafe/_speedups.c,sha256=X2XvQVtIdcK4Usz70BvkzoOfjTCmQlDkkjYSn-swE0g,7083 +markupsafe/_speedups.cpython-39-x86_64-linux-gnu.so,sha256=3wRXXXyXLhwOEl2dgZ0BOIOvNcp0sPJ5_LpKz7X20HI,44024 +markupsafe/_speedups.pyi,sha256=vfMCsOgbAXRNLUXkyuyonG8uEWKYU4PDqNuMaDELAYw,229 +markupsafe/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 diff --git a/.venv/lib/python3.9/site-packages/MarkupSafe-2.1.2.dist-info/WHEEL b/.venv/lib/python3.9/site-packages/MarkupSafe-2.1.2.dist-info/WHEEL new file mode 100644 index 0000000..d146683 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/MarkupSafe-2.1.2.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.38.4) +Root-Is-Purelib: false +Tag: cp39-cp39-manylinux_2_17_x86_64 +Tag: cp39-cp39-manylinux2014_x86_64 + diff --git a/.venv/lib/python3.9/site-packages/MarkupSafe-2.1.2.dist-info/top_level.txt b/.venv/lib/python3.9/site-packages/MarkupSafe-2.1.2.dist-info/top_level.txt new file mode 100644 index 0000000..75bf729 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/MarkupSafe-2.1.2.dist-info/top_level.txt @@ -0,0 +1 @@ +markupsafe diff --git a/.venv/lib/python3.9/site-packages/Werkzeug-2.3.3.dist-info/INSTALLER b/.venv/lib/python3.9/site-packages/Werkzeug-2.3.3.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/.venv/lib/python3.9/site-packages/Werkzeug-2.3.3.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.venv/lib/python3.9/site-packages/Werkzeug-2.3.3.dist-info/LICENSE.rst b/.venv/lib/python3.9/site-packages/Werkzeug-2.3.3.dist-info/LICENSE.rst new file mode 100644 index 0000000..c37cae4 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/Werkzeug-2.3.3.dist-info/LICENSE.rst @@ -0,0 +1,28 @@ +Copyright 2007 Pallets + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/.venv/lib/python3.9/site-packages/Werkzeug-2.3.3.dist-info/METADATA b/.venv/lib/python3.9/site-packages/Werkzeug-2.3.3.dist-info/METADATA new file mode 100644 index 0000000..e654c23 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/Werkzeug-2.3.3.dist-info/METADATA @@ -0,0 +1,120 @@ +Metadata-Version: 2.1 +Name: Werkzeug +Version: 2.3.3 +Summary: The comprehensive WSGI web application library. +Author-email: Armin Ronacher +Maintainer-email: Pallets +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: Chat, https://discord.gg/pallets +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Web Environment +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content +Classifier: Topic :: 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.8 +Description-Content-Type: text/x-rst +License-File: LICENSE.rst +Requires-Dist: MarkupSafe (>=2.1.1) +Provides-Extra: watchdog +Requires-Dist: watchdog (>=2.3) ; 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/getting-started/ + + +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/ +- Chat: https://discord.gg/pallets diff --git a/.venv/lib/python3.9/site-packages/Werkzeug-2.3.3.dist-info/RECORD b/.venv/lib/python3.9/site-packages/Werkzeug-2.3.3.dist-info/RECORD new file mode 100644 index 0000000..8232d4a --- /dev/null +++ b/.venv/lib/python3.9/site-packages/Werkzeug-2.3.3.dist-info/RECORD @@ -0,0 +1,126 @@ +Werkzeug-2.3.3.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +Werkzeug-2.3.3.dist-info/LICENSE.rst,sha256=O0nc7kEF6ze6wQ-vG-JgQI_oXSUrjp3y4JefweCUQ3s,1475 +Werkzeug-2.3.3.dist-info/METADATA,sha256=YAbwUaXbqxmqqPXA3PSkBJwWBrJDwoDRJo6_q5KiOws,4205 +Werkzeug-2.3.3.dist-info/RECORD,, +Werkzeug-2.3.3.dist-info/WHEEL,sha256=pkctZYzUS4AYVn6dJ-7367OJZivF2e8RA9b_ZBjif18,92 +Werkzeug-2.3.3.dist-info/top_level.txt,sha256=QRyj2VjwJoQkrwjwFIOlB8Xg3r9un0NtqVHQF-15xaw,9 +werkzeug/__init__.py,sha256=rmEN3vcS_ZTWsOEE_zeKquRh8gZdM588PFzDuZ4xmGs,188 +werkzeug/__pycache__/__init__.cpython-39.pyc,, +werkzeug/__pycache__/_internal.cpython-39.pyc,, +werkzeug/__pycache__/_reloader.cpython-39.pyc,, +werkzeug/__pycache__/exceptions.cpython-39.pyc,, +werkzeug/__pycache__/formparser.cpython-39.pyc,, +werkzeug/__pycache__/http.cpython-39.pyc,, +werkzeug/__pycache__/local.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__/utils.cpython-39.pyc,, +werkzeug/__pycache__/wsgi.cpython-39.pyc,, +werkzeug/_internal.py,sha256=10weRWsedmoDrJUvS4MkCe-VsOkwbAU9PxY4-J3nOXE,7830 +werkzeug/_reloader.py,sha256=1O1DDWlqVwYIX8kgJwH5B4a_Uh6acQnw3sQf01JpXtM,14745 +werkzeug/datastructures/__init__.py,sha256=yzBdOT9DdK3nraNG49pA3bVsvtPPLx2-t2N8ZmuAd9w,1900 +werkzeug/datastructures/__pycache__/__init__.cpython-39.pyc,, +werkzeug/datastructures/__pycache__/accept.cpython-39.pyc,, +werkzeug/datastructures/__pycache__/auth.cpython-39.pyc,, +werkzeug/datastructures/__pycache__/cache_control.cpython-39.pyc,, +werkzeug/datastructures/__pycache__/csp.cpython-39.pyc,, +werkzeug/datastructures/__pycache__/etag.cpython-39.pyc,, +werkzeug/datastructures/__pycache__/file_storage.cpython-39.pyc,, +werkzeug/datastructures/__pycache__/headers.cpython-39.pyc,, +werkzeug/datastructures/__pycache__/mixins.cpython-39.pyc,, +werkzeug/datastructures/__pycache__/range.cpython-39.pyc,, +werkzeug/datastructures/__pycache__/structures.cpython-39.pyc,, +werkzeug/datastructures/accept.py,sha256=CuCvBAxNzbt4QUb17rH986vvOVGURFUjo0DX2PQy_yI,10670 +werkzeug/datastructures/accept.pyi,sha256=6P114gncjZoy-i_n_3OQy2nJVwjEAIe7PcBxKYqCEfc,1917 +werkzeug/datastructures/auth.py,sha256=6b3LCrLRcFyhw3KvgiZjCozHllEEZ3xvcXDIFSu1aU8,16022 +werkzeug/datastructures/cache_control.py,sha256=RTUipZev50s-1TAn2rYGZrytm_6IOIxQd67fkR5bNF0,6043 +werkzeug/datastructures/cache_control.pyi,sha256=6Q93jRysAKMPWRA72OMksyn7d3ZysuxwGlHp_iwF9pA,3756 +werkzeug/datastructures/csp.py,sha256=DAOAO266LK0JKbvlG80bbkAgfrNsnU9HBoz-FdIYNdo,3244 +werkzeug/datastructures/csp.pyi,sha256=AmDWiZU4rrJA4SZmyMNI1L5PLdIfJsI5Li9r5lE1q6M,5765 +werkzeug/datastructures/etag.py,sha256=JsyI-yXayF-hQu26MyFzbHFIZsaQ6odj3RZO_jF-_cc,2913 +werkzeug/datastructures/etag.pyi,sha256=N9cuUBrZnxHmsbW0BBmjKW-djNY7WKbI6t_WopB8Zo0,1047 +werkzeug/datastructures/file_storage.py,sha256=d3E8riRUr2a9fbfXMIIJ57HeEEkWVMy6R4RKHRtu73I,6076 +werkzeug/datastructures/file_storage.pyi,sha256=2sdbKHhvbQF5FjrJuO6l_m1yZvZ4oPCUTspmdmjQlSU,1433 +werkzeug/datastructures/headers.py,sha256=yOWto7HUzcIKqQBhtom-oQ4OT8xE_ZfVZ3TwldGgT3A,18882 +werkzeug/datastructures/headers.pyi,sha256=66Gh9DbD8QNpLRBOuer4DMCj12csddHrcgxiJPLE5n8,4237 +werkzeug/datastructures/mixins.py,sha256=-IQSQ70UOMQlqtJEIyyhplOd4obaTOfzGvka-cunCtM,5337 +werkzeug/datastructures/mixins.pyi,sha256=y92tClxVslJBEGgAwDRsQLExfin2p0x7NfnP_b8w6xc,4191 +werkzeug/datastructures/range.py,sha256=JXSDPseG7iH5giJp3R1SnQC_SqQp634M8Iv6QTsbTxM,5669 +werkzeug/datastructures/range.pyi,sha256=bsM61iNp86gT2lyN0F_Dqg8xsnfPerdmElipuHppiJQ,1792 +werkzeug/datastructures/structures.py,sha256=_bhAf0adEk6WU2uy8jdmuxFMTFcuClY1p7jQ-3wYXj4,31761 +werkzeug/datastructures/structures.pyi,sha256=MRg-RubT3UPjh62i9-7Xht8DVL0zTApRzjs52Hfz_j4,8148 +werkzeug/debug/__init__.py,sha256=0nIJfNC8GCjaMMDBJHwp4bgWLDI-uAYIpAoNdHiVCCc,18757 +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=FIO8gDX2eQ1_4MtpJ4s0i2gR4fFCJZTPwhSVByF4kbo,6068 +werkzeug/debug/repr.py,sha256=ECmIpNVlCppTfCuIuEgrJVfuhr8iDqPSWeVJyxt1QOM,9328 +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=tg42SZs1SVmYWZ-_Fj5ELK5-FLHnGNQrei0K2By8Bw8,10521 +werkzeug/debug/shared/less.png,sha256=-4-kNRaXJSONVLahrQKUxMwXGm9R4OnZ9SxDGpHlIR4,191 +werkzeug/debug/shared/more.png,sha256=GngN7CioHQoV58rH6ojnkYi8c_qED2Aka5FO5UXrReY,200 +werkzeug/debug/shared/style.css,sha256=-xSxzUEZGw_IqlDR5iZxitNl8LQUjBM-_Y4UAvXVH8g,6078 +werkzeug/debug/tbtools.py,sha256=WyNL0RCDIi3YZGifMbs99BdmwmqQRL7zJFYZ1x_GJN4,13263 +werkzeug/exceptions.py,sha256=d6VNzGcVgLazIpfwRD8pN_d3yAJNyngBDFvlXQbR-38,26062 +werkzeug/formparser.py,sha256=jnc6NhnjaeR-88dSMYv2L3A60IEhtVswZJsZ-KSf-ks,19624 +werkzeug/http.py,sha256=9IYx3F7OSsWTNAnKvMABN7AqzRyERp-EqdS_s_7QeA0,48593 +werkzeug/local.py,sha256=zrXlO1IP3KTz310h9LSdVKMaFsJfNyXkfCYCkbvlBXQ,22075 +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=6ltzPtDsIdLTY_T1GW6kxBJL0KZftbipa_WVdKtpVQ8,2601 +werkzeug/middleware/http_proxy.py,sha256=vsSvt84m656x3mV_Fj78y7O2eYHmurWngErTcjeiz8U,7833 +werkzeug/middleware/lint.py,sha256=6CqcwMWro1p-GRUGPgQ1n21KFnTTqc6-81CGTzpcK74,13916 +werkzeug/middleware/profiler.py,sha256=KKr8nAiF9dr9pNd3G0D3xs7mUba9gvWkyK7X9ceke70,4906 +werkzeug/middleware/proxy_fix.py,sha256=dcOOSjSok2QsSh1VSNsw-a0Vy_Jn5DunlO6PRbXBq0A,6754 +werkzeug/middleware/shared_data.py,sha256=wHbKFw2vh6aZMFcL_SwHKAk6F99QHXYEC-8npqLwGt4,9489 +werkzeug/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +werkzeug/routing/__init__.py,sha256=HpvahY7WwkLdV4Cq3Bsc3GrqNon4u6t8-vhbb9E5o00,4819 +werkzeug/routing/__pycache__/__init__.cpython-39.pyc,, +werkzeug/routing/__pycache__/converters.cpython-39.pyc,, +werkzeug/routing/__pycache__/exceptions.cpython-39.pyc,, +werkzeug/routing/__pycache__/map.cpython-39.pyc,, +werkzeug/routing/__pycache__/matcher.cpython-39.pyc,, +werkzeug/routing/__pycache__/rules.cpython-39.pyc,, +werkzeug/routing/converters.py,sha256=GVXW-a6iC79fH48QCgW7a3Nd9Cu5hke9273OMOJsNy4,7602 +werkzeug/routing/exceptions.py,sha256=yGZ5AUL-buHp-vK8AJbZ0bLIbSckh1UyiGKgRg4ZjaA,4698 +werkzeug/routing/map.py,sha256=Gb_t8LNVP08iecqgDRSRzNz2YBzC12_4pB366Lopl7U,37254 +werkzeug/routing/matcher.py,sha256=FyPG45iqR1XwxFujejSqfNEKV7IgbR2td7Jp-ocSASY,7817 +werkzeug/routing/rules.py,sha256=O9FHwCWclj_MskT0fn49UC2JI_OYWIx4BrPbO9JFssw,32012 +werkzeug/sansio/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +werkzeug/sansio/__pycache__/__init__.cpython-39.pyc,, +werkzeug/sansio/__pycache__/http.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/http.py,sha256=UDQNOo8Z9j0xU2m0ZgsCTBNnhZ3BxpwufzC7lzsXoB4,6234 +werkzeug/sansio/multipart.py,sha256=VTP_jhRRxYDX-1_1oge_b2CK3KTLnw3LB0k8b2zpiHI,11087 +werkzeug/sansio/request.py,sha256=pHR5KJAalFOWZhcl033QAX4SkDJ7iV4zOd0ByJbzqxc,24243 +werkzeug/sansio/response.py,sha256=CPzkFIAzwIRANF6sMGHKdthTlH7Thbr3UyjIEP1Fmn0,29011 +werkzeug/sansio/utils.py,sha256=DCMLtg4S5TuNtaJl6PTHMNAUDdIegmEcMFwciq-x0ag,4908 +werkzeug/security.py,sha256=v-_qRo0-DEJi7ga_snNEuYXCBQSsKfUME2Y8tt2cvkQ,5814 +werkzeug/serving.py,sha256=HUyO2O5FYcvajDc8A6gGOt7gC01wk9sljugxxHYG7lU,39223 +werkzeug/test.py,sha256=vJgiiCd8NJzuS3i-Dc-6xy2uurBYnwxNgGBMxgR-7_g,55645 +werkzeug/testapp.py,sha256=w9AdbZcmSvydD-OP6EjxVENuaZof9MkbYNFVALhcoqQ,6151 +werkzeug/urls.py,sha256=vifWlqXEWkkvo7eht_LchyGqQT29f7MfLq5ZeDpr2r4,45871 +werkzeug/user_agent.py,sha256=lSlLYKCcbzCUSkbdAoO8zPk2UR-8Mdn6iu_iA2kYPBA,1416 +werkzeug/utils.py,sha256=FAKW7eW_JCobj4qacEZLN2oB1Fl51j2DuBxmQ8xMooc,24744 +werkzeug/wrappers/__init__.py,sha256=kGyK7rOud3qCxll_jFyW15YarJhj1xtdf3ocx9ZheB8,120 +werkzeug/wrappers/__pycache__/__init__.cpython-39.pyc,, +werkzeug/wrappers/__pycache__/request.cpython-39.pyc,, +werkzeug/wrappers/__pycache__/response.cpython-39.pyc,, +werkzeug/wrappers/request.py,sha256=PBVRWxH-BM0EykdlKYdhvqhCPP6uYLpNWKnjiTTNrFA,24984 +werkzeug/wrappers/response.py,sha256=wAjIzLNRGE1XerLET-hkdohFA0XSsPx0FPsKNfP0qmE,32615 +werkzeug/wsgi.py,sha256=BTfvnMfqhITyv6EwKet9hysrAmu4O12jyLREKUIdQ6Q,29399 diff --git a/.venv/lib/python3.9/site-packages/Werkzeug-2.3.3.dist-info/WHEEL b/.venv/lib/python3.9/site-packages/Werkzeug-2.3.3.dist-info/WHEEL new file mode 100644 index 0000000..1f37c02 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/Werkzeug-2.3.3.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.40.0) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/.venv/lib/python3.9/site-packages/Werkzeug-2.3.3.dist-info/top_level.txt b/.venv/lib/python3.9/site-packages/Werkzeug-2.3.3.dist-info/top_level.txt new file mode 100644 index 0000000..6fe8da8 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/Werkzeug-2.3.3.dist-info/top_level.txt @@ -0,0 +1 @@ +werkzeug diff --git a/.venv/lib/python3.9/site-packages/__pycache__/easy_install.cpython-39.pyc b/.venv/lib/python3.9/site-packages/__pycache__/easy_install.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..21713e7da0b03fc38086fbbe8fc1209f17e0d0b7 GIT binary patch literal 316 zcmYj~Jx;_h5QXg|K!n&6AVEZzV(o$o2_YH^8Va^RN@F?kD6!UmvYi#7py4!JDJ>NZ z2cW_Miha_|)01Yt$5*RkloY<6#B1`8CG0Cp;WkaUNK2pw0Ty_h`7@D;TxB|Ma*QB< zLl}u-gX)8q)5p#+5!Q}(Ti!#U`xV4Zb9t?;7!};$)DxDnA}jF;{sF~OS +Maintainer-email: Pallets Ecosystem +License: MIT License +Project-URL: Homepage, https://blinker.readthedocs.io +Project-URL: Documentation, https://blinker.readthedocs.io +Project-URL: Source Code, https://github.com/pallets-eco/blinker/ +Project-URL: Issue Tracker, https://github.com/pallets-eco/blinker/issues/ +Project-URL: Chat, https://discord.gg/pallets +Keywords: signal,emit,events,broadcast +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: Topic :: Software Development :: Libraries +Requires-Python: >=3.7 +Description-Content-Type: text/x-rst +License-File: LICENSE.rst + +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. + +.. code-block:: pycon + + >>> from blinker import signal + >>> started = signal('round-started') + >>> def each(round): + ... print(f"Round {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! + + +Links +----- + +- Documentation: https://blinker.readthedocs.io/ +- Changes: https://blinker.readthedocs.io/#changes +- PyPI Releases: https://pypi.org/project/blinker/ +- Source Code: https://github.com/pallets-eco/blinker/ +- Issue Tracker: https://github.com/pallets-eco/blinker/issues/ diff --git a/.venv/lib/python3.9/site-packages/blinker-1.6.2.dist-info/RECORD b/.venv/lib/python3.9/site-packages/blinker-1.6.2.dist-info/RECORD new file mode 100644 index 0000000..70bcdcb --- /dev/null +++ b/.venv/lib/python3.9/site-packages/blinker-1.6.2.dist-info/RECORD @@ -0,0 +1,15 @@ +blinker-1.6.2.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +blinker-1.6.2.dist-info/LICENSE.rst,sha256=nrc6HzhZekqhcCXSrhvjg5Ykx5XphdTw6Xac4p-spGc,1054 +blinker-1.6.2.dist-info/METADATA,sha256=7MRskabu2wQvWIMFwgqP3w2LDt8nR5nCxH7Anu1ZrBM,1964 +blinker-1.6.2.dist-info/RECORD,, +blinker-1.6.2.dist-info/WHEEL,sha256=pkctZYzUS4AYVn6dJ-7367OJZivF2e8RA9b_ZBjif18,92 +blinker-1.6.2.dist-info/top_level.txt,sha256=2NmsENM0J2t9Z8mkjxHDmGMQj7Bm8f5ZTTYe1x1fZtM,8 +blinker/__init__.py,sha256=Ko7EbvxyCl_UewgsP8XgDJqJcHZA7EsuhG72R_zDrcY,408 +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,sha256=kWOTIWnCY3kOb8lZP74Rbx7bR_BLVg4TjwzNCRLhKHs,9096 +blinker/_utilities.py,sha256=GPXtJzykzVotoxHC79mgFQMPJtICwpVDCCpus4_JtsA,4110 +blinker/base.py,sha256=7Y-C0ZVIe-NrrskPeqj0bLSp4R6Cpq5LrzI1DmLqMEA,20469 +blinker/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 diff --git a/.venv/lib/python3.9/site-packages/blinker-1.6.2.dist-info/WHEEL b/.venv/lib/python3.9/site-packages/blinker-1.6.2.dist-info/WHEEL new file mode 100644 index 0000000..1f37c02 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/blinker-1.6.2.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.40.0) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/.venv/lib/python3.9/site-packages/blinker-1.6.2.dist-info/top_level.txt b/.venv/lib/python3.9/site-packages/blinker-1.6.2.dist-info/top_level.txt new file mode 100644 index 0000000..1ff4ca5 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/blinker-1.6.2.dist-info/top_level.txt @@ -0,0 +1 @@ +blinker diff --git a/.venv/lib/python3.9/site-packages/blinker/__init__.py b/.venv/lib/python3.9/site-packages/blinker/__init__.py new file mode 100644 index 0000000..71d66d3 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/blinker/__init__.py @@ -0,0 +1,19 @@ +from blinker.base import ANY +from blinker.base import NamedSignal +from blinker.base import Namespace +from blinker.base import receiver_connected +from blinker.base import Signal +from blinker.base import signal +from blinker.base import WeakNamespace + +__all__ = [ + "ANY", + "NamedSignal", + "Namespace", + "Signal", + "WeakNamespace", + "receiver_connected", + "signal", +] + +__version__ = "1.6.2" diff --git a/.venv/lib/python3.9/site-packages/blinker/__pycache__/__init__.cpython-39.pyc b/.venv/lib/python3.9/site-packages/blinker/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c5f50e421c7ba2bb7fcb3343f211b638b6d2f33b GIT binary patch literal 501 zcmZ9JyKciE7=^+2*ojp2Ei%NYtrV$JRkbgWB|}seWE{H)y z036W3aR?mJ&~XGD(a3QO9Mc%18u8=>(}Yjxgr{pX&)(rCG{>WVzj}ZvTNSO~YgwzJ z0XwnQ^hG7WoNG}Dc@Uaaol>G21c%k=cXMQZVzZ4Xc7M@W+rqSOC9o1%NvwocVvwis zB46g0t9eSNWh2#2=)5e9_#brvm$xZ*$QUzLG!0{8#uz-$$WAf#igadoSoz$LZPyA? z>sph?eqWNZla0^|sZ0-rK!D^2p$?>xCFzgDwo_O6H8FA!3#fiq)WVSOXA#Dvk^^IT Ye;kvWw&VLo+`@Mva|SZSC*4cZFIu8_FaQ7m literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/blinker/__pycache__/_saferef.cpython-39.pyc b/.venv/lib/python3.9/site-packages/blinker/__pycache__/_saferef.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6238d0587aa160c85936aada4b60b17ee354ea14 GIT binary patch literal 7179 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 AWB>pF literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/blinker/__pycache__/_utilities.cpython-39.pyc b/.venv/lib/python3.9/site-packages/blinker/__pycache__/_utilities.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8011474563c15104f7098ac575e8709d607fbf5e GIT binary patch literal 4193 zcmaJ^&2!tv6~_V~2~reAOY(>O6-=APW?PeP(@EQ@<494QRP|VzvE@;XHiH4NO9>JP z(2J#Aa;TGhZ2yBC>|66M;o6gL?X{QY_ZFl?S($>we!ks(@9pn>AXr?i8F)VX&mM0y z4dY+bnSX3_)(wMY|3-rwoW(|;d5lrpj7`tfwdGm5wmnpOci`%GeUgBlc zC0+poPG0e|QNNqUAuXOg)VA<$(q_0eW5R4>V)*nj&aluN-`WJPN)AKi25ur=yGhU& z+%0x%Z@0|c@qHTe{oJM+BX%a@o^-I`_l6{3{P#uYAnl9JzTB6czYT`nPB)EWA@4;= zHV`4{qtkvRl2@HL>UIVrNS}P#{(UEll(;tt!lPhcWSwptB}YPb{Gp2CNJS!R4@Pu+ zltjw+Nq|g?XO5dwaoe-WkYRTAP@4#~+;ub*BtB$4*28Um&q%0pAz&c{lT{FDNs3|t z-Mr@eQt)92$yd=Yj%MTKv!iFz7Y-)|^xqDf+lc*v;XFvoT* zJtiCIhj`CDq{?-I&`mVX@aS0);n3uk#-R;3mGlO|7m$C$4GCC5qGnjC)m%IeA3mH8 z+`ZFF(>pCU%FYjHvRye8KTfYlv9j21!_DGo|3JAgq$m-6f%DRys}6+wWaFEy&CQ*i z$6Jp+PrHYJ;oX)y2$T{sX}j(&NCq@xGYHyw zrI0SmoyTJINXk^^wM-~3dciPO8VIluD~z)Ot!D_*c2R*p5U+i|14c4p%U~v}<2Pv1 zM-#%Cf!0G z-1-NCtDd)EBI}q3nlQh9Zr(0*N3mkO{{J-d%Nds7V%;t!O=n>0>+~sW%b$WJ9nDoB z_l5GRY2tGMk1Ql%8?bRvX);Oosz;04H?<07n?}!ILu!)0K=Eh18I2p}DqI`Adi;Vh zk&4y{E-o4~V=7EZ2S%6?FX;eThR~IKF%HHfe<0I=kZLr(yWy_SI)w=#;-pDDas^$X zjwLCDg$wsW2XE5BVudEHV-@ks>j& zDKEw(t(GEb zA}>03gg#6o;!{inbViXc!H3wA#>OSp-bhBd-A&V&5@U)0gX>DN>Zm z^jji#qO5QLI-pA8PX3O_5khBsQkV@>PRX$orhYFD_QBKUj=#C{bm#f**7hT>cFIU5 zAJFm&0HVFCrMJ_3cy0pKOL*W3>`;BKXxB$}(yPEqZN} z;xa;PR0Y#9Wc6)j0c>)IW((Z78ud<)fzMoKWg9^fQ5nd^Q&?$pg-9sp$1S`|t zQbz{u|1|71^K(i$HI&?WP<)g;B&1|?MjgTyKct9e-Mnr!>Se2Ot-0V>O~?3ObFo%! GEd2mRc_|-2`Ed2HY54ZdBR@CQ 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�lum$IwleLxdZ#RH@GMcah+sm5-3N0rH!P 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 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 + + +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__"), ( + f"safe_ref target {target!r} has im_self, but no im_func, " + "don't know how to create reference" + ) + reference = BoundMethodWeakref(target=target, on_delete=on_delete) + return reference + + +class BoundMethodWeakref: + """'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() # type: ignore[var-annotated] + + 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().__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( + f"Exception during saferef {self} " + f"cleanup function {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__) + + @classmethod + 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))) + + def __str__(self): + """Give a friendly representation of the object.""" + return "{}({}.{})".format( + self.__class__.__name__, + self.self_name, + self.func_name, + ) + + __repr__ = __str__ + + def __hash__(self): + return hash((self.self_name, self.key)) + + def __nonzero__(self): + """Whether we are still a valid reference.""" + return self() is not None + + def __eq__(self, other): + """Compare with another reference.""" + if not isinstance(other, self.__class__): + return operator.eq(self.__class__, type(other)) + return operator.eq(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/python3.9/site-packages/blinker/_utilities.py b/.venv/lib/python3.9/site-packages/blinker/_utilities.py new file mode 100644 index 0000000..068d94c --- /dev/null +++ b/.venv/lib/python3.9/site-packages/blinker/_utilities.py @@ -0,0 +1,142 @@ +from __future__ import annotations + +import asyncio +import inspect +import sys +import typing as t +from functools import partial +from weakref import ref + +from blinker._saferef import BoundMethodWeakref + +IdentityType = t.Union[t.Tuple[int, int], str, int] + + +class _symbol: + 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: + """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 = {} # type: ignore[var-annotated] + + def __new__(cls, name): + try: + return cls.symbols[name] + except KeyError: + return cls.symbols.setdefault(name, _symbol(name)) + + +def hashable_identity(obj: object) -> IdentityType: + if hasattr(obj, "__func__"): + return (id(obj.__func__), id(obj.__self__)) # type: ignore[attr-defined] + elif hasattr(obj, "im_func"): + return (id(obj.im_func), id(obj.im_self)) # type: ignore[attr-defined] + elif isinstance(obj, (int, str)): + return obj + else: + return id(obj) + + +WeakTypes = (ref, BoundMethodWeakref) + + +class annotatable_weakref(ref): + """A weakref.ref that supports custom instance attributes.""" + + receiver_id: t.Optional[IdentityType] + sender_id: t.Optional[IdentityType] + + +def reference( # type: ignore[no-untyped-def] + object, callback=None, **annotations +) -> annotatable_weakref: + """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 # type: ignore[no-any-return] + + +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: + """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 + + +def is_coroutine_function(func: t.Any) -> bool: + # Python < 3.8 does not correctly determine partially wrapped + # coroutine functions are coroutine functions, hence the need for + # this to exist. Code taken from CPython. + if sys.version_info >= (3, 8): + return asyncio.iscoroutinefunction(func) + else: + # Note that there is something special about the AsyncMock + # such that it isn't determined as a coroutine function + # without an explicit check. + try: + from unittest.mock import AsyncMock # type: ignore[attr-defined] + + if isinstance(func, AsyncMock): + return True + except ImportError: + # Not testing, no asynctest to import + pass + + while inspect.ismethod(func): + func = func.__func__ + while isinstance(func, partial): + func = func.func + if not inspect.isfunction(func): + return False + + if func.__code__.co_flags & inspect.CO_COROUTINE: + return True + + acic = asyncio.coroutines._is_coroutine # type: ignore[attr-defined] + return getattr(func, "_is_coroutine", None) is acic diff --git a/.venv/lib/python3.9/site-packages/blinker/base.py b/.venv/lib/python3.9/site-packages/blinker/base.py new file mode 100644 index 0000000..80e24e2 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/blinker/base.py @@ -0,0 +1,551 @@ +"""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 __future__ import annotations + +import typing as t +from collections import defaultdict +from contextlib import contextmanager +from warnings import warn +from weakref import WeakValueDictionary + +from blinker._utilities import annotatable_weakref +from blinker._utilities import hashable_identity +from blinker._utilities import IdentityType +from blinker._utilities import is_coroutine_function +from blinker._utilities import lazy_property +from blinker._utilities import reference +from blinker._utilities import symbol +from blinker._utilities import WeakTypes + +if t.TYPE_CHECKING: + import typing_extensions as te + + T_callable = t.TypeVar("T_callable", bound=t.Callable[..., t.Any]) + + T = t.TypeVar("T") + P = te.ParamSpec("P") + + AsyncWrapperType = t.Callable[[t.Callable[P, T]], t.Callable[P, t.Awaitable[T]]] + SyncWrapperType = t.Callable[[t.Callable[P, t.Awaitable[T]]], t.Callable[P, T]] + +ANY = symbol("ANY") +ANY.__doc__ = 'Token for "any sender".' +ANY_ID = 0 + + +class Signal: + """A notification emitter.""" + + #: An :obj:`ANY` convenience synonym, allows ``Signal.ANY`` + #: without an additional import. + ANY = ANY + + @lazy_property + def receiver_connected(self) -> Signal: + """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) -> Signal: + """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: str | None = None) -> 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: dict[IdentityType, t.Callable | annotatable_weakref] = {} + self.is_muted = False + self._by_receiver: dict[IdentityType, set[IdentityType]] = defaultdict(set) + self._by_sender: dict[IdentityType, set[IdentityType]] = defaultdict(set) + self._weak_senders: dict[IdentityType, annotatable_weakref] = {} + + def connect( + self, receiver: T_callable, sender: t.Any = ANY, weak: bool = True + ) -> T_callable: + """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) + receiver_ref: T_callable | annotatable_weakref + + if weak: + receiver_ref = reference(receiver, self._cleanup_receiver) + receiver_ref.receiver_id = receiver_id + else: + receiver_ref = receiver + sender_id: IdentityType + 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 TypeError as e: + self.disconnect(receiver, sender) + raise e + 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 TypeError as e: + self.disconnect(receiver, sender) + raise e + return receiver + + def connect_via( + self, sender: t.Any, weak: bool = False + ) -> t.Callable[[T_callable], T_callable]: + """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: T_callable) -> T_callable: + self.connect(fn, sender, weak) + return fn + + return decorator + + @contextmanager + def connected_to( + self, receiver: t.Callable, sender: t.Any = ANY + ) -> t.Generator[None, None, None]: + """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: + + .. code-block:: python + + 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 Exception as e: + self.disconnect(receiver) + raise e + else: + self.disconnect(receiver) + + @contextmanager + def muted(self) -> t.Generator[None, None, None]: + """Context manager for temporarily disabling signal. + Useful for test purposes. + """ + self.is_muted = True + try: + yield None + except Exception as e: + raise e + finally: + self.is_muted = False + + def temporarily_connected_to( + self, receiver: t.Callable, sender: t.Any = ANY + ) -> t.ContextManager[None]: + """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: t.Any, + _async_wrapper: AsyncWrapperType | None = None, + **kwargs: t.Any, + ) -> list[tuple[t.Callable, t.Any]]: + """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 _async_wrapper: A callable that should wrap a coroutine + receiver and run it when called synchronously. + + :param kwargs: Data to be sent to receivers. + """ + if self.is_muted: + return [] + + sender = self._extract_sender(sender) + results = [] + for receiver in self.receivers_for(sender): + if is_coroutine_function(receiver): + if _async_wrapper is None: + raise RuntimeError("Cannot send to a coroutine function") + receiver = _async_wrapper(receiver) + result = receiver(sender, **kwargs) # type: ignore[call-arg] + results.append((receiver, result)) + return results + + async def send_async( + self, + *sender: t.Any, + _sync_wrapper: SyncWrapperType | None = None, + **kwargs: t.Any, + ) -> list[tuple[t.Callable, t.Any]]: + """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 _sync_wrapper: A callable that should wrap a synchronous + receiver and run it when awaited. + + :param kwargs: Data to be sent to receivers. + """ + if self.is_muted: + return [] + + sender = self._extract_sender(sender) + results = [] + for receiver in self.receivers_for(sender): + if not is_coroutine_function(receiver): + if _sync_wrapper is None: + raise RuntimeError("Cannot send to a non-coroutine function") + receiver = _sync_wrapper(receiver) # type: ignore[arg-type] + result = await receiver(sender, **kwargs) # type: ignore[call-arg, misc] + results.append((receiver, result)) + return results + + def _extract_sender(self, sender: t.Any) -> t.Any: + if not self.receivers: + # Ensure correct signature even on no-op sends, disable with -O + # for lowest possible cost. + if __debug__ and sender and len(sender) > 1: + raise TypeError( + f"send() accepts only one positional argument, {len(sender)} given" + ) + return [] + + # 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( + f"send() accepts only one positional argument, {len(sender)} given" + ) + else: + sender = sender[0] + return sender + + def has_receivers_for(self, sender: t.Any) -> bool: + """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: t.Any + ) -> t.Generator[t.Callable | annotatable_weakref, None, None]: + """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 # type: ignore[misc] + + def disconnect(self, receiver: t.Callable, sender: t.Any = ANY) -> None: + """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``. + + """ + sender_id: IdentityType + 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: IdentityType, sender_id: IdentityType) -> None: + 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: annotatable_weakref) -> None: + """Disconnect a receiver from all senders.""" + self._disconnect(t.cast(IdentityType, receiver_ref.receiver_id), ANY_ID) + + def _cleanup_sender(self, sender_ref: annotatable_weakref) -> None: + """Disconnect all receivers from a sender.""" + sender_id = t.cast(IdentityType, 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) -> None: + """Prune unused sender/receiver bookkeeping. Not threadsafe. + + Connecting & disconnecting leave behind a small amount of bookkeeping + 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 bookkeeping. (An empty + set() for each stale sender/receiver pair.) + + This method will prune that bookkeeping 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) -> None: + """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: str, doc: str | None = None) -> None: + Signal.__init__(self, doc) + + #: The name of this signal. + self.name = name + + def __repr__(self) -> str: + base = Signal.__repr__(self) + return f"{base[:-1]}; {self.name!r}>" + + +class Namespace(dict): + """A mapping of signal names to signals.""" + + def signal(self, name: str, doc: str | None = None) -> NamedSignal: + """Return the :class:`NamedSignal` *name*, creating it if required. + + Repeated calls to this function will return the same signal object. + + """ + try: + return self[name] # type: ignore[no-any-return] + except KeyError: + result = self.setdefault(name, NamedSignal(name, doc)) + return result # type: ignore[no-any-return] + + +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: str, doc: str | None = None) -> NamedSignal: + """Return the :class:`NamedSignal` *name*, creating it if required. + + Repeated calls to this function will return the same signal object. + + """ + try: + return self[name] # type: ignore[no-any-return] + except KeyError: + result = self.setdefault(name, NamedSignal(name, doc)) + return result # type: ignore[no-any-return] + + +signal = Namespace().signal diff --git a/.venv/lib/python3.9/site-packages/blinker/py.typed b/.venv/lib/python3.9/site-packages/blinker/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/.venv/lib/python3.9/site-packages/click-8.1.3.dist-info/INSTALLER b/.venv/lib/python3.9/site-packages/click-8.1.3.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/.venv/lib/python3.9/site-packages/click-8.1.3.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.venv/lib/python3.9/site-packages/click-8.1.3.dist-info/LICENSE.rst b/.venv/lib/python3.9/site-packages/click-8.1.3.dist-info/LICENSE.rst new file mode 100644 index 0000000..d12a849 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/click-8.1.3.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/python3.9/site-packages/click-8.1.3.dist-info/METADATA b/.venv/lib/python3.9/site-packages/click-8.1.3.dist-info/METADATA new file mode 100644 index 0000000..8e5dc1e --- /dev/null +++ b/.venv/lib/python3.9/site-packages/click-8.1.3.dist-info/METADATA @@ -0,0 +1,111 @@ +Metadata-Version: 2.1 +Name: click +Version: 8.1.3 +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.7 +Description-Content-Type: text/x-rst +License-File: LICENSE.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/getting-started/ + + +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/python3.9/site-packages/click-8.1.3.dist-info/RECORD b/.venv/lib/python3.9/site-packages/click-8.1.3.dist-info/RECORD new file mode 100644 index 0000000..5408a74 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/click-8.1.3.dist-info/RECORD @@ -0,0 +1,39 @@ +click-8.1.3.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +click-8.1.3.dist-info/LICENSE.rst,sha256=morRBqOU6FO_4h9C9OctWSgZoigF2ZG18ydQKSkrZY0,1475 +click-8.1.3.dist-info/METADATA,sha256=tFJIX5lOjx7c5LjZbdTPFVDJSgyv9F74XY0XCPp_gnc,3247 +click-8.1.3.dist-info/RECORD,, +click-8.1.3.dist-info/WHEEL,sha256=G16H4A3IeoQmnOrYV4ueZGKSjhipXx8zc8nu9FGlvMA,92 +click-8.1.3.dist-info/top_level.txt,sha256=J1ZQogalYS4pphY_lPECoNMfw0HzTSrZglC4Yfwo4xA,6 +click/__init__.py,sha256=rQBLutqg-z6m8nOzivIfigDn_emijB_dKv9BZ2FNi5s,3138 +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__/_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=JIHLYs7Jzz4KT9t-ds4o4jBzLjnwCiJQKqur-5iwCKI,18810 +click/_termui_impl.py,sha256=qK6Cfy4mRFxvxE8dya8RBhLpSC8HjF-lvBc6aNrPdwg,23451 +click/_textwrap.py,sha256=10fQ64OcBUMuK7mFvh8363_uoOxPlRItZBmKzRJDgoY,1353 +click/_winconsole.py,sha256=5ju3jQkcZD0W27WEMGqmEP4y_crUVzPCqsX_FYb7BO0,7860 +click/core.py,sha256=mz87bYEKzIoNYEa56BFAiOJnvt1Y0L-i7wD4_ZecieE,112782 +click/decorators.py,sha256=yo3zvzgUm5q7h5CXjyV6q3h_PJAiUaem178zXwdWUFI,16350 +click/exceptions.py,sha256=7gDaLGuFZBeCNwY9ERMsF2-Z3R9Fvq09Zc6IZSKjseo,9167 +click/formatting.py,sha256=Frf0-5W33-loyY_i9qrwXR8-STnW3m5gvyxLVUdyxyk,9706 +click/globals.py,sha256=TP-qM88STzc7f127h35TD_v920FgfOD2EwzqA0oE8XU,1961 +click/parser.py,sha256=cAEt1uQR8gq3-S9ysqbVU-fdAZNvilxw4ReJ_T1OQMk,19044 +click/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +click/shell_completion.py,sha256=qOp_BeC9esEOSZKyu5G7RIxEUaLsXUX-mTb7hB1r4QY,18018 +click/termui.py,sha256=ACBQVOvFCTSqtD5VREeCAdRtlHd-Imla-Lte4wSfMjA,28355 +click/testing.py,sha256=ptpMYgRY7dVfE3UDgkgwayu9ePw98sQI3D7zZXiCpj4,16063 +click/types.py,sha256=rEb1aZSQKq3ciCMmjpG2Uva9vk498XRL7ThrcK2GRss,35805 +click/utils.py,sha256=33D6E7poH_nrKB-xr-UyDEXnxOcCiQqxuRLtrqeVv6o,18682 diff --git a/.venv/lib/python3.9/site-packages/click-8.1.3.dist-info/WHEEL b/.venv/lib/python3.9/site-packages/click-8.1.3.dist-info/WHEEL new file mode 100644 index 0000000..becc9a6 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/click-8.1.3.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.37.1) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/.venv/lib/python3.9/site-packages/click-8.1.3.dist-info/top_level.txt b/.venv/lib/python3.9/site-packages/click-8.1.3.dist-info/top_level.txt new file mode 100644 index 0000000..dca9a90 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/click-8.1.3.dist-info/top_level.txt @@ -0,0 +1 @@ +click diff --git a/.venv/lib/python3.9/site-packages/click/__init__.py b/.venv/lib/python3.9/site-packages/click/__init__.py new file mode 100644 index 0000000..e3ef423 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/click/__init__.py @@ -0,0 +1,73 @@ +""" +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 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_text_stream as get_text_stream +from .utils import open_file as open_file + +__version__ = "8.1.3" diff --git a/.venv/lib/python3.9/site-packages/click/__pycache__/__init__.cpython-39.pyc b/.venv/lib/python3.9/site-packages/click/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3172f4a3d08f0b63dede5eef18925098e7503921 GIT binary patch literal 2620 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 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/click/__pycache__/_termui_impl.cpython-39.pyc b/.venv/lib/python3.9/site-packages/click/__pycache__/_termui_impl.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..26e6805b4a15c0c70a99295297ae6e92fc63ce1a GIT binary patch literal 16006 zcmb7r3yfUXdER}^b7yCV%jI(Uka$H=5{D*tNlKO#mZeEbq|C-%DdbwLCT>QvbMEfW za&~5T?j4dlo~;8fTokR-plBGN zXxgF{ zRwhf6rs1V}d)3&6S(@_f<$bDf!_xmw?JDh8F}WT&`|yU`;I!G4&HT2Pk&_n z6Y5FSA3^;ygEpHp9Kr~$dI~L`#kvlwr?IZ5*R9fX>ILtR_Z0q)s%OyVS@j%NQA8R0 z9>eO6-nUdy9YfELdE@Vx`cLH~j+c&m=JE-&n)Z&O)yM9e>bN>_*I749GZ^iVnnu4F zbrStfHqJ<&Q>9b#JY72dSl?5R^*tT+Rn|45`0>u~WapM@zPr*??Yieyn~k7SYxr($ zrB(M1g#fMfztbo1#8e3IVrW5 zM`DP5Qy$qs_I^Q)s2tEWrwla;1k9^3pxdY_s9krBQXW0WxDKfsSG!SH@RE|7PrfMBXzf*{=>r$>gBqpn5_|_IhdQ^Q1b2F{a{NL~8ru9Ldco z?T>Ru)HBE(h;t-3r*tr0$x&5A?g@FnV<4c9spH5$sXmT(=3eq`Omzb6Vp`4M%@4)> zPO4LA=Wco9)9QJYJ*7+#(2L>7>$S1r(X)@tryrT_qtr7;=PSDCKeAkR?y|OV zoDZKwQY?h&8Zf6aU-!Z!uxBw?3Ns6}dc9m-s^~E5t+ayG2+m;ypf2dJ5_ss=SZJ1g zuN9{KrRMFj7gWNWKrK&KK~r>LcY%OtDgyN>W&us8{B_dgx#tFHBUM^`;IB?Uus0 z<&_#y+-vz^;&x48HA(FSZQZCAu-yJ<7k~u5FXQVU16p?Nu2D9+4pOt5Kx%c9NbPP4 zsnbm(O>{Fzlie)RRClEHqA~%cyFjV7p+6O5){V7XH;1xpcNFPJH;*)@ETp5|F{JtK zn6hp#Ki0LR*H^&N?RsHl*BWF?x6n0}vu>?fsC6JIzM>4kAQz1Pow&zu1>-8QZhp}W zcK0TN-OGD|$$dt!w`;AN8`hc`Om)q5u4}5xdG^Wd`xgzs2ZDpWgBylJ_z8TURD8oj zlDdkePYHZj;HQHlU1K@BZsu6?jFdgA3?N8qHZ*k1l18EvFE7;^!NU*n={(uT1{eG_ zss$f($tP>tQHXMreSpb9CQmRSF6l!|TqaL3In2ai@-&kpOrAjkMp$ie`EUPk=cF>H@Jq>QBD?wB1qSVZ-w!BMP169l*bgC5}7gswCe zz0lz+=zT1)iG6nE)?(;T3WNzisA%-7qqkse254Kp(SS?{(+jmm&0q3Vm|3aZDMyP) z0g*ktcbKcTwMM2)RMsh3QO(E3Ddjc7Y*|LE*BV}NSLpa&eL+v5jjZDTZ^O3kmF$Xa zz4d=i^5RLyxEx;;!D&0sopd7bC{<}gzSF^LuZVro6<)p6?oh;Hw@4j zvDliOD_Z(2D#J0Hi}G~26|+4Ic^>7mT~Tt8%`#<3RlISz{1&o4)l<Oq-oOTNf6$ zsuFekAHstr<&eQ*3@(Nw^`1%LJsK|~LZhN1iwM)+ooYD%>V_FY!1T5kTEYDG)nV_w zJKTE@gZrGycx=a_1|PjuBtj6q;pYP=*<> z1%HR0)o?5B?OL$p&exmO8`F3Z+2!2Tc7rq|B&aW8sxY(YHDVIIjQkF-L`p1|mr&`a zkwp8A9p8=$((JJpv(e*a_T`2LMu68@$7@{!w?;wH3Y`WNDg8V$`Xd&=2IX>tgUFsH zw@)24_~@xg3fxnj>?V<2k7{50HN)-SM-|#K}zkkr{Or! zwG1Ibo()L?75IzjFuc+yhgbS|^T!rGz{qDY5El;FZtBB;OrI%a_A<-uDPsZ)=SykW z#s*eRc_f>we}WJZO;Z#a6XYrYG+y9>+vT?d+l9X~BTAEZDGJJ%%e-l#36 zXCIPrJQO)dmfo?H8(Jq%KQhl~VrkLVKY~YSLkG}h79=9ZJ;TzJ@4@y{ewYF-wgV6P z;8L@#{q0;P3qx?FBGUY4(a+~}5(~8w1Jy}jt-cI4l#8ekEnnmd`Kqs7Y{Y%0cv9E&tma~ zofnISTNcZG9W0joKU%CZCqO;Q!ok@Opo}Wxr(kJeiHX^V3avVp`@$phnTX~s%_3c5 zvJ;697Fi9xhBkfSw9{U0Cw6ir>Pmu}Uk5VopSE8hD!8uT`+adjZI3<%EpM2+-E%JW8izAjBs}}BIwPXBS#q0^n{H%^%_ut z^d36E(WTI?HvNdNI?K&kBUTbhSz1=H@nr*sO$6iXBT*eKeidW-N01nTD_Lk=Ni%Ov znAxQB7nW(6Q>OEw#lK{BXH3E%TQJGNSV?^4sNM&F?E+#i@<4$81GJ|xL({^_5$*BR zFLkXRwwBn5o6_$Tkrk?6NpnX~CDG^bIzo zdJtJK|TR81?8VO?`$3imwg&d67@pj#eNPha71#sjPc;r1r+BkDF`7(toY%Wd$`oGAmgaRS!4eZ$bd2o%T%@T@|S0;EW{1yU#SwBHMo!fwaN zuydCSf(-wV5b~RSWv;apW=&y6wr5Mj|IdWFY0UH|V*LYAdiic@Xuf1OP42=H<@9zD zk=9c57c7H&!25YldLDXHF22 zA?1)m87xLC64(<0jRVPZsGBf52Zqsmo23U6AQgLyA}f-zyZ^BP@cS~p{#TH6;X8qL zW_|*e9DGH6rgjXFiM2=Y!4%V*%0tAAUPP6CiODCJPyrUz z38;0R@H!HB9pE+Fb{2GH-0;Mo1~fPPNi;OTNFXh&anrG~iKIDc<*i9;s}Vftg&8<_ z!~~xMROOcTKZN;x8DIZ05;(a`@bSDjukfnaa4-PbaLNji7ue>DU=bvEQ^-*y2Z#`i zrwm>W0c=;E#7TZu9D?HH@AEU7sP6o#TT!WcNP`PTf9j3E`qBE_EuB^B* z>YZ_C`xCn|Ar0$jh2He`KEpGfabJrU4Rt-f0J~4IXjQi5`$t}ZNfagl>ybsO(vgAGCr_)0(|Nk=fYYdJ=#OmH&@)$kw zHjgLXr4`Q$eAaAP)?06FcLd{Lf+hH~m%ynu_cP|Xi3l}62ILdxqW&pDbWaQuN7U#7 zt0cJyC`?8Z?ImH*e`plo+5$1;M9Ox?hZ#@Qbc-ZKw@sH22}^hfv|B-AA~S3SBJ=F! zIjlug3(+O08t$lQCnMLXN%!^Xf_tSkT)CO$f#{1sg!ggL7AHOJ??#YN_OTUKVND_-9NsdU+iAbcs z#N=-viOsms(zmuTS^YYyU*w^Q?U{p5ky;xBS~3Bl1~>=z=vD?h*sEfGR`2EW4sNn5uW9me{VJ2suvz3@rw9JWSayvG9aUi(X92%Tuqqyy z$Z`~~2CaB4s`I8|EebQ07op0CJ_oxhdpES77ub%}sp9RHM0D{u!KmU?6m4!O*mp?R8U6!L)^=%|8Ow5o`bf9UIC+VmS#~f=#|* zwO&v*X^A$DE(IAq8^G#82rFs{Lv;X0y+DB~am&!Rwv3qZzl0fcy-}6ifbGOFR4N`L zA0$+I-O}#|iQbsXpnn$B{eFP4)kxGvWpN#&0Jjg6-woq)P|eqpXg%Idkls6UK|BUj z;c*!y*GZN2L+SKt7^UKArAiYN^6-zt%s`{s`)?v?NNJTDie@Xu&m5Z~BPUwj0pD zZxmCTn2Pt5D+Q&DOjfS0DE(EAVl5~}x1g$ncMK65=`2i0mi|lRg?D9r_@9a9Kg*z{|ML=p{fT2`H5ooDO%pxEX|?coK+wm+gB=l~U;q_~2Bg zmqM+hEb7@C@LN8xU?nE~mde6QnSzIB1iJZz0t(+U{GU{#eCoR_?b^#}c=JZ^%%E-l zo&#$#+Zze8%eii9c@$CEbdc|+VO?fA$GZl^=Haz$*Ydv|jCB!T1)PQ(A`1&LgPTII zA|Wi_ziu=p9U~YIb}v|~|1;uTZyJs4Ndv7XC`LONy|;%D+D@SD?sv@9gZ-8_KR9W; zW8OS+^8+E1Lg(h|K({SqKs|T?cTPwiH7K;uIU7xAQ=l2T6e=szu6w0%t9e7zWRbOU z)SH9Ijh5QtQsDaSmRyyQP<$*ig*0Y$4TlzqA^Iis-$4>OpT2bMnoysJtw+|0t8<}= z!0c6cKNEBY2n%p`tzaR zWres{60h^#OX$3Q<@HM<-SkPePhW)jt5+emIF+Wtq={X6j+CPc`&)#56Gz-wJNd6z70I!04E z>O<@HJTAB#)_)h7VqTb^{uTfXlH738qvrhn6Y3unqNp z5lz0yx92Y7?d8oiORFpx{!qrA$Jf7uWN^oN#CE9Kj%=Lj4+q5B(@V4y@#bS(?4;F@uBB5?E>3Mxgb@-PK@56q=& zgE1sTU!>4J*R|vJ=i&e%w6^3MSFU~b!lz5gORrp+d+ll|1sMhfNfqtRn9t3{#k3RA z5tQs(wUv_7fSBtfBNMYza5r^1=ylJYiWEBQR;`$fwsRn0B8fsBM0BgzV;|Z#yjA_v zER`+Zio#)*H!`E?{|&cREIhg^!K`z@XHqj6IWN6*cEwx0*#|TuJmNWwUnM&9Kct8( zzOv`<0{|}54G;7n3x_kE0a->@P6nU>QG}?e({(5+M(35TwVaY2#uYQ{v;BafT7+cp z*>tjteE}6nnQ-OnAQNO2*gk?_wo2WTyCG0J&O+_Tt^THItfj!xk4P&hI&9TVsmwiC zG2PS`t?R~{z=yTeUr#jSYqnM+L!CRRDG@cV zjD%)6Oh#-@|6}CgSUt}=IT9I8j-1+u{-;R5HFyOFH*Y=tFIl(^88UpCGNsNR5f`3C z6S}v-*TjXLvn=xuDqsAyfm?L5RQ#YPmE;|-lWrKpq=F0Q4d4k}BAl=p={%Y|V)IJj ze$&ckzwf~0JZeErcCsH>*0>m)osl7WDmt@>p8NsE1e5ric<8^yN5!Yf(hw+EF$mK!ce#gVe&#_^%@NC|H9(U#S3(iW&a$>xA_7T=V?3EhX29>0~iOX zTzw3LV&_R1i^pA=rO(1CWdtk1qHNw*Fh{5 z7OzS`jDATa7frBu6A{`3tmQbS3D+%iX=>qws9mg!CJ$FoMr9FuO?D2#%1t7&np7kA zzz;S+&!BB_>w*em=%ms37MxPwgNXkPj?&AjoPr$;y^_uz1HvZv079mL$XUN`4f>7# zT{YIQ)aY7D6}l<3+7-7=tR;dGa1c-!=ywzp_^EE}J`zqLEA5X2u4Jh3p;=&a_v<(d zdLLE0<9Q}zo;^Fw)4?SKv|S2DHs{zt@0L=NYepj-?GHwzA7O9TSY-Sco&fauS?~kd zn``8ESTn?A0oM7ykF0ZYEj7il^mjRj|6TU-zbhvd=1A)~WZZUSIb_()c8LxyqZ9`FIqK5%_F-r{RLJQukWn~2B8 zPfyy7n{6mkofM_jnVFe~DU1i>JO_-##o%cKc6?9&DGGIuHSuz!KrmhsqJ218jt3LT zcMJ=R^m#ge%!1W9M3zy;xYK8ePly*n-ZG5`*Z8Cx;7VZ$rIv!Ag-z+C3HX4bfWYoZ#~`cFVj})Bq1#Nj zcD<=xe4SZQC4~)dnvGQZ^?yJTrc_M}bEM`Wn;d3ud-L<&`ui9i;>@ZpJ&w9WV=N(J zWiJ8%ike_pdHTnB7nwhv&~9MD#I)iP#+*(ECcaW4I;Ddjt=NxD=aIP|j`)9qfg)#W z`qFEc#g_x+@y}Vh4N)h`0e%4B^Vcc(!Ad!3;zo6qT6YxPgI<=zD@I@a0S9;mNfqBM zAHD_)eX?dHVK7;iHLH)IWN6lH^XGKswp^*_wG7^p;}=_^e}EJeCf7Se;o}tjj}2hk zuIXHW*!Y^+NaBefg}}c~%fM15oR-I6LL$#gad7H@DT6K0lWxI4e`Ff}n`8ZckFJCj z|5jB8CH@TUc6A9Z;|P#f$HsCw_z4bvkF%q^W;fnKyUiCVaJ4QB9SgTLthCMh{u+*< zt-HrpAyLTg(Idh;+sh1v91Lt;NGG4_IndZl+>@X&DKJb5qtgizFmA-O?3@cy@I{I2 z>dO8@A4|Y91$}lIp4SabjNXta^qvB(cunl97>;o@dQ=(!!zh7A#P*9Y_R)I zL&mOXq3W|i3P%A;5fGj@8nHX0^cA202EKK5u98@1gt;Cpy{#Ptc$?utPOP+#TiwKZ~ z%W}E9k>iZ~B;sUQFsA4?nSNxbQccHCpoAn-{Grnu(D@~3`^W6`1ty;cnwbh-jT@#R%|90IcoZB6qP=Q28RC=NSws*E$kdP@MC|__3209jYr3x p{DScX;}h-^?(Puz3suRp zYL%#DCUuJT?(jf)2GjfkLXwCI5;5s4xO5N6ySFDjK?Qs5w%K^plfHCew=d`+5$p0Jp zw7qsEm32=uQO&KVWmRjH0#NTpA65v`*D%fRAS%-E#yJ3}F4@{`$b#zc8YuaYti8q) zq;dbib+7RnSCDq?H|&}6WqV^hQqSj8Yog0FCf&ie-E?-ub=ZUEz zC#@^%Xlec;`aN)Y^CFxVD;du8OowM>Jq@QtmP`F4Q>K(@l@%%+pGb8Q=GinX&#QT% zJ{x}-nyivf;Nwf^wh7ZbOP67c1ULok$K|BR?~wvF#Z2F!Yy5>i##hwzl)N45ix-5TL-egpwMA!~-6Svw6hZ$TRR1z{wH9nk1j zqZf|^c|j;SA`N)ME_laOjk?+=o379aR4~d^R%J;ZZz{15Og%O7xQ42>rhw~vP=I~} zBHAl*0rqZ4^bl=#!rDG;#6CpYA)u&}?Kcm>V$e(gGLMevh%xFTd~ZE^anOaW8wT}q z?8aTZkQ~U8meJqCVg_gdtLZhpA(!+U^4s`VwszJW5JZMMf-brK7&HeocFi0272JVW zDkve(i<;Mu-Z^-Aibl1RfmtMJkp{TLkd0g7F8<%=Ryfwuwf zeY7xrVx3H!0bwDa1@r$1?QB{Q7b-vh2x80#1fiVmQ;+#D_u0kcE;JqgU}*g~R>?}n zu^q(mst`4>G`7PyKCY8|yF=l^{E!*AE2h#=4^ZzRp!pXV1?G$9{nK-yi6T| zEz=GN#Jgp^@A$xZhR>Vf9-{}tb{~-Pu~T?&tXG|vnVM-llpSn;sc`Yyw5@w5MVql@ SjQ@0VimwoTgBdZ^(D@gN(R`2q literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/click/__pycache__/_winconsole.cpython-39.pyc b/.venv/lib/python3.9/site-packages/click/__pycache__/_winconsole.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..425a39468035afd91ecc3c6ba259aeae9fe35bce GIT binary patch literal 7787 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ðQc4~|>yh5@ zqkMe*7knO0FMJyUpeVbrcyqeq#*sJKqAarp&t3Xp)oh;Y@&WM*jCU499l1lw)#<@E z9O-|CU=K71FyKNn{a2PHe;4_?lGy}8K__?^iuDTh`av{4R|o4va@I6H}m zowhSM`D04%nN?xeto-%xUmqe>|8-jOGaBCaLDXE}@qV+pVAmYeazy;=)>*$@F8ZT0;MZJ^3_z-$o-LQxJ34f5o=t0N6C_6WhS)^QwZ zQ1vxLKX(o`k~)W@?x{tG;3GXGh1bJWnC>FT*2UFWIj;y^A~e|Yr%IV9xmu;jiIjgx z<5m8r(uI+`Ekst@z6=GQ1scF-^IMVC3^r;qbaLd{`$0Vp1<4K6jqQ|6ds6a=iiA!Ua4Ko{;Mfpe6lo>bWfsZV8x9FXh zw08Z6ka+vNbdU%Uz*=YW2M9Q@4Gmx5GJSDbz`Ec|Zi8|cuxbW^2TGcP5C9OZYfvB( zd77-yDN8{KT1n!8=-1RlH_=Ux-6A&j53KmIPeYfVgG2@vE?$TRlDOBZm7(d#Kc;fC zzTT=Shkvg@olpja$SlS1`m5E`Ec{^>$Bs19dZFvq*ScZ-S~sm<=~f>8dzNlny8Kh> zkxiBE#MW*En}NJdomz-b(jlXe%c@bdGKja2R#nBH<4822t-8y^aca`lIC7c+hhTxK zIfgODP0;4YQH?xtLm*KSLvOaLVGOBu+pC5YN0&b#iNi!jh`dFz_ITb^4$;kR;rQlahkra*4Piz3dMYJB#mE29d*AxlN`>Eu=Lme3q@i6G5`Po literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/click/__pycache__/core.cpython-39.pyc b/.venv/lib/python3.9/site-packages/click/__pycache__/core.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..19af5ec0a08ac32ac82af6b9186e983c2cd2068b GIT binary patch literal 89662 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
  • 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 EU-rjuDgXcg literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/click/__pycache__/decorators.cpython-39.pyc b/.venv/lib/python3.9/site-packages/click/__pycache__/decorators.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c22363cfda87aa5c06e39479e7d7350c5b006953 GIT binary patch literal 15574 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 Giuyn7-$$eX literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/click/__pycache__/exceptions.cpython-39.pyc b/.venv/lib/python3.9/site-packages/click/__pycache__/exceptions.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..010944a97452d6e775e47c89194d7cadd15123ba GIT binary patch literal 10135 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

    9MuR}KiP}@ z%^+^>`$;$JbTeQMlXxd1tCs@Z$>@XhUa*e|QO}e(NZ=^96TLrfkqQN9#Z4Wy(bs@= zpcK5lgjgQF;b}1dN5tG1>K?OE6Ks?3^EwwU349QmB&LHHsf8gXV6Gv8jcIqVC}L!5@+Dyjpki*^{$i{mm=r(_m5tSlQP_7y6J(O3&}%N`H+4peQ17;K1<| z4a81Lz)3u#5MT8X@8JH!%wk8}+}oMccaBOA#g5yz4-sE2BIZ5h7U*AwyDl2u7983} zg(0Usc;P z$0@EeghaBpFs44sgKmgoU1QXrjAv+nY+Ss^v$*Mt9dU$6hU^gs)mHEI8%9C{zZ=5(vC&Chq0qbY3k=Bq(vpgFi3`lR5SHHIO2k6N=0K?8ajrO|1$R07-Lt$?`2H?1+m3}-Z($G3+#d{-T!`;Oal%Op>+;i{jJ;y z^cGSW#^HWa&q$&vmSz$7J#HvV0c~2y;J6&huV6lYOF9N)MC<#N_^tI7bdc+j*+88| z^^4-5)GwLbhAN#Fw@Nxzwt1cx2oMqQA$+qCI68e-x%VA*dETY^KHt-wDzo6(8QSki z^tLHeqP)`xsmExzBO1s0QAk$l{ntofQ&m4uq!Edub>%1>q6A6%xZ*r9T0|o^`QG7{}{IATq$XU*Z1bN=zHR z3#$K&76UkWcGCQcc7F_2c7&eAyd)Be5=d=f4tDS5;T%Tc_DQ*CQ5XU=&tMYNJY~2Z z()kzSo6R#UPw)9joDbV#HP}U`u;_{G8CCMmkaZ;^yhEyuy*ZK@r9{OJ%sC7?4EFsJ z&5dX2@GMK1rP~XNS?Un!O2R{OKqy#0In~0eF+n?AxmXRgX%K`W8#2suEeV(`x8Mv17OFHXLqlRINQCVYLLe1AjXmo)sB;7?3Wv3@I4ZVkY+pCLxzt$5fi2O_&`9B zI%(t3LYe&8Aap6h9ZEY5CAhFClqSCD2SeLKxjDoWNMupg90`?S3QD(G1^nAV)J24J zl29Q{#sUspdE@0%su2duSSaJ&PV8RDpdo>D{sz)?=*zj26gj_aFDx=ThSYI|r1_ux zAcvIjl8`?=8j+pz-nsYh&`Aa9KBjEAGSfY=34DGs!fwVtR`5sUU0ttBquZo2tMzG0 z7{FPk^8iiSmRGLtcf$9zPPD#4m6U}a0YB$2*lM!7Z;a@hXo55a;{mCj{xua1Do&$V ztMK%3#1Q2rzJ-6zQ`I-Tn(V-W4K9dbA&8JDjk-GqOKzhqSv60G3I?GziBh2t0_BlBtM|+}vKMWiu=T@5CWA9ru$Olbet}o;LDmg-w z@T1>P@yoUK#T+p=$xWk?R~wCXqPh{?*BXsoB$J9asWX37^t6!@P8zKwEE8BjP{zt|71vGGNa()wQVDp<*O; zd^baNKSM!XQ^F}E2d1HQQvAWPuumj_-_%acIy25pNqlD^U1@z=JBR1e>?zY~&bZ^> z)XrOSamJDh9#bS&yD(G+5wN(!54qVxS6kr$hj4Hu$en{>T|ThkY#@SDFtmsi;Zh(G zeoi@fFWKFLT<(Je%6j3dayvGFyh{No+S_9E~) zH?+YGlS?rO6vX>QMAT@djTCvx?fUvU!ov{9Uj$1A*o{dpSYXRcha7=9?jW>dHlRCv zh=pVzg2))@Lt}W0nVHB7Hhs?=?9gBatFE$*dn{M3KcRCZMijEJ()NM8OLTl-nOql( z5ey*th{r^Vq$&32DI`wH!OfFB;PM_N{QgXPx)Q*dP{2 z^;5bAe^c#J2YVbD%{8NCE?wpD; z7TuwL5}31ulDk{!>5)2aZK{R`*d0 zGvedNI3WZ#SL;i;o9)xlr0(E3uNEc{f45-G*R){zm-MO(N1@#biho16h*xlgZ{|d) z8@)?KLM@+aG=exzGDhkYz+~V9|GCf^HPTeDXVaxS@?>&nBf$V7_M0**}_^mvi6NDvUc)d@~I130WT3uFd?dc=P`NZJIJC literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/click/__pycache__/globals.cpython-39.pyc b/.venv/lib/python3.9/site-packages/click/__pycache__/globals.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c9ba48f93522be8bf44422c6487046d5b4f24b70 GIT binary patch literal 2427 zcmah~&yU+g6!thyHk-}e|i?df17GJ}y|A2uzoWxF_z|)Q0X{BEwjvxn~^6gHwU*i?-#Y@wAzYaT5>`ul zGBM)F+@5b5;r|#Jp;-L$Z5C_si}KLM&1*V}MPl-r5++kgfS;BZ&hg%ZTcbU9?l~_> z_azJpNmTW6_`BB*9GlA@~Qtt zTLY-;DGMI5iO}sJj)I5nNi0Wz(NB-_%M)P+L8cTy90Wx)D~QI0!EqJN{r|_Tpb8dX zmrrLFI~I!dvH_EY*K_{xAT+WZ+=0n_%Q(*oC&oRfaCgUf;ob*UJjZyZI(Co0C(a{6 zocZpFqi-6hohRgkJO-Nw4)-3BUrEv^o;REu@CLG1Wsl*IyOw@UO(^IhU~1{0i?Dr4 zdtsz$z!EwVG}D4prYWO`?3fzapm4Du5c&-t1!+`{4j>J5tmKrkF3i)6MKK%2MeYP}KW15sBrPZa8#qj{NDIm?a?%k;kiolIVxpoJ&Mq2x_0x3XETgF&fXJ1~cDY$Px}ccJRoNY-FdMDhZp z*s8rd|KP5e${7TJ#bjhkV=)uZRoL73s({{YRV->7LS9K_dIs95_~k3GQNsHKPAlPE zg*mWY4P-3UfB8m)6}T~F=rW?3C!mG~nC6w=$Rp4``}X}4asYoSi{G37zK^|} zuy`Q?NIeL>#aH;$*r)x2a)h#AKGcief)!{GYJ}j|s032f zA$Ei{iKTpq2B~Gt);_XUCce~LyV30_{9{0+;;%-%Q_S&kVY07)Ldke0fKXsZIw`h#e~)+44NH>uqmuRM)Ei0(=;96#xJL literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/click/__pycache__/parser.cpython-39.pyc b/.venv/lib/python3.9/site-packages/click/__pycache__/parser.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9a568f8e6a075edf24c1ac693a92429054ca4990 GIT binary patch literal 13570 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 TET2DEoXzh$HucPuH&ysQNecgA literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/click/__pycache__/shell_completion.cpython-39.pyc b/.venv/lib/python3.9/site-packages/click/__pycache__/shell_completion.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c8b4c1f51b1191052a76ba10e9d13fb7cc215fd3 GIT binary patch literal 16731 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 BWvl=I literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/click/__pycache__/utils.cpython-39.pyc b/.venv/lib/python3.9/site-packages/click/__pycache__/utils.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fb4ef42ed1d53ff7d26d4725c87c8e84856a622b GIT binary patch literal 17577 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 Z$3H*yB%jdNPpV&*nt#mzpRCQp{|icSO0@t0 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/click/_compat.py b/.venv/lib/python3.9/site-packages/click/_compat.py new file mode 100644 index 0000000..766d286 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/click/_compat.py @@ -0,0 +1,626 @@ +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 ignore the + # atomic flag. Use fsdecode to handle Path("-"). + if os.fsdecode(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/python3.9/site-packages/click/_termui_impl.py b/.venv/lib/python3.9/site-packages/click/_termui_impl.py new file mode 100644 index 0000000..4b979bc --- /dev/null +++ b/.venv/lib/python3.9/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 + + fd, 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.close(fd) + 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) + ) from 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/python3.9/site-packages/click/_textwrap.py b/.venv/lib/python3.9/site-packages/click/_textwrap.py new file mode 100644 index 0000000..b47dcbd --- /dev/null +++ b/.venv/lib/python3.9/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/python3.9/site-packages/click/_winconsole.py b/.venv/lib/python3.9/site-packages/click/_winconsole.py new file mode 100644 index 0000000..6b20df3 --- /dev/null +++ b/.venv/lib/python3.9/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/python3.9/site-packages/click/core.py b/.venv/lib/python3.9/site-packages/click/core.py new file mode 100644 index 0000000..5abfb0f --- /dev/null +++ b/.venv/lib/python3.9/site-packages/click/core.py @@ -0,0 +1,2998 @@ +import enum +import errno +import inspect +import os +import sys +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 .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 _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 the default value for commands. If this + value is not set, it defaults to the value from the parent + context. ``Command.show_default`` overrides this default for the + specific command. + + .. versionchanged:: 8.1 + The ``show_default`` parameter is overridden by + ``Command.show_default``, instead of the other way around. + + .. 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] = [] + #: the collected prefixes of the command's options. + self._opt_prefixes: t.Set[str] = set(parent._opt_prefixes) if parent else set() + + 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 + + @t.overload + def lookup_default( + self, name: str, call: "te.Literal[True]" = True + ) -> t.Optional[t.Any]: + ... + + @t.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.type_cast_value( # type: ignore + ctx, param.get_default(ctx) + ) + + # 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 + + @t.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": + ... + + @t.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. + """ + 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() from None + 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) + sys.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. + + :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. + + .. versionchanged:: 8.1 + ``help``, ``epilog``, and ``short_help`` are stored unprocessed, + all formatting is done when outputting help text, not at init, + and is done even if not using the ``@command`` decorator. + + .. versionchanged:: 8.0 + Added a ``repr`` showing the command name. + + .. versionchanged:: 7.1 + Added the ``no_args_is_help`` parameter. + + .. versionchanged:: 2.0 + Added the ``context_settings`` parameter. + """ + + 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 [] + 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. + """ + if self.short_help: + text = inspect.cleandoc(self.short_help) + elif self.help: + text = make_default_short_help(self.help, limit) + else: + text = "" + + 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 if self.help is not None else "" + + if self.deprecated: + text = _("(Deprecated) {text}").format(text=text) + + if text: + text = inspect.cleandoc(text).partition("\f")[0] + 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: + epilog = inspect.cleandoc(self.epilog) + formatter.write_paragraph() + + with formatter.indentation(): + formatter.write_text(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 + ctx._opt_prefixes.update(parser._opt_prefixes) + 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 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 the group return value for regular + # groups, or an empty list for chained groups. + with ctx: + rv = super().invoke(ctx) + return _process_result([] if self.chain else rv) + 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 + + @t.overload + def command(self, __func: t.Callable[..., t.Any]) -> Command: + ... + + @t.overload + def command( + self, *args: t.Any, **kwargs: t.Any + ) -> t.Callable[[t.Callable[..., t.Any]], Command]: + ... + + def command( + self, *args: t.Any, **kwargs: t.Any + ) -> t.Union[t.Callable[[t.Callable[..., t.Any]], Command], 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.1 + This decorator can be applied without parentheses. + + .. versionchanged:: 8.0 + Added the :attr:`command_class` attribute. + """ + from .decorators import command + + if self.command_class and kwargs.get("cls") is None: + kwargs["cls"] = self.command_class + + func: t.Optional[t.Callable] = None + + if args and callable(args[0]): + assert ( + len(args) == 1 and not kwargs + ), "Use 'command(**kwargs)(callable)' to provide arguments." + (func,) = args + args = () + + def decorator(f: t.Callable[..., t.Any]) -> Command: + cmd: Command = command(*args, **kwargs)(f) + self.add_command(cmd) + return cmd + + if func is not None: + return decorator(func) + + return decorator + + @t.overload + def group(self, __func: t.Callable[..., t.Any]) -> "Group": + ... + + @t.overload + def group( + self, *args: t.Any, **kwargs: t.Any + ) -> t.Callable[[t.Callable[..., t.Any]], "Group"]: + ... + + def group( + self, *args: t.Any, **kwargs: t.Any + ) -> t.Union[t.Callable[[t.Callable[..., t.Any]], "Group"], "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.1 + This decorator can be applied without parentheses. + + .. versionchanged:: 8.0 + Added the :attr:`group_class` attribute. + """ + from .decorators import group + + func: t.Optional[t.Callable] = None + + if args and callable(args[0]): + assert ( + len(args) == 1 and not kwargs + ), "Use 'group(**kwargs)(callable)' to provide arguments." + (func,) = args + args = () + + if self.group_class is not None and kwargs.get("cls") is None: + 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 = group(*args, **kwargs)(f) + self.add_command(cmd) + return cmd + + if func is not None: + return decorator(func) + + 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, + ) -> 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 + 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 + + @t.overload + def get_default( + self, ctx: Context, call: "te.Literal[True]" = True + ) -> t.Optional[t.Any]: + ... + + @t.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_default` 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.2 + Type casting is no longer performed when getting a default. + + .. 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 call and callable(value): + value = value() + + return value + + 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: + 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: Show the default value for this option in its + help text. Values are not shown by default, unless + :attr:`Context.show_default` is ``True``. If this value is a + string, it shows that string in parentheses instead of the + actual value. This is particularly useful for dynamic options. + For single option boolean flags, the default remains hidden if + its value is ``False``. + :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.1.0 + Help text indentation is cleaned here instead of only in the + ``@option`` decorator. + + .. versionchanged:: 8.1.0 + The ``show_default`` parameter overrides + ``Context.show_default``. + + .. versionchanged:: 8.1.0 + The default of a single option boolean flag is not shown if the + default value is ``False``. + + .. 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: t.Union[bool, str, None] = None, + 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: + if help: + help = inspect.cleandoc(help) + + 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 = 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 and not self.required: + 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 = is_flag and 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'.") + + if self.multiple and self.is_flag: + raise TypeError("'multiple' is not valid with 'is_flag', use 'count'.") + + 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(f"Name '{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 = False + show_default_is_str = False + + if self.show_default is not None: + if isinstance(self.show_default, str): + show_default_is_str = show_default = True + else: + show_default = self.show_default + elif ctx.show_default is not None: + show_default = ctx.show_default + + if show_default_is_str or (show_default and (default_value is not None)): + 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 inspect.isfunction(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] + elif self.is_bool_flag and not self.secondary_opts and not default_value: + default_string = "" + else: + default_string = str(default_value) + + if default_string: + extra.append(_("default: {default}").format(default=default_string)) + + if ( + isinstance(self.type, types._NumberRangeBase) + # skip count with default range type + and not (self.count and self.type.min == 0 and self.type.max is None) + ): + 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 + + @t.overload + def get_default( + self, ctx: Context, call: "te.Literal[True]" = True + ) -> t.Optional[t.Any]: + ... + + @t.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 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) + + 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 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 + + elif ( + self.multiple + and value is not None + and any(v is _flag_needs_value for v in value) + ): + value = [self.flag_value if v is _flag_needs_value else v for v in 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/python3.9/site-packages/click/decorators.py b/.venv/lib/python3.9/site-packages/click/decorators.py new file mode 100644 index 0000000..28618dc --- /dev/null +++ b/.venv/lib/python3.9/site-packages/click/decorators.py @@ -0,0 +1,497 @@ +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", bound=t.Union[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 + + +CmdType = t.TypeVar("CmdType", bound=Command) + + +@t.overload +def command( + __func: t.Callable[..., t.Any], +) -> Command: + ... + + +@t.overload +def command( + name: t.Optional[str] = None, + **attrs: t.Any, +) -> t.Callable[..., Command]: + ... + + +@t.overload +def command( + name: t.Optional[str] = None, + cls: t.Type[CmdType] = ..., + **attrs: t.Any, +) -> t.Callable[..., CmdType]: + ... + + +def command( + name: t.Union[str, t.Callable[..., t.Any], None] = None, + cls: t.Optional[t.Type[Command]] = None, + **attrs: t.Any, +) -> t.Union[Command, t.Callable[..., 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. + For the ``params`` argument, any decorated params are appended to + the end of the list. + + 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`. + + .. versionchanged:: 8.1 + This decorator can be applied without parentheses. + + .. versionchanged:: 8.1 + The ``params`` argument can be used. Decorated params are + appended to the end of the list. + """ + + func: t.Optional[t.Callable[..., t.Any]] = None + + if callable(name): + func = name + name = None + assert cls is None, "Use 'command(cls=cls)(callable)' to specify a class." + assert not attrs, "Use 'command(**kwargs)(callable)' to provide arguments." + + if cls is None: + cls = Command + + def decorator(f: t.Callable[..., t.Any]) -> Command: + if isinstance(f, Command): + raise TypeError("Attempted to convert a callback into a command twice.") + + attr_params = attrs.pop("params", None) + params = attr_params if attr_params is not None else [] + + try: + decorator_params = f.__click_params__ # type: ignore + except AttributeError: + pass + else: + del f.__click_params__ # type: ignore + params.extend(reversed(decorator_params)) + + if attrs.get("help") is None: + attrs["help"] = f.__doc__ + + cmd = cls( # type: ignore[misc] + name=name or f.__name__.lower().replace("_", "-"), # type: ignore[arg-type] + callback=f, + params=params, + **attrs, + ) + cmd.__doc__ = f.__doc__ + return cmd + + if func is not None: + return decorator(func) + + return decorator + + +@t.overload +def group( + __func: t.Callable[..., t.Any], +) -> Group: + ... + + +@t.overload +def group( + name: t.Optional[str] = None, + **attrs: t.Any, +) -> t.Callable[[F], Group]: + ... + + +def group( + name: t.Union[str, t.Callable[..., t.Any], None] = None, **attrs: t.Any +) -> t.Union[Group, 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`. + + .. versionchanged:: 8.1 + This decorator can be applied without parentheses. + """ + if attrs.get("cls") is None: + attrs["cls"] = Group + + if callable(name): + grp: t.Callable[[F], Group] = t.cast(Group, command(**attrs)) + return grp(name) + + 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", None) or 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() + OptionClass = option_attrs.pop("cls", None) or 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() + f_back = frame.f_back if frame is not None else None + f_globals = f_back.f_globals if f_back 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." + ) from None + + 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/python3.9/site-packages/click/exceptions.py b/.venv/lib/python3.9/site-packages/click/exceptions.py new file mode 100644 index 0000000..9e20b3e --- /dev/null +++ b/.venv/lib/python3.9/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/python3.9/site-packages/click/formatting.py b/.venv/lib/python3.9/site-packages/click/formatting.py new file mode 100644 index 0000000..ddd2a2f --- /dev/null +++ b/.venv/lib/python3.9/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/python3.9/site-packages/click/globals.py b/.venv/lib/python3.9/site-packages/click/globals.py new file mode 100644 index 0000000..480058f --- /dev/null +++ b/.venv/lib/python3.9/site-packages/click/globals.py @@ -0,0 +1,68 @@ +import typing as t +from threading import local + +if t.TYPE_CHECKING: + import typing_extensions as te + from .core import Context + +_local = local() + + +@t.overload +def get_current_context(silent: "te.Literal[False]" = False) -> "Context": + ... + + +@t.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) as e: + if not silent: + raise RuntimeError("There is no active click context.") from e + + 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/python3.9/site-packages/click/parser.py b/.venv/lib/python3.9/site-packages/click/parser.py new file mode 100644 index 0000000..2d5a2ed --- /dev/null +++ b/.venv/lib/python3.9/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``, ``append_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/python3.9/site-packages/click/py.typed b/.venv/lib/python3.9/site-packages/click/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/.venv/lib/python3.9/site-packages/click/shell_completion.py b/.venv/lib/python3.9/site-packages/click/shell_completion.py new file mode 100644 index 0000000..c17a8e6 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/click/shell_completion.py @@ -0,0 +1,580 @@ +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 + COMPREPLY=() + compopt -o dirnames + elif [[ $type == 'file' ]]; then + COMPREPLY=() + 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", "-c", "echo ${BASH_VERSION}"], stdout=subprocess.PIPE + ) + match = re.search(r"^(\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(ctx: Context, value: str) -> bool: + """Check if the value looks like the start of an option.""" + if not value: + return False + + c = value[0] + return c in ctx._opt_prefixes + + +def _is_incomplete_option(ctx: Context, 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 or param.count: + return False + + last_option = None + + for index, arg in enumerate(reversed(args)): + if index + 1 > param.nargs: + break + + if _start_of_option(ctx, 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(ctx, 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(ctx, 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(ctx, 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/python3.9/site-packages/click/termui.py b/.venv/lib/python3.9/site-packages/click/termui.py new file mode 100644 index 0000000..bfb2f5a --- /dev/null +++ b/.venv/lib/python3.9/site-packages/click/termui.py @@ -0,0 +1,787 @@ +import inspect +import io +import itertools +import os +import sys +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[t.Union[ParamType, t.Any]] = 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 an 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() from None + + 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 = _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: + value2 = prompt_func(confirmation_prompt) + is_empty = not value and not value2 + if value2 or is_empty: + 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.rstrip(" "), nl=False, err=err) + # Echo a space to stdout to work around an issue where + # readline causes backspace to clear the whole line. + value = visible_prompt_func(" ").lower().strip() + except (KeyboardInterrupt, EOFError): + raise Abort() from None + 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 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}") from None + + if bg: + try: + bits.append(f"\033[{_interpret_color(bg, 10)}m") + except KeyError: + raise TypeError(f"Unknown color {bg!r}") from None + + 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 overline else 55}m") + if italic is not None: + bits.append(f"\033[{3 if italic 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[t.AnyStr]] = 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/python3.9/site-packages/click/testing.py b/.venv/lib/python3.9/site-packages/click/testing.py new file mode 100644 index 0000000..e395c2e --- /dev/null +++ b/.venv/lib/python3.9/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() + dt = tempfile.mkdtemp(dir=temp_dir) # type: ignore[type-var] + os.chdir(dt) + + try: + yield t.cast(str, dt) + finally: + os.chdir(cwd) + + if temp_dir is None: + try: + shutil.rmtree(dt) + except OSError: # noqa: B014 + pass diff --git a/.venv/lib/python3.9/site-packages/click/types.py b/.venv/lib/python3.9/site-packages/click/types.py new file mode 100644 index 0000000..b45ee53 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/click/types.py @@ -0,0 +1,1073 @@ +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] + + # Custom subclasses might not remember to set a name. + if hasattr(self, "name"): + name = self.name + else: + name = param_type + + return {"param_type": param_type, "name": 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)}': {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 + returns the filename instead of an open file. Various checks can be + enabled to validate the type of file and permissions. + + :param exists: The file or directory needs to exist for the value to + be valid. If this is not set to ``True``, and the file does not + exist, then all further checks are silently skipped. + :param file_okay: Allow a file as a value. + :param dir_okay: Allow a directory as a value. + :param readable: if true, a readable check is performed. + :param writable: if true, a writable check is performed. + :param executable: if true, an executable check is performed. + :param resolve_path: Make the value absolute and resolve any + symlinks. A ``~`` is not expanded, as this is supposed to be + done by the shell only. + :param allow_dash: Allow a single dash as a value, which indicates + a standard stream (but does not open it). Use + :func:`~click.open_file` to handle opening this value. + :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.1 + Added the ``executable`` parameter. + + .. 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, + executable: bool = False, + ): + self.exists = exists + self.file_okay = file_okay + self.dir_okay = dir_okay + self.readable = readable + self.writable = writable + self.executable = executable + 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: + # os.path.realpath doesn't resolve symlinks on Windows + # until Python 3.8. Use pathlib for now. + import pathlib + + rv = os.fsdecode(pathlib.Path(rv).resolve()) + + 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}' is a directory.").format( + name=self.name.title(), filename=os.fsdecode(value) + ), + param, + ctx, + ) + + if self.readable and not os.access(rv, os.R_OK): + self.fail( + _("{name} {filename!r} is not readable.").format( + name=self.name.title(), filename=os.fsdecode(value) + ), + param, + ctx, + ) + + if self.writable and not os.access(rv, os.W_OK): + self.fail( + _("{name} {filename!r} is not writable.").format( + name=self.name.title(), filename=os.fsdecode(value) + ), + param, + ctx, + ) + + if self.executable and not os.access(value, os.X_OK): + self.fail( + _("{name} {filename!r} is not executable.").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/python3.9/site-packages/click/utils.py b/.venv/lib/python3.9/site-packages/click/utils.py new file mode 100644 index 0000000..8283788 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/click/utils.py @@ -0,0 +1,580 @@ +import os +import re +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) from e + 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[t.Any]] = 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: + """Open a file, with extra behavior to handle ``'-'`` to indicate + a standard stream, lazy open on write, and atomic write. Similar to + the behavior of the :class:`~click.File` param type. + + If ``'-'`` is given to open ``stdout`` or ``stdin``, the stream is + wrapped so that using it in a context manager will not close it. + This makes it possible to use the function without accidentally + closing a standard stream: + + .. code-block:: python + + with open_file(filename) as f: + ... + + :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 decode or encode a file opened in + text mode. + :param errors: The error handling mode. + :param lazy: Wait to open the file until it is accessed. For read + mode, the file is temporarily opened to raise access errors + early, then closed until it is read again. + :param atomic: Write to a temporary file and replace the given file + on close. + + .. versionadded:: 3.0 + """ + 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 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: t.Optional[ModuleType] = None +) -> 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 _main is None: + _main = sys.modules["__main__"] + + 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 is 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. + + .. versionchanged:: 8.1 + Invalid glob patterns are treated as empty expansions rather + than raising an error. + + .. 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) + + try: + matches = glob(arg, recursive=glob_recursive) + except re.error: + matches = [] + + if not matches: + out.append(arg) + else: + out.extend(matches) + + return out diff --git a/.venv/lib/python3.9/site-packages/easy_install.py b/.venv/lib/python3.9/site-packages/easy_install.py new file mode 100644 index 0000000..d87e984 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/easy_install.py @@ -0,0 +1,5 @@ +"""Run the EasyInstall command""" + +if __name__ == '__main__': + from setuptools.command.easy_install import main + main() diff --git a/.venv/lib/python3.9/site-packages/flask/__init__.py b/.venv/lib/python3.9/site-packages/flask/__init__.py new file mode 100644 index 0000000..0bef221 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/flask/__init__.py @@ -0,0 +1,102 @@ +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 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 abort as abort +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 redirect as redirect +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 template_rendered as template_rendered +from .templating import render_template as render_template +from .templating import render_template_string as render_template_string +from .templating import stream_template as stream_template +from .templating import stream_template_string as stream_template_string + +__version__ = "2.3.2" + + +def __getattr__(name): + if name == "_app_ctx_stack": + import warnings + from .globals import __app_ctx_stack + + warnings.warn( + "'_app_ctx_stack' is deprecated and will be removed in Flask 2.4.", + DeprecationWarning, + stacklevel=2, + ) + return __app_ctx_stack + + if name == "_request_ctx_stack": + import warnings + from .globals import __request_ctx_stack + + warnings.warn( + "'_request_ctx_stack' is deprecated and will be removed in Flask 2.4.", + DeprecationWarning, + stacklevel=2, + ) + return __request_ctx_stack + + if name == "escape": + import warnings + from markupsafe import escape + + warnings.warn( + "'flask.escape' is deprecated and will be removed in Flask 2.4. Import" + " 'markupsafe.escape' instead.", + DeprecationWarning, + stacklevel=2, + ) + return escape + + if name == "Markup": + import warnings + from markupsafe import Markup + + warnings.warn( + "'flask.Markup' is deprecated and will be removed in Flask 2.4. Import" + " 'markupsafe.Markup' instead.", + DeprecationWarning, + stacklevel=2, + ) + return Markup + + if name == "signals_available": + import warnings + + warnings.warn( + "'signals_available' is deprecated and will be removed in Flask 2.4." + " Signals are always available", + DeprecationWarning, + stacklevel=2, + ) + return True + + raise AttributeError(name) diff --git a/.venv/lib/python3.9/site-packages/flask/__main__.py b/.venv/lib/python3.9/site-packages/flask/__main__.py new file mode 100644 index 0000000..4e28416 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/flask/__main__.py @@ -0,0 +1,3 @@ +from .cli import main + +main() diff --git a/.venv/lib/python3.9/site-packages/flask/__pycache__/__init__.cpython-39.pyc b/.venv/lib/python3.9/site-packages/flask/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a7e6c8456b6b83b02df48147d33bb8b789b2442a GIT binary patch literal 2841 zcmb`JTW=dh7>0L!KE!csC(b$99G#?z-KMC3gixUci3=*!fGW++cI_S8+w6&%bxuld zfLkOaeg*%6-{D(&(>u6DAn|@S~_ldpA6vo%|HHBV={P8@S%UYx}h$H57fP@DiKSyFM*O?hdSR-AG(UY2DQ zr`?>FXL-dL@C2JsoCOzHL2(W|$tD%&-J&W$j zD4qgWSygcfJj-Skm%(#vPH_c1&*l|RgBRF>;u-KFTU1;HFR>-Xv*2a6tauK*!d4W| zySlf^RuwO}hPTGn6fc6;*}CE-@CMsZybRuCn~GP!TWm}5DtMc1D>lG2R#UtN-eEh6 z*TK7NSMdgTkL@YmbnkilY+vyf_<$WK-UioMU2zTEU=8pNyKnE>dp~LHfqm~t+ueVI zoz`~se&Ur3eEdy*>00s_kbcg;?{OIcGtapU17C7*_H(z#L*e+;ka`mMZKne&TkVJo zGwM3h6xTN{wSw^6Z1sfTel&`kt-z1?8Qov(TGF(_a4b}z(6EQGaDLPY+_+JvG1E5# zr6rfr87#nR27)?Iwy}~fV7kL2Q(bV|^kB3)Tq0hjc*MQXwIXg>Q6!vZFQP>jJnNWa z7T2rJ2yQ!qx2WH&xe7;uTb_C9MBTAfq1P5E}( zWG-P%!BW__AI;I_*(!N!j939i7eVaC7xGc2Hbb_1Q^RpHY zqd}&M!(5DV1Mk!*^KHj>hJzMIF&SARQuCM|g>FrvG#Yj|4Q0mtx2IIS6*47q@6(rN3^nAp`rOAd02l~KWx$SJNl#mN&KGRC;x<8qJ|yE#CDs0KDsCsEgq^a}KclW)#*WCJf&b;0KMQ~IirUGa8}rANKM(&e*Z!D2K{fCSwNd#C zWB!ElPs0EEn7;^r<_h&w`KQMGN#!rW|9a$Cp7M>yt{iDm_EP&%|3SAkoagChY;tg- zqBA7QY8}5p8ZD%_>Tl?sfnCa}!CZdAT~@w1z@u5fQ8Dj^FP_%!2H$xO{S=oiE5vyeb2!R{ zMa4X^9M@{P{NiQpPVC*sJ{ojuSb`gtdup9a<9dW|cXR#BsU>_IPuWj*+@OgWh&`;i zpP+cZ{B-!Vps2hU7{vjaS?(r%YFkvI`jelH>g*Rnpc1C~3E%R#xQ|;@x9aAb8-CNo zBQ*W}U85U#ywMRI(Krix%|YzjVVWpzk!jY$^M2km@fM)kZ<_lUM>2?}XYiefCG;45|4k(mvHyw0;Q1$& z8uN@tsF&+&@ez7|wtLvu1f3feM?n{HkVueF?P9s+=nX3c6|{Z|F9*D3MVc<+t>{c- z$ebmi|K~)W1lCEs-pftdOjd7$j>X>m(Z_n-G>8mT^2hVvC~NBsG#92xb2SGsEVgHc)ERUXRI# aF1$NKnpHdN%-#{kx_xOz^;{O2$2MSC8 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/flask/__pycache__/__main__.cpython-39.pyc b/.venv/lib/python3.9/site-packages/flask/__pycache__/__main__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..514da3d70b0991a6e91ceb4986a1e9e51bd03825 GIT binary patch literal 216 zcmYjLu?oU46inIB7P9D5F zj^nsT;&_T2*6%Z2IDLBakHDF|J3Dto5yb*E_<}KF_>M55!4W0l3%fAEW7H5X4@oeF zh2X|lAIL;be)Ks(r5XmMDGl6omqV_&U}njs)vRc_l92XH_8@o;dT1*pSLp^UZ`o3l hq9!G?a23SXkY#=<%ThgzlWn7DC)mz^Vg2eX_yU;qHH82G literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/flask/__pycache__/app.cpython-39.pyc b/.venv/lib/python3.9/site-packages/flask/__pycache__/app.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..93894da7fa96ad91994bf5d5c012bb277fe245b6 GIT binary patch literal 67463 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 F{{u*@0HOc@ literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/flask/__pycache__/config.cpython-39.pyc b/.venv/lib/python3.9/site-packages/flask/__pycache__/config.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5fccb9312ec6af75085a3ee31a8a34f98fb0d5b3 GIT binary patch literal 12647 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 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/flask/__pycache__/ctx.cpython-39.pyc b/.venv/lib/python3.9/site-packages/flask/__pycache__/ctx.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3c6ad522a6f2683911569ce21e6be1495e2ef6c0 GIT binary patch literal 14518 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 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/flask/__pycache__/helpers.cpython-39.pyc b/.venv/lib/python3.9/site-packages/flask/__pycache__/helpers.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e4aff497fe7888dbbd196720c5912be9c2896178 GIT binary patch literal 23721 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 I`4RX30_M3)R{#J2 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/flask/__pycache__/logging.cpython-39.pyc b/.venv/lib/python3.9/site-packages/flask/__pycache__/logging.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..30cd3782aed6d0a7b0a5604b979a2bc27089d63e GIT binary patch literal 2502 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 M%H_k8L*+C75B5npwEzGB literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/flask/__pycache__/sessions.cpython-39.pyc b/.venv/lib/python3.9/site-packages/flask/__pycache__/sessions.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a1c4472494d610e884ee866b4a1fcfd0a294ff8f GIT binary patch literal 13101 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 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/flask/__pycache__/typing.cpython-39.pyc b/.venv/lib/python3.9/site-packages/flask/__pycache__/typing.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..949e0684a8dd44a1283855e35ba17829e5b89975 GIT binary patch literal 1725 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 RZo@y?j?L=dy0w#|{{rAR-G~4H literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/flask/__pycache__/wrappers.cpython-39.pyc b/.venv/lib/python3.9/site-packages/flask/__pycache__/wrappers.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..680a9dc87a79413738b3b350b89f94668dd7f0bc GIT binary patch literal 5153 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 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/flask/app.py b/.venv/lib/python3.9/site-packages/flask/app.py new file mode 100644 index 0000000..3b6b38d --- /dev/null +++ b/.venv/lib/python3.9/site-packages/flask/app.py @@ -0,0 +1,2213 @@ +from __future__ import annotations + +import logging +import os +import sys +import typing as t +import weakref +from collections.abc import Iterator as _abc_Iterator +from datetime import timedelta +from inspect import iscoroutinefunction +from itertools import chain +from types import TracebackType +from urllib.parse import quote as _url_quote + +import click +from werkzeug.datastructures import Headers +from werkzeug.datastructures import ImmutableDict +from werkzeug.exceptions import Aborter +from werkzeug.exceptions import BadRequest +from werkzeug.exceptions import BadRequestKeyError +from werkzeug.exceptions import HTTPException +from werkzeug.exceptions import InternalServerError +from werkzeug.routing import BuildError +from werkzeug.routing import Map +from werkzeug.routing import MapAdapter +from werkzeug.routing import RequestRedirect +from werkzeug.routing import RoutingException +from werkzeug.routing import Rule +from werkzeug.serving import is_running_from_reloader +from werkzeug.utils import cached_property +from werkzeug.utils import redirect as _wz_redirect +from werkzeug.wrappers import Response as BaseResponse + +from . import cli +from . import typing as ft +from .config import Config +from .config import ConfigAttribute +from .ctx import _AppCtxGlobals +from .ctx import AppContext +from .ctx import RequestContext +from .globals import _cv_app +from .globals import _cv_request +from .globals import g +from .globals import request +from .globals import request_ctx +from .globals import session +from .helpers import _split_blueprint_path +from .helpers import get_debug_flag +from .helpers import get_flashed_messages +from .helpers import get_load_dotenv +from .json.provider import DefaultJSONProvider +from .json.provider import JSONProvider +from .logging import create_logger +from .scaffold import _endpoint_from_view_func +from .scaffold import _sentinel +from .scaffold import find_package +from .scaffold import Scaffold +from .scaffold import setupmethod +from .sessions import SecureCookieSessionInterface +from .sessions import SessionInterface +from .signals import appcontext_tearing_down +from .signals import got_request_exception +from .signals import request_finished +from .signals import request_started +from .signals import request_tearing_down +from .templating import DispatchingJinjaLoader +from .templating import Environment +from .wrappers import Request +from .wrappers import Response + +if t.TYPE_CHECKING: # pragma: no cover + from .blueprints import Blueprint + from .testing import FlaskClient + from .testing import FlaskCliRunner + +T_shell_context_processor = t.TypeVar( + "T_shell_context_processor", bound=ft.ShellContextProcessorCallable +) +T_teardown = t.TypeVar("T_teardown", bound=ft.TeardownCallable) +T_template_filter = t.TypeVar("T_template_filter", bound=ft.TemplateFilterCallable) +T_template_global = t.TypeVar("T_template_global", bound=ft.TemplateGlobalCallable) +T_template_test = t.TypeVar("T_template_test", bound=ft.TemplateTestCallable) + + +def _make_timedelta(value: timedelta | int | None) -> timedelta | None: + 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 of the object assigned to :attr:`aborter`, created by + #: :meth:`create_aborter`. That object is called by + #: :func:`flask.abort` to raise HTTP errors, and can be + #: called directly as well. + #: + #: Defaults to :class:`werkzeug.exceptions.Aborter`. + #: + #: .. versionadded:: 2.2 + aborter_class = Aborter + + #: 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") + + #: 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 + ) + + json_provider_class: type[JSONProvider] = DefaultJSONProvider + """A subclass of :class:`~flask.json.provider.JSONProvider`. An + instance is created and assigned to :attr:`app.json` when creating + the app. + + The default, :class:`~flask.json.provider.DefaultJSONProvider`, uses + Python's built-in :mod:`json` library. A different provider can use + a different JSON library. + + .. versionadded:: 2.2 + """ + + #: 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( + { + "DEBUG": None, + "TESTING": False, + "PROPAGATE_EXCEPTIONS": 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", + "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 :meth:`test_client` method creates an instance of this test + #: client class. Defaults to :class:`~flask.testing.FlaskClient`. + #: + #: .. versionadded:: 0.7 + test_client_class: type[FlaskClient] | None = 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: type[FlaskCliRunner] | None = None + + #: the session interface to use. By default an instance of + #: :class:`~flask.sessions.SecureCookieSessionInterface` is used here. + #: + #: .. versionadded:: 0.8 + session_interface: SessionInterface = SecureCookieSessionInterface() + + def __init__( + self, + import_name: str, + static_url_path: str | None = None, + static_folder: str | os.PathLike | None = "static", + static_host: str | None = None, + host_matching: bool = False, + subdomain_matching: bool = False, + template_folder: str | os.PathLike | None = "templates", + instance_path: str | None = None, + instance_relative_config: bool = False, + root_path: str | None = 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) + + #: An instance of :attr:`aborter_class` created by + #: :meth:`make_aborter`. This is called by :func:`flask.abort` + #: to raise HTTP errors, and can be called directly as well. + #: + #: .. versionadded:: 2.2 + #: Moved from ``flask.abort``, which calls this object. + self.aborter = self.make_aborter() + + self.json: JSONProvider = self.json_provider_class(self) + """Provides access to JSON methods. Functions in ``flask.json`` + will call methods on this provider when the application context + is active. Used for handling JSON requests and responses. + + An instance of :attr:`json_provider_class`. Can be customized by + changing that attribute on a subclass, or by assigning to this + attribute afterwards. + + The default, :class:`~flask.json.provider.DefaultJSONProvider`, + uses Python's built-in :mod:`json` library. A different provider + can use a different JSON library. + + .. versionadded:: 2.2 + """ + + #: A list of functions that are called by + #: :meth:`handle_url_build_error` when :meth:`.url_for` raises a + #: :exc:`~werkzeug.routing.BuildError`. Each function is called + #: with ``error``, ``endpoint`` and ``values``. If a function + #: returns ``None`` or raises a ``BuildError``, it is skipped. + #: Otherwise, its return value is returned by ``url_for``. + #: + #: .. versionadded:: 0.9 + self.url_build_error_handlers: list[ + t.Callable[[Exception, str, dict[str, t.Any]], str] + ] = [] + + #: 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: list[ft.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: list[ft.ShellContextProcessorCallable] = [] + + #: 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: 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 + + # 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 _check_setup_finished(self, f_name: str) -> None: + if self._got_first_request: + raise AssertionError( + f"The setup method '{f_name}' can no longer be called" + " on the application. It has already handled its first" + " request, any changes will not be applied" + " consistently.\n" + "Make sure all imports, decorators, functions, etc." + " needed to set up the application are done before" + " running it." + ) + + @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 + + @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) + + @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. + + .. deprecated:: 2.3 + Will be removed in Flask 2.4. + + .. versionadded:: 0.8 + """ + import warnings + + warnings.warn( + "'got_first_request' is deprecated and will be removed in Flask 2.4.", + DeprecationWarning, + stacklevel=2, + ) + 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["DEBUG"] = get_debug_flag() + return self.config_class(root_path, defaults) + + def make_aborter(self) -> Aborter: + """Create the object to assign to :attr:`aborter`. That object + is called by :func:`flask.abort` to raise HTTP errors, and can + be called directly as well. + + By default, this creates an instance of :attr:`aborter_class`, + which defaults to :class:`werkzeug.exceptions.Aborter`. + + .. versionadded:: 2.2 + """ + return self.aborter_class() + + 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) + + 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: + auto_reload = self.config["TEMPLATES_AUTO_RELOAD"] + + if auto_reload is None: + auto_reload = self.debug + + options["auto_reload"] = auto_reload + + rv = self.jinja_environment(self, **options) + rv.globals.update( + url_for=self.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"] = self.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`. + + .. versionchanged:: 2.2 + Autoescaping is now enabled by default for ``.svg`` files. + + .. versionadded:: 0.5 + """ + if filename is None: + return True + return filename.endswith((".html", ".htm", ".xml", ".xhtml", ".svg")) + + 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. + """ + names: t.Iterable[str | None] = (None,) + + # A template may be rendered outside a request context. + if request: + names = chain(names, reversed(request.blueprints)) + + # The values passed to render_template take precedence. Keep a + # copy to re-apply after all context functions. + orig_ctx = context.copy() + + for name in names: + if name in self.template_context_processors: + for func in self.template_context_processors[name]: + context.update(func()) + + 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 + + @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. It may not behave as expected if set late. + + **Do not enable debug mode when deploying in production.** + + Default: ``False`` + """ + return self.config["DEBUG"] + + @debug.setter + def debug(self, value: bool) -> None: + self.config["DEBUG"] = value + + if self.config["TEMPLATES_AUTO_RELOAD"] is None: + self.jinja_env.auto_reload = value + + def run( + self, + host: str | None = None, + port: int | None = None, + debug: bool | None = 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. + + The :envvar:`FLASK_DEBUG` environment variable will override :attr:`debug`. + + Threaded mode is enabled by default. + + .. versionchanged:: 0.10 + The default port is now picked from the ``SERVER_NAME`` + variable. + """ + # Ignore this call so that it doesn't start another server if + # the 'flask run' command is used. + if os.environ.get("FLASK_RUN_FROM_CLI") == "true": + if not is_running_from_reloader(): + click.secho( + " * Ignoring a call to 'app.run()' that would block" + " the current 'flask' CLI command.\n" + " Only call 'app.run()' in an 'if __name__ ==" + ' "__main__"\' guard.', + fg="red", + ) + + return + + if get_load_dotenv(load_dotenv): + cli.load_dotenv() + + # if set, env var overrides existing value + if "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.debug, self.name) + + 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 + 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 + + 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: str | None = None, + view_func: ft.RouteCallable | None = None, + provide_automatic_options: bool | None = 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: str | None = None + ) -> t.Callable[[T_template_filter], T_template_filter]: + """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: T_template_filter) -> T_template_filter: + self.add_template_filter(f, name=name) + return f + + return decorator + + @setupmethod + def add_template_filter( + self, f: ft.TemplateFilterCallable, name: str | None = 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: str | None = None + ) -> t.Callable[[T_template_test], T_template_test]: + """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: T_template_test) -> T_template_test: + self.add_template_test(f, name=name) + return f + + return decorator + + @setupmethod + def add_template_test( + self, f: ft.TemplateTestCallable, name: str | None = 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: str | None = None + ) -> t.Callable[[T_template_global], T_template_global]: + """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: T_template_global) -> T_template_global: + self.add_template_global(f, name=name) + return f + + return decorator + + @setupmethod + def add_template_global( + self, f: ft.TemplateGlobalCallable, name: str | None = 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 teardown_appcontext(self, f: T_teardown) -> T_teardown: + """Registers a function to be called when the application + context is popped. The application context is typically popped + after the request context for each request, at the end of CLI + commands, or after a manually pushed context ends. + + .. code-block:: python + + with app.app_context(): + ... + + When the ``with`` block exits (or ``ctx.pop()`` is called), the + teardown functions are called just before the app context is + made inactive. 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. + + Teardown functions must avoid raising exceptions. If they + execute code that might fail they must surround that code with a + ``try``/``except`` block and log any errors. + + 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_shell_context_processor + ) -> T_shell_context_processor: + """Registers a shell context processor function. + + .. versionadded:: 0.11 + """ + self.shell_context_processors.append(f) + return f + + def _find_error_handler(self, e: Exception) -> ft.ErrorHandlerCallable | None: + """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)) + names = (*request.blueprints, None) + + for c in (code, None) if code is not None else (None,): + for name in names: + 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 + ) -> HTTPException | ft.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 + ``HTTPException`` 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 + ) -> HTTPException | ft.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 :data:`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, _async_wrapper=self.ensure_sync, exception=e) + propagate = self.config["PROPAGATE_EXCEPTIONS"] + + if propagate is None: + propagate = self.testing or self.debug + + if propagate: + # 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: InternalServerError | ft.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: (tuple[type, BaseException, TracebackType] | 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) -> t.NoReturn: + """Intercept routing exceptions and possibly do something else. + + In debug mode, intercept a routing redirect and replace it with + an error if the body will be discarded. + + With modern Werkzeug this shouldn't occur, since it now uses a + 308 status which tells the browser to resend the method and + body. + + .. versionchanged:: 2.1 + Don't intercept 307 and 308 redirects. + + :meta private: + :internal: + """ + if ( + not self.debug + or not isinstance(request.routing_exception, RequestRedirect) + or request.routing_exception.code in {307, 308} + or request.method in {"GET", "HEAD", "OPTIONS"} + ): + raise request.routing_exception # type: ignore + + from .debughelpers import FormDataRoutingRedirect + + raise FormDataRoutingRedirect(request) + + def dispatch_request(self) -> ft.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.request + if req.routing_exception is not None: + self.raise_routing_exception(req) + rule: Rule = req.url_rule # type: ignore[assignment] + # 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 + view_args: dict[str, t.Any] = req.view_args # type: ignore[assignment] + return self.ensure_sync(self.view_functions[rule.endpoint])(**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._got_first_request = True + + try: + request_started.send(self, _async_wrapper=self.ensure_sync) + 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: ft.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, _async_wrapper=self.ensure_sync, 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 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.url_adapter + methods = adapter.allowed_methods() # type: ignore[union-attr] + rv = self.response_class() + rv.allow.update(methods) + return rv + + def should_ignore_error(self, error: BaseException | None) -> 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." + ) from None + + return asgiref_async_to_sync(func) + + def url_for( + self, + endpoint: str, + *, + _anchor: str | None = None, + _method: str | None = None, + _scheme: str | None = None, + _external: bool | None = None, + **values: t.Any, + ) -> str: + """Generate a URL to the given endpoint with the given values. + + This is called by :func:`flask.url_for`, and can be called + directly as well. + + An *endpoint* is the name of a URL rule, usually added with + :meth:`@app.route() `, and usually the same name as the + view function. A route defined in a :class:`~flask.Blueprint` + will prepend the blueprint's name separated by a ``.`` to the + endpoint. + + In some cases, such as email messages, you want URLs to include + the scheme and domain, like ``https://example.com/hello``. When + not in an active request, URLs will be external by default, but + this requires setting :data:`SERVER_NAME` so Flask knows what + domain to use. :data:`APPLICATION_ROOT` and + :data:`PREFERRED_URL_SCHEME` should also be configured as + needed. This config is only used when not in an active request. + + Functions can be decorated with :meth:`url_defaults` to modify + keyword arguments before the URL is built. + + If building fails for some reason, such as an unknown endpoint + or incorrect values, the app's :meth:`handle_url_build_error` + method is called. If that returns a string, that is returned, + otherwise a :exc:`~werkzeug.routing.BuildError` is raised. + + :param endpoint: The endpoint name associated with the URL to + generate. If this starts with a ``.``, the current blueprint + name (if any) will be used. + :param _anchor: If given, append this as ``#anchor`` to the URL. + :param _method: If given, generate the URL associated with this + method for the endpoint. + :param _scheme: If given, the URL will have this scheme if it + is external. + :param _external: If given, prefer the URL to be internal + (False) or require it to be external (True). External URLs + include the scheme and domain. When not in an active + request, URLs are external by default. + :param values: Values to use for the variable parts of the URL + rule. Unknown keys are appended as query string arguments, + like ``?a=b&c=d``. + + .. versionadded:: 2.2 + Moved from ``flask.url_for``, which calls this method. + """ + req_ctx = _cv_request.get(None) + + if req_ctx is not None: + url_adapter = req_ctx.url_adapter + blueprint_name = req_ctx.request.blueprint + + # If the endpoint starts with "." and the request matches a + # blueprint, the endpoint is relative to the blueprint. + if endpoint[:1] == ".": + if blueprint_name is not None: + endpoint = f"{blueprint_name}{endpoint}" + else: + endpoint = endpoint[1:] + + # When in a request, generate a URL without scheme and + # domain by default, unless a scheme is given. + if _external is None: + _external = _scheme is not None + else: + app_ctx = _cv_app.get(None) + + # If called by helpers.url_for, an app context is active, + # use its url_adapter. Otherwise, app.url_for was called + # directly, build an adapter. + if app_ctx is not None: + url_adapter = app_ctx.url_adapter + else: + url_adapter = self.create_url_adapter(None) + + if url_adapter is None: + raise RuntimeError( + "Unable to build URLs outside an active request" + " without 'SERVER_NAME' configured. Also configure" + " 'APPLICATION_ROOT' and 'PREFERRED_URL_SCHEME' as" + " needed." + ) + + # When outside a request, generate a URL with scheme and + # domain by default. + if _external is None: + _external = True + + # It is an error to set _scheme when _external=False, in order + # to avoid accidental insecure URLs. + if _scheme is not None and not _external: + raise ValueError("When specifying '_scheme', '_external' must be True.") + + self.inject_url_defaults(endpoint, values) + + try: + rv = url_adapter.build( # type: ignore[union-attr] + endpoint, + values, + method=_method, + url_scheme=_scheme, + force_external=_external, + ) + except BuildError as error: + values.update( + _anchor=_anchor, _method=_method, _scheme=_scheme, _external=_external + ) + return self.handle_url_build_error(error, endpoint, values) + + if _anchor is not None: + _anchor = _url_quote(_anchor, safe="%!#$&'()*+,/:;=?@") + rv = f"{rv}#{_anchor}" + + return rv + + def redirect(self, location: str, code: int = 302) -> BaseResponse: + """Create a redirect response object. + + This is called by :func:`flask.redirect`, and can be called + directly as well. + + :param location: The URL to redirect to. + :param code: The status code for the redirect. + + .. versionadded:: 2.2 + Moved from ``flask.redirect``, which calls this method. + """ + return _wz_redirect(location, code=code, Response=self.response_class) + + def make_response(self, rv: ft.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. + + ``list`` + A list that will be jsonify'd before being returned. + + ``generator`` or ``iterator`` + A generator that returns ``str`` or ``bytes`` to be + streamed as the response. + + ``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:: 2.2 + A generator will be converted to a streaming response. + A list will be converted to a JSON response. + + .. versionchanged:: 1.1 + A dict will be converted to a JSON response. + + .. 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 # type: ignore[misc] + # 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 # type: ignore[assignment,misc] + # 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)) or isinstance(rv, _abc_Iterator): + # 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, # type: ignore[arg-type] + ) + status = headers = None + elif isinstance(rv, (dict, list)): + rv = self.json.response(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[arg-type] + ) + 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, list, tuple with headers or status," + " Response instance, or WSGI callable, but it" + f" was a {type(rv).__name__}." + ).with_traceback(sys.exc_info()[2]) from None + else: + raise TypeError( + "The view function did not return a valid" + " response. The return type must be a string," + " dict, list, tuple with headers or status," + " Response instance, or WSGI callable, but it was a" + f" {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 + else: + rv.status_code = status + + # extend existing headers with provided headers + if headers: + rv.headers.update(headers) # type: ignore[arg-type] + + return rv + + def create_url_adapter(self, request: Request | None) -> MapAdapter | None: + """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 + """ + names: t.Iterable[str | None] = (None,) + + # url_for may be called outside a request context, parse the + # passed endpoint instead of using request.blueprints. + if "." in endpoint: + names = chain( + names, reversed(_split_blueprint_path(endpoint.rpartition(".")[0])) + ) + + for name in names: + if name in self.url_default_functions: + for func in self.url_default_functions[name]: + func(endpoint, values) + + def handle_url_build_error( + self, error: BuildError, endpoint: str, values: dict[str, t.Any] + ) -> str: + """Called by :meth:`.url_for` if a + :exc:`~werkzeug.routing.BuildError` was raised. If this returns + a value, it will be returned by ``url_for``, otherwise the error + will be re-raised. + + Each function in :attr:`url_build_error_handlers` is called with + ``error``, ``endpoint`` and ``values``. If a function returns + ``None`` or raises a ``BuildError``, it is skipped. Otherwise, + its return value is returned by ``url_for``. + + :param error: The active ``BuildError`` being handled. + :param endpoint: The endpoint being built. + :param values: The keyword arguments passed to ``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) -> ft.ResponseReturnValue | None: + """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. + """ + names = (None, *reversed(request.blueprints)) + + for name in names: + if name in self.url_value_preprocessors: + for url_func in self.url_value_preprocessors[name]: + url_func(request.endpoint, request.view_args) + + for name in names: + if name in self.before_request_funcs: + for before_func in self.before_request_funcs[name]: + rv = self.ensure_sync(before_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._get_current_object() # type: ignore[attr-defined] + + for func in ctx._after_request_functions: + response = self.ensure_sync(func)(response) + + for name in chain(request.blueprints, (None,)): + if name in self.after_request_funcs: + for func in reversed(self.after_request_funcs[name]): + response = self.ensure_sync(func)(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: BaseException | None = _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] + + for name in chain(request.blueprints, (None,)): + if name in self.teardown_request_funcs: + for func in reversed(self.teardown_request_funcs[name]): + self.ensure_sync(func)(exc) + + request_tearing_down.send(self, _async_wrapper=self.ensure_sync, exc=exc) + + def do_teardown_appcontext( + self, exc: BaseException | None = _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, _async_wrapper=self.ensure_sync, 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 app.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: BaseException | None = 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 "werkzeug.debug.preserve_context" in environ: + environ["werkzeug.debug.preserve_context"](_cv_app.get()) + environ["werkzeug.debug.preserve_context"](_cv_request.get()) + + if error is not None and self.should_ignore_error(error): + error = None + + ctx.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/python3.9/site-packages/flask/blueprints.py b/.venv/lib/python3.9/site-packages/flask/blueprints.py new file mode 100644 index 0000000..0407f86 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/flask/blueprints.py @@ -0,0 +1,626 @@ +from __future__ import annotations + +import os +import typing as t +from collections import defaultdict +from functools import update_wrapper + +from . import typing as ft +from .scaffold import _endpoint_from_view_func +from .scaffold import _sentinel +from .scaffold import Scaffold +from .scaffold import setupmethod + +if t.TYPE_CHECKING: # pragma: no cover + from .app import Flask + +DeferredSetupFunction = t.Callable[["BlueprintSetupState"], t.Callable] +T_after_request = t.TypeVar("T_after_request", bound=ft.AfterRequestCallable) +T_before_request = t.TypeVar("T_before_request", bound=ft.BeforeRequestCallable) +T_error_handler = t.TypeVar("T_error_handler", bound=ft.ErrorHandlerCallable) +T_teardown = t.TypeVar("T_teardown", bound=ft.TeardownCallable) +T_template_context_processor = t.TypeVar( + "T_template_context_processor", bound=ft.TemplateContextProcessorCallable +) +T_template_filter = t.TypeVar("T_template_filter", bound=ft.TemplateFilterCallable) +T_template_global = t.TypeVar("T_template_global", bound=ft.TemplateGlobalCallable) +T_template_test = t.TypeVar("T_template_test", bound=ft.TemplateTestCallable) +T_url_defaults = t.TypeVar("T_url_defaults", bound=ft.URLDefaultCallable) +T_url_value_preprocessor = t.TypeVar( + "T_url_value_preprocessor", bound=ft.URLValuePreprocessorCallable +) + + +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: str | None = None, + view_func: t.Callable | None = 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 + """ + + _got_registered_once = False + + def __init__( + self, + name: str, + import_name: str, + static_folder: str | os.PathLike | None = None, + static_url_path: str | None = None, + template_folder: str | os.PathLike | None = None, + url_prefix: str | None = None, + subdomain: str | None = None, + url_defaults: dict | None = None, + root_path: str | None = None, + cli_group: str | None = _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 not name: + raise ValueError("'name' may not be empty.") + + 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: list[DeferredSetupFunction] = [] + + if url_defaults is None: + url_defaults = {} + + self.url_values_defaults = url_defaults + self.cli_group = cli_group + self._blueprints: list[tuple[Blueprint, dict]] = [] + + def _check_setup_finished(self, f_name: str) -> None: + if self._got_registered_once: + raise AssertionError( + f"The setup method '{f_name}' can no longer be called on the blueprint" + f" '{self.name}'. It has already been registered at least once, any" + " changes will not be applied consistently.\n" + "Make sure all imports, decorators, functions, etc. needed to set up" + " the blueprint are done before registering it." + ) + + @setupmethod + 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. + """ + self.deferred_functions.append(func) + + @setupmethod + 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) + + 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) + + @setupmethod + 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.3 + Nested blueprints now correctly apply subdomains. + + .. versionchanged:: 2.1 + Registering the same blueprint with the same name multiple + times is an error. + + .. 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``. + """ + 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: + bp_desc = "this" if app.blueprints[name] is self else "a different" + existing_at = f" '{name}'" if self_name != name else "" + + raise ValueError( + f"The name '{self_name}' is already registered for" + f" {bp_desc} blueprint{existing_at}. Use 'name=' to" + f" provide a unique name." + ) + + first_bp_registration = not any(bp is self for bp in app.blueprints.values()) + first_name_registration = name not in app.blueprints + + app.blueprints[name] = self + self._got_registered_once = True + state = self.make_setup_state(app, options, first_bp_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_bp_registration or first_name_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") + bp_subdomain = bp_options.get("subdomain") + + if bp_subdomain is None: + bp_subdomain = blueprint.subdomain + + if state.subdomain is not None and bp_subdomain is not None: + bp_options["subdomain"] = bp_subdomain + "." + state.subdomain + elif bp_subdomain is not None: + bp_options["subdomain"] = bp_subdomain + elif state.subdomain is not None: + bp_options["subdomain"] = state.subdomain + + 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) + + @setupmethod + def add_url_rule( + self, + rule: str, + endpoint: str | None = None, + view_func: ft.RouteCallable | None = None, + provide_automatic_options: bool | None = None, + **options: t.Any, + ) -> None: + """Register a URL rule with the blueprint. See :meth:`.Flask.add_url_rule` for + full documentation. + + The URL rule is prefixed with the blueprint's URL prefix. The endpoint name, + used with :func:`url_for`, is prefixed with the blueprint's name. + """ + 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, + ) + ) + + @setupmethod + def app_template_filter( + self, name: str | None = None + ) -> t.Callable[[T_template_filter], T_template_filter]: + """Register a template filter, available in any template rendered by the + application. Equivalent to :meth:`.Flask.template_filter`. + + :param name: the optional name of the filter, otherwise the + function name will be used. + """ + + def decorator(f: T_template_filter) -> T_template_filter: + self.add_app_template_filter(f, name=name) + return f + + return decorator + + @setupmethod + def add_app_template_filter( + self, f: ft.TemplateFilterCallable, name: str | None = None + ) -> None: + """Register a template filter, available in any template rendered by the + application. Works like the :meth:`app_template_filter` decorator. Equivalent to + :meth:`.Flask.add_template_filter`. + + :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) + + @setupmethod + def app_template_test( + self, name: str | None = None + ) -> t.Callable[[T_template_test], T_template_test]: + """Register a template test, available in any template rendered by the + application. Equivalent to :meth:`.Flask.template_test`. + + .. versionadded:: 0.10 + + :param name: the optional name of the test, otherwise the + function name will be used. + """ + + def decorator(f: T_template_test) -> T_template_test: + self.add_app_template_test(f, name=name) + return f + + return decorator + + @setupmethod + def add_app_template_test( + self, f: ft.TemplateTestCallable, name: str | None = None + ) -> None: + """Register a template test, available in any template rendered by the + application. Works like the :meth:`app_template_test` decorator. Equivalent to + :meth:`.Flask.add_template_test`. + + .. 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) + + @setupmethod + def app_template_global( + self, name: str | None = None + ) -> t.Callable[[T_template_global], T_template_global]: + """Register a template global, available in any template rendered by the + application. Equivalent to :meth:`.Flask.template_global`. + + .. versionadded:: 0.10 + + :param name: the optional name of the global, otherwise the + function name will be used. + """ + + def decorator(f: T_template_global) -> T_template_global: + self.add_app_template_global(f, name=name) + return f + + return decorator + + @setupmethod + def add_app_template_global( + self, f: ft.TemplateGlobalCallable, name: str | None = None + ) -> None: + """Register a template global, available in any template rendered by the + application. Works like the :meth:`app_template_global` decorator. Equivalent to + :meth:`.Flask.add_template_global`. + + .. 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) + + @setupmethod + def before_app_request(self, f: T_before_request) -> T_before_request: + """Like :meth:`before_request`, but before every request, not only those handled + by the blueprint. Equivalent to :meth:`.Flask.before_request`. + """ + self.record_once( + lambda s: s.app.before_request_funcs.setdefault(None, []).append(f) + ) + return f + + @setupmethod + def after_app_request(self, f: T_after_request) -> T_after_request: + """Like :meth:`after_request`, but after every request, not only those handled + by the blueprint. Equivalent to :meth:`.Flask.after_request`. + """ + self.record_once( + lambda s: s.app.after_request_funcs.setdefault(None, []).append(f) + ) + return f + + @setupmethod + def teardown_app_request(self, f: T_teardown) -> T_teardown: + """Like :meth:`teardown_request`, but after every request, not only those + handled by the blueprint. Equivalent to :meth:`.Flask.teardown_request`. + """ + self.record_once( + lambda s: s.app.teardown_request_funcs.setdefault(None, []).append(f) + ) + return f + + @setupmethod + def app_context_processor( + self, f: T_template_context_processor + ) -> T_template_context_processor: + """Like :meth:`context_processor`, but for templates rendered by every view, not + only by the blueprint. Equivalent to :meth:`.Flask.context_processor`. + """ + self.record_once( + lambda s: s.app.template_context_processors.setdefault(None, []).append(f) + ) + return f + + @setupmethod + def app_errorhandler( + self, code: type[Exception] | int + ) -> t.Callable[[T_error_handler], T_error_handler]: + """Like :meth:`errorhandler`, but for every request, not only those handled by + the blueprint. Equivalent to :meth:`.Flask.errorhandler`. + """ + + def decorator(f: T_error_handler) -> T_error_handler: + self.record_once(lambda s: s.app.errorhandler(code)(f)) + return f + + return decorator + + @setupmethod + def app_url_value_preprocessor( + self, f: T_url_value_preprocessor + ) -> T_url_value_preprocessor: + """Like :meth:`url_value_preprocessor`, but for every request, not only those + handled by the blueprint. Equivalent to :meth:`.Flask.url_value_preprocessor`. + """ + self.record_once( + lambda s: s.app.url_value_preprocessors.setdefault(None, []).append(f) + ) + return f + + @setupmethod + def app_url_defaults(self, f: T_url_defaults) -> T_url_defaults: + """Like :meth:`url_defaults`, but for every request, not only those handled by + the blueprint. Equivalent to :meth:`.Flask.url_defaults`. + """ + self.record_once( + lambda s: s.app.url_default_functions.setdefault(None, []).append(f) + ) + return f diff --git a/.venv/lib/python3.9/site-packages/flask/cli.py b/.venv/lib/python3.9/site-packages/flask/cli.py new file mode 100644 index 0000000..f7e1f29 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/flask/cli.py @@ -0,0 +1,1067 @@ +from __future__ import annotations + +import ast +import inspect +import os +import platform +import re +import sys +import traceback +import typing as t +from functools import update_wrapper +from operator import itemgetter + +import click +from click.core import ParameterSource +from werkzeug import run_simple +from werkzeug.serving import is_running_from_reloader +from werkzeug.utils import import_string + +from .globals import current_app +from .helpers import get_debug_flag +from .helpers import get_load_dotenv + +if t.TYPE_CHECKING: + from .app import Flask + + +class NoAppException(click.UsageError): + """Raised if an application cannot be found or loaded.""" + + +def find_best_app(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__}'. Use '{module.__name__}:name'" + " 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 = app_factory() + + if isinstance(app, Flask): + return app + except TypeError as e: + if not _called_with_wrong_args(app_factory): + raise + + raise NoAppException( + f"Detected factory '{attr_name}' in module '{module.__name__}'," + " but could not call it without arguments. Use" + f" '{module.__name__}:{attr_name}(args)'" + " to specify arguments." + ) from e + + raise NoAppException( + "Failed to find Flask application or factory in module" + f" '{module.__name__}'. Use '{module.__name__}:name'" + " to specify one." + ) + + +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(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." + ) from None + + if isinstance(expr, ast.Name): + name = expr.id + args = [] + kwargs = {} + 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}." + ) from None + else: + raise NoAppException( + f"Failed to parse {app_name!r} as an attribute name or function call." + ) + + try: + attr = getattr(module, name) + except AttributeError as e: + raise NoAppException( + f"Failed to find attribute {name!r} in {module.__name__!r}." + ) from e + + # If the attribute is a function, call it with any args and kwargs + # to get the real application. + if inspect.isfunction(attr): + try: + app = attr(*args, **kwargs) + except TypeError as e: + 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." + ) from e + 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(module_name, app_name, raise_if_not_found=True): + 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()}" + ) from None + elif raise_if_not_found: + raise NoAppException(f"Could not import {module_name!r}.") from None + else: + return + + module = sys.modules[module_name] + + if app_name is None: + return find_best_app(module) + else: + return find_app_by_string(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 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: str | None = None, + create_app: t.Callable[..., Flask] | None = None, + set_debug_flag: bool = True, + ) -> None: + #: Optionally the import path for the Flask application. + self.app_import_path = app_import_path + #: 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: dict[t.Any, t.Any] = {} + self.set_debug_flag = set_debug_flag + self._loaded_app: Flask | None = None + + def load_app(self) -> Flask: + """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. + """ + if self._loaded_app is not None: + return self._loaded_app + + if self.create_app is not None: + app = 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(import_name, name) + else: + for path in ("wsgi.py", "app.py"): + import_name = prepare_import(path) + app = locate_app(import_name, None, raise_if_not_found=False) + + if app: + break + + if not app: + raise NoAppException( + "Could not locate a Flask application. Use the" + " 'flask --app' option, 'FLASK_APP' environment" + " variable, or a 'wsgi.py' or 'app.py' file 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. + + Custom commands (and their options) registered under ``app.cli`` or + ``blueprint.cli`` will always have an app context available, this + decorator is not required in that case. + + .. versionchanged:: 2.2 + The app context is active for subcommands as well as the + decorated callback. The app context is always available to + ``app.cli`` command and parameter callbacks. + """ + + @click.pass_context + def decorator(__ctx, *args, **kwargs): + if not current_app: + app = __ctx.ensure_object(ScriptInfo).load_app() + __ctx.with_resource(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) + + +def _set_app(ctx: click.Context, param: click.Option, value: str | None) -> str | None: + if value is None: + return None + + info = ctx.ensure_object(ScriptInfo) + info.app_import_path = value + return value + + +# This option is eager so the app will be available if --help is given. +# --help is also eager, so --app must be before it in the param list. +# no_args_is_help bypasses eager processing, so this option must be +# processed manually in that case to ensure FLASK_APP gets picked up. +_app_option = click.Option( + ["-A", "--app"], + metavar="IMPORT", + help=( + "The Flask application or factory function to load, in the form 'module:name'." + " Module can be a dotted import or file path. Name is not required if it is" + " 'app', 'application', 'create_app', or 'make_app', and can be 'name(args)' to" + " pass arguments." + ), + is_eager=True, + expose_value=False, + callback=_set_app, +) + + +def _set_debug(ctx: click.Context, param: click.Option, value: bool) -> bool | None: + # If the flag isn't provided, it will default to False. Don't use + # that, let debug be set by env in that case. + source = ctx.get_parameter_source(param.name) # type: ignore[arg-type] + + if source is not None and source in ( + ParameterSource.DEFAULT, + ParameterSource.DEFAULT_MAP, + ): + return None + + # Set with env var instead of ScriptInfo.load so that it can be + # accessed early during a factory function. + os.environ["FLASK_DEBUG"] = "1" if value else "0" + return value + + +_debug_option = click.Option( + ["--debug/--no-debug"], + help="Set debug mode.", + expose_value=False, + callback=_set_debug, +) + + +def _env_file_callback( + ctx: click.Context, param: click.Option, value: str | None +) -> str | None: + if value is None: + return None + + import importlib + + try: + importlib.import_module("dotenv") + except ImportError: + raise click.BadParameter( + "python-dotenv must be installed to load an env file.", + ctx=ctx, + param=param, + ) from None + + # Don't check FLASK_SKIP_DOTENV, that only disables automatically + # loading .env and .flaskenv files. + load_dotenv(value) + return value + + +# This option is eager so env vars are loaded as early as possible to be +# used by other options. +_env_file_option = click.Option( + ["-e", "--env-file"], + type=click.Path(exists=True, dir_okay=False), + help="Load environment variables from this file. python-dotenv must be installed.", + is_eager=True, + expose_value=False, + callback=_env_file_callback, +) + + +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. + + .. versionchanged:: 2.2 + Added the ``-A/--app``, ``--debug/--no-debug``, ``-e/--env-file`` options. + + .. versionchanged:: 2.2 + An app context is pushed when running ``app.cli`` commands, so + ``@with_appcontext`` is no longer required for those commands. + + .. 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: bool = True, + create_app: t.Callable[..., Flask] | None = None, + add_version_option: bool = True, + load_dotenv: bool = True, + set_debug_flag: bool = True, + **extra: t.Any, + ) -> None: + params = list(extra.pop("params", None) or ()) + # Processing is done with option callbacks instead of a group + # callback. This allows users to make a custom group callback + # without losing the behavior. --env-file must come first so + # that it is eagerly evaluated before --app. + params.extend((_env_file_option, _app_option, _debug_option)) + + if add_version_option: + params.append(version_option) + + if "context_settings" not in extra: + extra["context_settings"] = {} + + extra["context_settings"].setdefault("auto_envvar_prefix", "FLASK") + + super().__init__(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 + + if sys.version_info >= (3, 10): + from importlib import metadata + else: + # Use a backport on Python < 3.10. We technically have + # importlib.metadata on 3.8+, but the API changed in 3.10, + # so use the backport for consistency. + import importlib_metadata as metadata + + for ep in metadata.entry_points(group="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: + app = info.load_app() + except NoAppException as e: + click.secho(f"Error: {e.format_message()}\n", err=True, fg="red") + return None + + # Push an app context for the loaded app unless it is already + # active somehow. This makes the context available to parameter + # and command callbacks without needing @with_appcontext. + if not current_app or current_app._get_current_object() is not app: + ctx.with_resource(app.app_context()) + + return app.cli.get_command(ctx, name) + + 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 make_context( + self, + info_name: str | None, + args: list[str], + parent: click.Context | None = None, + **extra: t.Any, + ) -> click.Context: + # Set a flag to tell app.run to become a no-op. If app.run was + # not in a __name__ == __main__ guard, it would start the server + # when importing, blocking whatever command is being called. + os.environ["FLASK_RUN_FROM_CLI"] = "true" + + # Attempt to load .env and .flask env files. The --env-file + # option can cause another file to be loaded. + if get_load_dotenv(self.load_dotenv): + load_dotenv() + + if "obj" not in extra and "obj" not in self.context_settings: + extra["obj"] = ScriptInfo( + create_app=self.create_app, set_debug_flag=self.set_debug_flag + ) + + return super().make_context(info_name, args, parent=parent, **extra) + + def parse_args(self, ctx: click.Context, args: list[str]) -> list[str]: + if not args and self.no_args_is_help: + # Attempt to load --env-file and --app early in case they + # were given as env vars. Otherwise no_args_is_help will not + # see commands from app.cli. + _env_file_option.handle_parse_result(ctx, {}, []) + _app_option.handle_parse_result(ctx, {}, []) + + return super().parse_args(ctx, args) + + +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: str | os.PathLike | None = None) -> bool: + """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:: 2.0 + The current directory is not changed to the location of the + loaded file. + + .. versionchanged:: 2.0 + When loading the env files, set the default encoding to UTF-8. + + .. versionchanged:: 1.1.0 + Returns ``False`` when python-dotenv is not installed, or when + the given path isn't a file. + + .. versionadded:: 1.0 + """ + try: + import dotenv + except ImportError: + 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 + + # Always return after attempting to load a given path, don't load + # the default files. + if path is not None: + if os.path.isfile(path): + return dotenv.load_dotenv(path, encoding="utf-8") + + return False + + loaded = False + + for name in (".env", ".flaskenv"): + path = dotenv.find_dotenv(name, usecwd=True) + + if not path: + continue + + dotenv.load_dotenv(path, encoding="utf-8") + loaded = True + + return loaded # True if at least one file was located and loaded. + + +def show_server_banner(debug, app_import_path): + """Show extra startup messages the first time the server is run, + ignoring the reloader. + """ + if is_running_from_reloader(): + return + + if app_import_path is not None: + click.echo(f" * Serving Flask app '{app_import_path}'") + + 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): + try: + import ssl + except ImportError: + raise click.BadParameter( + 'Using "--cert" requires Python to be compiled with SSL support.', + ctx, + param, + ) from None + + 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, + ) from None + + 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" + + try: + import ssl + except ImportError: + is_context = False + else: + is_context = 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.", + is_eager=True, +) +@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( + "--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}." + ), +) +@click.option( + "--exclude-patterns", + default=None, + type=SeparatedPathType(), + help=( + "Files matching these fnmatch patterns will not trigger a reload" + " on change. Multiple patterns are separated by" + f" {os.path.pathsep!r}." + ), +) +@pass_script_info +def run_command( + info, + host, + port, + reload, + debugger, + with_threads, + cert, + extra_files, + exclude_patterns, +): + """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 with the '--debug' + option. + """ + try: + app = info.load_app() + except Exception as e: + if is_running_from_reloader(): + # When reloading, print out the error immediately, but raise + # it later so the debugger or server can handle it. + traceback.print_exc() + err = e + + def app(environ, start_response): + raise err from None + + else: + # When not reloading, raise the error immediately so the + # command fails. + raise e from None + + debug = get_debug_flag() + + if reload is None: + reload = debug + + if debugger is None: + debugger = debug + + show_server_banner(debug, info.app_import_path) + + run_simple( + host, + port, + app, + use_reloader=reload, + use_debugger=debugger, + threaded=with_threads, + ssl_context=cert, + extra_files=extra_files, + exclude_patterns=exclude_patterns, + ) + + +run_command.params.insert(0, _debug_option) + + +@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 + + banner = ( + f"Python {sys.version} on {sys.platform}\n" + f"App: {current_app.import_name}\n" + f"Instance: {current_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(current_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", "domain", "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"} + host_matching = current_app.url_map.host_matching + has_domain = any(rule.host if host_matching else rule.subdomain for rule in rules) + rows = [] + + for rule in rules: + row = [ + rule.endpoint, + ", ".join(sorted((rule.methods or set()) - ignored_methods)), + ] + + if has_domain: + row.append((rule.host if host_matching else rule.subdomain) or "") + + row.append(rule.rule) + rows.append(row) + + headers = ["Endpoint", "Methods"] + sorts = ["endpoint", "methods"] + + if has_domain: + headers.append("Host" if host_matching else "Subdomain") + sorts.append("domain") + + headers.append("Rule") + sorts.append("rule") + + try: + rows.sort(key=itemgetter(sorts.index(sort))) + except ValueError: + pass + + rows.insert(0, headers) + widths = [max(len(row[i]) for row in rows) for i in range(len(headers))] + rows.insert(1, ["-" * w for w in widths]) + template = " ".join(f"{{{i}:<{w}}}" for i, w in enumerate(widths)) + + for row in rows: + click.echo(template.format(*row)) + + +cli = FlaskGroup( + name="flask", + help="""\ +A general utility script for Flask applications. + +An application to load must be given with the '--app' option, +'FLASK_APP' environment variable, or with a 'wsgi.py' or 'app.py' file +in the current directory. +""", +) + + +def main() -> None: + cli.main() + + +if __name__ == "__main__": + main() diff --git a/.venv/lib/python3.9/site-packages/flask/config.py b/.venv/lib/python3.9/site-packages/flask/config.py new file mode 100644 index 0000000..a73dd78 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/flask/config.py @@ -0,0 +1,345 @@ +from __future__ import annotations + +import errno +import json +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.Callable | None = 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: dict | None = None) -> None: + super().__init__(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: ``True`` if the file was loaded successfully. + """ + 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_prefixed_env( + self, prefix: str = "FLASK", *, loads: t.Callable[[str], t.Any] = json.loads + ) -> bool: + """Load any environment variables that start with ``FLASK_``, + dropping the prefix from the env key for the config key. Values + are passed through a loading function to attempt to convert them + to more specific types than strings. + + Keys are loaded in :func:`sorted` order. + + The default loading function attempts to parse values as any + valid JSON type, including dicts and lists. + + Specific items in nested dicts can be set by separating the + keys with double underscores (``__``). If an intermediate key + doesn't exist, it will be initialized to an empty dict. + + :param prefix: Load env vars that start with this prefix, + separated with an underscore (``_``). + :param loads: Pass each string value to this function and use + the returned value as the config value. If any error is + raised it is ignored and the value remains a string. The + default is :func:`json.loads`. + + .. versionadded:: 2.1 + """ + prefix = f"{prefix}_" + len_prefix = len(prefix) + + for key in sorted(os.environ): + if not key.startswith(prefix): + continue + + value = os.environ[key] + + try: + value = loads(value) + except Exception: + # Keep the value as a string if loading failed. + pass + + # Change to key.removeprefix(prefix) on Python >= 3.9. + key = key[len_prefix:] + + if "__" not in key: + # A non-nested key, set directly. + self[key] = value + continue + + # Traverse nested dictionaries with keys separated by "__". + current = self + *parts, tail = key.split("__") + + for part in parts: + # If an intermediate dict does not exist, create it. + if part not in current: + current[part] = {} + + current = current[part] + + current[tail] = value + + return True + + 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. + :return: ``True`` if the file was loaded successfully. + + .. 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: 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, + text: bool = True, + ) -> 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 json + app.config.from_file("config.json", load=json.load) + + import tomllib + app.config.from_file("config.toml", load=tomllib.load, text=False) + + :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. + :param text: Open the file in text or binary mode. + :return: ``True`` if the file was loaded successfully. + + .. versionchanged:: 2.3 + The ``text`` parameter was added. + + .. versionadded:: 2.0 + """ + filename = os.path.join(self.root_path, filename) + + try: + with open(filename, "r" if text else "rb") 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_mapping( + self, mapping: t.Mapping[str, t.Any] | None = None, **kwargs: t.Any + ) -> bool: + """Updates the config like :meth:`update` ignoring items with + non-upper keys. + + :return: Always returns ``True``. + + .. versionadded:: 0.11 + """ + mappings: 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 + ) -> 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/python3.9/site-packages/flask/ctx.py b/.venv/lib/python3.9/site-packages/flask/ctx.py new file mode 100644 index 0000000..b37e4e0 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/flask/ctx.py @@ -0,0 +1,440 @@ +from __future__ import annotations + +import contextvars +import sys +import typing as t +from functools import update_wrapper +from types import TracebackType + +from werkzeug.exceptions import HTTPException + +from . import typing as ft +from .globals import _cv_app +from .globals import _cv_request +from .signals import appcontext_popped +from .signals import appcontext_pushed + +if t.TYPE_CHECKING: # pragma: no cover + 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.Any | None = 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: + ctx = _cv_app.get(None) + if ctx is not None: + return f"" + return object.__repr__(self) + + +def after_this_request(f: ft.AfterRequestCallable) -> ft.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 + """ + ctx = _cv_request.get(None) + + if ctx is None: + raise RuntimeError( + "'after_this_request' can only be used when a request" + " context is active, such as in a view function." + ) + + ctx._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 + """ + ctx = _cv_request.get(None) + + if ctx is None: + raise RuntimeError( + "'copy_current_request_context' can only be used when a" + " request context is active, such as in a view function." + ) + + ctx = ctx.copy() + + def wrapper(*args, **kwargs): + with ctx: + return ctx.app.ensure_sync(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 _cv_request.get(None) 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 _cv_app.get(None) is not None + + +class AppContext: + """The app context contains application-specific information. An app + context is created and pushed at the beginning of each request if + one is not already active. An app context is also pushed when + running CLI commands. + """ + + def __init__(self, app: Flask) -> None: + self.app = app + self.url_adapter = app.create_url_adapter(None) + self.g: _AppCtxGlobals = app.app_ctx_globals_class() + self._cv_tokens: list[contextvars.Token] = [] + + def push(self) -> None: + """Binds the app context to the current context.""" + self._cv_tokens.append(_cv_app.set(self)) + appcontext_pushed.send(self.app, _async_wrapper=self.app.ensure_sync) + + def pop(self, exc: BaseException | None = _sentinel) -> None: # type: ignore + """Pops the app context.""" + try: + if len(self._cv_tokens) == 1: + if exc is _sentinel: + exc = sys.exc_info()[1] + self.app.do_teardown_appcontext(exc) + finally: + ctx = _cv_app.get() + _cv_app.reset(self._cv_tokens.pop()) + + if ctx is not self: + raise AssertionError( + f"Popped wrong app context. ({ctx!r} instead of {self!r})" + ) + + appcontext_popped.send(self.app, _async_wrapper=self.app.ensure_sync) + + def __enter__(self) -> AppContext: + self.push() + return self + + def __exit__( + self, + exc_type: type | None, + exc_value: BaseException | None, + tb: TracebackType | None, + ) -> None: + self.pop(exc_value) + + +class RequestContext: + """The request context contains per-request information. The Flask + app creates and pushes it at the beginning of the request, then pops + it at the end of the request. 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. When using the interactive debugger, the context will be + restored so ``request`` is still accessible. Similarly, the test + client can preserve the context after the request ends. However, + teardown functions may already have closed some resources such as + database connections. + """ + + def __init__( + self, + app: Flask, + environ: dict, + request: Request | None = None, + session: SessionMixin | None = None, + ) -> None: + self.app = app + if request is None: + request = app.request_class(environ) + request.json_module = app.json + self.request: 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: list[tuple[str, str]] | None = None + self.session: SessionMixin | None = session + # 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: list[ft.AfterRequestCallable] = [] + + self._cv_tokens: list[tuple[contextvars.Token, AppContext | None]] = [] + + 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: + # Before we push the request context we have to ensure that there + # is an application context. + app_ctx = _cv_app.get(None) + + if app_ctx is None or app_ctx.app is not self.app: + app_ctx = self.app.app_context() + app_ctx.push() + else: + app_ctx = None + + self._cv_tokens.append((_cv_request.set(self), app_ctx)) + + # 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: BaseException | None = _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. + """ + clear_request = len(self._cv_tokens) == 1 + + try: + if clear_request: + 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() + finally: + ctx = _cv_request.get() + token, app_ctx = self._cv_tokens.pop() + _cv_request.reset(token) + + # 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: + ctx.request.environ["werkzeug.request"] = None + + if app_ctx is not None: + app_ctx.pop(exc) + + if ctx is not self: + raise AssertionError( + f"Popped wrong request context. ({ctx!r} instead of {self!r})" + ) + + def __enter__(self) -> RequestContext: + self.push() + return self + + def __exit__( + self, + exc_type: type | None, + exc_value: BaseException | None, + tb: TracebackType | None, + ) -> None: + self.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/python3.9/site-packages/flask/debughelpers.py b/.venv/lib/python3.9/site-packages/flask/debughelpers.py new file mode 100644 index 0000000..6061441 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/flask/debughelpers.py @@ -0,0 +1,160 @@ +from __future__ import annotations + +import typing as t + +from .app import Flask +from .blueprints import Blueprint +from .globals import request_ctx + + +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 in debug mode if a routing redirect + would cause the browser to drop the method or body. This happens + when method is not GET, HEAD or OPTIONS and the status code is not + 307 or 308. + """ + + def __init__(self, request): + exc = request.routing_exception + buf = [ + f"A request was sent to '{request.url}', but routing issued" + f" a redirect to the canonical URL '{exc.new_url}'." + ] + + if f"{request.base_url}/" == exc.new_url.partition("?")[0]: + buf.append( + " The URL was defined with a trailing slash. Flask" + " will redirect to the URL with a trailing slash if it" + " was accessed without one." + ) + + buf.append( + " Send requests to the canonical URL, or use 307 or 308 for" + " routing redirects. Otherwise, browsers will drop form" + " data.\n\n" + "This exception is only raised in debug mode." + ) + super().__init__("".join(buf)) + + +def attach_enctype_error_multidict(request): + """Patch ``request.files.__getitem__`` to raise a descriptive error + about ``enctype=multipart/form-data``. + + :param request: The request to patch. + :meta private: + """ + oldcls = request.files.__class__ + + class newcls(oldcls): + def __getitem__(self, key): + try: + return super().__getitem__(key) + except KeyError as e: + if key not in request.form: + raise + + raise DebugFilesKeyError(request, key).with_traceback( + e.__traceback__ + ) from None + + 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 + if request_ctx and request_ctx.request.blueprint is not None: + blueprint = request_ctx.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)) diff --git a/.venv/lib/python3.9/site-packages/flask/globals.py b/.venv/lib/python3.9/site-packages/flask/globals.py new file mode 100644 index 0000000..e9cd4ac --- /dev/null +++ b/.venv/lib/python3.9/site-packages/flask/globals.py @@ -0,0 +1,96 @@ +from __future__ import annotations + +import typing as t +from contextvars import ContextVar + +from werkzeug.local import LocalProxy + +if t.TYPE_CHECKING: # pragma: no cover + from .app import Flask + from .ctx import _AppCtxGlobals + from .ctx import AppContext + from .ctx import RequestContext + from .sessions import SessionMixin + from .wrappers import Request + + +class _FakeStack: + def __init__(self, name: str, cv: ContextVar[t.Any]) -> None: + self.name = name + self.cv = cv + + @property + def top(self) -> t.Any | None: + import warnings + + warnings.warn( + f"'_{self.name}_ctx_stack' is deprecated and will be removed in Flask 2.4." + f" Use 'g' to store data, or '{self.name}_ctx' to access the current" + " context.", + DeprecationWarning, + stacklevel=2, + ) + return self.cv.get(None) + + +_no_app_msg = """\ +Working outside of application context. + +This typically means that you attempted to use functionality that needed +the current application. To solve this, set up an application context +with app.app_context(). See the documentation for more information.\ +""" +_cv_app: ContextVar[AppContext] = ContextVar("flask.app_ctx") +__app_ctx_stack = _FakeStack("app", _cv_app) +app_ctx: AppContext = LocalProxy( # type: ignore[assignment] + _cv_app, unbound_message=_no_app_msg +) +current_app: Flask = LocalProxy( # type: ignore[assignment] + _cv_app, "app", unbound_message=_no_app_msg +) +g: _AppCtxGlobals = LocalProxy( # type: ignore[assignment] + _cv_app, "g", unbound_message=_no_app_msg +) + +_no_req_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.\ +""" +_cv_request: ContextVar[RequestContext] = ContextVar("flask.request_ctx") +__request_ctx_stack = _FakeStack("request", _cv_request) +request_ctx: RequestContext = LocalProxy( # type: ignore[assignment] + _cv_request, unbound_message=_no_req_msg +) +request: Request = LocalProxy( # type: ignore[assignment] + _cv_request, "request", unbound_message=_no_req_msg +) +session: SessionMixin = LocalProxy( # type: ignore[assignment] + _cv_request, "session", unbound_message=_no_req_msg +) + + +def __getattr__(name: str) -> t.Any: + if name == "_app_ctx_stack": + import warnings + + warnings.warn( + "'_app_ctx_stack' is deprecated and will be removed in Flask 2.4.", + DeprecationWarning, + stacklevel=2, + ) + return __app_ctx_stack + + if name == "_request_ctx_stack": + import warnings + + warnings.warn( + "'_request_ctx_stack' is deprecated and will be removed in Flask 2.4.", + DeprecationWarning, + stacklevel=2, + ) + return __request_ctx_stack + + raise AttributeError(name) diff --git a/.venv/lib/python3.9/site-packages/flask/helpers.py b/.venv/lib/python3.9/site-packages/flask/helpers.py new file mode 100644 index 0000000..61a0f81 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/flask/helpers.py @@ -0,0 +1,693 @@ +from __future__ import annotations + +import os +import pkgutil +import socket +import sys +import typing as t +import warnings +from datetime import datetime +from functools import lru_cache +from functools import update_wrapper +from threading import RLock + +import werkzeug.utils +from werkzeug.exceptions import abort as _wz_abort +from werkzeug.utils import redirect as _wz_redirect + +from .globals import _cv_request +from .globals import current_app +from .globals import request +from .globals import request_ctx +from .globals import session +from .signals import message_flashed + +if t.TYPE_CHECKING: # pragma: no cover + from werkzeug.wrappers import Response as BaseResponse + from .wrappers import Response + + +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 ``False``. + """ + val = os.environ.get("FLASK_DEBUG") + return bool(val and val.lower() not in {"0", "false", "no"}) + + +def get_load_dotenv(default: bool = True) -> bool: + """Get whether the user has disabled loading default 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.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 = _cv_request.get(None) + if ctx is None: + raise RuntimeError( + "'stream_with_context' can only be used when a request" + " context is active, such as in a view function." + ) + 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() + + # 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) # type: ignore + + +def url_for( + endpoint: str, + *, + _anchor: str | None = None, + _method: str | None = None, + _scheme: str | None = None, + _external: bool | None = None, + **values: t.Any, +) -> str: + """Generate a URL to the given endpoint with the given values. + + This requires an active request or application context, and calls + :meth:`current_app.url_for() `. See that method + for full documentation. + + :param endpoint: The endpoint name associated with the URL to + generate. If this starts with a ``.``, the current blueprint + name (if any) will be used. + :param _anchor: If given, append this as ``#anchor`` to the URL. + :param _method: If given, generate the URL associated with this + method for the endpoint. + :param _scheme: If given, the URL will have this scheme if it is + external. + :param _external: If given, prefer the URL to be internal (False) or + require it to be external (True). External URLs include the + scheme and domain. When not in an active request, URLs are + external by default. + :param values: Values to use for the variable parts of the URL rule. + Unknown keys are appended as query string arguments, like + ``?a=b&c=d``. + + .. versionchanged:: 2.2 + Calls ``current_app.url_for``, allowing an app to override the + behavior. + + .. versionchanged:: 0.10 + The ``_scheme`` parameter was added. + + .. versionchanged:: 0.9 + The ``_anchor`` and ``_method`` parameters were added. + + .. versionchanged:: 0.9 + Calls ``app.handle_url_build_error`` on build errors. + """ + return current_app.url_for( + endpoint, + _anchor=_anchor, + _method=_method, + _scheme=_scheme, + _external=_external, + **values, + ) + + +def redirect( + location: str, code: int = 302, Response: type[BaseResponse] | None = None +) -> BaseResponse: + """Create a redirect response object. + + If :data:`~flask.current_app` is available, it will use its + :meth:`~flask.Flask.redirect` method, otherwise it will use + :func:`werkzeug.utils.redirect`. + + :param location: The URL to redirect to. + :param code: The status code for the redirect. + :param Response: The response class to use. Not used when + ``current_app`` is active, which uses ``app.response_class``. + + .. versionadded:: 2.2 + Calls ``current_app.redirect`` if available instead of always + using Werkzeug's default ``redirect``. + """ + if current_app: + return current_app.redirect(location, code=code) + + return _wz_redirect(location, code=code, Response=Response) + + +def abort(code: int | BaseResponse, *args: t.Any, **kwargs: t.Any) -> t.NoReturn: + """Raise an :exc:`~werkzeug.exceptions.HTTPException` for the given + status code. + + If :data:`~flask.current_app` is available, it will call its + :attr:`~flask.Flask.aborter` object, otherwise it will use + :func:`werkzeug.exceptions.abort`. + + :param code: The status code for the exception, which must be + registered in ``app.aborter``. + :param args: Passed to the exception. + :param kwargs: Passed to the exception. + + .. versionadded:: 2.2 + Calls ``current_app.aborter`` if available instead of always + using Werkzeug's default ``abort``. + """ + if current_app: + current_app.aborter(code, *args, **kwargs) + + _wz_abort(code, *args, **kwargs) + + +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 + app = current_app._get_current_object() # type: ignore + message_flashed.send( + app, + _async_wrapper=app.ensure_sync, + message=message, + category=category, + ) + + +def get_flashed_messages( + with_categories: bool = False, category_filter: t.Iterable[str] = () +) -> list[str] | list[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.flashes + if flashes is None: + flashes = session.pop("_flashes") if "_flashes" in session else [] + request_ctx.flashes = flashes + 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(**kwargs: t.Any) -> dict[str, t.Any]: + if kwargs.get("max_age") is None: + kwargs["max_age"] = current_app.get_send_file_max_age + + kwargs.update( + environ=request.environ, + use_x_sendfile=current_app.config["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: os.PathLike | str | t.BinaryIO, + mimetype: str | None = None, + as_attachment: bool = False, + download_name: str | None = None, + conditional: bool = True, + etag: bool | str = True, + last_modified: datetime | int | float | None = None, + max_age: None | (int | t.Callable[[str | None], int | None]) = 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. 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( # type: ignore[return-value] + **_prepare_send_file_kwargs( + path_or_file=path_or_file, + environ=request.environ, + mimetype=mimetype, + as_attachment=as_attachment, + download_name=download_name, + conditional=conditional, + etag=etag, + last_modified=last_modified, + max_age=max_age, + ) + ) + + +def send_from_directory( + directory: os.PathLike | str, + path: os.PathLike | str, + **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, + relative to the current application's root path. + :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 + """ + return werkzeug.utils.send_from_directory( # type: ignore[return-value] + 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__") and mod.__file__ is not None: + 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) + 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. + + .. deprecated:: 2.3 + Will be removed in Flask 2.4. Use a lock inside the decorated function if + locking is needed. + + .. versionchanged:: 2.0 + Inherits from Werkzeug's ``cached_property`` (and ``property``). + """ + + def __init__( + self, + fget: t.Callable[[t.Any], t.Any], + name: str | None = None, + doc: str | None = None, + ) -> None: + import warnings + + warnings.warn( + "'locked_cached_property' is deprecated and will be removed in Flask 2.4." + " Use a lock inside the decorated function if locking is needed.", + DeprecationWarning, + stacklevel=2, + ) + 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 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 + + .. deprecated:: 2.3 + Will be removed in Flask 2.4. + """ + warnings.warn( + "The 'is_ip' function is deprecated and will be removed in Flask 2.4.", + DeprecationWarning, + stacklevel=2, + ) + + 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) -> list[str]: + out: list[str] = [name] + + if "." in name: + out.extend(_split_blueprint_path(name.rpartition(".")[0])) + + return out diff --git a/.venv/lib/python3.9/site-packages/flask/json/__init__.py b/.venv/lib/python3.9/site-packages/flask/json/__init__.py new file mode 100644 index 0000000..f15296f --- /dev/null +++ b/.venv/lib/python3.9/site-packages/flask/json/__init__.py @@ -0,0 +1,170 @@ +from __future__ import annotations + +import json as _json +import typing as t + +from ..globals import current_app +from .provider import _default + +if t.TYPE_CHECKING: # pragma: no cover + from ..wrappers import Response + + +def dumps(obj: t.Any, **kwargs: t.Any) -> str: + """Serialize data as JSON. + + If :data:`~flask.current_app` is available, it will use its + :meth:`app.json.dumps() ` + method, otherwise it will use :func:`json.dumps`. + + :param obj: The data to serialize. + :param kwargs: Arguments passed to the ``dumps`` implementation. + + .. versionchanged:: 2.3 + The ``app`` parameter was removed. + + .. versionchanged:: 2.2 + Calls ``current_app.json.dumps``, allowing an app to override + the behavior. + + .. versionchanged:: 2.0.2 + :class:`decimal.Decimal` is supported by converting to a string. + + .. versionchanged:: 2.0 + ``encoding`` will be removed in Flask 2.1. + + .. versionchanged:: 1.0.3 + ``app`` can be passed directly, rather than requiring an app + context for configuration. + """ + if current_app: + return current_app.json.dumps(obj, **kwargs) + + kwargs.setdefault("default", _default) + return _json.dumps(obj, **kwargs) + + +def dump(obj: t.Any, fp: t.IO[str], **kwargs: t.Any) -> None: + """Serialize data as JSON and write to a file. + + If :data:`~flask.current_app` is available, it will use its + :meth:`app.json.dump() ` + method, otherwise it will use :func:`json.dump`. + + :param obj: The data to serialize. + :param fp: A file opened for writing text. Should use the UTF-8 + encoding to be valid JSON. + :param kwargs: Arguments passed to the ``dump`` implementation. + + .. versionchanged:: 2.3 + The ``app`` parameter was removed. + + .. versionchanged:: 2.2 + Calls ``current_app.json.dump``, allowing an app to override + the behavior. + + .. versionchanged:: 2.0 + Writing to a binary file, and the ``encoding`` argument, will be + removed in Flask 2.1. + """ + if current_app: + current_app.json.dump(obj, fp, **kwargs) + else: + kwargs.setdefault("default", _default) + _json.dump(obj, fp, **kwargs) + + +def loads(s: str | bytes, **kwargs: t.Any) -> t.Any: + """Deserialize data as JSON. + + If :data:`~flask.current_app` is available, it will use its + :meth:`app.json.loads() ` + method, otherwise it will use :func:`json.loads`. + + :param s: Text or UTF-8 bytes. + :param kwargs: Arguments passed to the ``loads`` implementation. + + .. versionchanged:: 2.3 + The ``app`` parameter was removed. + + .. versionchanged:: 2.2 + Calls ``current_app.json.loads``, allowing an app to override + the behavior. + + .. versionchanged:: 2.0 + ``encoding`` 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. + """ + if current_app: + return current_app.json.loads(s, **kwargs) + + return _json.loads(s, **kwargs) + + +def load(fp: t.IO[t.AnyStr], **kwargs: t.Any) -> t.Any: + """Deserialize data as JSON read from a file. + + If :data:`~flask.current_app` is available, it will use its + :meth:`app.json.load() ` + method, otherwise it will use :func:`json.load`. + + :param fp: A file opened for reading text or UTF-8 bytes. + :param kwargs: Arguments passed to the ``load`` implementation. + + .. versionchanged:: 2.3 + The ``app`` parameter was removed. + + .. versionchanged:: 2.2 + Calls ``current_app.json.load``, allowing an app to override + the behavior. + + .. versionchanged:: 2.2 + The ``app`` parameter will be removed in Flask 2.3. + + .. versionchanged:: 2.0 + ``encoding`` will be removed in Flask 2.1. The file must be text + mode, or binary mode with UTF-8 bytes. + """ + if current_app: + return current_app.json.load(fp, **kwargs) + + return _json.load(fp, **kwargs) + + +def jsonify(*args: t.Any, **kwargs: t.Any) -> Response: + """Serialize the given arguments as JSON, and return a + :class:`~flask.Response` object with the ``application/json`` + mimetype. A dict or list returned from a view will be converted to a + JSON response automatically without needing to call this. + + This requires an active request or application context, and calls + :meth:`app.json.response() `. + + In debug mode, the output is formatted with indentation to make it + easier to read. This may also be controlled by the provider. + + Either positional or keyword arguments can be given, not both. + If no arguments are given, ``None`` is serialized. + + :param args: A single value to serialize, or multiple values to + treat as a list to serialize. + :param kwargs: Treat as a dict to serialize. + + .. versionchanged:: 2.2 + Calls ``current_app.json.response``, allowing an app to override + the behavior. + + .. versionchanged:: 2.0.2 + :class:`decimal.Decimal` is supported by converting to a string. + + .. versionchanged:: 0.11 + Added support for serializing top-level arrays. This was a + security risk in ancient browsers. See :ref:`security-json`. + + .. versionadded:: 0.2 + """ + return current_app.json.response(*args, **kwargs) diff --git a/.venv/lib/python3.9/site-packages/flask/json/__pycache__/__init__.cpython-39.pyc b/.venv/lib/python3.9/site-packages/flask/json/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9149eabbdfb853b97b168b5a06297c28b425aea8 GIT binary patch literal 5970 zcmd5=OK%&=5uPC_iF)~=c-QiJ<89;=2GYv*U8nz;U5L);KI z(RNL2nD2gsuWpGC&~jaTXj*QgWmDWi%MJ7Xk+_SNkHwGCa#PfHoz}fqw1bwLukt90 zQ=a;9q<3D`ipD{vl#J3YPm))zZEJRg9P%tkF{t@lsS^y4#|x?VB%0=RovKznugCqv zeDP?))kx=yN~W2L1~k6BNPYwNm!z&w@sK)dGj$H#!`g{6bbaT<{fGNkcNhQvRu^vC z_;76k_n!2#x?u(?#mr-Wrg8-F=QQT?)3gN47fh>=JwQM zzGi&P{ebraxyAgHP5dBWnU;9gW_%}<=|QIl4S0t-jy#ctiT-JeeP*SZwnW8aUr6QA zykC_s=F7)DBZY*-VvEJ;fm9RU$e+#C8D`O-)0@ff6^rjAT=9@WogKD!Q0y;_nJ#zi z*)`hFtin6&i5g`gj!moYl>Q*w0$5LD<=V2x1DGve-NplA=r`ld`i{XQK;zuLK zBZm2C{gAH|bX-ZH4fW*#AN#SoaN_&kZ0gPcCZIb#AqRfQ1Mds_-`Ii9k|b6s)YYG| zK^$Sil-7XBIfGr}`Qmx1rS*C;8pHwvpebWeeOc;=`4M|gb_mrzxIo?m@BW&z^;>Wq$aBtPtT+@2IoO&-_{s9N|$GouJ>OB6bBhc)0(jc~|yB6dLy(4`w>@Db0mOW7%1ud6n-Iwn_#k4B3UJ< zK>ag_M(3c`;7mI$izABsHE$4%sOxw%@S$$tsa3b&af@0BK@5J_pAC8i%W~Sdt+(H9 zxSQnd)Ox%MM`tI1>Qrhl4p-0l_#2tV&oJ^$Oe3wo#x7ib4c`2PtfK7?!7qZCi_7r~ z^W|H6uNig5eKptxMFu#d@i(j1x8xR9@cYUwiq%~;x2T-ql+Rok^JhdZ%yddH#w#h7 zX1sO|rvE&8_QSITL{%aO)~OeaR;@4B)Q8l5o*Umr$8T_JA`Z?S3fhX=j<1=JZBmC_ z#BJcg3<^xNzB*6-=mJj$W#R}$Dh`2&-(y%NZCxrX(}(Zm*k}Cu{CG>dE#vKLruJ50 zl-Bv)hEcn?D|YxHkYg z|0&X!qmb1l{DDb?Ouo@8GIk$1`!tar7_0Cx1d0rN0JfcnxT^(>u`eejH!AZ+n{!)Sr@kEOI)D9b@}Qp~jR?ev?To2g{n< z0WE-xz^a5~onOT$*K;X2DnEW)!pn<@3E9s^%$gYK5xYpR5v&!I9vh@Q+Sr;OiL%Z> ztUcsMR9i7dD!KM4YmKoPE<6NA3=jDf`M);&o;IW6Ab>37Nt|qNvy=2ofK)aF`q4lIv4As5M%iba<2$XxGI@&RoK}UcrfLW1aNv*aVe`D zMXo_sPDzahq9Uz9Mv|HQuHuZw14Goye3V>lzs8m;FHN!clq{KB^MW?2vF9?dz1aX` zm!}69x#v-ZhjV3+H&$TElfL@&V{Z1+H?O`3bKiULpi=A;Az(K}RmUWnrCv?&i<9j@ zj%9$OSA41q>!WJc937LWQTgeVDIWzJRBm`Q@L|oYui^=$VbrdatfSPr+f=kD zJ!N(hoR))%T3L<)*_jckW~q48Q|QPqxv8+2#LEyjWq_XMp3B%wd>}B-o1Xy}vOqp2`lg8%uGN}u6S>=3ZP8t;Z~Wxe Ky^T*c?)@KIxX_>g literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/flask/json/__pycache__/provider.cpython-39.pyc b/.venv/lib/python3.9/site-packages/flask/json/__pycache__/provider.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..376429160af6810c578db35bb3aa8d032752bcff GIT binary patch literal 7553 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 to an instance of the class. + + :param app: An application instance. This will be stored as a + :class:`weakref.proxy` on the :attr:`_app` attribute. + + .. versionadded:: 2.2 + """ + + def __init__(self, app: Flask) -> None: + self._app = weakref.proxy(app) + + def dumps(self, obj: t.Any, **kwargs: t.Any) -> str: + """Serialize data as JSON. + + :param obj: The data to serialize. + :param kwargs: May be passed to the underlying JSON library. + """ + raise NotImplementedError + + def dump(self, obj: t.Any, fp: t.IO[str], **kwargs: t.Any) -> None: + """Serialize data as JSON and write to a file. + + :param obj: The data to serialize. + :param fp: A file opened for writing text. Should use the UTF-8 + encoding to be valid JSON. + :param kwargs: May be passed to the underlying JSON library. + """ + fp.write(self.dumps(obj, **kwargs)) + + def loads(self, s: str | bytes, **kwargs: t.Any) -> t.Any: + """Deserialize data as JSON. + + :param s: Text or UTF-8 bytes. + :param kwargs: May be passed to the underlying JSON library. + """ + raise NotImplementedError + + def load(self, fp: t.IO[t.AnyStr], **kwargs: t.Any) -> t.Any: + """Deserialize data as JSON read from a file. + + :param fp: A file opened for reading text or UTF-8 bytes. + :param kwargs: May be passed to the underlying JSON library. + """ + return self.loads(fp.read(), **kwargs) + + def _prepare_response_obj( + self, args: tuple[t.Any, ...], kwargs: dict[str, t.Any] + ) -> t.Any: + if args and kwargs: + raise TypeError("app.json.response() takes either args or kwargs, not both") + + if not args and not kwargs: + return None + + if len(args) == 1: + return args[0] + + return args or kwargs + + def response(self, *args: t.Any, **kwargs: t.Any) -> Response: + """Serialize the given arguments as JSON, and return a + :class:`~flask.Response` object with the ``application/json`` + mimetype. + + The :func:`~flask.json.jsonify` function calls this method for + the current application. + + Either positional or keyword arguments can be given, not both. + If no arguments are given, ``None`` is serialized. + + :param args: A single value to serialize, or multiple values to + treat as a list to serialize. + :param kwargs: Treat as a dict to serialize. + """ + obj = self._prepare_response_obj(args, kwargs) + return self._app.response_class(self.dumps(obj), mimetype="application/json") + + +def _default(o: t.Any) -> t.Any: + if isinstance(o, date): + return http_date(o) + + if isinstance(o, (decimal.Decimal, uuid.UUID)): + return str(o) + + if dataclasses and dataclasses.is_dataclass(o): + return dataclasses.asdict(o) + + if hasattr(o, "__html__"): + return str(o.__html__()) + + raise TypeError(f"Object of type {type(o).__name__} is not JSON serializable") + + +class DefaultJSONProvider(JSONProvider): + """Provide JSON operations using Python's built-in :mod:`json` + library. Serializes the following additional data types: + + - :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. + """ + + default: t.Callable[[t.Any], t.Any] = staticmethod( + _default + ) # type: ignore[assignment] + """Apply this function to any object that :meth:`json.dumps` does + not know how to serialize. It should return a valid JSON type or + raise a ``TypeError``. + """ + + ensure_ascii = True + """Replace non-ASCII characters with escape sequences. This may be + more compatible with some clients, but can be disabled for better + performance and size. + """ + + sort_keys = True + """Sort the keys in any serialized dicts. This may be useful for + some caching situations, but can be disabled for better performance. + When enabled, keys must all be strings, they are not converted + before sorting. + """ + + compact: bool | None = None + """If ``True``, or ``None`` out of debug mode, the :meth:`response` + output will not add indentation, newlines, or spaces. If ``False``, + or ``None`` in debug mode, it will use a non-compact representation. + """ + + mimetype = "application/json" + """The mimetype set in :meth:`response`.""" + + def dumps(self, obj: t.Any, **kwargs: t.Any) -> str: + """Serialize data as JSON to a string. + + Keyword arguments are passed to :func:`json.dumps`. Sets some + parameter defaults from the :attr:`default`, + :attr:`ensure_ascii`, and :attr:`sort_keys` attributes. + + :param obj: The data to serialize. + :param kwargs: Passed to :func:`json.dumps`. + """ + kwargs.setdefault("default", self.default) + kwargs.setdefault("ensure_ascii", self.ensure_ascii) + kwargs.setdefault("sort_keys", self.sort_keys) + return json.dumps(obj, **kwargs) + + def loads(self, s: str | bytes, **kwargs: t.Any) -> t.Any: + """Deserialize data as JSON from a string or bytes. + + :param s: Text or UTF-8 bytes. + :param kwargs: Passed to :func:`json.loads`. + """ + return json.loads(s, **kwargs) + + def response(self, *args: t.Any, **kwargs: t.Any) -> Response: + """Serialize the given arguments as JSON, and return a + :class:`~flask.Response` object with it. The response mimetype + will be "application/json" and can be changed with + :attr:`mimetype`. + + If :attr:`compact` is ``False`` or debug mode is enabled, the + output will be formatted to be easier to read. + + Either positional or keyword arguments can be given, not both. + If no arguments are given, ``None`` is serialized. + + :param args: A single value to serialize, or multiple values to + treat as a list to serialize. + :param kwargs: Treat as a dict to serialize. + """ + obj = self._prepare_response_obj(args, kwargs) + dump_args: dict[str, t.Any] = {} + + if (self.compact is None and self._app.debug) or self.compact is False: + dump_args.setdefault("indent", 2) + else: + dump_args.setdefault("separators", (",", ":")) + + return self._app.response_class( + f"{self.dumps(obj, **dump_args)}\n", mimetype=self.mimetype + ) diff --git a/.venv/lib/python3.9/site-packages/flask/json/tag.py b/.venv/lib/python3.9/site-packages/flask/json/tag.py new file mode 100644 index 0000000..91cc441 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/flask/json/tag.py @@ -0,0 +1,314 @@ +""" +Tagged JSON +~~~~~~~~~~~ + +A compact representation for lossless serialization of non-standard JSON +types. :class:`~flask.sessions.SecureCookieSessionInterface` uses this +to serialize the session data, but it may be useful in other places. It +can be extended to support other types. + +.. autoclass:: TaggedJSONSerializer + :members: + +.. autoclass:: JSONTag + :members: + +Let's see an example that adds support for +:class:`~collections.OrderedDict`. Dicts don't have an order in JSON, so +to handle this we will dump the items as a list of ``[key, value]`` +pairs. Subclass :class:`JSONTag` and give it the new key ``' od'`` to +identify the type. The session serializer processes dicts first, so +insert the new tag at the front of the order since ``OrderedDict`` must +be processed before ``dict``. + +.. code-block:: python + + from flask.json.tag import JSONTag + + class TagOrderedDict(JSONTag): + __slots__ = ('serializer',) + key = ' od' + + def check(self, value): + return isinstance(value, OrderedDict) + + def to_json(self, value): + return [[k, self.serializer.tag(v)] for k, v in iteritems(value)] + + def to_python(self, value): + return OrderedDict(value) + + app.session_interface.serializer.register(TagOrderedDict, index=0) +""" +from __future__ import annotations + +import typing as t +from base64 import b64decode +from base64 import b64encode +from datetime import datetime +from uuid import UUID + +from markupsafe import Markup +from werkzeug.http import http_date +from werkzeug.http import parse_date + +from ..json import dumps +from ..json import loads + + +class JSONTag: + """Base class for defining type tags for :class:`TaggedJSONSerializer`.""" + + __slots__ = ("serializer",) + + #: The tag to mark the serialized object with. If ``None``, this tag is + #: only used as an intermediate step during tagging. + key: str | None = None + + def __init__(self, serializer: TaggedJSONSerializer) -> None: + """Create a tagger for the given serializer.""" + self.serializer = serializer + + def check(self, value: t.Any) -> bool: + """Check if the given value should be tagged by this tag.""" + raise NotImplementedError + + def to_json(self, value: t.Any) -> t.Any: + """Convert the Python object to an object that is a valid JSON type. + The tag will be added later.""" + raise NotImplementedError + + def to_python(self, value: t.Any) -> t.Any: + """Convert the JSON representation back to the correct type. The tag + will already be removed.""" + raise NotImplementedError + + def tag(self, value: t.Any) -> t.Any: + """Convert the value to a valid JSON type and add the tag structure + around it.""" + return {self.key: self.to_json(value)} + + +class TagDict(JSONTag): + """Tag for 1-item dicts whose only key matches a registered tag. + + Internally, the dict key is suffixed with `__`, and the suffix is removed + when deserializing. + """ + + __slots__ = () + key = " di" + + def check(self, value: t.Any) -> bool: + return ( + isinstance(value, dict) + and len(value) == 1 + and next(iter(value)) in self.serializer.tags + ) + + def to_json(self, value: t.Any) -> t.Any: + key = next(iter(value)) + return {f"{key}__": self.serializer.tag(value[key])} + + def to_python(self, value: t.Any) -> t.Any: + key = next(iter(value)) + return {key[:-2]: value[key]} + + +class PassDict(JSONTag): + __slots__ = () + + def check(self, value: t.Any) -> bool: + return isinstance(value, dict) + + def to_json(self, value: t.Any) -> t.Any: + # JSON objects may only have string keys, so don't bother tagging the + # key here. + return {k: self.serializer.tag(v) for k, v in value.items()} + + tag = to_json + + +class TagTuple(JSONTag): + __slots__ = () + key = " t" + + def check(self, value: t.Any) -> bool: + return isinstance(value, tuple) + + def to_json(self, value: t.Any) -> t.Any: + return [self.serializer.tag(item) for item in value] + + def to_python(self, value: t.Any) -> t.Any: + return tuple(value) + + +class PassList(JSONTag): + __slots__ = () + + def check(self, value: t.Any) -> bool: + return isinstance(value, list) + + def to_json(self, value: t.Any) -> t.Any: + return [self.serializer.tag(item) for item in value] + + tag = to_json + + +class TagBytes(JSONTag): + __slots__ = () + key = " b" + + def check(self, value: t.Any) -> bool: + return isinstance(value, bytes) + + def to_json(self, value: t.Any) -> t.Any: + return b64encode(value).decode("ascii") + + def to_python(self, value: t.Any) -> t.Any: + return b64decode(value) + + +class TagMarkup(JSONTag): + """Serialize anything matching the :class:`~markupsafe.Markup` API by + having a ``__html__`` method to the result of that method. Always + deserializes to an instance of :class:`~markupsafe.Markup`.""" + + __slots__ = () + key = " m" + + def check(self, value: t.Any) -> bool: + return callable(getattr(value, "__html__", None)) + + def to_json(self, value: t.Any) -> t.Any: + return str(value.__html__()) + + def to_python(self, value: t.Any) -> t.Any: + return Markup(value) + + +class TagUUID(JSONTag): + __slots__ = () + key = " u" + + def check(self, value: t.Any) -> bool: + return isinstance(value, UUID) + + def to_json(self, value: t.Any) -> t.Any: + return value.hex + + def to_python(self, value: t.Any) -> t.Any: + return UUID(value) + + +class TagDateTime(JSONTag): + __slots__ = () + key = " d" + + def check(self, value: t.Any) -> bool: + return isinstance(value, datetime) + + def to_json(self, value: t.Any) -> t.Any: + return http_date(value) + + def to_python(self, value: t.Any) -> t.Any: + return parse_date(value) + + +class TaggedJSONSerializer: + """Serializer that uses a tag system to compactly represent objects that + are not JSON types. Passed as the intermediate serializer to + :class:`itsdangerous.Serializer`. + + The following extra types are supported: + + * :class:`dict` + * :class:`tuple` + * :class:`bytes` + * :class:`~markupsafe.Markup` + * :class:`~uuid.UUID` + * :class:`~datetime.datetime` + """ + + __slots__ = ("tags", "order") + + #: Tag classes to bind when creating the serializer. Other tags can be + #: added later using :meth:`~register`. + default_tags = [ + TagDict, + PassDict, + TagTuple, + PassList, + TagBytes, + TagMarkup, + TagUUID, + TagDateTime, + ] + + def __init__(self) -> None: + self.tags: dict[str, JSONTag] = {} + self.order: list[JSONTag] = [] + + for cls in self.default_tags: + self.register(cls) + + def register( + self, + tag_class: type[JSONTag], + force: bool = False, + index: int | None = None, + ) -> None: + """Register a new tag with this serializer. + + :param tag_class: tag class to register. Will be instantiated with this + serializer instance. + :param force: overwrite an existing tag. If false (default), a + :exc:`KeyError` is raised. + :param index: index to insert the new tag in the tag order. Useful when + the new tag is a special case of an existing tag. If ``None`` + (default), the tag is appended to the end of the order. + + :raise KeyError: if the tag key is already registered and ``force`` is + not true. + """ + tag = tag_class(self) + key = tag.key + + if key is not None: + if not force and key in self.tags: + raise KeyError(f"Tag '{key}' is already registered.") + + self.tags[key] = tag + + if index is None: + self.order.append(tag) + else: + self.order.insert(index, tag) + + def tag(self, value: t.Any) -> dict[str, t.Any]: + """Convert a value to a tagged representation if necessary.""" + for tag in self.order: + if tag.check(value): + return tag.tag(value) + + return value + + def untag(self, value: dict[str, t.Any]) -> t.Any: + """Convert a tagged representation back to the original type.""" + if len(value) != 1: + return value + + key = next(iter(value)) + + if key not in self.tags: + return value + + return self.tags[key].to_python(value[key]) + + def dumps(self, value: t.Any) -> str: + """Tag the value and dump it to a compact JSON string.""" + return dumps(self.tag(value), separators=(",", ":")) + + def loads(self, value: str) -> t.Any: + """Load data from a JSON string and deserialized any tagged objects.""" + return loads(value, object_hook=self.untag) diff --git a/.venv/lib/python3.9/site-packages/flask/logging.py b/.venv/lib/python3.9/site-packages/flask/logging.py new file mode 100644 index 0000000..99f6be8 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/flask/logging.py @@ -0,0 +1,76 @@ +from __future__ import annotations + +import logging +import sys +import typing as t + +from werkzeug.local import LocalProxy + +from .globals import request + +if t.TYPE_CHECKING: # pragma: no cover + from .app import Flask + + +@LocalProxy +def wsgi_errors_stream() -> t.TextIO: + """Find the most appropriate error stream for the application. If a request + is active, log to ``wsgi.errors``, otherwise use ``sys.stderr``. + + If you configure your own :class:`logging.StreamHandler`, you may want to + use this for the stream. If you are using file or dict configuration and + can't import this directly, you can refer to it as + ``ext://flask.logging.wsgi_errors_stream``. + """ + return request.environ["wsgi.errors"] if request else sys.stderr + + +def has_level_handler(logger: logging.Logger) -> bool: + """Check if there is a handler in the logging chain that will handle the + given logger's :meth:`effective level <~logging.Logger.getEffectiveLevel>`. + """ + level = logger.getEffectiveLevel() + current = logger + + while current: + if any(handler.level <= level for handler in current.handlers): + return True + + if not current.propagate: + break + + current = current.parent # type: ignore + + return False + + +#: Log messages to :func:`~flask.logging.wsgi_errors_stream` with the format +#: ``[%(asctime)s] %(levelname)s in %(module)s: %(message)s``. +default_handler = logging.StreamHandler(wsgi_errors_stream) # type: ignore +default_handler.setFormatter( + logging.Formatter("[%(asctime)s] %(levelname)s in %(module)s: %(message)s") +) + + +def create_logger(app: Flask) -> logging.Logger: + """Get the Flask app's logger and configure it if needed. + + The logger name will be the same as + :attr:`app.import_name `. + + When :attr:`~flask.Flask.debug` is enabled, set the logger level to + :data:`logging.DEBUG` if it is not set. + + If there is no handler for the logger's effective level, add a + :class:`~logging.StreamHandler` for + :func:`~flask.logging.wsgi_errors_stream` with a basic format. + """ + logger = logging.getLogger(app.name) + + if app.debug and not logger.level: + logger.setLevel(logging.DEBUG) + + if not has_level_handler(logger): + logger.addHandler(default_handler) + + return logger diff --git a/.venv/lib/python3.9/site-packages/flask/py.typed b/.venv/lib/python3.9/site-packages/flask/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/.venv/lib/python3.9/site-packages/flask/scaffold.py b/.venv/lib/python3.9/site-packages/flask/scaffold.py new file mode 100644 index 0000000..6af6906 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/flask/scaffold.py @@ -0,0 +1,923 @@ +from __future__ import annotations + +import importlib.util +import os +import pathlib +import pkgutil +import sys +import typing as t +from collections import defaultdict +from datetime import timedelta +from functools import update_wrapper + +from jinja2 import FileSystemLoader +from werkzeug.exceptions import default_exceptions +from werkzeug.exceptions import HTTPException +from werkzeug.utils import cached_property + +from . import typing as ft +from .cli import AppGroup +from .globals import current_app +from .helpers import get_root_path +from .helpers import send_from_directory +from .templating import _default_template_ctx_processor + +if t.TYPE_CHECKING: # pragma: no cover + from .wrappers import Response + +# a singleton sentinel value for parameter defaults +_sentinel = object() + +F = t.TypeVar("F", bound=t.Callable[..., t.Any]) +T_after_request = t.TypeVar("T_after_request", bound=ft.AfterRequestCallable) +T_before_request = t.TypeVar("T_before_request", bound=ft.BeforeRequestCallable) +T_error_handler = t.TypeVar("T_error_handler", bound=ft.ErrorHandlerCallable) +T_teardown = t.TypeVar("T_teardown", bound=ft.TeardownCallable) +T_template_context_processor = t.TypeVar( + "T_template_context_processor", bound=ft.TemplateContextProcessorCallable +) +T_url_defaults = t.TypeVar("T_url_defaults", bound=ft.URLDefaultCallable) +T_url_value_preprocessor = t.TypeVar( + "T_url_value_preprocessor", bound=ft.URLValuePreprocessorCallable +) +T_route = t.TypeVar("T_route", bound=ft.RouteCallable) + + +def setupmethod(f: F) -> F: + f_name = f.__name__ + + def wrapper_func(self, *args: t.Any, **kwargs: t.Any) -> t.Any: + self._check_setup_finished(f_name) + return f(self, *args, **kwargs) + + return t.cast(F, update_wrapper(wrapper_func, f)) + + +class Scaffold: + """Common behavior shared between :class:`~flask.Flask` and + :class:`~flask.blueprints.Blueprint`. + + :param import_name: The import name of the module where this object + is defined. Usually :attr:`__name__` should be used. + :param static_folder: Path to a folder of static files to serve. + If this is set, a static route will be added. + :param static_url_path: URL prefix for the static route. + :param template_folder: Path to a folder containing template files. + for rendering. If this is set, a Jinja loader will be added. + :param root_path: The path that static, template, and resource files + are relative to. Typically not set, it is discovered based on + the ``import_name``. + + .. versionadded:: 2.0 + """ + + name: str + _static_folder: str | None = None + _static_url_path: str | None = None + + def __init__( + self, + import_name: str, + static_folder: str | os.PathLike | None = None, + static_url_path: str | None = None, + template_folder: str | os.PathLike | None = None, + root_path: str | None = None, + ): + #: The name of the package or module that this object belongs + #: to. Do not change this once it is set by the constructor. + self.import_name = import_name + + self.static_folder = static_folder # type: ignore + self.static_url_path = static_url_path + + #: The path to the templates folder, relative to + #: :attr:`root_path`, to add to the template loader. ``None`` if + #: templates should not be added. + self.template_folder = template_folder + + if root_path is None: + root_path = get_root_path(self.import_name) + + #: Absolute path to the package on the filesystem. Used to look + #: up resources contained in the package. + self.root_path = root_path + + #: The Click command group for registering CLI commands for this + #: object. The commands are available from the ``flask`` command + #: once the application has been discovered and blueprints have + #: been registered. + self.cli = AppGroup() + + #: A dictionary mapping endpoint names to view functions. + #: + #: To register a view function, use the :meth:`route` decorator. + #: + #: This data structure is internal. It should not be modified + #: directly and its format may change at any time. + self.view_functions: dict[str, t.Callable] = {} + + #: A data structure of registered error handlers, in the format + #: ``{scope: {code: {class: handler}}}``. The ``scope`` key is + #: the name of a blueprint the handlers are active for, or + #: ``None`` for all requests. The ``code`` key is the HTTP + #: status code for ``HTTPException``, or ``None`` for + #: other exceptions. The innermost dictionary maps exception + #: classes to handler functions. + #: + #: To register an error handler, use the :meth:`errorhandler` + #: decorator. + #: + #: This data structure is internal. It should not be modified + #: directly and its format may change at any time. + self.error_handler_spec: dict[ + ft.AppOrBlueprintKey, + dict[int | None, dict[type[Exception], ft.ErrorHandlerCallable]], + ] = defaultdict(lambda: defaultdict(dict)) + + #: A data structure of functions to call at the beginning of + #: each request, in the format ``{scope: [functions]}``. The + #: ``scope`` key is the name of a blueprint the functions are + #: active for, or ``None`` for all requests. + #: + #: To register a function, use the :meth:`before_request` + #: decorator. + #: + #: This data structure is internal. It should not be modified + #: directly and its format may change at any time. + self.before_request_funcs: dict[ + ft.AppOrBlueprintKey, list[ft.BeforeRequestCallable] + ] = defaultdict(list) + + #: A data structure of functions to call at the end of each + #: request, in the format ``{scope: [functions]}``. The + #: ``scope`` key is the name of a blueprint the functions are + #: active for, or ``None`` for all requests. + #: + #: To register a function, use the :meth:`after_request` + #: decorator. + #: + #: This data structure is internal. It should not be modified + #: directly and its format may change at any time. + self.after_request_funcs: dict[ + ft.AppOrBlueprintKey, list[ft.AfterRequestCallable] + ] = defaultdict(list) + + #: A data structure of functions to call at the end of each + #: request even if an exception is raised, in the format + #: ``{scope: [functions]}``. The ``scope`` key is the name of a + #: blueprint the functions are active for, or ``None`` for all + #: requests. + #: + #: To register a function, use the :meth:`teardown_request` + #: decorator. + #: + #: This data structure is internal. It should not be modified + #: directly and its format may change at any time. + self.teardown_request_funcs: dict[ + ft.AppOrBlueprintKey, list[ft.TeardownCallable] + ] = defaultdict(list) + + #: A data structure of functions to call to pass extra context + #: values when rendering templates, in the format + #: ``{scope: [functions]}``. The ``scope`` key is the name of a + #: blueprint the functions are active for, or ``None`` for all + #: requests. + #: + #: To register a function, use the :meth:`context_processor` + #: decorator. + #: + #: This data structure is internal. It should not be modified + #: directly and its format may change at any time. + self.template_context_processors: dict[ + ft.AppOrBlueprintKey, list[ft.TemplateContextProcessorCallable] + ] = defaultdict(list, {None: [_default_template_ctx_processor]}) + + #: A data structure of functions to call to modify the keyword + #: arguments passed to the view function, in the format + #: ``{scope: [functions]}``. The ``scope`` key is the name of a + #: blueprint the functions are active for, or ``None`` for all + #: requests. + #: + #: To register a function, use the + #: :meth:`url_value_preprocessor` decorator. + #: + #: This data structure is internal. It should not be modified + #: directly and its format may change at any time. + self.url_value_preprocessors: dict[ + ft.AppOrBlueprintKey, + list[ft.URLValuePreprocessorCallable], + ] = defaultdict(list) + + #: A data structure of functions to call to modify the keyword + #: arguments when generating URLs, in the format + #: ``{scope: [functions]}``. The ``scope`` key is the name of a + #: blueprint the functions are active for, or ``None`` for all + #: requests. + #: + #: To register a function, use the :meth:`url_defaults` + #: decorator. + #: + #: This data structure is internal. It should not be modified + #: directly and its format may change at any time. + self.url_default_functions: dict[ + ft.AppOrBlueprintKey, list[ft.URLDefaultCallable] + ] = defaultdict(list) + + def __repr__(self) -> str: + return f"<{type(self).__name__} {self.name!r}>" + + def _check_setup_finished(self, f_name: str) -> None: + raise NotImplementedError + + @property + def static_folder(self) -> str | None: + """The absolute path to the configured static folder. ``None`` + if no static folder is set. + """ + if self._static_folder is not None: + return os.path.join(self.root_path, self._static_folder) + else: + return None + + @static_folder.setter + def static_folder(self, value: str | os.PathLike | None) -> None: + if value is not None: + value = os.fspath(value).rstrip(r"\/") + + self._static_folder = value + + @property + def has_static_folder(self) -> bool: + """``True`` if :attr:`static_folder` is set. + + .. versionadded:: 0.5 + """ + return self.static_folder is not None + + @property + def static_url_path(self) -> str | None: + """The URL prefix that the static route will be accessible from. + + If it was not configured during init, it is derived from + :attr:`static_folder`. + """ + if self._static_url_path is not None: + return self._static_url_path + + if self.static_folder is not None: + basename = os.path.basename(self.static_folder) + return f"/{basename}".rstrip("/") + + return None + + @static_url_path.setter + def static_url_path(self, value: str | None) -> None: + if value is not None: + value = value.rstrip("/") + + self._static_url_path = value + + def get_send_file_max_age(self, filename: str | None) -> int | None: + """Used by :func:`send_file` to determine the ``max_age`` cache + value for a given file path if it wasn't passed. + + By default, this returns :data:`SEND_FILE_MAX_AGE_DEFAULT` from + the configuration of :data:`~flask.current_app`. This defaults + to ``None``, which tells the browser to use conditional requests + instead of a timed cache, which is usually preferable. + + .. versionchanged:: 2.0 + The default configuration is ``None`` instead of 12 hours. + + .. versionadded:: 0.9 + """ + value = current_app.config["SEND_FILE_MAX_AGE_DEFAULT"] + + if value is None: + return None + + if isinstance(value, timedelta): + return int(value.total_seconds()) + + return value + + def send_static_file(self, filename: str) -> Response: + """The view function used to serve files from + :attr:`static_folder`. A route is automatically registered for + this view at :attr:`static_url_path` if :attr:`static_folder` is + set. + + .. versionadded:: 0.5 + """ + if not self.has_static_folder: + raise RuntimeError("'static_folder' must be set to serve static_files.") + + # send_file only knows to call get_send_file_max_age on the app, + # call it here so it works for blueprints too. + max_age = self.get_send_file_max_age(filename) + return send_from_directory( + t.cast(str, self.static_folder), filename, max_age=max_age + ) + + @cached_property + def jinja_loader(self) -> FileSystemLoader | None: + """The Jinja loader for this object's templates. By default this + is a class :class:`jinja2.loaders.FileSystemLoader` to + :attr:`template_folder` if it is set. + + .. versionadded:: 0.5 + """ + if self.template_folder is not None: + return FileSystemLoader(os.path.join(self.root_path, self.template_folder)) + else: + return None + + def open_resource(self, resource: str, mode: str = "rb") -> t.IO[t.AnyStr]: + """Open a resource file relative to :attr:`root_path` for + reading. + + For example, if the file ``schema.sql`` is next to the file + ``app.py`` where the ``Flask`` app is defined, it can be opened + with: + + .. code-block:: python + + with app.open_resource("schema.sql") as f: + conn.executescript(f.read()) + + :param resource: Path to the resource relative to + :attr:`root_path`. + :param mode: Open the file in this mode. Only reading is + supported, valid values are "r" (or "rt") and "rb". + """ + if mode not in {"r", "rt", "rb"}: + raise ValueError("Resources can only be opened for reading.") + + return open(os.path.join(self.root_path, resource), mode) + + def _method_route( + self, + method: str, + rule: str, + options: dict, + ) -> t.Callable[[T_route], T_route]: + if "methods" in options: + raise TypeError("Use the 'route' decorator to use the 'methods' argument.") + + return self.route(rule, methods=[method], **options) + + @setupmethod + def get(self, rule: str, **options: t.Any) -> t.Callable[[T_route], T_route]: + """Shortcut for :meth:`route` with ``methods=["GET"]``. + + .. versionadded:: 2.0 + """ + return self._method_route("GET", rule, options) + + @setupmethod + def post(self, rule: str, **options: t.Any) -> t.Callable[[T_route], T_route]: + """Shortcut for :meth:`route` with ``methods=["POST"]``. + + .. versionadded:: 2.0 + """ + return self._method_route("POST", rule, options) + + @setupmethod + def put(self, rule: str, **options: t.Any) -> t.Callable[[T_route], T_route]: + """Shortcut for :meth:`route` with ``methods=["PUT"]``. + + .. versionadded:: 2.0 + """ + return self._method_route("PUT", rule, options) + + @setupmethod + def delete(self, rule: str, **options: t.Any) -> t.Callable[[T_route], T_route]: + """Shortcut for :meth:`route` with ``methods=["DELETE"]``. + + .. versionadded:: 2.0 + """ + return self._method_route("DELETE", rule, options) + + @setupmethod + def patch(self, rule: str, **options: t.Any) -> t.Callable[[T_route], T_route]: + """Shortcut for :meth:`route` with ``methods=["PATCH"]``. + + .. versionadded:: 2.0 + """ + return self._method_route("PATCH", rule, options) + + @setupmethod + def route(self, rule: str, **options: t.Any) -> t.Callable[[T_route], T_route]: + """Decorate a view function to register it with the given URL + rule and options. Calls :meth:`add_url_rule`, which has more + details about the implementation. + + .. code-block:: python + + @app.route("/") + def index(): + return "Hello, World!" + + See :ref:`url-route-registrations`. + + The endpoint name for the route defaults to the name of the view + function if the ``endpoint`` parameter isn't passed. + + The ``methods`` parameter defaults to ``["GET"]``. ``HEAD`` and + ``OPTIONS`` are added automatically. + + :param rule: The URL rule string. + :param options: Extra options passed to the + :class:`~werkzeug.routing.Rule` object. + """ + + def decorator(f: T_route) -> T_route: + endpoint = options.pop("endpoint", None) + self.add_url_rule(rule, endpoint, f, **options) + return f + + return decorator + + @setupmethod + def add_url_rule( + self, + rule: str, + endpoint: str | None = None, + view_func: ft.RouteCallable | None = None, + provide_automatic_options: bool | None = None, + **options: t.Any, + ) -> None: + """Register a rule for routing incoming requests and building + URLs. The :meth:`route` decorator is a shortcut to call this + with the ``view_func`` argument. These are equivalent: + + .. code-block:: python + + @app.route("/") + def index(): + ... + + .. code-block:: python + + def index(): + ... + + app.add_url_rule("/", view_func=index) + + See :ref:`url-route-registrations`. + + The endpoint name for the route defaults to the name of the view + function if the ``endpoint`` parameter isn't passed. An error + will be raised if a function has already been registered for the + endpoint. + + The ``methods`` parameter defaults to ``["GET"]``. ``HEAD`` is + always added automatically, and ``OPTIONS`` is added + automatically by default. + + ``view_func`` does not necessarily need to be passed, but if the + rule should participate in routing an endpoint name must be + associated with a view function at some point with the + :meth:`endpoint` decorator. + + .. code-block:: python + + app.add_url_rule("/", endpoint="index") + + @app.endpoint("index") + def index(): + ... + + If ``view_func`` has a ``required_methods`` attribute, those + methods are added to the passed and automatic methods. If it + has a ``provide_automatic_methods`` attribute, it is used as the + default if the parameter is not passed. + + :param rule: The URL rule string. + :param endpoint: The endpoint name to associate with the rule + and view function. Used when routing and building URLs. + Defaults to ``view_func.__name__``. + :param view_func: The view function to associate with the + endpoint name. + :param provide_automatic_options: Add the ``OPTIONS`` method and + respond to ``OPTIONS`` requests automatically. + :param options: Extra options passed to the + :class:`~werkzeug.routing.Rule` object. + """ + raise NotImplementedError + + @setupmethod + def endpoint(self, endpoint: str) -> t.Callable[[F], F]: + """Decorate a view function to register it for the given + endpoint. Used if a rule is added without a ``view_func`` with + :meth:`add_url_rule`. + + .. code-block:: python + + app.add_url_rule("/ex", endpoint="example") + + @app.endpoint("example") + def example(): + ... + + :param endpoint: The endpoint name to associate with the view + function. + """ + + def decorator(f: F) -> F: + self.view_functions[endpoint] = f + return f + + return decorator + + @setupmethod + def before_request(self, f: T_before_request) -> T_before_request: + """Register a function to run before each request. + + For example, this can be used to open a database connection, or + to load the logged in user from the session. + + .. code-block:: python + + @app.before_request + def load_user(): + if "user_id" in session: + g.user = db.session.get(session["user_id"]) + + The function will be called without any arguments. If it 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. + + This is available on both app and blueprint objects. When used on an app, this + executes before every request. When used on a blueprint, this executes before + every request that the blueprint handles. To register with a blueprint and + execute before every request, use :meth:`.Blueprint.before_app_request`. + """ + self.before_request_funcs.setdefault(None, []).append(f) + return f + + @setupmethod + def after_request(self, f: T_after_request) -> T_after_request: + """Register a function to run after each request to this object. + + The function is called with the response object, and must return + a response object. This allows the functions to modify or + replace the response before it is sent. + + If a function raises an exception, any remaining + ``after_request`` functions will not be called. Therefore, this + should not be used for actions that must execute, such as to + close resources. Use :meth:`teardown_request` for that. + + This is available on both app and blueprint objects. When used on an app, this + executes after every request. When used on a blueprint, this executes after + every request that the blueprint handles. To register with a blueprint and + execute after every request, use :meth:`.Blueprint.after_app_request`. + """ + self.after_request_funcs.setdefault(None, []).append(f) + return f + + @setupmethod + def teardown_request(self, f: T_teardown) -> T_teardown: + """Register a function to be called when the request context is + popped. Typically this happens at the end of each request, but + contexts may be pushed manually as well during testing. + + .. code-block:: python + + with app.test_request_context(): + ... + + When the ``with`` block exits (or ``ctx.pop()`` is called), the + teardown functions are called just before the request context is + made inactive. + + 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. + + Teardown functions must avoid raising exceptions. If they + execute code that might fail they must surround that code with a + ``try``/``except`` block and log any errors. + + The return values of teardown functions are ignored. + + This is available on both app and blueprint objects. When used on an app, this + executes after every request. When used on a blueprint, this executes after + every request that the blueprint handles. To register with a blueprint and + execute after every request, use :meth:`.Blueprint.teardown_app_request`. + """ + self.teardown_request_funcs.setdefault(None, []).append(f) + return f + + @setupmethod + def context_processor( + self, + f: T_template_context_processor, + ) -> T_template_context_processor: + """Registers a template context processor function. These functions run before + rendering a template. The keys of the returned dict are added as variables + available in the template. + + This is available on both app and blueprint objects. When used on an app, this + is called for every rendered template. When used on a blueprint, this is called + for templates rendered from the blueprint's views. To register with a blueprint + and affect every template, use :meth:`.Blueprint.app_context_processor`. + """ + self.template_context_processors[None].append(f) + return f + + @setupmethod + def url_value_preprocessor( + self, + f: T_url_value_preprocessor, + ) -> T_url_value_preprocessor: + """Register a URL value preprocessor function for all view + functions in the application. These functions will be called before the + :meth:`before_request` functions. + + The function can modify the values captured from the matched url before + they are passed to the view. For example, this can be used to pop a + common language code value and place it in ``g`` rather than pass it to + every view. + + The function is passed the endpoint name and values dict. The return + value is ignored. + + This is available on both app and blueprint objects. When used on an app, this + is called for every request. When used on a blueprint, this is called for + requests that the blueprint handles. To register with a blueprint and affect + every request, use :meth:`.Blueprint.app_url_value_preprocessor`. + """ + self.url_value_preprocessors[None].append(f) + return f + + @setupmethod + def url_defaults(self, f: T_url_defaults) -> T_url_defaults: + """Callback function for URL defaults for all view functions of the + application. It's called with the endpoint and values and should + update the values passed in place. + + This is available on both app and blueprint objects. When used on an app, this + is called for every request. When used on a blueprint, this is called for + requests that the blueprint handles. To register with a blueprint and affect + every request, use :meth:`.Blueprint.app_url_defaults`. + """ + self.url_default_functions[None].append(f) + return f + + @setupmethod + def errorhandler( + self, code_or_exception: type[Exception] | int + ) -> t.Callable[[T_error_handler], T_error_handler]: + """Register a function to handle errors by code or exception class. + + A decorator that is used to register a function given an + error code. Example:: + + @app.errorhandler(404) + def page_not_found(error): + return 'This page does not exist', 404 + + You can also register handlers for arbitrary exceptions:: + + @app.errorhandler(DatabaseError) + def special_exception_handler(error): + return 'Database connection failed', 500 + + This is available on both app and blueprint objects. When used on an app, this + can handle errors from every request. When used on a blueprint, this can handle + errors from requests that the blueprint handles. To register with a blueprint + and affect every request, use :meth:`.Blueprint.app_errorhandler`. + + .. versionadded:: 0.7 + Use :meth:`register_error_handler` instead of modifying + :attr:`error_handler_spec` directly, for application wide error + handlers. + + .. versionadded:: 0.7 + One can now additionally also register custom exception types + that do not necessarily have to be a subclass of the + :class:`~werkzeug.exceptions.HTTPException` class. + + :param code_or_exception: the code as integer for the handler, or + an arbitrary exception + """ + + def decorator(f: T_error_handler) -> T_error_handler: + self.register_error_handler(code_or_exception, f) + return f + + return decorator + + @setupmethod + def register_error_handler( + self, + code_or_exception: type[Exception] | int, + f: ft.ErrorHandlerCallable, + ) -> None: + """Alternative error attach function to the :meth:`errorhandler` + decorator that is more straightforward to use for non decorator + usage. + + .. versionadded:: 0.7 + """ + exc_class, code = self._get_exc_class_and_code(code_or_exception) + self.error_handler_spec[None][code][exc_class] = f + + @staticmethod + def _get_exc_class_and_code( + exc_class_or_code: type[Exception] | int, + ) -> tuple[type[Exception], int | None]: + """Get the exception class being handled. For HTTP status codes + or ``HTTPException`` subclasses, return both the exception and + status code. + + :param exc_class_or_code: Any exception class, or an HTTP status + code as an integer. + """ + exc_class: type[Exception] + + if isinstance(exc_class_or_code, int): + try: + exc_class = default_exceptions[exc_class_or_code] + except KeyError: + raise ValueError( + f"'{exc_class_or_code}' is not a recognized HTTP" + " error code. Use a subclass of HTTPException with" + " that code instead." + ) from None + else: + exc_class = exc_class_or_code + + if isinstance(exc_class, Exception): + raise TypeError( + f"{exc_class!r} is an instance, not a class. Handlers" + " can only be registered for Exception classes or HTTP" + " error codes." + ) + + if not issubclass(exc_class, Exception): + raise ValueError( + f"'{exc_class.__name__}' is not a subclass of Exception." + " Handlers can only be registered for Exception classes" + " or HTTP error codes." + ) + + if issubclass(exc_class, HTTPException): + return exc_class, exc_class.code + else: + return exc_class, None + + +def _endpoint_from_view_func(view_func: t.Callable) -> str: + """Internal helper that returns the default endpoint for a given + function. This always is the function name. + """ + assert view_func is not None, "expected view func if endpoint is not provided." + return view_func.__name__ + + +def _matching_loader_thinks_module_is_package(loader, mod_name): + """Attempt to figure out if the given name is a package or a module. + + :param: loader: The loader that handled the name. + :param mod_name: The name of the package or module. + """ + # Use loader.is_package if it's available. + if hasattr(loader, "is_package"): + return loader.is_package(mod_name) + + cls = type(loader) + + # NamespaceLoader doesn't implement is_package, but all names it + # loads must be packages. + if cls.__module__ == "_frozen_importlib" and cls.__name__ == "NamespaceLoader": + return True + + # Otherwise we need to fail with an error that explains what went + # wrong. + raise AttributeError( + f"'{cls.__name__}.is_package()' must be implemented for PEP 302" + f" import hooks." + ) + + +def _path_is_relative_to(path: pathlib.PurePath, base: str) -> bool: + # Path.is_relative_to doesn't exist until Python 3.9 + try: + path.relative_to(base) + return True + except ValueError: + return False + + +def _find_package_path(import_name): + """Find the path that contains the package or module.""" + root_mod_name, _, _ = import_name.partition(".") + + try: + root_spec = importlib.util.find_spec(root_mod_name) + + if root_spec is None: + raise ValueError("not found") + # ImportError: the machinery told us it does not exist + # ValueError: + # - the module name was invalid + # - the module name is __main__ + # - *we* raised `ValueError` due to `root_spec` being `None` + except (ImportError, ValueError): + pass # handled below + else: + # namespace package + if root_spec.origin in {"namespace", None}: + package_spec = importlib.util.find_spec(import_name) + if package_spec is not None and package_spec.submodule_search_locations: + # Pick the path in the namespace that contains the submodule. + package_path = pathlib.Path( + os.path.commonpath(package_spec.submodule_search_locations) + ) + search_locations = ( + location + for location in root_spec.submodule_search_locations + if _path_is_relative_to(package_path, location) + ) + else: + # Pick the first path. + search_locations = iter(root_spec.submodule_search_locations) + return os.path.dirname(next(search_locations)) + # a package (with __init__.py) + elif root_spec.submodule_search_locations: + return os.path.dirname(os.path.dirname(root_spec.origin)) + # just a normal module + else: + return os.path.dirname(root_spec.origin) + + # we were unable to find the `package_path` using PEP 451 loaders + loader = pkgutil.get_loader(root_mod_name) + + if loader is None or root_mod_name == "__main__": + # import name is not found, or interactive/main module + return os.getcwd() + + if hasattr(loader, "get_filename"): + filename = loader.get_filename(root_mod_name) + elif hasattr(loader, "archive"): + # zipimporter's loader.archive points to the .egg or .zip file. + filename = loader.archive + else: + # At least one loader is missing both get_filename and archive: + # Google App Engine's HardenedModulesHook, use __file__. + filename = importlib.import_module(root_mod_name).__file__ + + package_path = os.path.abspath(os.path.dirname(filename)) + + # If the imported name is a package, filename is currently pointing + # to the root of the package, need to get the current directory. + if _matching_loader_thinks_module_is_package(loader, root_mod_name): + package_path = os.path.dirname(package_path) + + return package_path + + +def find_package(import_name: str): + """Find the prefix that a package is installed under, and the path + that it would be imported from. + + The prefix is the directory containing the standard directory + hierarchy (lib, bin, etc.). If the package is not installed to the + system (:attr:`sys.prefix`) or a virtualenv (``site-packages``), + ``None`` is returned. + + The path is the entry in :attr:`sys.path` that contains the package + for import. If the package is not installed, it's assumed that the + package was imported from the current working directory. + """ + package_path = _find_package_path(import_name) + py_prefix = os.path.abspath(sys.prefix) + + # installed to the system + if _path_is_relative_to(pathlib.PurePath(package_path), py_prefix): + return py_prefix, package_path + + site_parent, site_folder = os.path.split(package_path) + + # installed to a virtualenv + if site_folder.lower() == "site-packages": + parent, folder = os.path.split(site_parent) + + # Windows (prefix/lib/site-packages) + if folder.lower() == "lib": + return parent, package_path + + # Unix (prefix/lib/pythonX.Y/site-packages) + if os.path.basename(parent).lower() == "lib": + return os.path.dirname(parent), package_path + + # something else (prefix/site-packages) + return site_parent, package_path + + # not installed + return None, package_path diff --git a/.venv/lib/python3.9/site-packages/flask/sessions.py b/.venv/lib/python3.9/site-packages/flask/sessions.py new file mode 100644 index 0000000..e5650d6 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/flask/sessions.py @@ -0,0 +1,367 @@ +from __future__ import annotations + +import hashlib +import typing as t +from collections.abc import MutableMapping +from datetime import datetime +from datetime import timezone + +from itsdangerous import BadSignature +from itsdangerous import URLSafeTimedSerializer +from werkzeug.datastructures import CallbackDict + +from .json.tag import TaggedJSONSerializer + +if t.TYPE_CHECKING: # pragma: no cover + from .app import Flask + from .wrappers import Request, Response + + +class SessionMixin(MutableMapping): + """Expands a basic dictionary with session attributes.""" + + @property + def permanent(self) -> bool: + """This reflects the ``'_permanent'`` key in the dict.""" + return self.get("_permanent", False) + + @permanent.setter + def permanent(self, value: bool) -> None: + self["_permanent"] = bool(value) + + #: Some implementations can detect whether a session is newly + #: created, but that is not guaranteed. Use with caution. The mixin + # default is hard-coded ``False``. + new = False + + #: Some implementations can detect changes to the session and set + #: this when that happens. The mixin default is hard coded to + #: ``True``. + modified = True + + #: Some implementations can detect when session data is read or + #: written and set this when that happens. The mixin default is hard + #: coded to ``True``. + accessed = True + + +class SecureCookieSession(CallbackDict, SessionMixin): + """Base class for sessions based on signed cookies. + + This session backend will set the :attr:`modified` and + :attr:`accessed` attributes. It cannot reliably track whether a + session is new (vs. empty), so :attr:`new` remains hard coded to + ``False``. + """ + + #: When data is changed, this is set to ``True``. Only the session + #: dictionary itself is tracked; if the session contains mutable + #: data (for example a nested dict) then this must be set to + #: ``True`` manually when modifying that data. The session cookie + #: will only be written to the response if this is ``True``. + modified = False + + #: When data is read or written, this is set to ``True``. Used by + # :class:`.SecureCookieSessionInterface` to add a ``Vary: Cookie`` + #: header, which allows caching proxies to cache different pages for + #: different users. + accessed = False + + def __init__(self, initial: t.Any = None) -> None: + def on_update(self) -> None: + self.modified = True + self.accessed = True + + super().__init__(initial, on_update) + + def __getitem__(self, key: str) -> t.Any: + self.accessed = True + return super().__getitem__(key) + + def get(self, key: str, default: t.Any = None) -> t.Any: + self.accessed = True + return super().get(key, default) + + def setdefault(self, key: str, default: t.Any = None) -> t.Any: + self.accessed = True + return super().setdefault(key, default) + + +class NullSession(SecureCookieSession): + """Class used to generate nicer error messages if sessions are not + available. Will still allow read-only access to the empty session + but fail on setting. + """ + + def _fail(self, *args: t.Any, **kwargs: t.Any) -> t.NoReturn: + raise RuntimeError( + "The session is unavailable because no secret " + "key was set. Set the secret_key on the " + "application to something unique and secret." + ) + + __setitem__ = __delitem__ = clear = pop = popitem = update = setdefault = _fail # type: ignore # noqa: B950 + del _fail + + +class SessionInterface: + """The basic interface you have to implement in order to replace the + default session interface which uses werkzeug's securecookie + implementation. The only methods you have to implement are + :meth:`open_session` and :meth:`save_session`, the others have + useful defaults which you don't need to change. + + The session object returned by the :meth:`open_session` method has to + provide a dictionary like interface plus the properties and methods + from the :class:`SessionMixin`. We recommend just subclassing a dict + and adding that mixin:: + + class Session(dict, SessionMixin): + pass + + If :meth:`open_session` returns ``None`` Flask will call into + :meth:`make_null_session` to create a session that acts as replacement + if the session support cannot work because some requirement is not + fulfilled. The default :class:`NullSession` class that is created + will complain that the secret key was not set. + + To replace the session interface on an application all you have to do + is to assign :attr:`flask.Flask.session_interface`:: + + app = Flask(__name__) + app.session_interface = MySessionInterface() + + Multiple requests with the same session may be sent and handled + concurrently. When implementing a new session interface, consider + whether reads or writes to the backing store must be synchronized. + There is no guarantee on the order in which the session for each + request is opened or saved, it will occur in the order that requests + begin and end processing. + + .. versionadded:: 0.8 + """ + + #: :meth:`make_null_session` will look here for the class that should + #: be created when a null session is requested. Likewise the + #: :meth:`is_null_session` method will perform a typecheck against + #: this type. + null_session_class = NullSession + + #: A flag that indicates if the session interface is pickle based. + #: This can be used by Flask extensions to make a decision in regards + #: to how to deal with the session object. + #: + #: .. versionadded:: 0.10 + pickle_based = False + + def make_null_session(self, app: Flask) -> NullSession: + """Creates a null session which acts as a replacement object if the + real session support could not be loaded due to a configuration + error. This mainly aids the user experience because the job of the + null session is to still support lookup without complaining but + modifications are answered with a helpful error message of what + failed. + + This creates an instance of :attr:`null_session_class` by default. + """ + return self.null_session_class() + + def is_null_session(self, obj: object) -> bool: + """Checks if a given object is a null session. Null sessions are + not asked to be saved. + + This checks if the object is an instance of :attr:`null_session_class` + by default. + """ + return isinstance(obj, self.null_session_class) + + def get_cookie_name(self, app: Flask) -> str: + """The name of the session cookie. Uses``app.config["SESSION_COOKIE_NAME"]``.""" + return app.config["SESSION_COOKIE_NAME"] + + def get_cookie_domain(self, app: Flask) -> str | None: + """The value of the ``Domain`` parameter on the session cookie. If not set, + browsers will only send the cookie to the exact domain it was set from. + Otherwise, they will send it to any subdomain of the given value as well. + + Uses the :data:`SESSION_COOKIE_DOMAIN` config. + + .. versionchanged:: 2.3 + Not set by default, does not fall back to ``SERVER_NAME``. + """ + rv = app.config["SESSION_COOKIE_DOMAIN"] + return rv if rv else None + + def get_cookie_path(self, app: Flask) -> str: + """Returns the path for which the cookie should be valid. The + default implementation uses the value from the ``SESSION_COOKIE_PATH`` + config var if it's set, and falls back to ``APPLICATION_ROOT`` or + uses ``/`` if it's ``None``. + """ + return app.config["SESSION_COOKIE_PATH"] or app.config["APPLICATION_ROOT"] + + def get_cookie_httponly(self, app: Flask) -> bool: + """Returns True if the session cookie should be httponly. This + currently just returns the value of the ``SESSION_COOKIE_HTTPONLY`` + config var. + """ + return app.config["SESSION_COOKIE_HTTPONLY"] + + def get_cookie_secure(self, app: Flask) -> bool: + """Returns True if the cookie should be secure. This currently + just returns the value of the ``SESSION_COOKIE_SECURE`` setting. + """ + return app.config["SESSION_COOKIE_SECURE"] + + def get_cookie_samesite(self, app: Flask) -> str: + """Return ``'Strict'`` or ``'Lax'`` if the cookie should use the + ``SameSite`` attribute. This currently just returns the value of + the :data:`SESSION_COOKIE_SAMESITE` setting. + """ + return app.config["SESSION_COOKIE_SAMESITE"] + + def get_expiration_time(self, app: Flask, session: SessionMixin) -> datetime | None: + """A helper method that returns an expiration date for the session + or ``None`` if the session is linked to the browser session. The + default implementation returns now + the permanent session + lifetime configured on the application. + """ + if session.permanent: + return datetime.now(timezone.utc) + app.permanent_session_lifetime + return None + + def should_set_cookie(self, app: Flask, session: SessionMixin) -> bool: + """Used by session backends to determine if a ``Set-Cookie`` header + should be set for this session cookie for this response. If the session + has been modified, the cookie is set. If the session is permanent and + the ``SESSION_REFRESH_EACH_REQUEST`` config is true, the cookie is + always set. + + This check is usually skipped if the session was deleted. + + .. versionadded:: 0.11 + """ + + return session.modified or ( + session.permanent and app.config["SESSION_REFRESH_EACH_REQUEST"] + ) + + def open_session(self, app: Flask, request: Request) -> SessionMixin | None: + """This is called at the beginning of each request, after + pushing the request context, before matching the URL. + + This must return an object which implements a dictionary-like + interface as well as the :class:`SessionMixin` interface. + + This will return ``None`` to indicate that loading failed in + some way that is not immediately an error. The request + context will fall back to using :meth:`make_null_session` + in this case. + """ + raise NotImplementedError() + + def save_session( + self, app: Flask, session: SessionMixin, response: Response + ) -> None: + """This is called at the end of each request, after generating + a response, before removing the request context. It is skipped + if :meth:`is_null_session` returns ``True``. + """ + raise NotImplementedError() + + +session_json_serializer = TaggedJSONSerializer() + + +class SecureCookieSessionInterface(SessionInterface): + """The default session interface that stores sessions in signed cookies + through the :mod:`itsdangerous` module. + """ + + #: the salt that should be applied on top of the secret key for the + #: signing of cookie based sessions. + salt = "cookie-session" + #: the hash function to use for the signature. The default is sha1 + digest_method = staticmethod(hashlib.sha1) + #: the name of the itsdangerous supported key derivation. The default + #: is hmac. + key_derivation = "hmac" + #: A python serializer for the payload. The default is a compact + #: JSON derived serializer with support for some extra Python types + #: such as datetime objects or tuples. + serializer = session_json_serializer + session_class = SecureCookieSession + + def get_signing_serializer(self, app: Flask) -> URLSafeTimedSerializer | None: + if not app.secret_key: + return None + signer_kwargs = dict( + key_derivation=self.key_derivation, digest_method=self.digest_method + ) + return URLSafeTimedSerializer( + app.secret_key, + salt=self.salt, + serializer=self.serializer, + signer_kwargs=signer_kwargs, + ) + + def open_session(self, app: Flask, request: Request) -> SecureCookieSession | None: + s = self.get_signing_serializer(app) + if s is None: + return None + val = request.cookies.get(self.get_cookie_name(app)) + if not val: + return self.session_class() + max_age = int(app.permanent_session_lifetime.total_seconds()) + try: + data = s.loads(val, max_age=max_age) + return self.session_class(data) + except BadSignature: + return self.session_class() + + def save_session( + self, app: Flask, session: SessionMixin, response: Response + ) -> None: + name = self.get_cookie_name(app) + domain = self.get_cookie_domain(app) + path = self.get_cookie_path(app) + secure = self.get_cookie_secure(app) + samesite = self.get_cookie_samesite(app) + httponly = self.get_cookie_httponly(app) + + # Add a "Vary: Cookie" header if the session was accessed at all. + if session.accessed: + response.vary.add("Cookie") + + # If the session is modified to be empty, remove the cookie. + # If the session is empty, return without setting the cookie. + if not session: + if session.modified: + response.delete_cookie( + name, + domain=domain, + path=path, + secure=secure, + samesite=samesite, + httponly=httponly, + ) + response.vary.add("Cookie") + + return + + if not self.should_set_cookie(app, session): + return + + expires = self.get_expiration_time(app, session) + val = self.get_signing_serializer(app).dumps(dict(session)) # type: ignore + response.set_cookie( + name, + val, # type: ignore + expires=expires, + httponly=httponly, + domain=domain, + path=path, + secure=secure, + samesite=samesite, + ) + response.vary.add("Cookie") diff --git a/.venv/lib/python3.9/site-packages/flask/signals.py b/.venv/lib/python3.9/site-packages/flask/signals.py new file mode 100644 index 0000000..d79f21f --- /dev/null +++ b/.venv/lib/python3.9/site-packages/flask/signals.py @@ -0,0 +1,33 @@ +from __future__ import annotations + +import typing as t +import warnings + +from blinker import Namespace + +# This namespace is only for signals provided by Flask itself. +_signals = Namespace() + +template_rendered = _signals.signal("template-rendered") +before_render_template = _signals.signal("before-render-template") +request_started = _signals.signal("request-started") +request_finished = _signals.signal("request-finished") +request_tearing_down = _signals.signal("request-tearing-down") +got_request_exception = _signals.signal("got-request-exception") +appcontext_tearing_down = _signals.signal("appcontext-tearing-down") +appcontext_pushed = _signals.signal("appcontext-pushed") +appcontext_popped = _signals.signal("appcontext-popped") +message_flashed = _signals.signal("message-flashed") + + +def __getattr__(name: str) -> t.Any: + if name == "signals_available": + warnings.warn( + "The 'signals_available' attribute is deprecated and will be removed in" + " Flask 2.4. Signals are always available.", + DeprecationWarning, + stacklevel=2, + ) + return True + + raise AttributeError(name) diff --git a/.venv/lib/python3.9/site-packages/flask/templating.py b/.venv/lib/python3.9/site-packages/flask/templating.py new file mode 100644 index 0000000..769108f --- /dev/null +++ b/.venv/lib/python3.9/site-packages/flask/templating.py @@ -0,0 +1,220 @@ +from __future__ import annotations + +import typing as t + +from jinja2 import BaseLoader +from jinja2 import Environment as BaseEnvironment +from jinja2 import Template +from jinja2 import TemplateNotFound + +from .globals import _cv_app +from .globals import _cv_request +from .globals import current_app +from .globals import request +from .helpers import stream_with_context +from .signals import before_render_template +from .signals import template_rendered + +if t.TYPE_CHECKING: # pragma: no cover + from .app import Flask + from .scaffold import Scaffold + + +def _default_template_ctx_processor() -> dict[str, t.Any]: + """Default template context processor. Injects `request`, + `session` and `g`. + """ + appctx = _cv_app.get(None) + reqctx = _cv_request.get(None) + rv: dict[str, t.Any] = {} + if appctx is not None: + rv["g"] = appctx.g + if reqctx is not None: + rv["request"] = reqctx.request + rv["session"] = reqctx.session + return rv + + +class Environment(BaseEnvironment): + """Works like a regular Jinja2 environment but has some additional + knowledge of how Flask's blueprint works so that it can prepend the + name of the blueprint to referenced templates if necessary. + """ + + def __init__(self, app: Flask, **options: t.Any) -> None: + if "loader" not in options: + options["loader"] = app.create_global_jinja_loader() + BaseEnvironment.__init__(self, **options) + self.app = app + + +class DispatchingJinjaLoader(BaseLoader): + """A loader that looks for templates in the application and all + the blueprint folders. + """ + + def __init__(self, app: Flask) -> None: + self.app = app + + def get_source( # type: ignore + self, environment: Environment, template: str + ) -> tuple[str, str | None, t.Callable | None]: + if self.app.config["EXPLAIN_TEMPLATE_LOADING"]: + return self._get_source_explained(environment, template) + return self._get_source_fast(environment, template) + + def _get_source_explained( + self, environment: Environment, template: str + ) -> tuple[str, str | None, t.Callable | None]: + attempts = [] + rv: tuple[str, str | None, t.Callable[[], bool] | None] | None + trv: None | (tuple[str, str | None, t.Callable[[], bool] | None]) = None + + for srcobj, loader in self._iter_loaders(template): + try: + rv = loader.get_source(environment, template) + if trv is None: + trv = rv + except TemplateNotFound: + rv = None + attempts.append((loader, srcobj, rv)) + + from .debughelpers import explain_template_loading_attempts + + explain_template_loading_attempts(self.app, template, attempts) + + if trv is not None: + return trv + raise TemplateNotFound(template) + + def _get_source_fast( + self, environment: Environment, template: str + ) -> tuple[str, str | None, t.Callable | None]: + for _srcobj, loader in self._iter_loaders(template): + try: + return loader.get_source(environment, template) + except TemplateNotFound: + continue + raise TemplateNotFound(template) + + def _iter_loaders( + self, template: str + ) -> t.Generator[tuple[Scaffold, BaseLoader], None, None]: + loader = self.app.jinja_loader + if loader is not None: + yield self.app, loader + + for blueprint in self.app.iter_blueprints(): + loader = blueprint.jinja_loader + if loader is not None: + yield blueprint, loader + + def list_templates(self) -> list[str]: + result = set() + loader = self.app.jinja_loader + if loader is not None: + result.update(loader.list_templates()) + + for blueprint in self.app.iter_blueprints(): + loader = blueprint.jinja_loader + if loader is not None: + for template in loader.list_templates(): + result.add(template) + + return list(result) + + +def _render(app: Flask, template: Template, context: dict[str, t.Any]) -> str: + app.update_template_context(context) + before_render_template.send( + app, _async_wrapper=app.ensure_sync, template=template, context=context + ) + rv = template.render(context) + template_rendered.send( + app, _async_wrapper=app.ensure_sync, template=template, context=context + ) + return rv + + +def render_template( + template_name_or_list: str | Template | list[str | Template], + **context: t.Any, +) -> str: + """Render a template by name with the given context. + + :param template_name_or_list: The name of the template to render. If + a list is given, the first name to exist will be rendered. + :param context: The variables to make available in the template. + """ + app = current_app._get_current_object() # type: ignore[attr-defined] + template = app.jinja_env.get_or_select_template(template_name_or_list) + return _render(app, template, context) + + +def render_template_string(source: str, **context: t.Any) -> str: + """Render a template from the given source string with the given + context. + + :param source: The source code of the template to render. + :param context: The variables to make available in the template. + """ + app = current_app._get_current_object() # type: ignore[attr-defined] + template = app.jinja_env.from_string(source) + return _render(app, template, context) + + +def _stream( + app: Flask, template: Template, context: dict[str, t.Any] +) -> t.Iterator[str]: + app.update_template_context(context) + before_render_template.send( + app, _async_wrapper=app.ensure_sync, template=template, context=context + ) + + def generate() -> t.Iterator[str]: + yield from template.generate(context) + template_rendered.send( + app, _async_wrapper=app.ensure_sync, template=template, context=context + ) + + rv = generate() + + # If a request context is active, keep it while generating. + if request: + rv = stream_with_context(rv) + + return rv + + +def stream_template( + template_name_or_list: str | Template | list[str | Template], + **context: t.Any, +) -> t.Iterator[str]: + """Render a template by name with the given context as a stream. + This returns an iterator of strings, which can be used as a + streaming response from a view. + + :param template_name_or_list: The name of the template to render. If + a list is given, the first name to exist will be rendered. + :param context: The variables to make available in the template. + + .. versionadded:: 2.2 + """ + app = current_app._get_current_object() # type: ignore[attr-defined] + template = app.jinja_env.get_or_select_template(template_name_or_list) + return _stream(app, template, context) + + +def stream_template_string(source: str, **context: t.Any) -> t.Iterator[str]: + """Render a template from the given source string with the given + context as a stream. This returns an iterator of strings, which can + be used as a streaming response from a view. + + :param source: The source code of the template to render. + :param context: The variables to make available in the template. + + .. versionadded:: 2.2 + """ + app = current_app._get_current_object() # type: ignore[attr-defined] + template = app.jinja_env.from_string(source) + return _stream(app, template, context) diff --git a/.venv/lib/python3.9/site-packages/flask/testing.py b/.venv/lib/python3.9/site-packages/flask/testing.py new file mode 100644 index 0000000..773f152 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/flask/testing.py @@ -0,0 +1,282 @@ +from __future__ import annotations + +import typing as t +from contextlib import contextmanager +from contextlib import ExitStack +from copy import copy +from types import TracebackType +from urllib.parse import urlsplit + +import werkzeug.test +from click.testing import CliRunner +from werkzeug.test import Client +from werkzeug.wrappers import Request as BaseRequest + +from .cli import ScriptInfo +from .sessions import SessionMixin + +if t.TYPE_CHECKING: # pragma: no cover + from werkzeug.test import TestResponse + + from .app import Flask + + +class EnvironBuilder(werkzeug.test.EnvironBuilder): + """An :class:`~werkzeug.test.EnvironBuilder`, that takes defaults from the + application. + + :param app: The Flask application to configure the environment from. + :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 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`. + """ + + def __init__( + self, + app: Flask, + path: str = "/", + base_url: str | None = None, + subdomain: str | None = None, + url_scheme: str | None = None, + *args: t.Any, + **kwargs: t.Any, + ) -> None: + assert not (base_url or subdomain or url_scheme) or ( + base_url is not None + ) != bool( + subdomain or url_scheme + ), 'Cannot pass "subdomain" or "url_scheme" with "base_url".' + + if base_url is None: + http_host = app.config.get("SERVER_NAME") or "localhost" + app_root = app.config["APPLICATION_ROOT"] + + if subdomain: + http_host = f"{subdomain}.{http_host}" + + if url_scheme is None: + url_scheme = app.config["PREFERRED_URL_SCHEME"] + + url = urlsplit(path) + base_url = ( + f"{url.scheme or url_scheme}://{url.netloc or http_host}" + f"/{app_root.lstrip('/')}" + ) + path = url.path + + if url.query: + sep = b"?" if isinstance(url.query, bytes) else "?" + path += sep + url.query + + self.app = app + super().__init__(path, base_url, *args, **kwargs) + + def json_dumps(self, obj: t.Any, **kwargs: t.Any) -> str: # type: ignore + """Serialize ``obj`` to a JSON-formatted string. + + The serialization will be configured according to the config associated + with this EnvironBuilder's ``app``. + """ + return self.app.json.dumps(obj, **kwargs) + + +class FlaskClient(Client): + """Works like a regular Werkzeug test client but has knowledge about + Flask's contexts to defer the cleanup of the request context until + the end of a ``with`` block. For general information about how to + use this class refer to :class:`werkzeug.test.Client`. + + .. versionchanged:: 0.12 + `app.test_client()` includes preset default environment, which can be + set after instantiation of the `app.test_client()` object in + `client.environ_base`. + + Basic usage is outlined in the :doc:`/testing` chapter. + """ + + application: Flask + + def __init__(self, *args: t.Any, **kwargs: t.Any) -> None: + super().__init__(*args, **kwargs) + self.preserve_context = False + self._new_contexts: list[t.ContextManager[t.Any]] = [] + self._context_stack = ExitStack() + self.environ_base = { + "REMOTE_ADDR": "127.0.0.1", + "HTTP_USER_AGENT": f"werkzeug/{werkzeug.__version__}", + } + + @contextmanager + def session_transaction( + self, *args: t.Any, **kwargs: t.Any + ) -> t.Generator[SessionMixin, None, None]: + """When used in combination with a ``with`` statement this opens a + session transaction. This can be used to modify the session that + the test client uses. Once the ``with`` block is left the session is + stored back. + + :: + + with client.session_transaction() as session: + session['value'] = 42 + + Internally this is implemented by going through a temporary test + request context and since session handling could depend on + request variables this function accepts the same arguments as + :meth:`~flask.Flask.test_request_context` which are directly + passed through. + """ + if self._cookies is None: + raise TypeError( + "Cookies are disabled. Create a client with 'use_cookies=True'." + ) + + app = self.application + ctx = app.test_request_context(*args, **kwargs) + self._add_cookies_to_wsgi(ctx.request.environ) + + with ctx: + sess = app.session_interface.open_session(app, ctx.request) + + if sess is None: + raise RuntimeError("Session backend did not open a session.") + + yield sess + resp = app.response_class() + + if app.session_interface.is_null_session(sess): + return + + with ctx: + app.session_interface.save_session(app, sess, resp) + + self._update_cookies_from_response( + ctx.request.host.partition(":")[0], + ctx.request.path, + resp.headers.getlist("Set-Cookie"), + ) + + def _copy_environ(self, other): + out = {**self.environ_base, **other} + + if self.preserve_context: + out["werkzeug.debug.preserve_context"] = self._new_contexts.append + + return out + + def _request_from_builder_args(self, args, kwargs): + kwargs["environ_base"] = self._copy_environ(kwargs.get("environ_base", {})) + builder = EnvironBuilder(self.application, *args, **kwargs) + + try: + return builder.get_request() + finally: + builder.close() + + def open( + self, + *args: t.Any, + buffered: bool = False, + follow_redirects: bool = False, + **kwargs: t.Any, + ) -> TestResponse: + if args and isinstance( + args[0], (werkzeug.test.EnvironBuilder, dict, BaseRequest) + ): + if isinstance(args[0], werkzeug.test.EnvironBuilder): + builder = copy(args[0]) + builder.environ_base = self._copy_environ(builder.environ_base or {}) + request = builder.get_request() + elif isinstance(args[0], dict): + request = EnvironBuilder.from_environ( + args[0], app=self.application, environ_base=self._copy_environ({}) + ).get_request() + else: + # isinstance(args[0], BaseRequest) + request = copy(args[0]) + request.environ = self._copy_environ(request.environ) + else: + # request is None + request = self._request_from_builder_args(args, kwargs) + + # Pop any previously preserved contexts. This prevents contexts + # from being preserved across redirects or multiple requests + # within a single block. + self._context_stack.close() + + response = super().open( + request, + buffered=buffered, + follow_redirects=follow_redirects, + ) + response.json_module = self.application.json # type: ignore[assignment] + + # Re-push contexts that were preserved during the request. + while self._new_contexts: + cm = self._new_contexts.pop() + self._context_stack.enter_context(cm) + + return response + + def __enter__(self) -> FlaskClient: + if self.preserve_context: + raise RuntimeError("Cannot nest client invocations") + self.preserve_context = True + return self + + def __exit__( + self, + exc_type: type | None, + exc_value: BaseException | None, + tb: TracebackType | None, + ) -> None: + self.preserve_context = False + self._context_stack.close() + + +class FlaskCliRunner(CliRunner): + """A :class:`~click.testing.CliRunner` for testing a Flask app's + CLI commands. Typically created using + :meth:`~flask.Flask.test_cli_runner`. See :ref:`testing-cli`. + """ + + def __init__(self, app: Flask, **kwargs: t.Any) -> None: + self.app = app + super().__init__(**kwargs) + + def invoke( # type: ignore + self, cli: t.Any = None, args: t.Any = None, **kwargs: t.Any + ) -> t.Any: + """Invokes a CLI command in an isolated environment. See + :meth:`CliRunner.invoke ` for + full method documentation. See :ref:`testing-cli` for examples. + + If the ``obj`` argument is not given, passes an instance of + :class:`~flask.cli.ScriptInfo` that knows how to load the Flask + app being tested. + + :param cli: Command object to invoke. Default is the app's + :attr:`~flask.app.Flask.cli` group. + :param args: List of strings to invoke the command with. + + :return: a :class:`~click.testing.Result` object. + """ + if cli is None: + cli = self.app.cli # type: ignore + + if "obj" not in kwargs: + kwargs["obj"] = ScriptInfo(create_app=lambda: self.app) + + return super().invoke(cli, args, **kwargs) diff --git a/.venv/lib/python3.9/site-packages/flask/typing.py b/.venv/lib/python3.9/site-packages/flask/typing.py new file mode 100644 index 0000000..50aef7f --- /dev/null +++ b/.venv/lib/python3.9/site-packages/flask/typing.py @@ -0,0 +1,82 @@ +from __future__ import annotations + +import typing as t + +if t.TYPE_CHECKING: # pragma: no cover + from _typeshed.wsgi import WSGIApplication # noqa: F401 + from werkzeug.datastructures import Headers # noqa: F401 + from werkzeug.wrappers import Response # noqa: F401 + +# The possible types that are directly convertible or are a Response object. +ResponseValue = t.Union[ + "Response", + str, + bytes, + t.List[t.Any], + # Only dict is actually accepted, but Mapping allows for TypedDict. + t.Mapping[str, t.Any], + t.Iterator[str], + t.Iterator[bytes], +] + +# the possible types for an individual HTTP header +# This should be a Union, but mypy doesn't pass unless it's a TypeVar. +HeaderValue = t.Union[str, t.List[str], t.Tuple[str, ...]] + +# the possible types for HTTP headers +HeadersValue = t.Union[ + "Headers", + t.Mapping[str, HeaderValue], + t.Sequence[t.Tuple[str, HeaderValue]], +] + +# The possible types returned by a route function. +ResponseReturnValue = t.Union[ + ResponseValue, + t.Tuple[ResponseValue, HeadersValue], + t.Tuple[ResponseValue, int], + t.Tuple[ResponseValue, int, HeadersValue], + "WSGIApplication", +] + +# Allow any subclass of werkzeug.Response, such as the one from Flask, +# as a callback argument. Using werkzeug.Response directly makes a +# callback annotated with flask.Response fail type checking. +ResponseClass = t.TypeVar("ResponseClass", bound="Response") + +AppOrBlueprintKey = t.Optional[str] # The App key is None, whereas blueprints are named +AfterRequestCallable = t.Union[ + t.Callable[[ResponseClass], ResponseClass], + t.Callable[[ResponseClass], t.Awaitable[ResponseClass]], +] +BeforeFirstRequestCallable = t.Union[ + t.Callable[[], None], t.Callable[[], t.Awaitable[None]] +] +BeforeRequestCallable = t.Union[ + t.Callable[[], t.Optional[ResponseReturnValue]], + t.Callable[[], t.Awaitable[t.Optional[ResponseReturnValue]]], +] +ShellContextProcessorCallable = t.Callable[[], t.Dict[str, t.Any]] +TeardownCallable = t.Union[ + t.Callable[[t.Optional[BaseException]], None], + t.Callable[[t.Optional[BaseException]], t.Awaitable[None]], +] +TemplateContextProcessorCallable = t.Callable[[], t.Dict[str, t.Any]] +TemplateFilterCallable = t.Callable[..., t.Any] +TemplateGlobalCallable = t.Callable[..., t.Any] +TemplateTestCallable = t.Callable[..., bool] +URLDefaultCallable = t.Callable[[str, dict], None] +URLValuePreprocessorCallable = t.Callable[[t.Optional[str], t.Optional[dict]], None] + +# This should take Exception, but that either breaks typing the argument +# with a specific exception, or decorating multiple times with different +# exceptions (and using a union type on the argument). +# https://github.com/pallets/flask/issues/4095 +# https://github.com/pallets/flask/issues/4295 +# https://github.com/pallets/flask/issues/4297 +ErrorHandlerCallable = t.Callable[[t.Any], ResponseReturnValue] + +RouteCallable = t.Union[ + t.Callable[..., ResponseReturnValue], + t.Callable[..., t.Awaitable[ResponseReturnValue]], +] diff --git a/.venv/lib/python3.9/site-packages/flask/views.py b/.venv/lib/python3.9/site-packages/flask/views.py new file mode 100644 index 0000000..c7a2b62 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/flask/views.py @@ -0,0 +1,190 @@ +from __future__ import annotations + +import typing as t + +from . import typing as ft +from .globals import current_app +from .globals import request + + +http_method_funcs = frozenset( + ["get", "post", "head", "options", "delete", "put", "trace", "patch"] +) + + +class View: + """Subclass this class and override :meth:`dispatch_request` to + create a generic class-based view. Call :meth:`as_view` to create a + view function that creates an instance of the class with the given + arguments and calls its ``dispatch_request`` method with any URL + variables. + + See :doc:`views` for a detailed guide. + + .. code-block:: python + + class Hello(View): + init_every_request = False + + def dispatch_request(self, name): + return f"Hello, {name}!" + + app.add_url_rule( + "/hello/", view_func=Hello.as_view("hello") + ) + + Set :attr:`methods` on the class to change what methods the view + accepts. + + Set :attr:`decorators` on the class to apply a list of decorators to + the generated view function. Decorators applied to the class itself + will not be applied to the generated view function! + + Set :attr:`init_every_request` to ``False`` for efficiency, unless + you need to store request-global data on ``self``. + """ + + #: The methods this view is registered for. Uses the same default + #: (``["GET", "HEAD", "OPTIONS"]``) as ``route`` and + #: ``add_url_rule`` by default. + methods: t.ClassVar[t.Collection[str] | None] = None + + #: Control whether the ``OPTIONS`` method is handled automatically. + #: Uses the same default (``True``) as ``route`` and + #: ``add_url_rule`` by default. + provide_automatic_options: t.ClassVar[bool | None] = None + + #: A list of decorators to apply, in order, to the generated view + #: function. Remember that ``@decorator`` syntax is applied bottom + #: to top, so the first decorator in the list would be the bottom + #: decorator. + #: + #: .. versionadded:: 0.8 + decorators: t.ClassVar[list[t.Callable]] = [] + + #: Create a new instance of this view class for every request by + #: default. If a view subclass sets this to ``False``, the same + #: instance is used for every request. + #: + #: A single instance is more efficient, especially if complex setup + #: is done during init. However, storing data on ``self`` is no + #: longer safe across requests, and :data:`~flask.g` should be used + #: instead. + #: + #: .. versionadded:: 2.2 + init_every_request: t.ClassVar[bool] = True + + def dispatch_request(self) -> ft.ResponseReturnValue: + """The actual view function behavior. Subclasses must override + this and return a valid response. Any variables from the URL + rule are passed as keyword arguments. + """ + raise NotImplementedError() + + @classmethod + def as_view( + cls, name: str, *class_args: t.Any, **class_kwargs: t.Any + ) -> ft.RouteCallable: + """Convert the class into a view function that can be registered + for a route. + + By default, the generated view will create a new instance of the + view class for every request and call its + :meth:`dispatch_request` method. If the view class sets + :attr:`init_every_request` to ``False``, the same instance will + be used for every request. + + Except for ``name``, all other arguments passed to this method + are forwarded to the view class ``__init__`` method. + + .. versionchanged:: 2.2 + Added the ``init_every_request`` class attribute. + """ + if cls.init_every_request: + + def view(**kwargs: t.Any) -> ft.ResponseReturnValue: + self = view.view_class( # type: ignore[attr-defined] + *class_args, **class_kwargs + ) + return current_app.ensure_sync(self.dispatch_request)(**kwargs) + + else: + self = cls(*class_args, **class_kwargs) + + def view(**kwargs: t.Any) -> ft.ResponseReturnValue: + return current_app.ensure_sync(self.dispatch_request)(**kwargs) + + if cls.decorators: + view.__name__ = name + view.__module__ = cls.__module__ + for decorator in cls.decorators: + view = decorator(view) + + # We attach the view class to the view function for two reasons: + # first of all it allows us to easily figure out what class-based + # view this thing came from, secondly it's also used for instantiating + # the view class so you can actually replace it with something else + # for testing purposes and debugging. + view.view_class = cls # type: ignore + view.__name__ = name + view.__doc__ = cls.__doc__ + view.__module__ = cls.__module__ + view.methods = cls.methods # type: ignore + view.provide_automatic_options = cls.provide_automatic_options # type: ignore + return view + + +class MethodView(View): + """Dispatches request methods to the corresponding instance methods. + For example, if you implement a ``get`` method, it will be used to + handle ``GET`` requests. + + This can be useful for defining a REST API. + + :attr:`methods` is automatically set based on the methods defined on + the class. + + See :doc:`views` for a detailed guide. + + .. code-block:: python + + class CounterAPI(MethodView): + def get(self): + return str(session.get("counter", 0)) + + def post(self): + session["counter"] = session.get("counter", 0) + 1 + return redirect(url_for("counter")) + + app.add_url_rule( + "/counter", view_func=CounterAPI.as_view("counter") + ) + """ + + def __init_subclass__(cls, **kwargs: t.Any) -> None: + super().__init_subclass__(**kwargs) + + if "methods" not in cls.__dict__: + methods = set() + + for base in cls.__bases__: + if getattr(base, "methods", None): + methods.update(base.methods) # type: ignore[attr-defined] + + for key in http_method_funcs: + if hasattr(cls, key): + methods.add(key.upper()) + + if methods: + cls.methods = methods + + def dispatch_request(self, **kwargs: t.Any) -> ft.ResponseReturnValue: + meth = getattr(self, request.method.lower(), None) + + # If the request method is HEAD and we don't have a handler for it + # retry with GET. + if meth is None and request.method == "HEAD": + meth = getattr(self, "get", None) + + assert meth is not None, f"Unimplemented method {request.method!r}" + return current_app.ensure_sync(meth)(**kwargs) diff --git a/.venv/lib/python3.9/site-packages/flask/wrappers.py b/.venv/lib/python3.9/site-packages/flask/wrappers.py new file mode 100644 index 0000000..ef7aa38 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/flask/wrappers.py @@ -0,0 +1,173 @@ +from __future__ import annotations + +import typing as t + +from werkzeug.exceptions import BadRequest +from werkzeug.wrappers import Request as RequestBase +from werkzeug.wrappers import Response as ResponseBase + +from . import json +from .globals import current_app +from .helpers import _split_blueprint_path + +if t.TYPE_CHECKING: # pragma: no cover + from werkzeug.routing import Rule + + +class Request(RequestBase): + """The request object used by default in Flask. Remembers the + matched endpoint and view arguments. + + It is what ends up as :class:`~flask.request`. If you want to replace + the request object used you can subclass this and set + :attr:`~flask.Flask.request_class` to your subclass. + + The request object is a :class:`~werkzeug.wrappers.Request` subclass and + provides all of the attributes Werkzeug defines plus a few Flask + specific ones. + """ + + json_module: t.Any = json + + #: The internal URL rule that matched the request. This can be + #: useful to inspect which methods are allowed for the URL from + #: a before/after handler (``request.url_rule.methods``) etc. + #: Though if the request's method was invalid for the URL rule, + #: the valid list is available in ``routing_exception.valid_methods`` + #: instead (an attribute of the Werkzeug exception + #: :exc:`~werkzeug.exceptions.MethodNotAllowed`) + #: because the request was never internally bound. + #: + #: .. versionadded:: 0.6 + url_rule: Rule | None = None + + #: A dict of view arguments that matched the request. If an exception + #: happened when matching, this will be ``None``. + view_args: dict[str, t.Any] | None = None + + #: If matching the URL failed, this is the exception that will be + #: raised / was raised as part of the request handling. This is + #: usually a :exc:`~werkzeug.exceptions.NotFound` exception or + #: something similar. + routing_exception: Exception | None = None + + @property + def max_content_length(self) -> int | None: # type: ignore + """Read-only view of the ``MAX_CONTENT_LENGTH`` config key.""" + if current_app: + return current_app.config["MAX_CONTENT_LENGTH"] + else: + return None + + @property + def endpoint(self) -> str | None: + """The endpoint that matched the request URL. + + This will be ``None`` if matching failed or has not been + performed yet. + + This in combination with :attr:`view_args` can be used to + reconstruct the same URL or a modified URL. + """ + if self.url_rule is not None: + return self.url_rule.endpoint + + return None + + @property + def blueprint(self) -> str | None: + """The registered name of the current blueprint. + + This will be ``None`` if the endpoint is not part of a + blueprint, or if URL matching failed or has not been performed + yet. + + This does not necessarily match the name the blueprint was + created with. It may have been nested, or registered with a + different name. + """ + endpoint = self.endpoint + + if endpoint is not None and "." in endpoint: + return endpoint.rpartition(".")[0] + + return None + + @property + def blueprints(self) -> list[str]: + """The registered names of the current blueprint upwards through + parent blueprints. + + This will be an empty list if there is no current blueprint, or + if URL matching failed. + + .. versionadded:: 2.0.1 + """ + name = self.blueprint + + if name is None: + return [] + + return _split_blueprint_path(name) + + def _load_form_data(self) -> None: + super()._load_form_data() + + # In debug mode we're replacing the files multidict with an ad-hoc + # subclass that raises a different error for key errors. + if ( + current_app + and current_app.debug + and self.mimetype != "multipart/form-data" + and not self.files + ): + from .debughelpers import attach_enctype_error_multidict + + attach_enctype_error_multidict(self) + + def on_json_loading_failed(self, e: ValueError | None) -> t.Any: + try: + return super().on_json_loading_failed(e) + except BadRequest as e: + if current_app and current_app.debug: + raise + + raise BadRequest() from e + + +class Response(ResponseBase): + """The response object that is used by default in Flask. Works like the + response object from Werkzeug but is set to have an HTML mimetype by + default. Quite often you don't have to create this object yourself because + :meth:`~flask.Flask.make_response` will take care of that for you. + + If you want to replace the response object used you can subclass this and + set :attr:`~flask.Flask.response_class` to your subclass. + + .. versionchanged:: 1.0 + JSON support is added to the response, like the request. This is useful + when testing to get the test client response data as JSON. + + .. versionchanged:: 1.0 + + Added :attr:`max_cookie_size`. + """ + + default_mimetype: str | None = "text/html" + + json_module = json + + autocorrect_location_header = False + + @property + def max_cookie_size(self) -> int: # type: ignore + """Read-only view of the :data:`MAX_COOKIE_SIZE` config key. + + See :attr:`~werkzeug.wrappers.Response.max_cookie_size` in + Werkzeug's docs. + """ + if current_app: + return current_app.config["MAX_COOKIE_SIZE"] + + # return Werkzeug's default when not in an app context + return super().max_cookie_size diff --git a/.venv/lib/python3.9/site-packages/importlib_metadata-6.6.0.dist-info/INSTALLER b/.venv/lib/python3.9/site-packages/importlib_metadata-6.6.0.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/.venv/lib/python3.9/site-packages/importlib_metadata-6.6.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.venv/lib/python3.9/site-packages/importlib_metadata-6.6.0.dist-info/LICENSE b/.venv/lib/python3.9/site-packages/importlib_metadata-6.6.0.dist-info/LICENSE new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/importlib_metadata-6.6.0.dist-info/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/.venv/lib/python3.9/site-packages/importlib_metadata-6.6.0.dist-info/METADATA b/.venv/lib/python3.9/site-packages/importlib_metadata-6.6.0.dist-info/METADATA new file mode 100644 index 0000000..ee0ffd9 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/importlib_metadata-6.6.0.dist-info/METADATA @@ -0,0 +1,135 @@ +Metadata-Version: 2.1 +Name: importlib-metadata +Version: 6.6.0 +Summary: Read metadata from Python packages +Home-page: https://github.com/python/importlib_metadata +Author: Jason R. Coombs +Author-email: jaraco@jaraco.com +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: Apache Software License +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3 :: Only +Requires-Python: >=3.7 +License-File: LICENSE +Requires-Dist: zipp (>=0.5) +Requires-Dist: typing-extensions (>=3.6.4) ; python_version < "3.8" +Provides-Extra: docs +Requires-Dist: sphinx (>=3.5) ; extra == 'docs' +Requires-Dist: jaraco.packaging (>=9) ; extra == 'docs' +Requires-Dist: rst.linker (>=1.9) ; extra == 'docs' +Requires-Dist: furo ; extra == 'docs' +Requires-Dist: sphinx-lint ; extra == 'docs' +Requires-Dist: jaraco.tidelift (>=1.4) ; extra == 'docs' +Provides-Extra: perf +Requires-Dist: ipython ; extra == 'perf' +Provides-Extra: testing +Requires-Dist: pytest (>=6) ; extra == 'testing' +Requires-Dist: pytest-checkdocs (>=2.4) ; extra == 'testing' +Requires-Dist: flake8 (<5) ; extra == 'testing' +Requires-Dist: pytest-cov ; extra == 'testing' +Requires-Dist: pytest-enabler (>=1.3) ; extra == 'testing' +Requires-Dist: packaging ; extra == 'testing' +Requires-Dist: pyfakefs ; extra == 'testing' +Requires-Dist: flufl.flake8 ; extra == 'testing' +Requires-Dist: pytest-perf (>=0.9.2) ; extra == 'testing' +Requires-Dist: pytest-black (>=0.3.7) ; (platform_python_implementation != "PyPy") and extra == 'testing' +Requires-Dist: pytest-mypy (>=0.9.1) ; (platform_python_implementation != "PyPy") and extra == 'testing' +Requires-Dist: pytest-flake8 ; (python_version < "3.12") and extra == 'testing' +Requires-Dist: importlib-resources (>=1.3) ; (python_version < "3.9") and extra == 'testing' + +.. image:: https://img.shields.io/pypi/v/importlib_metadata.svg + :target: https://pypi.org/project/importlib_metadata + +.. image:: https://img.shields.io/pypi/pyversions/importlib_metadata.svg + +.. image:: https://github.com/python/importlib_metadata/workflows/tests/badge.svg + :target: https://github.com/python/importlib_metadata/actions?query=workflow%3A%22tests%22 + :alt: tests + +.. image:: https://img.shields.io/badge/code%20style-black-000000.svg + :target: https://github.com/psf/black + :alt: Code style: Black + +.. image:: https://readthedocs.org/projects/importlib-metadata/badge/?version=latest + :target: https://importlib-metadata.readthedocs.io/en/latest/?badge=latest + +.. image:: https://img.shields.io/badge/skeleton-2023-informational + :target: https://blog.jaraco.com/skeleton + +.. image:: https://tidelift.com/badges/package/pypi/importlib-metadata + :target: https://tidelift.com/subscription/pkg/pypi-importlib-metadata?utm_source=pypi-importlib-metadata&utm_medium=readme + +Library to access the metadata for a Python package. + +This package supplies third-party access to the functionality of +`importlib.metadata `_ +including improvements added to subsequent Python versions. + + +Compatibility +============= + +New features are introduced in this third-party library and later merged +into CPython. The following table indicates which versions of this library +were contributed to different versions in the standard library: + +.. list-table:: + :header-rows: 1 + + * - importlib_metadata + - stdlib + * - 6.5 + - 3.12 + * - 4.13 + - 3.11 + * - 4.6 + - 3.10 + * - 1.4 + - 3.8 + + +Usage +===== + +See the `online documentation `_ +for usage details. + +`Finder authors +`_ can +also add support for custom package installers. See the above documentation +for details. + + +Caveats +======= + +This project primarily supports third-party packages installed by PyPA +tools (or other conforming packages). It does not support: + +- Packages in the stdlib. +- Packages installed without metadata. + +Project details +=============== + + * Project home: https://github.com/python/importlib_metadata + * Report bugs at: https://github.com/python/importlib_metadata/issues + * Code hosting: https://github.com/python/importlib_metadata + * Documentation: https://importlib-metadata.readthedocs.io/ + +For Enterprise +============== + +Available as part of the Tidelift Subscription. + +This project and the maintainers of thousands of other packages are working with Tidelift to deliver one enterprise subscription that covers all of the open source you use. + +`Learn more `_. + +Security Contact +================ + +To report a security vulnerability, please use the +`Tidelift security contact `_. +Tidelift will coordinate the fix and disclosure. diff --git a/.venv/lib/python3.9/site-packages/importlib_metadata-6.6.0.dist-info/RECORD b/.venv/lib/python3.9/site-packages/importlib_metadata-6.6.0.dist-info/RECORD new file mode 100644 index 0000000..d7c167a --- /dev/null +++ b/.venv/lib/python3.9/site-packages/importlib_metadata-6.6.0.dist-info/RECORD @@ -0,0 +1,25 @@ +importlib_metadata-6.6.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +importlib_metadata-6.6.0.dist-info/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358 +importlib_metadata-6.6.0.dist-info/METADATA,sha256=PkZEyOdZ2YQVAGc1nwMGJ2o6cs7hWeBL2Ya5NoyUGbA,4958 +importlib_metadata-6.6.0.dist-info/RECORD,, +importlib_metadata-6.6.0.dist-info/WHEEL,sha256=pkctZYzUS4AYVn6dJ-7367OJZivF2e8RA9b_ZBjif18,92 +importlib_metadata-6.6.0.dist-info/top_level.txt,sha256=CO3fD9yylANiXkrMo4qHLV_mqXL2sC5JFKgt1yWAT-A,19 +importlib_metadata/__init__.py,sha256=e_TtkQm2IsKEBa4H9j502DqMUZ-k6KQa53o9VX47ofs,29949 +importlib_metadata/__pycache__/__init__.cpython-39.pyc,, +importlib_metadata/__pycache__/_adapters.cpython-39.pyc,, +importlib_metadata/__pycache__/_collections.cpython-39.pyc,, +importlib_metadata/__pycache__/_compat.cpython-39.pyc,, +importlib_metadata/__pycache__/_functools.cpython-39.pyc,, +importlib_metadata/__pycache__/_itertools.cpython-39.pyc,, +importlib_metadata/__pycache__/_meta.cpython-39.pyc,, +importlib_metadata/__pycache__/_py39compat.cpython-39.pyc,, +importlib_metadata/__pycache__/_text.cpython-39.pyc,, +importlib_metadata/_adapters.py,sha256=i8S6Ib1OQjcILA-l4gkzktMZe18TaeUNI49PLRp6OBU,2454 +importlib_metadata/_collections.py,sha256=CJ0OTCHIjWA0ZIVS4voORAsn2R4R2cQBEtPsZEJpASY,743 +importlib_metadata/_compat.py,sha256=VZUVAQntwXR1lZUYgZjOnXsV_B5mRV5FjNjwVP_EMSo,2096 +importlib_metadata/_functools.py,sha256=PsY2-4rrKX4RVeRC1oGp1lB1pmC9eKN88_f-bD9uOoA,2895 +importlib_metadata/_itertools.py,sha256=cvr_2v8BRbxcIl5x5ldfqdHjhI8Yi8s8yk50G_nm6jQ,2068 +importlib_metadata/_meta.py,sha256=I2AuaUMr5a6cTdZleV9WpyqUCSooqqV-zSzr1qn7FMw,1615 +importlib_metadata/_py39compat.py,sha256=2Tk5twb_VgLCY-1NEAQjdZp_S9OFMC-pUzP2isuaPsQ,1098 +importlib_metadata/_text.py,sha256=HCsFksZpJLeTP3NEk_ngrAeXVRRtTrtyh9eOABoRP4A,2166 +importlib_metadata/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 diff --git a/.venv/lib/python3.9/site-packages/importlib_metadata-6.6.0.dist-info/WHEEL b/.venv/lib/python3.9/site-packages/importlib_metadata-6.6.0.dist-info/WHEEL new file mode 100644 index 0000000..1f37c02 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/importlib_metadata-6.6.0.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.40.0) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/.venv/lib/python3.9/site-packages/importlib_metadata-6.6.0.dist-info/top_level.txt b/.venv/lib/python3.9/site-packages/importlib_metadata-6.6.0.dist-info/top_level.txt new file mode 100644 index 0000000..bbb0754 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/importlib_metadata-6.6.0.dist-info/top_level.txt @@ -0,0 +1 @@ +importlib_metadata diff --git a/.venv/lib/python3.9/site-packages/importlib_metadata/__init__.py b/.venv/lib/python3.9/site-packages/importlib_metadata/__init__.py new file mode 100644 index 0000000..281cfb0 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/importlib_metadata/__init__.py @@ -0,0 +1,987 @@ +import os +import re +import abc +import csv +import sys +import zipp +import email +import inspect +import pathlib +import operator +import textwrap +import warnings +import functools +import itertools +import posixpath +import contextlib +import collections + +from . import _adapters, _meta, _py39compat +from ._collections import FreezableDefaultDict, Pair +from ._compat import ( + NullFinder, + StrPath, + install, + pypy_partial, +) +from ._functools import method_cache, pass_none +from ._itertools import always_iterable, unique_everseen +from ._meta import PackageMetadata, SimplePath + +from contextlib import suppress +from importlib import import_module +from importlib.abc import MetaPathFinder +from itertools import starmap +from typing import Iterable, List, Mapping, Optional, Set, cast + +__all__ = [ + 'Distribution', + 'DistributionFinder', + 'PackageMetadata', + 'PackageNotFoundError', + 'distribution', + 'distributions', + 'entry_points', + 'files', + 'metadata', + 'packages_distributions', + 'requires', + 'version', +] + + +class PackageNotFoundError(ModuleNotFoundError): + """The package was not found.""" + + def __str__(self) -> str: + return f"No package metadata was found for {self.name}" + + @property + def name(self) -> str: # type: ignore[override] + (name,) = self.args + return name + + +class Sectioned: + """ + A simple entry point config parser for performance + + >>> for item in Sectioned.read(Sectioned._sample): + ... print(item) + Pair(name='sec1', value='# comments ignored') + Pair(name='sec1', value='a = 1') + Pair(name='sec1', value='b = 2') + Pair(name='sec2', value='a = 2') + + >>> res = Sectioned.section_pairs(Sectioned._sample) + >>> item = next(res) + >>> item.name + 'sec1' + >>> item.value + Pair(name='a', value='1') + >>> item = next(res) + >>> item.value + Pair(name='b', value='2') + >>> item = next(res) + >>> item.name + 'sec2' + >>> item.value + Pair(name='a', value='2') + >>> list(res) + [] + """ + + _sample = textwrap.dedent( + """ + [sec1] + # comments ignored + a = 1 + b = 2 + + [sec2] + a = 2 + """ + ).lstrip() + + @classmethod + def section_pairs(cls, text): + return ( + section._replace(value=Pair.parse(section.value)) + for section in cls.read(text, filter_=cls.valid) + if section.name is not None + ) + + @staticmethod + def read(text, filter_=None): + lines = filter(filter_, map(str.strip, text.splitlines())) + name = None + for value in lines: + section_match = value.startswith('[') and value.endswith(']') + if section_match: + name = value.strip('[]') + continue + yield Pair(name, value) + + @staticmethod + def valid(line: str): + return line and not line.startswith('#') + + +class DeprecatedTuple: + """ + Provide subscript item access for backward compatibility. + + >>> recwarn = getfixture('recwarn') + >>> ep = EntryPoint(name='name', value='value', group='group') + >>> ep[:] + ('name', 'value', 'group') + >>> ep[0] + 'name' + >>> len(recwarn) + 1 + """ + + # Do not remove prior to 2023-05-01 or Python 3.13 + _warn = functools.partial( + warnings.warn, + "EntryPoint tuple interface is deprecated. Access members by name.", + DeprecationWarning, + stacklevel=pypy_partial(2), + ) + + def __getitem__(self, item): + self._warn() + return self._key()[item] + + +class EntryPoint(DeprecatedTuple): + """An entry point as defined by Python packaging conventions. + + See `the packaging docs on entry points + `_ + for more information. + + >>> ep = EntryPoint( + ... name=None, group=None, value='package.module:attr [extra1, extra2]') + >>> ep.module + 'package.module' + >>> ep.attr + 'attr' + >>> ep.extras + ['extra1', 'extra2'] + """ + + pattern = re.compile( + r'(?P[\w.]+)\s*' + r'(:\s*(?P[\w.]+)\s*)?' + r'((?P\[.*\])\s*)?$' + ) + """ + A regular expression describing the syntax for an entry point, + which might look like: + + - module + - package.module + - package.module:attribute + - package.module:object.attribute + - package.module:attr [extra1, extra2] + + Other combinations are possible as well. + + The expression is lenient about whitespace around the ':', + following the attr, and following any extras. + """ + + name: str + value: str + group: str + + dist: Optional['Distribution'] = None + + def __init__(self, name: str, value: str, group: str) -> None: + vars(self).update(name=name, value=value, group=group) + + def load(self): + """Load the entry point from its definition. If only a module + is indicated by the value, return that module. Otherwise, + return the named object. + """ + match = self.pattern.match(self.value) + module = import_module(match.group('module')) + attrs = filter(None, (match.group('attr') or '').split('.')) + return functools.reduce(getattr, attrs, module) + + @property + def module(self) -> str: + match = self.pattern.match(self.value) + assert match is not None + return match.group('module') + + @property + def attr(self) -> str: + match = self.pattern.match(self.value) + assert match is not None + return match.group('attr') + + @property + def extras(self) -> List[str]: + match = self.pattern.match(self.value) + assert match is not None + return re.findall(r'\w+', match.group('extras') or '') + + def _for(self, dist): + vars(self).update(dist=dist) + return self + + def matches(self, **params): + """ + EntryPoint matches the given parameters. + + >>> ep = EntryPoint(group='foo', name='bar', value='bing:bong [extra1, extra2]') + >>> ep.matches(group='foo') + True + >>> ep.matches(name='bar', value='bing:bong [extra1, extra2]') + True + >>> ep.matches(group='foo', name='other') + False + >>> ep.matches() + True + >>> ep.matches(extras=['extra1', 'extra2']) + True + >>> ep.matches(module='bing') + True + >>> ep.matches(attr='bong') + True + """ + attrs = (getattr(self, param) for param in params) + return all(map(operator.eq, params.values(), attrs)) + + def _key(self): + return self.name, self.value, self.group + + def __lt__(self, other): + return self._key() < other._key() + + def __eq__(self, other): + return self._key() == other._key() + + def __setattr__(self, name, value): + raise AttributeError("EntryPoint objects are immutable.") + + def __repr__(self): + return ( + f'EntryPoint(name={self.name!r}, value={self.value!r}, ' + f'group={self.group!r})' + ) + + def __hash__(self) -> int: + return hash(self._key()) + + +class EntryPoints(tuple): + """ + An immutable collection of selectable EntryPoint objects. + """ + + __slots__ = () + + def __getitem__(self, name: str) -> EntryPoint: # type: ignore[override] + """ + Get the EntryPoint in self matching name. + """ + try: + return next(iter(self.select(name=name))) + except StopIteration: + raise KeyError(name) + + def select(self, **params): + """ + Select entry points from self that match the + given parameters (typically group and/or name). + """ + return EntryPoints(ep for ep in self if _py39compat.ep_matches(ep, **params)) + + @property + def names(self) -> Set[str]: + """ + Return the set of all names of all entry points. + """ + return {ep.name for ep in self} + + @property + def groups(self) -> Set[str]: + """ + Return the set of all groups of all entry points. + """ + return {ep.group for ep in self} + + @classmethod + def _from_text_for(cls, text, dist): + return cls(ep._for(dist) for ep in cls._from_text(text)) + + @staticmethod + def _from_text(text): + return ( + EntryPoint(name=item.value.name, value=item.value.value, group=item.name) + for item in Sectioned.section_pairs(text or '') + ) + + +class PackagePath(pathlib.PurePosixPath): + """A reference to a path in a package""" + + hash: Optional["FileHash"] + size: int + dist: "Distribution" + + def read_text(self, encoding: str = 'utf-8') -> str: # type: ignore[override] + with self.locate().open(encoding=encoding) as stream: + return stream.read() + + def read_binary(self) -> bytes: + with self.locate().open('rb') as stream: + return stream.read() + + def locate(self) -> pathlib.Path: + """Return a path-like object for this path""" + return self.dist.locate_file(self) + + +class FileHash: + def __init__(self, spec: str) -> None: + self.mode, _, self.value = spec.partition('=') + + def __repr__(self) -> str: + return f'' + + +class DeprecatedNonAbstract: + def __new__(cls, *args, **kwargs): + all_names = { + name for subclass in inspect.getmro(cls) for name in vars(subclass) + } + abstract = { + name + for name in all_names + if getattr(getattr(cls, name), '__isabstractmethod__', False) + } + if abstract: + warnings.warn( + f"Unimplemented abstract methods {abstract}", + DeprecationWarning, + stacklevel=2, + ) + return super().__new__(cls) + + +class Distribution(DeprecatedNonAbstract): + """A Python distribution package.""" + + @abc.abstractmethod + def read_text(self, filename) -> Optional[str]: + """Attempt to load metadata file given by the name. + + :param filename: The name of the file in the distribution info. + :return: The text if found, otherwise None. + """ + + @abc.abstractmethod + def locate_file(self, path: StrPath) -> pathlib.Path: + """ + Given a path to a file in this distribution, return a path + to it. + """ + + @classmethod + def from_name(cls, name: str) -> "Distribution": + """Return the Distribution for the given package name. + + :param name: The name of the distribution package to search for. + :return: The Distribution instance (or subclass thereof) for the named + package, if found. + :raises PackageNotFoundError: When the named package's distribution + metadata cannot be found. + :raises ValueError: When an invalid value is supplied for name. + """ + if not name: + raise ValueError("A distribution name is required.") + try: + return next(iter(cls.discover(name=name))) + except StopIteration: + raise PackageNotFoundError(name) + + @classmethod + def discover(cls, **kwargs) -> Iterable["Distribution"]: + """Return an iterable of Distribution objects for all packages. + + Pass a ``context`` or pass keyword arguments for constructing + a context. + + :context: A ``DistributionFinder.Context`` object. + :return: Iterable of Distribution objects for all packages. + """ + context = kwargs.pop('context', None) + if context and kwargs: + raise ValueError("cannot accept context and kwargs") + context = context or DistributionFinder.Context(**kwargs) + return itertools.chain.from_iterable( + resolver(context) for resolver in cls._discover_resolvers() + ) + + @staticmethod + def at(path: StrPath) -> "Distribution": + """Return a Distribution for the indicated metadata path + + :param path: a string or path-like object + :return: a concrete Distribution instance for the path + """ + return PathDistribution(pathlib.Path(path)) + + @staticmethod + def _discover_resolvers(): + """Search the meta_path for resolvers.""" + declared = ( + getattr(finder, 'find_distributions', None) for finder in sys.meta_path + ) + return filter(None, declared) + + @property + def metadata(self) -> _meta.PackageMetadata: + """Return the parsed metadata for this Distribution. + + The returned object will have keys that name the various bits of + metadata. See PEP 566 for details. + """ + opt_text = ( + self.read_text('METADATA') + or self.read_text('PKG-INFO') + # This last clause is here to support old egg-info files. Its + # effect is to just end up using the PathDistribution's self._path + # (which points to the egg-info file) attribute unchanged. + or self.read_text('') + ) + text = cast(str, opt_text) + return _adapters.Message(email.message_from_string(text)) + + @property + def name(self) -> str: + """Return the 'Name' metadata for the distribution package.""" + return self.metadata['Name'] + + @property + def _normalized_name(self): + """Return a normalized version of the name.""" + return Prepared.normalize(self.name) + + @property + def version(self) -> str: + """Return the 'Version' metadata for the distribution package.""" + return self.metadata['Version'] + + @property + def entry_points(self) -> EntryPoints: + return EntryPoints._from_text_for(self.read_text('entry_points.txt'), self) + + @property + def files(self) -> Optional[List[PackagePath]]: + """Files in this distribution. + + :return: List of PackagePath for this distribution or None + + Result is `None` if the metadata file that enumerates files + (i.e. RECORD for dist-info, or installed-files.txt or + SOURCES.txt for egg-info) is missing. + Result may be empty if the metadata exists but is empty. + """ + + def make_file(name, hash=None, size_str=None): + result = PackagePath(name) + result.hash = FileHash(hash) if hash else None + result.size = int(size_str) if size_str else None + result.dist = self + return result + + @pass_none + def make_files(lines): + return starmap(make_file, csv.reader(lines)) + + @pass_none + def skip_missing_files(package_paths): + return list(filter(lambda path: path.locate().exists(), package_paths)) + + return skip_missing_files( + make_files( + self._read_files_distinfo() + or self._read_files_egginfo_installed() + or self._read_files_egginfo_sources() + ) + ) + + def _read_files_distinfo(self): + """ + Read the lines of RECORD + """ + text = self.read_text('RECORD') + return text and text.splitlines() + + def _read_files_egginfo_installed(self): + """ + Read installed-files.txt and return lines in a similar + CSV-parsable format as RECORD: each file must be placed + relative to the site-packages directory and must also be + quoted (since file names can contain literal commas). + + This file is written when the package is installed by pip, + but it might not be written for other installation methods. + Assume the file is accurate if it exists. + """ + text = self.read_text('installed-files.txt') + # Prepend the .egg-info/ subdir to the lines in this file. + # But this subdir is only available from PathDistribution's + # self._path. + subdir = getattr(self, '_path', None) + if not text or not subdir: + return + + paths = ( + (subdir / name) + .resolve() + .relative_to(self.locate_file('').resolve()) + .as_posix() + for name in text.splitlines() + ) + return map('"{}"'.format, paths) + + def _read_files_egginfo_sources(self): + """ + Read SOURCES.txt and return lines in a similar CSV-parsable + format as RECORD: each file name must be quoted (since it + might contain literal commas). + + Note that SOURCES.txt is not a reliable source for what + files are installed by a package. This file is generated + for a source archive, and the files that are present + there (e.g. setup.py) may not correctly reflect the files + that are present after the package has been installed. + """ + text = self.read_text('SOURCES.txt') + return text and map('"{}"'.format, text.splitlines()) + + @property + def requires(self) -> Optional[List[str]]: + """Generated requirements specified for this Distribution""" + reqs = self._read_dist_info_reqs() or self._read_egg_info_reqs() + return reqs and list(reqs) + + def _read_dist_info_reqs(self): + return self.metadata.get_all('Requires-Dist') + + def _read_egg_info_reqs(self): + source = self.read_text('requires.txt') + return pass_none(self._deps_from_requires_text)(source) + + @classmethod + def _deps_from_requires_text(cls, source): + return cls._convert_egg_info_reqs_to_simple_reqs(Sectioned.read(source)) + + @staticmethod + def _convert_egg_info_reqs_to_simple_reqs(sections): + """ + Historically, setuptools would solicit and store 'extra' + requirements, including those with environment markers, + in separate sections. More modern tools expect each + dependency to be defined separately, with any relevant + extras and environment markers attached directly to that + requirement. This method converts the former to the + latter. See _test_deps_from_requires_text for an example. + """ + + def make_condition(name): + return name and f'extra == "{name}"' + + def quoted_marker(section): + section = section or '' + extra, sep, markers = section.partition(':') + if extra and markers: + markers = f'({markers})' + conditions = list(filter(None, [markers, make_condition(extra)])) + return '; ' + ' and '.join(conditions) if conditions else '' + + def url_req_space(req): + """ + PEP 508 requires a space between the url_spec and the quoted_marker. + Ref python/importlib_metadata#357. + """ + # '@' is uniquely indicative of a url_req. + return ' ' * ('@' in req) + + for section in sections: + space = url_req_space(section.value) + yield section.value + space + quoted_marker(section.name) + + +class DistributionFinder(MetaPathFinder): + """ + A MetaPathFinder capable of discovering installed distributions. + """ + + class Context: + """ + Keyword arguments presented by the caller to + ``distributions()`` or ``Distribution.discover()`` + to narrow the scope of a search for distributions + in all DistributionFinders. + + Each DistributionFinder may expect any parameters + and should attempt to honor the canonical + parameters defined below when appropriate. + """ + + name = None + """ + Specific name for which a distribution finder should match. + A name of ``None`` matches all distributions. + """ + + def __init__(self, **kwargs): + vars(self).update(kwargs) + + @property + def path(self) -> List[str]: + """ + The sequence of directory path that a distribution finder + should search. + + Typically refers to Python installed package paths such as + "site-packages" directories and defaults to ``sys.path``. + """ + return vars(self).get('path', sys.path) + + @abc.abstractmethod + def find_distributions(self, context=Context()) -> Iterable[Distribution]: + """ + Find distributions. + + Return an iterable of all Distribution instances capable of + loading the metadata for packages matching the ``context``, + a DistributionFinder.Context instance. + """ + + +class FastPath: + """ + Micro-optimized class for searching a path for + children. + + >>> FastPath('').children() + ['...'] + """ + + @functools.lru_cache() # type: ignore + def __new__(cls, root): + return super().__new__(cls) + + def __init__(self, root): + self.root = root + + def joinpath(self, child): + return pathlib.Path(self.root, child) + + def children(self): + with suppress(Exception): + return os.listdir(self.root or '.') + with suppress(Exception): + return self.zip_children() + return [] + + def zip_children(self): + zip_path = zipp.Path(self.root) + names = zip_path.root.namelist() + self.joinpath = zip_path.joinpath + + return dict.fromkeys(child.split(posixpath.sep, 1)[0] for child in names) + + def search(self, name): + return self.lookup(self.mtime).search(name) + + @property + def mtime(self): + with suppress(OSError): + return os.stat(self.root).st_mtime + self.lookup.cache_clear() + + @method_cache + def lookup(self, mtime): + return Lookup(self) + + +class Lookup: + def __init__(self, path: FastPath): + base = os.path.basename(path.root).lower() + base_is_egg = base.endswith(".egg") + self.infos = FreezableDefaultDict(list) + self.eggs = FreezableDefaultDict(list) + + for child in path.children(): + low = child.lower() + if low.endswith((".dist-info", ".egg-info")): + # rpartition is faster than splitext and suitable for this purpose. + name = low.rpartition(".")[0].partition("-")[0] + normalized = Prepared.normalize(name) + self.infos[normalized].append(path.joinpath(child)) + elif base_is_egg and low == "egg-info": + name = base.rpartition(".")[0].partition("-")[0] + legacy_normalized = Prepared.legacy_normalize(name) + self.eggs[legacy_normalized].append(path.joinpath(child)) + + self.infos.freeze() + self.eggs.freeze() + + def search(self, prepared): + infos = ( + self.infos[prepared.normalized] + if prepared + else itertools.chain.from_iterable(self.infos.values()) + ) + eggs = ( + self.eggs[prepared.legacy_normalized] + if prepared + else itertools.chain.from_iterable(self.eggs.values()) + ) + return itertools.chain(infos, eggs) + + +class Prepared: + """ + A prepared search for metadata on a possibly-named package. + """ + + normalized = None + legacy_normalized = None + + def __init__(self, name): + self.name = name + if name is None: + return + self.normalized = self.normalize(name) + self.legacy_normalized = self.legacy_normalize(name) + + @staticmethod + def normalize(name): + """ + PEP 503 normalization plus dashes as underscores. + """ + return re.sub(r"[-_.]+", "-", name).lower().replace('-', '_') + + @staticmethod + def legacy_normalize(name): + """ + Normalize the package name as found in the convention in + older packaging tools versions and specs. + """ + return name.lower().replace('-', '_') + + def __bool__(self): + return bool(self.name) + + +@install +class MetadataPathFinder(NullFinder, DistributionFinder): + """A degenerate finder for distribution packages on the file system. + + This finder supplies only a find_distributions() method for versions + of Python that do not have a PathFinder find_distributions(). + """ + + def find_distributions( + self, context=DistributionFinder.Context() + ) -> Iterable["PathDistribution"]: + """ + Find distributions. + + Return an iterable of all Distribution instances capable of + loading the metadata for packages matching ``context.name`` + (or all names if ``None`` indicated) along the paths in the list + of directories ``context.path``. + """ + found = self._search_paths(context.name, context.path) + return map(PathDistribution, found) + + @classmethod + def _search_paths(cls, name, paths): + """Find metadata directories in paths heuristically.""" + prepared = Prepared(name) + return itertools.chain.from_iterable( + path.search(prepared) for path in map(FastPath, paths) + ) + + def invalidate_caches(cls) -> None: + FastPath.__new__.cache_clear() + + +class PathDistribution(Distribution): + def __init__(self, path: SimplePath) -> None: + """Construct a distribution. + + :param path: SimplePath indicating the metadata directory. + """ + self._path = path + + def read_text(self, filename: StrPath) -> Optional[str]: + with suppress( + FileNotFoundError, + IsADirectoryError, + KeyError, + NotADirectoryError, + PermissionError, + ): + return self._path.joinpath(filename).read_text(encoding='utf-8') + + return None + + read_text.__doc__ = Distribution.read_text.__doc__ + + def locate_file(self, path: StrPath) -> pathlib.Path: + return self._path.parent / path + + @property + def _normalized_name(self): + """ + Performance optimization: where possible, resolve the + normalized name from the file system path. + """ + stem = os.path.basename(str(self._path)) + return ( + pass_none(Prepared.normalize)(self._name_from_stem(stem)) + or super()._normalized_name + ) + + @staticmethod + def _name_from_stem(stem): + """ + >>> PathDistribution._name_from_stem('foo-3.0.egg-info') + 'foo' + >>> PathDistribution._name_from_stem('CherryPy-3.0.dist-info') + 'CherryPy' + >>> PathDistribution._name_from_stem('face.egg-info') + 'face' + >>> PathDistribution._name_from_stem('foo.bar') + """ + filename, ext = os.path.splitext(stem) + if ext not in ('.dist-info', '.egg-info'): + return + name, sep, rest = filename.partition('-') + return name + + +def distribution(distribution_name) -> Distribution: + """Get the ``Distribution`` instance for the named package. + + :param distribution_name: The name of the distribution package as a string. + :return: A ``Distribution`` instance (or subclass thereof). + """ + return Distribution.from_name(distribution_name) + + +def distributions(**kwargs) -> Iterable[Distribution]: + """Get all ``Distribution`` instances in the current environment. + + :return: An iterable of ``Distribution`` instances. + """ + return Distribution.discover(**kwargs) + + +def metadata(distribution_name) -> _meta.PackageMetadata: + """Get the metadata for the named package. + + :param distribution_name: The name of the distribution package to query. + :return: A PackageMetadata containing the parsed metadata. + """ + return Distribution.from_name(distribution_name).metadata + + +def version(distribution_name) -> str: + """Get the version string for the named package. + + :param distribution_name: The name of the distribution package to query. + :return: The version string for the package as defined in the package's + "Version" metadata key. + """ + return distribution(distribution_name).version + + +_unique = functools.partial( + unique_everseen, + key=_py39compat.normalized_name, +) +""" +Wrapper for ``distributions`` to return unique distributions by name. +""" + + +def entry_points(**params) -> EntryPoints: + """Return EntryPoint objects for all installed packages. + + Pass selection parameters (group or name) to filter the + result to entry points matching those properties (see + EntryPoints.select()). + + :return: EntryPoints for all installed packages. + """ + eps = itertools.chain.from_iterable( + dist.entry_points for dist in _unique(distributions()) + ) + return EntryPoints(eps).select(**params) + + +def files(distribution_name) -> Optional[List[PackagePath]]: + """Return a list of files for the named package. + + :param distribution_name: The name of the distribution package to query. + :return: List of files composing the distribution. + """ + return distribution(distribution_name).files + + +def requires(distribution_name) -> Optional[List[str]]: + """ + Return a list of requirements for the named package. + + :return: An iterable of requirements, suitable for + packaging.requirement.Requirement. + """ + return distribution(distribution_name).requires + + +def packages_distributions() -> Mapping[str, List[str]]: + """ + Return a mapping of top-level packages to their + distributions. + + >>> import collections.abc + >>> pkgs = packages_distributions() + >>> all(isinstance(dist, collections.abc.Sequence) for dist in pkgs.values()) + True + """ + pkg_to_dist = collections.defaultdict(list) + for dist in distributions(): + for pkg in _top_level_declared(dist) or _top_level_inferred(dist): + pkg_to_dist[pkg].append(dist.metadata['Name']) + return dict(pkg_to_dist) + + +def _top_level_declared(dist): + return (dist.read_text('top_level.txt') or '').split() + + +def _top_level_inferred(dist): + opt_names = { + f.parts[0] if len(f.parts) > 1 else inspect.getmodulename(f) + for f in always_iterable(dist.files) + } + + @pass_none + def importable_name(name): + return '.' not in name + + return filter(importable_name, opt_names) diff --git a/.venv/lib/python3.9/site-packages/importlib_metadata/__pycache__/__init__.cpython-39.pyc b/.venv/lib/python3.9/site-packages/importlib_metadata/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bd73e7aceebe1930eb3a8826dd42b05f03fa505e GIT binary patch literal 36366 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 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/importlib_metadata/__pycache__/_adapters.cpython-39.pyc b/.venv/lib/python3.9/site-packages/importlib_metadata/__pycache__/_adapters.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4909225cd1242fdadf7a599829732e357d6c20d3 GIT binary patch literal 2961 zcmai0O>Y~=8J^i)l1qw|WaYz+nzB5<8ze^S<-E&-)CcR;$7A z-1+-w;xBc^zNF5pPZgc}cy)n7Fu_ySo7TL?%f8cd%G&L@W$pDU!Vzv-9oBj^&d~N! ze^~F;Ig@PAkbJNtDzYi-qI$|j?N45>^)VB^sGl)WmzBZtVCB^9tq8uu+Krd6%C=r| zlx=Ps5A#$=u@PyBUb8qZj>95SCW+GV!(R=HG>H=vY~`5@@+?r&j8qmJM(IfEAkl%4 zg_3b(qzIx+1V>4l1}aLl41ObzA1IY8-FfNEOlcFv2dO-iX-q!6`c!5eouVNb;m~uW zD?M511EIh1Q|FZTsb!s3IELRHOqEgG|+B| zCDFwEvRn}@v3$m`)9M$@W4&v@W5w1!m0CxA*>2dzMjC0I>?Kl-mmlrwJe5Z3_02>Z z>u;x#*~`^%ytJ+IfsD=iZy$d$ZlQCSh}l=;#=+~O4NM%+M-SI?cFNBAgkA6x$2b!pb?&0( zr~HC*HgR^?3%=F%Y(scbC_GkFw-);o-pw$)J~5Wu!;-8j{XxNBWF{DRwh7{&xh z7`|X%#ohgUD7$^tSKVjDXt%qYC#h8HNu~>ck>pvob11XJZkp_Ni(|8&XSX{)>*~bF z^#Xwh!n&Ib>8SW397+?xZqyA?6~IDwieuG+^&ir?C`lM^@-;qQnZ>6w^E)Q$&L0AS zzjZ!!k^S>NHFjmtLtRM$4^nd|=Ggw=kgA zs3`4|Y4tXW+jzgpYPs&e$UzoMtM8yOBL}E(0%{08V(Nx*PT7(QvXhUV9SqSM~Nt9=x# z(&Rp`^6_hzL0W8iB~BD0lAjW>duRwQoHOHMuxpswuF=Ay=yt3>)OGa3<_J@)yh9@jDy2f~cAR zViP=mxbfkwTS1ZunVE>P%<{(ApN9<8_KkdIj#O0GDuf(gK{P6*LVumaRaMUaf;=hn z%V~0tkKbP)bTvmhchfwMQhl#8U3cYJgchOp8f``t?c-p-fBkdkgm}iELv~L2p7Uqt zsfR3FWD~N@>D;s~Sfaj%Rp9*hQC-sAHtwcS+c+N<_ddqVKk%~g2mjZ_%T?++R@kLY zsbiHCgjm~CqzuTkB05gl;k3!`6t{k!zbKu<2+}$=Kx&EgWoOkVk6HEr82?x zR7O9YNoQbk=eTRArt+aJqxtS&e%5TL)?ldtmeyYom5be>(Zd$ESD0+GbV zy2GeYgvyK)>cfSZ(qMTdc(ltWn9zhBt2Jxm=BozUYb5Qs#w9eY5r)HDjBwo0HpB4g zC`xBDu9il9MEjFLbqj^9$YGSEwlgaB zW|wv>(=-e(Q*SDlC6#GE0!`mQ;d7__RsZGu+k4~9itjbuX2WkfytLb1we`JG7Mnaz zaczdl)vQrUGwl@2BX+7G?u5Hf#K_XA- u{=werU3{lVrKSg8h{r2|_U}&NyhrjnJBE)q;|Eb|dLD$0j#d3<)&CE3)8Y#N literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/importlib_metadata/__pycache__/_collections.cpython-39.pyc b/.venv/lib/python3.9/site-packages/importlib_metadata/__pycache__/_collections.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e188cf3f266319bd9cd0f61c898e7ce04a09d31a GIT binary patch literal 1559 zcmaJ>&x;#36qe?P?Ra-nnufNJ9@JB8=+1Nr1OkZ(G_*Ij&_lsX5b{X2XCu!{CC!pG zd)q*A>fewY_g~VrhmvFIEvLSx@orqQRG>F+r1w5a-}@wSa4;m$9{&BC{3{~lPyE?! zefW6_-97{(iKLp$$WKH@GJZv5tjGzyh-VRuBr=7ORFSu_Ol9v~1mlUMzmjqPJ>-&U zfp)jW+aEguN0AwoWEM#}i(iuk4J!-)A>hu^*CZjc9-#I7-5-ro8!^}FnOcZeyJuzL z#J7}?A%phw!l{atj+K^4WlMvR%+;(h>Qq&Zxs_t;)(KbE6{{B^NkC}8ViLfZ01N{d zmX%fMN}*X%SJs)fz@+b(ZHrZxXLpgWQDtk&Y6$?!gk`c}h@MO)OiK2I-E@MHF0C8y z#2tVCJQGc$Dml7;bbogyTOb2!v^#cu^gQGqcOP6zt7;@=7jzUR3#jpRLqCCzb+C8A zIC2gZcJzXrK=sb)1y(Pb`aWNndZVgw?E6av#5v|InSFjx2mfL z*`wS7g@=tOUWlc#dAV+C;~;{sVH;97k@KR~x&yFTb7p#Q4m^tuC#9Pksm^wGU4U+` z8~R8`pyUoXL5d*8SA#c%!vz3B;^`+TxYifY?EuUh@|L{AKsp^$|Jhbod?5-~o3k-B z7<5SuUM;hSW*>|nJl0}8mtyiY{QU!+Yz}Xd$+q^{V_g?Q+X*;b9w6hk&22ORH+TWO z*9qn;n7Sh7-v5A)`+mTACDw{_KLoOB*=qFnIsd&CdOOqST-F8WfpqgZfct}O%?qV9 zOoVL&a|?Hcdof5>3f*F<1~j?7ABV95e*e?(G*6*hWaR37P_e{vkP8ZLLkuh@KKVtI z<`ZQjy+2%KpM$m?J&DdKyiSl0uT%6#SIiIe$$Kn20L5$3_!zD^sN0#cfls4L2fB_6 zZGGa@OSf~Rz--zGW7RjfP*7a78RB-bFgzWn232HG90q0S_rjF``L1ed7!H5rD0qP* zEYv;d7VqbPM%3WQW{AYIQ=wZmP66tp^!?seXZU;tI(-P1u5I9&VfIZ6f$F;2PLP6b JwdBs1{{ng@eWm~a literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/importlib_metadata/__pycache__/_compat.cpython-39.pyc b/.venv/lib/python3.9/site-packages/importlib_metadata/__pycache__/_compat.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b2895fc16a65ade4b4cbb0f21e878bf31c1eae22 GIT binary patch literal 2201 zcmZuy&5zSY6rUOA!^visWm&|>fx3%S5ve3lq#`7QKu}Pnc3EjvAzUGM;+Z7g*d8-8 z*^+2EW&aT9t+)OO{Tq7pwI`0f_CWjIIAmE`Co}du&&PYe-|s#1_4PJ^>+8Rsi5os4 zf8yZd3vA>*H1ivDf(V+Du)5Qbx-knG+*#^nwXjCXhzRc&8rJ8VV6B0*5jH4!M)E5) zBAc_268yP$e)aA-$=MAe>Y{N>!j@bimq?^k7}APwM)O ztjP^Y4$jDpOJv9{k#IBbpiOL4BSH5`_sl!+n(TIIS^ptVRNnQ{z$AMF6H zhbcEE5HePpTcv{`wB@nff*S$+&CK*NX?av|JMOjJx(7m-z>Z}w;PIqT+6IpjV|6l^ z;;PEmz^c=01D*@Fnp-Qg!om(J5Q*V~RJzTEF2-Ou&12k#r-_{fYUth>E7D}(mjCyt zp0jsrr3cQIt(CWBgBOL&MR|UCOjKE3X%T10PA#Pcno9W<=O5h!HPTMwi(QVOyv_fgTQP& z#`(y0gPe5Gm)_2tU5)G`C+1T zMc;pH^7iBX;JfyjXer-TUD5H&k7-R2i z%zr=s;(vwKyPK+*r{-R7xqKJ23MZuCmPRS)CU$3_>oR=~pHU^B9Ijhn`wUv8o|a$V zq|SCqJ@DSC=RQshppyjaAJje?da@>%@Q%p>>Yl8J4N(L9>Kc)rA2?1P1Urvi8tw$w z_=E>L-vc(TOf1b^;E~%Pm!P8nK{AlnC>Mm^d|44YwL!)~bMX4Kx|USKd8-TMj+N^v zG=rW9SD|)Y^uSJy>yarNlb`Ya2_$6;kBBfoZ{u>@i?I3jNyY4~ChT^br5{B(&p;)5 z9VV8YD0(^NX=N&#Q6y9h3hF@JwMh&KJXS(qz?o}qn<~hUe89S)Q5X)woA6-pJLJ<^ zozZs7Sx@O29mMt!r*1=M$s76>ls#dP&*X?+z~I^Zc}0dem>TOc>5%Kh6-w|eo}_?& ziaAwqZ4;gbkZ?f9-fVBS%*Pxwh9UcL3z?F##{hO+=yzeu@-f}fS=t73);lgdoy z7(IM~Sm_<|G;Ag&DrQi;T(GN%%MKdhqu?BPgz+GGq|u@JbGUceJ1k=FP_*++WqNy1 zaK1|>@<%8(FS=(-rc9;%D=?#M*jWK(Mk6@L1bp~_CH%ydYu~z>`i<5~hi9vK5vFu* zz)j-{sLKNfw>!M-fI9JQl;lGNp9M&6|APZzbKhzld=$}1SpMCL<$I_}gTDC`eyrp9 W9;2JI!?rws-QU_c=eOH*+xr(KeLYtI literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/importlib_metadata/__pycache__/_functools.cpython-39.pyc b/.venv/lib/python3.9/site-packages/importlib_metadata/__pycache__/_functools.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d853754c109a48359dcd26d1bdfe926b53c993ca GIT binary patch literal 3149 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 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/importlib_metadata/__pycache__/_py39compat.cpython-39.pyc b/.venv/lib/python3.9/site-packages/importlib_metadata/__pycache__/_py39compat.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f03b365d3f04c744dc47b4d71bc04bd872a5df3f GIT binary patch literal 1195 zcmZ9ML2uML5PHoGoK98J7^ zWYu@#o%sbEK;pt*@|6=uuACUB?E-b=iR~GC#`8_WH#YhN+s$u>;t?a{i<|t~e4PA+ zy?Kd3kccq=9PZtmn>tHNDqd>7p&-&K^Bx@7E)40(q|+KvQP$+$!MF1-g{33 zBK)6aYciC55#fAI^l`o})}NAOuuS^9x~#cT6ICeF#szOB#Q!QYj}KZi*HyeTyp`?@ zZ}-1qI3)4y#ppTW1@pzn{?~)>Sr;NwL?p)|$vk}$h+ zai_g>-<*1rdjJ|N0aq7xZRNp+Gif*)*{H;U;D#q2L}=7mekQPqr$pOm_hoc zUQE)7R)vJ?s%mOEb(c$r$Fe$33pGjWjw#|y8)fA6nonQznQT&3)*1|2%9uZ+Xny^df(2@B=ZH BEwKOq literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/importlib_metadata/__pycache__/_text.cpython-39.pyc b/.venv/lib/python3.9/site-packages/importlib_metadata/__pycache__/_text.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a249333cbfbb7a4ea73e7a90c0c358831c95e7b3 GIT binary patch literal 3092 zcmb7GOK%%D5GHpeTUKPpc{Go<2R0W;fFm7R6lhbsO;RKc&?G?JBB+dXK`m)(<9&ss zX;*#($qG9ncgUG;MOe=f(ROt9ubakLmD|fhmyxcR7CZFh^ln=-Cjk|Rnne$539+u_Y`ECW{pUi zK_Wb#`vd8ts(hgfytD!#NskKBa|G?V=;nZcN$a3j6*FQM<};!u=HP!;)Ws$Eue~Gd zR5Zl=L9I6@7Q|(ER+pFT(?xORptetY4RKXmgXi<&mbf8q9?;&xJ5nLN%K-LvR)3U) zLW+l6Oa2Dn)EQj&nGbUo#9GEWFu_PNZB!6%FhAs4-(!6_;3KKotD!b53^uLaZk5}< zObzpsDCG)P$k#(2vaNxPnHfly#NiGbaTRbABryxL_0*}`y(=p#OtWQHKx#DyG7J;8 zm8eiO+f&PyS@Somd|{Q=!k=m=i$zm~+-^0W0}4#h zcZD0>(PA#BcQ53lP7#a9?SM-MgAodx96)wy7D{EKGp%@Q=(b z%(0~ZO8Um`h5Q1IQLiw^CD?Z?b8T9St*0U=pxe;{NK>3Z=GM>mh}Ast7Rq(=V?n8s zhrDB&Hak-dU|p1G{RDAyER2>*Y-s!4`?yVL0BV`xzdcPw^;Ud)|`^J)-tAJi4FaF_xb9`-L7+>*M=%eBpRP z7R-}Dy3ixC4{v0f(Kaw-!1X}Qg7%oIIrz)dicHCKTk||z;(5>E(Kx%;rs|pN&qRx_ zarCdRgD_;@`aX1ktbpNYg2BzIdYDROL1YaGB8d$LHhP{dHt=~49^xw&o=kwLFTm;_ zX#S+)H>WTaM$X`x%E=yPh3?Z4G*6rEe)($xuV?HU#?w@Cg~zFFpP$=QVMON=(1j!5 zuL%}sccFv@RA@(Y;LZbgb{9*|Y~!Da7;)Qn zM05F(aY*w+qo{+E7ocii>21f7A#?Sxe~J<6HW>M5;-60Wx0^purjUF-W_o-f2JB*s zgTcT6AK(T)9Kru9_sDtc?zxARJ+kKQ!1O0+UKdv=o4TKeqXdJ z7HF?#XGW^57V&L6$!1e-;7NQs0QAn`)`*GF>(e3RW*zkv=xiA6#SEGt<`FblR`I2CgycSazKw}) zi*C!0{cHJ$)z@gGMl1Jf?)cYmYgLCXz}&8CSJl^OwC=4BW8Wl6sI5~ds8KnOt?feC TgS7Tj9^49+&!(s5G%EiC_u<#Z literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/importlib_metadata/_adapters.py b/.venv/lib/python3.9/site-packages/importlib_metadata/_adapters.py new file mode 100644 index 0000000..e33cba5 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/importlib_metadata/_adapters.py @@ -0,0 +1,90 @@ +import functools +import warnings +import re +import textwrap +import email.message + +from ._text import FoldedCase +from ._compat import pypy_partial + + +# Do not remove prior to 2024-01-01 or Python 3.14 +_warn = functools.partial( + warnings.warn, + "Implicit None on return values is deprecated and will raise KeyErrors.", + DeprecationWarning, + stacklevel=pypy_partial(2), +) + + +class Message(email.message.Message): + multiple_use_keys = set( + map( + FoldedCase, + [ + 'Classifier', + 'Obsoletes-Dist', + 'Platform', + 'Project-URL', + 'Provides-Dist', + 'Provides-Extra', + 'Requires-Dist', + 'Requires-External', + 'Supported-Platform', + 'Dynamic', + ], + ) + ) + """ + Keys that may be indicated multiple times per PEP 566. + """ + + def __new__(cls, orig: email.message.Message): + res = super().__new__(cls) + vars(res).update(vars(orig)) + return res + + def __init__(self, *args, **kwargs): + self._headers = self._repair_headers() + + # suppress spurious error from mypy + def __iter__(self): + return super().__iter__() + + def __getitem__(self, item): + """ + Warn users that a ``KeyError`` can be expected when a + mising key is supplied. Ref python/importlib_metadata#371. + """ + res = super().__getitem__(item) + if res is None: + _warn() + return res + + def _repair_headers(self): + def redent(value): + "Correct for RFC822 indentation" + if not value or '\n' not in value: + return value + return textwrap.dedent(' ' * 8 + value) + + headers = [(key, redent(value)) for key, value in vars(self)['_headers']] + if self._payload: + headers.append(('Description', self.get_payload())) + return headers + + @property + def json(self): + """ + Convert PackageMetadata to a JSON-compatible format + per PEP 0566. + """ + + def transform(key): + value = self.get_all(key) if key in self.multiple_use_keys else self[key] + if key == 'Keywords': + value = re.split(r'\s+', value) + tk = key.lower().replace('-', '_') + return tk, value + + return dict(map(transform, map(FoldedCase, self))) diff --git a/.venv/lib/python3.9/site-packages/importlib_metadata/_collections.py b/.venv/lib/python3.9/site-packages/importlib_metadata/_collections.py new file mode 100644 index 0000000..cf0954e --- /dev/null +++ b/.venv/lib/python3.9/site-packages/importlib_metadata/_collections.py @@ -0,0 +1,30 @@ +import collections + + +# from jaraco.collections 3.3 +class FreezableDefaultDict(collections.defaultdict): + """ + Often it is desirable to prevent the mutation of + a default dict after its initial construction, such + as to prevent mutation during iteration. + + >>> dd = FreezableDefaultDict(list) + >>> dd[0].append('1') + >>> dd.freeze() + >>> dd[1] + [] + >>> len(dd) + 1 + """ + + def __missing__(self, key): + return getattr(self, '_frozen', super().__missing__)(key) + + def freeze(self): + self._frozen = lambda key: self.default_factory() + + +class Pair(collections.namedtuple('Pair', 'name value')): + @classmethod + def parse(cls, text): + return cls(*map(str.strip, text.split("=", 1))) diff --git a/.venv/lib/python3.9/site-packages/importlib_metadata/_compat.py b/.venv/lib/python3.9/site-packages/importlib_metadata/_compat.py new file mode 100644 index 0000000..638e779 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/importlib_metadata/_compat.py @@ -0,0 +1,82 @@ +import os +import sys +import platform + +from typing import Union + + +__all__ = ['install', 'NullFinder', 'Protocol'] + + +try: + from typing import Protocol +except ImportError: # pragma: no cover + # Python 3.7 compatibility + from typing_extensions import Protocol # type: ignore + + +def install(cls): + """ + Class decorator for installation on sys.meta_path. + + Adds the backport DistributionFinder to sys.meta_path and + attempts to disable the finder functionality of the stdlib + DistributionFinder. + """ + sys.meta_path.append(cls()) + disable_stdlib_finder() + return cls + + +def disable_stdlib_finder(): + """ + Give the backport primacy for discovering path-based distributions + by monkey-patching the stdlib O_O. + + See #91 for more background for rationale on this sketchy + behavior. + """ + + def matches(finder): + return getattr( + finder, '__module__', None + ) == '_frozen_importlib_external' and hasattr(finder, 'find_distributions') + + for finder in filter(matches, sys.meta_path): # pragma: nocover + del finder.find_distributions + + +class NullFinder: + """ + A "Finder" (aka "MetaClassFinder") that never finds any modules, + but may find distributions. + """ + + @staticmethod + def find_spec(*args, **kwargs): + return None + + # In Python 2, the import system requires finders + # to have a find_module() method, but this usage + # is deprecated in Python 3 in favor of find_spec(). + # For the purposes of this finder (i.e. being present + # on sys.meta_path but having no other import + # system functionality), the two methods are identical. + find_module = find_spec + + +def pypy_partial(val): + """ + Adjust for variable stacklevel on partial under PyPy. + + Workaround for #327. + """ + is_pypy = platform.python_implementation() == 'PyPy' + return val + is_pypy + + +if sys.version_info >= (3, 9): + StrPath = Union[str, os.PathLike[str]] +else: + # PathLike is only subscriptable at runtime in 3.9+ + StrPath = Union[str, "os.PathLike[str]"] # pragma: no cover diff --git a/.venv/lib/python3.9/site-packages/importlib_metadata/_functools.py b/.venv/lib/python3.9/site-packages/importlib_metadata/_functools.py new file mode 100644 index 0000000..71f66bd --- /dev/null +++ b/.venv/lib/python3.9/site-packages/importlib_metadata/_functools.py @@ -0,0 +1,104 @@ +import types +import functools + + +# from jaraco.functools 3.3 +def method_cache(method, cache_wrapper=None): + """ + Wrap lru_cache to support storing the cache data in the object instances. + + Abstracts the common paradigm where the method explicitly saves an + underscore-prefixed protected property on first call and returns that + subsequently. + + >>> class MyClass: + ... calls = 0 + ... + ... @method_cache + ... def method(self, value): + ... self.calls += 1 + ... return value + + >>> a = MyClass() + >>> a.method(3) + 3 + >>> for x in range(75): + ... res = a.method(x) + >>> a.calls + 75 + + Note that the apparent behavior will be exactly like that of lru_cache + except that the cache is stored on each instance, so values in one + instance will not flush values from another, and when an instance is + deleted, so are the cached values for that instance. + + >>> b = MyClass() + >>> for x in range(35): + ... res = b.method(x) + >>> b.calls + 35 + >>> a.method(0) + 0 + >>> a.calls + 75 + + Note that if method had been decorated with ``functools.lru_cache()``, + a.calls would have been 76 (due to the cached value of 0 having been + flushed by the 'b' instance). + + Clear the cache with ``.cache_clear()`` + + >>> a.method.cache_clear() + + Same for a method that hasn't yet been called. + + >>> c = MyClass() + >>> c.method.cache_clear() + + Another cache wrapper may be supplied: + + >>> cache = functools.lru_cache(maxsize=2) + >>> MyClass.method2 = method_cache(lambda self: 3, cache_wrapper=cache) + >>> a = MyClass() + >>> a.method2() + 3 + + Caution - do not subsequently wrap the method with another decorator, such + as ``@property``, which changes the semantics of the function. + + See also + http://code.activestate.com/recipes/577452-a-memoize-decorator-for-instance-methods/ + for another implementation and additional justification. + """ + cache_wrapper = cache_wrapper or functools.lru_cache() + + def wrapper(self, *args, **kwargs): + # it's the first call, replace the method with a cached, bound method + bound_method = types.MethodType(method, self) + cached_method = cache_wrapper(bound_method) + setattr(self, method.__name__, cached_method) + return cached_method(*args, **kwargs) + + # Support cache clear even before cache has been created. + wrapper.cache_clear = lambda: None + + return wrapper + + +# From jaraco.functools 3.3 +def pass_none(func): + """ + Wrap func so it's not called if its first param is None + + >>> print_text = pass_none(print) + >>> print_text('text') + text + >>> print_text(None) + """ + + @functools.wraps(func) + def wrapper(param, *args, **kwargs): + if param is not None: + return func(param, *args, **kwargs) + + return wrapper diff --git a/.venv/lib/python3.9/site-packages/importlib_metadata/_itertools.py b/.venv/lib/python3.9/site-packages/importlib_metadata/_itertools.py new file mode 100644 index 0000000..d4ca9b9 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/importlib_metadata/_itertools.py @@ -0,0 +1,73 @@ +from itertools import filterfalse + + +def unique_everseen(iterable, key=None): + "List unique elements, preserving order. Remember all elements ever seen." + # unique_everseen('AAAABBBCCDAABBB') --> A B C D + # unique_everseen('ABBCcAD', str.lower) --> A B C D + seen = set() + seen_add = seen.add + if key is None: + for element in filterfalse(seen.__contains__, iterable): + seen_add(element) + yield element + else: + for element in iterable: + k = key(element) + if k not in seen: + seen_add(k) + yield element + + +# copied from more_itertools 8.8 +def always_iterable(obj, base_type=(str, bytes)): + """If *obj* is iterable, return an iterator over its items:: + + >>> obj = (1, 2, 3) + >>> list(always_iterable(obj)) + [1, 2, 3] + + If *obj* is not iterable, return a one-item iterable containing *obj*:: + + >>> obj = 1 + >>> list(always_iterable(obj)) + [1] + + If *obj* is ``None``, return an empty iterable: + + >>> obj = None + >>> list(always_iterable(None)) + [] + + By default, binary and text strings are not considered iterable:: + + >>> obj = 'foo' + >>> list(always_iterable(obj)) + ['foo'] + + If *base_type* is set, objects for which ``isinstance(obj, base_type)`` + returns ``True`` won't be considered iterable. + + >>> obj = {'a': 1} + >>> list(always_iterable(obj)) # Iterate over the dict's keys + ['a'] + >>> list(always_iterable(obj, base_type=dict)) # Treat dicts as a unit + [{'a': 1}] + + Set *base_type* to ``None`` to avoid any special handling and treat objects + Python considers iterable as iterable: + + >>> obj = 'foo' + >>> list(always_iterable(obj, base_type=None)) + ['f', 'o', 'o'] + """ + if obj is None: + return iter(()) + + if (base_type is not None) and isinstance(obj, base_type): + return iter((obj,)) + + try: + return iter(obj) + except TypeError: + return iter((obj,)) diff --git a/.venv/lib/python3.9/site-packages/importlib_metadata/_meta.py b/.venv/lib/python3.9/site-packages/importlib_metadata/_meta.py new file mode 100644 index 0000000..0c7e879 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/importlib_metadata/_meta.py @@ -0,0 +1,63 @@ +from ._compat import Protocol +from typing import Any, Dict, Iterator, List, Optional, TypeVar, Union, overload + + +_T = TypeVar("_T") + + +class PackageMetadata(Protocol): + def __len__(self) -> int: + ... # pragma: no cover + + def __contains__(self, item: str) -> bool: + ... # pragma: no cover + + def __getitem__(self, key: str) -> str: + ... # pragma: no cover + + def __iter__(self) -> Iterator[str]: + ... # pragma: no cover + + @overload + def get(self, name: str, failobj: None = None) -> Optional[str]: + ... # pragma: no cover + + @overload + def get(self, name: str, failobj: _T) -> Union[str, _T]: + ... # pragma: no cover + + # overload per python/importlib_metadata#435 + @overload + def get_all(self, name: str, failobj: None = None) -> Optional[List[Any]]: + ... # pragma: no cover + + @overload + def get_all(self, name: str, failobj: _T) -> Union[List[Any], _T]: + """ + Return all values associated with a possibly multi-valued key. + """ + + @property + def json(self) -> Dict[str, Union[str, List[str]]]: + """ + A JSON-compatible form of the metadata. + """ + + +class SimplePath(Protocol[_T]): + """ + A minimal subset of pathlib.Path required by PathDistribution. + """ + + def joinpath(self, other: Union[str, _T]) -> _T: + ... # pragma: no cover + + def __truediv__(self, other: Union[str, _T]) -> _T: + ... # pragma: no cover + + @property + def parent(self) -> _T: + ... # pragma: no cover + + def read_text(self) -> str: + ... # pragma: no cover diff --git a/.venv/lib/python3.9/site-packages/importlib_metadata/_py39compat.py b/.venv/lib/python3.9/site-packages/importlib_metadata/_py39compat.py new file mode 100644 index 0000000..cde4558 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/importlib_metadata/_py39compat.py @@ -0,0 +1,35 @@ +""" +Compatibility layer with Python 3.8/3.9 +""" +from typing import TYPE_CHECKING, Any, Optional + +if TYPE_CHECKING: # pragma: no cover + # Prevent circular imports on runtime. + from . import Distribution, EntryPoint +else: + Distribution = EntryPoint = Any + + +def normalized_name(dist: Distribution) -> Optional[str]: + """ + Honor name normalization for distributions that don't provide ``_normalized_name``. + """ + try: + return dist._normalized_name + except AttributeError: + from . import Prepared # -> delay to prevent circular imports. + + return Prepared.normalize(getattr(dist, "name", None) or dist.metadata['Name']) + + +def ep_matches(ep: EntryPoint, **params) -> bool: + """ + Workaround for ``EntryPoint`` objects without the ``matches`` method. + """ + try: + return ep.matches(**params) + except AttributeError: + from . import EntryPoint # -> delay to prevent circular imports. + + # Reconstruct the EntryPoint object to make sure it is compatible. + return EntryPoint(ep.name, ep.value, ep.group).matches(**params) diff --git a/.venv/lib/python3.9/site-packages/importlib_metadata/_text.py b/.venv/lib/python3.9/site-packages/importlib_metadata/_text.py new file mode 100644 index 0000000..c88cfbb --- /dev/null +++ b/.venv/lib/python3.9/site-packages/importlib_metadata/_text.py @@ -0,0 +1,99 @@ +import re + +from ._functools import method_cache + + +# from jaraco.text 3.5 +class FoldedCase(str): + """ + A case insensitive string class; behaves just like str + except compares equal when the only variation is case. + + >>> s = FoldedCase('hello world') + + >>> s == 'Hello World' + True + + >>> 'Hello World' == s + True + + >>> s != 'Hello World' + False + + >>> s.index('O') + 4 + + >>> s.split('O') + ['hell', ' w', 'rld'] + + >>> sorted(map(FoldedCase, ['GAMMA', 'alpha', 'Beta'])) + ['alpha', 'Beta', 'GAMMA'] + + Sequence membership is straightforward. + + >>> "Hello World" in [s] + True + >>> s in ["Hello World"] + True + + You may test for set inclusion, but candidate and elements + must both be folded. + + >>> FoldedCase("Hello World") in {s} + True + >>> s in {FoldedCase("Hello World")} + True + + String inclusion works as long as the FoldedCase object + is on the right. + + >>> "hello" in FoldedCase("Hello World") + True + + But not if the FoldedCase object is on the left: + + >>> FoldedCase('hello') in 'Hello World' + False + + In that case, use in_: + + >>> FoldedCase('hello').in_('Hello World') + True + + >>> FoldedCase('hello') > FoldedCase('Hello') + False + """ + + def __lt__(self, other): + return self.lower() < other.lower() + + def __gt__(self, other): + return self.lower() > other.lower() + + def __eq__(self, other): + return self.lower() == other.lower() + + def __ne__(self, other): + return self.lower() != other.lower() + + def __hash__(self): + return hash(self.lower()) + + def __contains__(self, other): + return super().lower().__contains__(other.lower()) + + def in_(self, other): + "Does self appear in other?" + return self in FoldedCase(other) + + # cache lower since it's likely to be called frequently. + @method_cache + def lower(self): + return super().lower() + + def index(self, sub): + return self.lower().index(sub.lower()) + + def split(self, splitter=' ', maxsplit=0): + pattern = re.compile(re.escape(splitter), re.I) + return pattern.split(self, maxsplit) diff --git a/.venv/lib/python3.9/site-packages/importlib_metadata/py.typed b/.venv/lib/python3.9/site-packages/importlib_metadata/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/.venv/lib/python3.9/site-packages/itsdangerous-2.1.2.dist-info/INSTALLER b/.venv/lib/python3.9/site-packages/itsdangerous-2.1.2.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/.venv/lib/python3.9/site-packages/itsdangerous-2.1.2.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.venv/lib/python3.9/site-packages/itsdangerous-2.1.2.dist-info/LICENSE.rst b/.venv/lib/python3.9/site-packages/itsdangerous-2.1.2.dist-info/LICENSE.rst new file mode 100644 index 0000000..7b190ca --- /dev/null +++ b/.venv/lib/python3.9/site-packages/itsdangerous-2.1.2.dist-info/LICENSE.rst @@ -0,0 +1,28 @@ +Copyright 2011 Pallets + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/.venv/lib/python3.9/site-packages/itsdangerous-2.1.2.dist-info/METADATA b/.venv/lib/python3.9/site-packages/itsdangerous-2.1.2.dist-info/METADATA new file mode 100644 index 0000000..1d935ed --- /dev/null +++ b/.venv/lib/python3.9/site-packages/itsdangerous-2.1.2.dist-info/METADATA @@ -0,0 +1,97 @@ +Metadata-Version: 2.1 +Name: itsdangerous +Version: 2.1.2 +Summary: Safely pass data to untrusted environments and back. +Home-page: https://palletsprojects.com/p/itsdangerous/ +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://itsdangerous.palletsprojects.com/ +Project-URL: Changes, https://itsdangerous.palletsprojects.com/changes/ +Project-URL: Source Code, https://github.com/pallets/itsdangerous/ +Project-URL: Issue Tracker, https://github.com/pallets/itsdangerous/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.7 +Description-Content-Type: text/x-rst +License-File: LICENSE.rst + +ItsDangerous +============ + +... so better sign this + +Various helpers to pass data to untrusted environments and to get it +back safe and sound. Data is cryptographically signed to ensure that a +token has not been tampered with. + +It's possible to customize how data is serialized. Data is compressed as +needed. A timestamp can be added and verified automatically while +loading a token. + + +Installing +---------- + +Install and update using `pip`_: + +.. code-block:: text + + pip install -U itsdangerous + +.. _pip: https://pip.pypa.io/en/stable/getting-started/ + + +A Simple Example +---------------- + +Here's how you could generate a token for transmitting a user's id and +name between web requests. + +.. code-block:: python + + from itsdangerous import URLSafeSerializer + auth_s = URLSafeSerializer("secret key", "auth") + token = auth_s.dumps({"id": 5, "name": "itsdangerous"}) + + print(token) + # eyJpZCI6NSwibmFtZSI6Iml0c2Rhbmdlcm91cyJ9.6YP6T0BaO67XP--9UzTrmurXSmg + + data = auth_s.loads(token) + print(data["name"]) + # itsdangerous + + +Donate +------ + +The Pallets organization develops and supports ItsDangerous 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://itsdangerous.palletsprojects.com/ +- Changes: https://itsdangerous.palletsprojects.com/changes/ +- PyPI Releases: https://pypi.org/project/ItsDangerous/ +- Source Code: https://github.com/pallets/itsdangerous/ +- Issue Tracker: https://github.com/pallets/itsdangerous/issues/ +- Website: https://palletsprojects.com/p/itsdangerous/ +- Twitter: https://twitter.com/PalletsTeam +- Chat: https://discord.gg/pallets + + diff --git a/.venv/lib/python3.9/site-packages/itsdangerous-2.1.2.dist-info/RECORD b/.venv/lib/python3.9/site-packages/itsdangerous-2.1.2.dist-info/RECORD new file mode 100644 index 0000000..1ddee7d --- /dev/null +++ b/.venv/lib/python3.9/site-packages/itsdangerous-2.1.2.dist-info/RECORD @@ -0,0 +1,23 @@ +itsdangerous-2.1.2.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +itsdangerous-2.1.2.dist-info/LICENSE.rst,sha256=Y68JiRtr6K0aQlLtQ68PTvun_JSOIoNnvtfzxa4LCdc,1475 +itsdangerous-2.1.2.dist-info/METADATA,sha256=ThrHIJQ_6XlfbDMCAVe_hawT7IXiIxnTBIDrwxxtucQ,2928 +itsdangerous-2.1.2.dist-info/RECORD,, +itsdangerous-2.1.2.dist-info/WHEEL,sha256=G16H4A3IeoQmnOrYV4ueZGKSjhipXx8zc8nu9FGlvMA,92 +itsdangerous-2.1.2.dist-info/top_level.txt,sha256=gKN1OKLk81i7fbWWildJA88EQ9NhnGMSvZqhfz9ICjk,13 +itsdangerous/__init__.py,sha256=n4mkyjlIVn23pgsgCIw0MJKPdcHIetyeRpe5Fwsn8qg,876 +itsdangerous/__pycache__/__init__.cpython-39.pyc,, +itsdangerous/__pycache__/_json.cpython-39.pyc,, +itsdangerous/__pycache__/encoding.cpython-39.pyc,, +itsdangerous/__pycache__/exc.cpython-39.pyc,, +itsdangerous/__pycache__/serializer.cpython-39.pyc,, +itsdangerous/__pycache__/signer.cpython-39.pyc,, +itsdangerous/__pycache__/timed.cpython-39.pyc,, +itsdangerous/__pycache__/url_safe.cpython-39.pyc,, +itsdangerous/_json.py,sha256=wIhs_7-_XZolmyr-JvKNiy_LgAcfevYR0qhCVdlIhg8,450 +itsdangerous/encoding.py,sha256=pgh86snHC76dPLNCnPlrjR5SaYL_M8H-gWRiiLNbhCU,1419 +itsdangerous/exc.py,sha256=VFxmP2lMoSJFqxNMzWonqs35ROII4-fvCBfG0v1Tkbs,3206 +itsdangerous/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +itsdangerous/serializer.py,sha256=zgZ1-U705jHDpt62x_pmLJdryEKDNAbt5UkJtnkcCSw,11144 +itsdangerous/signer.py,sha256=QUH0iX0in-OTptMAXKU5zWMwmOCXn1fsDsubXiGdFN4,9367 +itsdangerous/timed.py,sha256=5CBWLds4Nm8-3bFVC8RxNzFjx6PSwjch8wuZ5cwcHFI,8174 +itsdangerous/url_safe.py,sha256=5bC4jSKOjWNRkWrFseifWVXUnHnPgwOLROjiOwb-eeo,2402 diff --git a/.venv/lib/python3.9/site-packages/itsdangerous-2.1.2.dist-info/WHEEL b/.venv/lib/python3.9/site-packages/itsdangerous-2.1.2.dist-info/WHEEL new file mode 100644 index 0000000..becc9a6 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/itsdangerous-2.1.2.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.37.1) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/.venv/lib/python3.9/site-packages/itsdangerous-2.1.2.dist-info/top_level.txt b/.venv/lib/python3.9/site-packages/itsdangerous-2.1.2.dist-info/top_level.txt new file mode 100644 index 0000000..e163955 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/itsdangerous-2.1.2.dist-info/top_level.txt @@ -0,0 +1 @@ +itsdangerous diff --git a/.venv/lib/python3.9/site-packages/itsdangerous/__init__.py b/.venv/lib/python3.9/site-packages/itsdangerous/__init__.py new file mode 100644 index 0000000..fdb2dfd --- /dev/null +++ b/.venv/lib/python3.9/site-packages/itsdangerous/__init__.py @@ -0,0 +1,19 @@ +from .encoding import base64_decode as base64_decode +from .encoding import base64_encode as base64_encode +from .encoding import want_bytes as want_bytes +from .exc import BadData as BadData +from .exc import BadHeader as BadHeader +from .exc import BadPayload as BadPayload +from .exc import BadSignature as BadSignature +from .exc import BadTimeSignature as BadTimeSignature +from .exc import SignatureExpired as SignatureExpired +from .serializer import Serializer as Serializer +from .signer import HMACAlgorithm as HMACAlgorithm +from .signer import NoneAlgorithm as NoneAlgorithm +from .signer import Signer as Signer +from .timed import TimedSerializer as TimedSerializer +from .timed import TimestampSigner as TimestampSigner +from .url_safe import URLSafeSerializer as URLSafeSerializer +from .url_safe import URLSafeTimedSerializer as URLSafeTimedSerializer + +__version__ = "2.1.2" diff --git a/.venv/lib/python3.9/site-packages/itsdangerous/__pycache__/__init__.cpython-39.pyc b/.venv/lib/python3.9/site-packages/itsdangerous/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..da9d51c0298913a9ee291bbada235433f60f957c GIT binary patch literal 869 zcmaiyOOMkq6or$vNz*jxbEd71&j~{Az4U~TbtAx=ONoI?ffJD z536ihv1H4Nd#zE`vLR89zH@wiWk;9Bap=Hv`TH}IO~?6(=J08Nc>$mP0m2;SN+)-T z>moOR8>C^m3EU)2%PrZ;J>psRfPLay_JP}^ZMh8`kic>P+#wyy9pI3JmP6UiBNAEe z$~f|mYl$=@~0#C>UctpAKYGH?;N zxyTA?)(V>rz<(4uKg#yz&9)MXs^PF|h}j6F z?xyL6D=o?*O}{(p3>Q%Td`VVi&XY`KD%n=+MY1S`=Lr7;37Du(_tGQpkMb@c*#{y_1nlT6~TW4AB){;Rm;rn?Z2CxK0YwA zuC*CSQ#GX;Ep1hfR+VZBJ#q`_M%J0Uwxg3p?zDwxdYmxdujRU^r1CwZU1Q1&sQ9zc zMjCcOEVAXu6+I)@2$xRabtv!`A7TcZP=lAh64Qt6=0m!uRyv)Vxk*3O%`BZ&d7;gA zUfNn`F0aaTe5}jkw8&>^y>^SLd@_EPTBvRtsymW%ZPVOYCCj-6NH%Rh8`o>ogZvSQ zWuOQ>Z3v|uhlmD@(fgcH ngZ?D+0nS5*wddA#Ue3*3e2d}#<@3>tuJV^SYZe<4(TM*7nDO75 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/itsdangerous/__pycache__/encoding.cpython-39.pyc b/.venv/lib/python3.9/site-packages/itsdangerous/__pycache__/encoding.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..43d14e6c5039898437e3ade8356d6958133b5e9f GIT binary patch literal 1882 zcma)6&2Jk;6rY)0uRmft&X=3C6)Y-3Si*J@A%H|H)WQJ?1wm6HwuIH%GjTR-cCDG& z5L*cc(i`G0Xpx*F{|bM?Tsh^4xb*;%rhI^FCzrWUI>RXR$C!Z=_Lw4)u-aU`NH%Sb9f zek8I1+p{nFd)a~LkK{=9kH-0+Kgi-l$S!CZi=mFQwBI`v>0v*K2mSFx?`7#5y?6R5 z)}lL(hF?V^q582_JW5AGX1VHL2G$!-OzrC^)!|^Gg_;9v3Vk3>jnI7b&r^?iaE8k&j5HBR|{qt!+P( zz7|Ki=Wp!^Uka5c8ow7GBPnV=EEVkvUuS- z**5u{@bU@$hNiV*zfEq#3BXyIetZEZ7g#y~CqU7)==={{{a9JhEVc!HrWC1R92+kl zrI{2R_E#Co1qkgNVVK_dFg=WtnEM4x-2x`=b7-N>PPqWzNkl3a;res%7mk|h)?_R` zz-}_N&!QwRc}zcV6@1a4R3fNu27P9Vl-n%@6*gwy`#f+MdzNir%y(hyBqAK~<=; zqKS-`TvphN@a*rg)UV!s9WE#=BSPgZprEX5{nYZGJ%UkPlpB^NdW8OP7%-=lJ8L*% z+|6^G9nwc)ryM$Hm7x*GaqW>>B$(nHejJPjyM|H#G2B>6o!<<@nT9=e^3u~2yY6^L zv=%sZc|3ygB{AngE4*mYC>ifX0|7Q4SlfY3!^S$QMOR!Leexx==?)_W_wv*py@E#$ z0AV4Q7}pgLEF5+gRNg(vc%F#6h@wKQD%Ad1oi$zZJEIa2X2YT#V@u4MO{N9^xZctZf@Gb;ntz~$k;LUP20G=!yAVtce(e_*>oQn z4)nk&e&K!1_)V6x=^sC_SP34)JHa;N0S;wBwiB>)KTw&>gZoScB8{?1nhPm-FSs*| zRe)bU6j-4|D?r)FWWbUn5POjr=W&+OJj;Xca&?oX10l1C3b@#bQ=xh-y`^apYv`+MWb zc7HpI6Ctm}sTzxjwCeYEMY`Kh;_d!;KObi4$Gy+`D$d1~F^hh|byYvk6|Xez!yLWw zzVvbODyq`RnQe30nVG*=-41}eQsfhvMkcVxOA zW*NQ03qzI5Fgy=IX%m{$)y0{54lICiQZ^D{ShT`$l<`SI{dO4sI$_Cd23zqg!my21 zS`|sU6w5QyMIuRH-NgmS`6S&!5>}+UzPWEY?m0(~onSOULq)xhrp{>imLnS6KD0Ee z!RCj{H6D9K8%o`d2PuQ;#Psrl%q&pVaKJO6f;7v65zC`tZ=-8z{0nc)_LGe9h%QR0t7$*hWG8iKAZJ$znT<#qHhpy42eBn?Py4Q(845;Dgn)QSq;^g>;O8Xq>gzU


    `qP31Uq9I%`-KjC%(@gg@)0=?xTfv~>^snQ_0KJELip((MHvW-IM}o5CXqG^; zG}+gd7_#wLAYD=hRYJyahAGlwnLdYXSHMuS9d$+>P%OJ2=R-=sdWSg%&q=ITug%=W zskw_=?pv&6G9JV!OF|)KCZj4ElZB{H-eaSxx6z#DIR_5>$EMUNHRK9!qrIFuWDVb*AU$MqfCQy#X(3o=OdhlBE|_p%Mgpf%}kpH|SVe4EJfl zeN5wN=|FIzr;@%nL8i)FFQ2?x(CNoA1+i=J#5$Q~si{Vz8FTOyOF%MB8+x_TJ8brG zpMBbLE2od@EN=L5Hwl6f2z{^)A~Yzb{0pcVEyFTj_1*Jf%Ac zY*q4S@%+;Na;%&>8=!RzCVT`*xRg literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/itsdangerous/__pycache__/serializer.cpython-39.pyc b/.venv/lib/python3.9/site-packages/itsdangerous/__pycache__/serializer.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..164f4033b9a9966afcc0c3980734d06c5ada68f4 GIT binary patch literal 9746 zcma)C+jAS&dEZ?uKoA5eijpXax_D(fwvb4m`b5f`lV`EaV?a} za|hl;s<@&gruaSDBmGd_#`g}s;UbF2m>SPb&v;=*)>8{7GbcChY{}kE=O^Jf_;PV|63&22x$c)S>J}Uf{1bA4&hw>cHDr9gd@|!2fRZz17f* zF4dAFnwa$4g$=p|f@J=96?=3x#2+V<>x6Eq-S4@IJnw z0)eE_Higl)K7mlSOVVzaWo3ItR)uxTkhATYoI|~I%h)v95Hr;6TZXtI7vwP>w}|q% zoP$8<+Wc8V%siVBRWbWTxxEC@Iw@+;Ocn|Iso6dyuF8`<2fJVvN-_VeBo@Rmj9=z8 zPl`o|({Zr`aXKxU*lAXrK+8#S3S-WQRo?rooW)+$zAR3oeMMa3*~jELaRw`%6)R{x zFRpWIW&46WCMx3GGh3V&7tnK2yd%#gx1dv8M9*u&LCzUxX zEwJNuQRw!RI}9O*t=_;5!&di6k^Rk(g}l4b)82zV}I;mT!cdExm#}Nza2RnQuqmUQO1~{G{N#(l(ToEVEw&w$7XGV{{XYrst;DrNNal)-&G{7zr zBv;rk*ma=BM696ibh|vWs}G(G^#e^VObOjbQqb1DKuE#|5G{mKnlh{D2lxQx&T4IBYz!9j=9Yjs!A*ptI3?D8^w zZ14FfcU{62$K41Pf@|85u=|P=3?K|RPT=@*549`Iu+eZBD9r~IM11ZhFOVJt zM{7ABJdTvh%wkIK0)(YQyMjOYQNbj17buyXgbvIGB?N(9M$`jaW~$k8Zu?HKu??Ko zT9C%VR}k(k<&vz$@Pgw zLs*fZpB<#zy{u;wOw(18RT|yyHBv@-muhft?8JDuTP71hu`H}nN|+Mk)upszl)hfjFayfEOET5BA!G!_&EtMOb; zg;15yqL!&RP6b_oLkqP;wKG(lMG@O%^VJD@UZLVNibfSwl!LxHMJ>Dt&!@h8svb`v z6Q4pYpcYp<9nbfoPUkZ`g$@eiSk0=M)lvmz#j2R}l>RQ@d2(VsH(BXygk%)*^bUuH z85bya2YnC$on=Z){)Qx0`zg+4c;Qh49yXyg+y@fF#6lNH)MXgb8kfm^qHw)ncL>*J z!yAwfM(6=Y37RMmf?A4pdoH|x68_vqe)(H`aE)^~D1I*BN0W&OMzmt;evXQc{K}J4 z1w!QKMBF+v)WNpqA`^z#n|{iu5ItBtAEN@HG$DS=L?DQyv2E^`{AvVYg(#IKuTM+r z=G1y_P0i;eVZAV4n4f}(x<%J*BeKVcHTRL^?;aOmyj!lPwaDECsib&rvj7cMriqM*7q*Xy)04uH$Lf2HS8JjMvx|bmu@#X z40cJP3wN;RjzdUDwn`Gb5Hize?W^)A>RT%oKyWb zfCQgB`0(oY^2Hmr24jZMIowBMtfdKmnHmm(Xq`Owl+M4zfD|E$v@DKkSQ@1n8V=Jk zOQ+FRQ#~Uz5q84Jbto2q8(k zw3;M#JJSSj7ycUn7OiV!)dTH7BB;7<%jKu z4x?&m^0)U8cQ7vqz97Vd#IXe}nf?e(0b1-Taxw@2fwV*9swl_TEG{W{q?$x5*PH;+ zo?7Tg3Q}}u#th1SdVSglCiP|RZZ7f(-)1%6z6}CuXMwC#Y*V=!My-4P}Bx z#M8K%`eboMN5k43ImGG(gi@-bqc1qge}|bN35roBZr81vSpzCnQP!*qo+f7t{69Fi zQBtgpEKx7DvY9HqgAH_Mn&g12DzemY{$9YMPD?Lf9l>K84xs(KEHDDr)h35+@{b=g zAW^cA$%}|dXRcCc2YE9_rFfLHAor9w$#VeRl5kQY$-+r-vOyFhj(m@hKVL*|DCAK3 zf-Fg5>mWTyL#67S?p?sboWJT4?hh+d!jEcDK|F@j2wm1*=9@Gi;bu-$helcI(ILQ7 zgo^)!Z%6`Slo!lJb`&Rz1zvGB15iM##b&<%c*K?TLv_+zHXg)Ccs0;b(pT^W{K2~z zG91?AukL$0a0?HlIyaDg<9#6FP%PK!BlE9ujcN|Er-FZStWY+-p%ku(w|a7jisrW;9`zUv{ux9On&j9zUV?8&i1#A?{F8S1G`TI7gC z&!Cm|`w=ZNK%v8$eV7Q6U(gKn0z%#0FHhi0LcYt8Zb&ovh!zlP9j|KOC;k+EBHn#X z7N?bcJMj)HPbv?Wu?~L8JAAktm7iB4duqS1KF9jZ0q-Z~)8?3Dp=a{eCS z4{2A1IP@+IJb)#7ZoYt2ztHc(8bvAQHM(3enjdql}|ER}SpIHJ%DRo?|tunSqpzi$Kq5;zok0Ej)HbB~RF z5(H&5AV)R}fl%)!5mj~|pA-vOBuY5ygce01pMVrz#9o4s8>3A`3b{@LP4UXOrt*q6c>fY~7MB7T>3nH&)L*d>?miX-se00W2t*p*>1f73AZ zdOKJor+uSv&^*=1K{XPM$W7hI4uF7Xslx(JcHiyG4mp_+NJh%sM|6`nb^$BZntuT1*)AsPdS*1*}4gCR*+ z8p=b_GDknPsYMRu?^)nG=1fPrc!OR3VlcM$fRVRheYo}^SU{k~D6Jf-5qRs!wQ`u> z->0rWq~a|q4zgd}LL0IfNz7U!XtvDr0Op#xfRq%T@VxZ(l3h0^7Ye5IsHF7>n9~o{ zGmu`wgJ!A*X<`0?VA`5|b_DDaIeZmb=UV&!Dnmjo^#|Boy+=ihii6irqG{| zbr|--RavU_5IA@AM+s~6X_&A0Z|S~oQP$6QcV(v&*E$`I52)8WorfcLko3%SI(P+& zW}99V>#XEWY!R1k`(ymkNXPX77i_-6PDhb*!nw+Lwu3*F0Q|$;QkoLWMx8V?2;+I` z&9lFYcnpSUij@9y6&^q>I);d=A8}&E9Vp6O#8tc?gd|7fxXd{l9b>SN<$i7T9*19n zQsh7I0kZYkV~eeOAE|fg#4N49Pooxak^(_Bdq@b?`&8uY;Tk6d$;MMsHrzsiu;u20 zT`$$ktgBjMe?vk_X%-714ZgprH!O2;#`vve)=m5C`njcsA$)Qqdve-T#3d*SOvw5sd7$(9!bdK68s<5Jgto!jT6K% ztK#w}KJrzHY`yx3ia({|PpBaOGPdth)b}B(ZDME=(cR~+2V=0{hg_wXZgKemz0^|o zxP@$@TBoXJ1O8}2{V^VOoI!_w87<@LEiH%Nhpg*FG?LKBO(p-}metHfiV95!zb8ve G<^KbAU|&7} literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/itsdangerous/__pycache__/signer.cpython-39.pyc b/.venv/lib/python3.9/site-packages/itsdangerous/__pycache__/signer.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8d2a94498318b7882ee50f6648d04b88e3690f08 GIT binary patch literal 8437 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 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/itsdangerous/__pycache__/timed.cpython-39.pyc b/.venv/lib/python3.9/site-packages/itsdangerous/__pycache__/timed.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..491fca35c7676948aaed266f6793a1e7bb851652 GIT binary patch literal 6483 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 M0meUj{_KALFCE^ _t.Any: + return _json.loads(payload) + + @staticmethod + def dumps(obj: _t.Any, **kwargs: _t.Any) -> str: + kwargs.setdefault("ensure_ascii", False) + kwargs.setdefault("separators", (",", ":")) + return _json.dumps(obj, **kwargs) diff --git a/.venv/lib/python3.9/site-packages/itsdangerous/encoding.py b/.venv/lib/python3.9/site-packages/itsdangerous/encoding.py new file mode 100644 index 0000000..edb04d1 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/itsdangerous/encoding.py @@ -0,0 +1,54 @@ +import base64 +import string +import struct +import typing as _t + +from .exc import BadData + +_t_str_bytes = _t.Union[str, bytes] + + +def want_bytes( + s: _t_str_bytes, encoding: str = "utf-8", errors: str = "strict" +) -> bytes: + if isinstance(s, str): + s = s.encode(encoding, errors) + + return s + + +def base64_encode(string: _t_str_bytes) -> bytes: + """Base64 encode a string of bytes or text. The resulting bytes are + safe to use in URLs. + """ + string = want_bytes(string) + return base64.urlsafe_b64encode(string).rstrip(b"=") + + +def base64_decode(string: _t_str_bytes) -> bytes: + """Base64 decode a URL-safe string of bytes or text. The result is + bytes. + """ + string = want_bytes(string, encoding="ascii", errors="ignore") + string += b"=" * (-len(string) % 4) + + try: + return base64.urlsafe_b64decode(string) + except (TypeError, ValueError) as e: + raise BadData("Invalid base64-encoded data") from e + + +# The alphabet used by base64.urlsafe_* +_base64_alphabet = f"{string.ascii_letters}{string.digits}-_=".encode("ascii") + +_int64_struct = struct.Struct(">Q") +_int_to_bytes = _int64_struct.pack +_bytes_to_int = _t.cast("_t.Callable[[bytes], _t.Tuple[int]]", _int64_struct.unpack) + + +def int_to_bytes(num: int) -> bytes: + return _int_to_bytes(num).lstrip(b"\x00") + + +def bytes_to_int(bytestr: bytes) -> int: + return _bytes_to_int(bytestr.rjust(8, b"\x00"))[0] diff --git a/.venv/lib/python3.9/site-packages/itsdangerous/exc.py b/.venv/lib/python3.9/site-packages/itsdangerous/exc.py new file mode 100644 index 0000000..c38a6af --- /dev/null +++ b/.venv/lib/python3.9/site-packages/itsdangerous/exc.py @@ -0,0 +1,107 @@ +import typing as _t +from datetime import datetime + +_t_opt_any = _t.Optional[_t.Any] +_t_opt_exc = _t.Optional[Exception] + + +class BadData(Exception): + """Raised if bad data of any sort was encountered. This is the base + for all exceptions that ItsDangerous defines. + + .. versionadded:: 0.15 + """ + + def __init__(self, message: str): + super().__init__(message) + self.message = message + + def __str__(self) -> str: + return self.message + + +class BadSignature(BadData): + """Raised if a signature does not match.""" + + def __init__(self, message: str, payload: _t_opt_any = None): + super().__init__(message) + + #: The payload that failed the signature test. In some + #: situations you might still want to inspect this, even if + #: you know it was tampered with. + #: + #: .. versionadded:: 0.14 + self.payload: _t_opt_any = payload + + +class BadTimeSignature(BadSignature): + """Raised if a time-based signature is invalid. This is a subclass + of :class:`BadSignature`. + """ + + def __init__( + self, + message: str, + payload: _t_opt_any = None, + date_signed: _t.Optional[datetime] = None, + ): + super().__init__(message, payload) + + #: If the signature expired this exposes the date of when the + #: signature was created. This can be helpful in order to + #: tell the user how long a link has been gone stale. + #: + #: .. versionchanged:: 2.0 + #: The datetime value is timezone-aware rather than naive. + #: + #: .. versionadded:: 0.14 + self.date_signed = date_signed + + +class SignatureExpired(BadTimeSignature): + """Raised if a signature timestamp is older than ``max_age``. This + is a subclass of :exc:`BadTimeSignature`. + """ + + +class BadHeader(BadSignature): + """Raised if a signed header is invalid in some form. This only + happens for serializers that have a header that goes with the + signature. + + .. versionadded:: 0.24 + """ + + def __init__( + self, + message: str, + payload: _t_opt_any = None, + header: _t_opt_any = None, + original_error: _t_opt_exc = None, + ): + super().__init__(message, payload) + + #: If the header is actually available but just malformed it + #: might be stored here. + self.header: _t_opt_any = header + + #: If available, the error that indicates why the payload was + #: not valid. This might be ``None``. + self.original_error: _t_opt_exc = original_error + + +class BadPayload(BadData): + """Raised if a payload is invalid. This could happen if the payload + is loaded despite an invalid signature, or if there is a mismatch + between the serializer and deserializer. The original exception + that occurred during loading is stored on as :attr:`original_error`. + + .. versionadded:: 0.15 + """ + + def __init__(self, message: str, original_error: _t_opt_exc = None): + super().__init__(message) + + #: If available, the error that indicates why the payload was + #: not valid. This might be ``None``. + self.original_error: _t_opt_exc = original_error diff --git a/.venv/lib/python3.9/site-packages/itsdangerous/py.typed b/.venv/lib/python3.9/site-packages/itsdangerous/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/.venv/lib/python3.9/site-packages/itsdangerous/serializer.py b/.venv/lib/python3.9/site-packages/itsdangerous/serializer.py new file mode 100644 index 0000000..9f4a84a --- /dev/null +++ b/.venv/lib/python3.9/site-packages/itsdangerous/serializer.py @@ -0,0 +1,295 @@ +import json +import typing as _t + +from .encoding import want_bytes +from .exc import BadPayload +from .exc import BadSignature +from .signer import _make_keys_list +from .signer import Signer + +_t_str_bytes = _t.Union[str, bytes] +_t_opt_str_bytes = _t.Optional[_t_str_bytes] +_t_kwargs = _t.Dict[str, _t.Any] +_t_opt_kwargs = _t.Optional[_t_kwargs] +_t_signer = _t.Type[Signer] +_t_fallbacks = _t.List[_t.Union[_t_kwargs, _t.Tuple[_t_signer, _t_kwargs], _t_signer]] +_t_load_unsafe = _t.Tuple[bool, _t.Any] +_t_secret_key = _t.Union[_t.Iterable[_t_str_bytes], _t_str_bytes] + + +def is_text_serializer(serializer: _t.Any) -> bool: + """Checks whether a serializer generates text or binary.""" + return isinstance(serializer.dumps({}), str) + + +class Serializer: + """A serializer wraps a :class:`~itsdangerous.signer.Signer` to + enable serializing and securely signing data other than bytes. It + can unsign to verify that the data hasn't been changed. + + The serializer provides :meth:`dumps` and :meth:`loads`, similar to + :mod:`json`, and by default uses :mod:`json` internally to serialize + the data to bytes. + + The secret key should be a random string of ``bytes`` and should not + be saved to code or version control. Different salts should be used + to distinguish signing in different contexts. See :doc:`/concepts` + for information about the security of the secret key and salt. + + :param secret_key: The secret key to sign and verify with. Can be a + list of keys, oldest to newest, to support key rotation. + :param salt: Extra key to combine with ``secret_key`` to distinguish + signatures in different contexts. + :param serializer: An object that provides ``dumps`` and ``loads`` + methods for serializing data to a string. Defaults to + :attr:`default_serializer`, which defaults to :mod:`json`. + :param serializer_kwargs: Keyword arguments to pass when calling + ``serializer.dumps``. + :param signer: A ``Signer`` class to instantiate when signing data. + Defaults to :attr:`default_signer`, which defaults to + :class:`~itsdangerous.signer.Signer`. + :param signer_kwargs: Keyword arguments to pass when instantiating + the ``Signer`` class. + :param fallback_signers: List of signer parameters to try when + unsigning with the default signer fails. Each item can be a dict + of ``signer_kwargs``, a ``Signer`` class, or a tuple of + ``(signer, signer_kwargs)``. Defaults to + :attr:`default_fallback_signers`. + + .. versionchanged:: 2.0 + Added support for key rotation by passing a list to + ``secret_key``. + + .. versionchanged:: 2.0 + Removed the default SHA-512 fallback signer from + ``default_fallback_signers``. + + .. versionchanged:: 1.1 + Added support for ``fallback_signers`` and configured a default + SHA-512 fallback. This fallback is for users who used the yanked + 1.0.0 release which defaulted to SHA-512. + + .. versionchanged:: 0.14 + The ``signer`` and ``signer_kwargs`` parameters were added to + the constructor. + """ + + #: The default serialization module to use to serialize data to a + #: string internally. The default is :mod:`json`, but can be changed + #: to any object that provides ``dumps`` and ``loads`` methods. + default_serializer: _t.Any = json + + #: The default ``Signer`` class to instantiate when signing data. + #: The default is :class:`itsdangerous.signer.Signer`. + default_signer: _t_signer = Signer + + #: The default fallback signers to try when unsigning fails. + default_fallback_signers: _t_fallbacks = [] + + def __init__( + self, + secret_key: _t_secret_key, + salt: _t_opt_str_bytes = b"itsdangerous", + serializer: _t.Any = None, + serializer_kwargs: _t_opt_kwargs = None, + signer: _t.Optional[_t_signer] = None, + signer_kwargs: _t_opt_kwargs = None, + fallback_signers: _t.Optional[_t_fallbacks] = None, + ): + #: The list of secret keys to try for verifying signatures, from + #: oldest to newest. The newest (last) key is used for signing. + #: + #: This allows a key rotation system to keep a list of allowed + #: keys and remove expired ones. + self.secret_keys: _t.List[bytes] = _make_keys_list(secret_key) + + if salt is not None: + salt = want_bytes(salt) + # if salt is None then the signer's default is used + + self.salt = salt + + if serializer is None: + serializer = self.default_serializer + + self.serializer: _t.Any = serializer + self.is_text_serializer: bool = is_text_serializer(serializer) + + if signer is None: + signer = self.default_signer + + self.signer: _t_signer = signer + self.signer_kwargs: _t_kwargs = signer_kwargs or {} + + if fallback_signers is None: + fallback_signers = list(self.default_fallback_signers or ()) + + self.fallback_signers: _t_fallbacks = fallback_signers + self.serializer_kwargs: _t_kwargs = serializer_kwargs or {} + + @property + def secret_key(self) -> bytes: + """The newest (last) entry in the :attr:`secret_keys` list. This + is for compatibility from before key rotation support was added. + """ + return self.secret_keys[-1] + + def load_payload( + self, payload: bytes, serializer: _t.Optional[_t.Any] = None + ) -> _t.Any: + """Loads the encoded object. This function raises + :class:`.BadPayload` if the payload is not valid. The + ``serializer`` parameter can be used to override the serializer + stored on the class. The encoded ``payload`` should always be + bytes. + """ + if serializer is None: + serializer = self.serializer + is_text = self.is_text_serializer + else: + is_text = is_text_serializer(serializer) + + try: + if is_text: + return serializer.loads(payload.decode("utf-8")) + + return serializer.loads(payload) + except Exception as e: + raise BadPayload( + "Could not load the payload because an exception" + " occurred on unserializing the data.", + original_error=e, + ) from e + + def dump_payload(self, obj: _t.Any) -> bytes: + """Dumps the encoded object. The return value is always bytes. + If the internal serializer returns text, the value will be + encoded as UTF-8. + """ + return want_bytes(self.serializer.dumps(obj, **self.serializer_kwargs)) + + def make_signer(self, salt: _t_opt_str_bytes = None) -> Signer: + """Creates a new instance of the signer to be used. The default + implementation uses the :class:`.Signer` base class. + """ + if salt is None: + salt = self.salt + + return self.signer(self.secret_keys, salt=salt, **self.signer_kwargs) + + def iter_unsigners(self, salt: _t_opt_str_bytes = None) -> _t.Iterator[Signer]: + """Iterates over all signers to be tried for unsigning. Starts + with the configured signer, then constructs each signer + specified in ``fallback_signers``. + """ + if salt is None: + salt = self.salt + + yield self.make_signer(salt) + + for fallback in self.fallback_signers: + if isinstance(fallback, dict): + kwargs = fallback + fallback = self.signer + elif isinstance(fallback, tuple): + fallback, kwargs = fallback + else: + kwargs = self.signer_kwargs + + for secret_key in self.secret_keys: + yield fallback(secret_key, salt=salt, **kwargs) + + def dumps(self, obj: _t.Any, salt: _t_opt_str_bytes = None) -> _t_str_bytes: + """Returns a signed string serialized with the internal + serializer. The return value can be either a byte or unicode + string depending on the format of the internal serializer. + """ + payload = want_bytes(self.dump_payload(obj)) + rv = self.make_signer(salt).sign(payload) + + if self.is_text_serializer: + return rv.decode("utf-8") + + return rv + + def dump(self, obj: _t.Any, f: _t.IO, salt: _t_opt_str_bytes = None) -> None: + """Like :meth:`dumps` but dumps into a file. The file handle has + to be compatible with what the internal serializer expects. + """ + f.write(self.dumps(obj, salt)) + + def loads( + self, s: _t_str_bytes, salt: _t_opt_str_bytes = None, **kwargs: _t.Any + ) -> _t.Any: + """Reverse of :meth:`dumps`. Raises :exc:`.BadSignature` if the + signature validation fails. + """ + s = want_bytes(s) + last_exception = None + + for signer in self.iter_unsigners(salt): + try: + return self.load_payload(signer.unsign(s)) + except BadSignature as err: + last_exception = err + + raise _t.cast(BadSignature, last_exception) + + def load(self, f: _t.IO, salt: _t_opt_str_bytes = None) -> _t.Any: + """Like :meth:`loads` but loads from a file.""" + return self.loads(f.read(), salt) + + def loads_unsafe( + self, s: _t_str_bytes, salt: _t_opt_str_bytes = None + ) -> _t_load_unsafe: + """Like :meth:`loads` but without verifying the signature. This + is potentially very dangerous to use depending on how your + serializer works. The return value is ``(signature_valid, + payload)`` instead of just the payload. The first item will be a + boolean that indicates if the signature is valid. This function + never fails. + + Use it for debugging only and if you know that your serializer + module is not exploitable (for example, do not use it with a + pickle serializer). + + .. versionadded:: 0.15 + """ + return self._loads_unsafe_impl(s, salt) + + def _loads_unsafe_impl( + self, + s: _t_str_bytes, + salt: _t_opt_str_bytes, + load_kwargs: _t_opt_kwargs = None, + load_payload_kwargs: _t_opt_kwargs = None, + ) -> _t_load_unsafe: + """Low level helper function to implement :meth:`loads_unsafe` + in serializer subclasses. + """ + if load_kwargs is None: + load_kwargs = {} + + try: + return True, self.loads(s, salt=salt, **load_kwargs) + except BadSignature as e: + if e.payload is None: + return False, None + + if load_payload_kwargs is None: + load_payload_kwargs = {} + + try: + return ( + False, + self.load_payload(e.payload, **load_payload_kwargs), + ) + except BadPayload: + return False, None + + def load_unsafe(self, f: _t.IO, salt: _t_opt_str_bytes = None) -> _t_load_unsafe: + """Like :meth:`loads_unsafe` but loads from a file. + + .. versionadded:: 0.15 + """ + return self.loads_unsafe(f.read(), salt=salt) diff --git a/.venv/lib/python3.9/site-packages/itsdangerous/signer.py b/.venv/lib/python3.9/site-packages/itsdangerous/signer.py new file mode 100644 index 0000000..aa12005 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/itsdangerous/signer.py @@ -0,0 +1,257 @@ +import hashlib +import hmac +import typing as _t + +from .encoding import _base64_alphabet +from .encoding import base64_decode +from .encoding import base64_encode +from .encoding import want_bytes +from .exc import BadSignature + +_t_str_bytes = _t.Union[str, bytes] +_t_opt_str_bytes = _t.Optional[_t_str_bytes] +_t_secret_key = _t.Union[_t.Iterable[_t_str_bytes], _t_str_bytes] + + +class SigningAlgorithm: + """Subclasses must implement :meth:`get_signature` to provide + signature generation functionality. + """ + + def get_signature(self, key: bytes, value: bytes) -> bytes: + """Returns the signature for the given key and value.""" + raise NotImplementedError() + + def verify_signature(self, key: bytes, value: bytes, sig: bytes) -> bool: + """Verifies the given signature matches the expected + signature. + """ + return hmac.compare_digest(sig, self.get_signature(key, value)) + + +class NoneAlgorithm(SigningAlgorithm): + """Provides an algorithm that does not perform any signing and + returns an empty signature. + """ + + def get_signature(self, key: bytes, value: bytes) -> bytes: + return b"" + + +class HMACAlgorithm(SigningAlgorithm): + """Provides signature generation using HMACs.""" + + #: The digest method to use with the MAC algorithm. This defaults to + #: SHA1, but can be changed to any other function in the hashlib + #: module. + default_digest_method: _t.Any = staticmethod(hashlib.sha1) + + def __init__(self, digest_method: _t.Any = None): + if digest_method is None: + digest_method = self.default_digest_method + + self.digest_method: _t.Any = digest_method + + def get_signature(self, key: bytes, value: bytes) -> bytes: + mac = hmac.new(key, msg=value, digestmod=self.digest_method) + return mac.digest() + + +def _make_keys_list(secret_key: _t_secret_key) -> _t.List[bytes]: + if isinstance(secret_key, (str, bytes)): + return [want_bytes(secret_key)] + + return [want_bytes(s) for s in secret_key] + + +class Signer: + """A signer securely signs bytes, then unsigns them to verify that + the value hasn't been changed. + + The secret key should be a random string of ``bytes`` and should not + be saved to code or version control. Different salts should be used + to distinguish signing in different contexts. See :doc:`/concepts` + for information about the security of the secret key and salt. + + :param secret_key: The secret key to sign and verify with. Can be a + list of keys, oldest to newest, to support key rotation. + :param salt: Extra key to combine with ``secret_key`` to distinguish + signatures in different contexts. + :param sep: Separator between the signature and value. + :param key_derivation: How to derive the signing key from the secret + key and salt. Possible values are ``concat``, ``django-concat``, + or ``hmac``. Defaults to :attr:`default_key_derivation`, which + defaults to ``django-concat``. + :param digest_method: Hash function to use when generating the HMAC + signature. Defaults to :attr:`default_digest_method`, which + defaults to :func:`hashlib.sha1`. Note that the security of the + hash alone doesn't apply when used intermediately in HMAC. + :param algorithm: A :class:`SigningAlgorithm` instance to use + instead of building a default :class:`HMACAlgorithm` with the + ``digest_method``. + + .. versionchanged:: 2.0 + Added support for key rotation by passing a list to + ``secret_key``. + + .. versionchanged:: 0.18 + ``algorithm`` was added as an argument to the class constructor. + + .. versionchanged:: 0.14 + ``key_derivation`` and ``digest_method`` were added as arguments + to the class constructor. + """ + + #: The default digest method to use for the signer. The default is + #: :func:`hashlib.sha1`, but can be changed to any :mod:`hashlib` or + #: compatible object. Note that the security of the hash alone + #: doesn't apply when used intermediately in HMAC. + #: + #: .. versionadded:: 0.14 + default_digest_method: _t.Any = staticmethod(hashlib.sha1) + + #: The default scheme to use to derive the signing key from the + #: secret key and salt. The default is ``django-concat``. Possible + #: values are ``concat``, ``django-concat``, and ``hmac``. + #: + #: .. versionadded:: 0.14 + default_key_derivation: str = "django-concat" + + def __init__( + self, + secret_key: _t_secret_key, + salt: _t_opt_str_bytes = b"itsdangerous.Signer", + sep: _t_str_bytes = b".", + key_derivation: _t.Optional[str] = None, + digest_method: _t.Optional[_t.Any] = None, + algorithm: _t.Optional[SigningAlgorithm] = None, + ): + #: The list of secret keys to try for verifying signatures, from + #: oldest to newest. The newest (last) key is used for signing. + #: + #: This allows a key rotation system to keep a list of allowed + #: keys and remove expired ones. + self.secret_keys: _t.List[bytes] = _make_keys_list(secret_key) + self.sep: bytes = want_bytes(sep) + + if self.sep in _base64_alphabet: + raise ValueError( + "The given separator cannot be used because it may be" + " contained in the signature itself. ASCII letters," + " digits, and '-_=' must not be used." + ) + + if salt is not None: + salt = want_bytes(salt) + else: + salt = b"itsdangerous.Signer" + + self.salt = salt + + if key_derivation is None: + key_derivation = self.default_key_derivation + + self.key_derivation: str = key_derivation + + if digest_method is None: + digest_method = self.default_digest_method + + self.digest_method: _t.Any = digest_method + + if algorithm is None: + algorithm = HMACAlgorithm(self.digest_method) + + self.algorithm: SigningAlgorithm = algorithm + + @property + def secret_key(self) -> bytes: + """The newest (last) entry in the :attr:`secret_keys` list. This + is for compatibility from before key rotation support was added. + """ + return self.secret_keys[-1] + + def derive_key(self, secret_key: _t_opt_str_bytes = None) -> bytes: + """This method is called to derive the key. The default key + derivation choices can be overridden here. Key derivation is not + intended to be used as a security method to make a complex key + out of a short password. Instead you should use large random + secret keys. + + :param secret_key: A specific secret key to derive from. + Defaults to the last item in :attr:`secret_keys`. + + .. versionchanged:: 2.0 + Added the ``secret_key`` parameter. + """ + if secret_key is None: + secret_key = self.secret_keys[-1] + else: + secret_key = want_bytes(secret_key) + + if self.key_derivation == "concat": + return _t.cast(bytes, self.digest_method(self.salt + secret_key).digest()) + elif self.key_derivation == "django-concat": + return _t.cast( + bytes, self.digest_method(self.salt + b"signer" + secret_key).digest() + ) + elif self.key_derivation == "hmac": + mac = hmac.new(secret_key, digestmod=self.digest_method) + mac.update(self.salt) + return mac.digest() + elif self.key_derivation == "none": + return secret_key + else: + raise TypeError("Unknown key derivation method") + + def get_signature(self, value: _t_str_bytes) -> bytes: + """Returns the signature for the given value.""" + value = want_bytes(value) + key = self.derive_key() + sig = self.algorithm.get_signature(key, value) + return base64_encode(sig) + + def sign(self, value: _t_str_bytes) -> bytes: + """Signs the given string.""" + value = want_bytes(value) + return value + self.sep + self.get_signature(value) + + def verify_signature(self, value: _t_str_bytes, sig: _t_str_bytes) -> bool: + """Verifies the signature for the given value.""" + try: + sig = base64_decode(sig) + except Exception: + return False + + value = want_bytes(value) + + for secret_key in reversed(self.secret_keys): + key = self.derive_key(secret_key) + + if self.algorithm.verify_signature(key, value, sig): + return True + + return False + + def unsign(self, signed_value: _t_str_bytes) -> bytes: + """Unsigns the given string.""" + signed_value = want_bytes(signed_value) + + if self.sep not in signed_value: + raise BadSignature(f"No {self.sep!r} found in value") + + value, sig = signed_value.rsplit(self.sep, 1) + + if self.verify_signature(value, sig): + return value + + raise BadSignature(f"Signature {sig!r} does not match", payload=value) + + def validate(self, signed_value: _t_str_bytes) -> bool: + """Only validates the given signed value. Returns ``True`` if + the signature exists and is valid. + """ + try: + self.unsign(signed_value) + return True + except BadSignature: + return False diff --git a/.venv/lib/python3.9/site-packages/itsdangerous/timed.py b/.venv/lib/python3.9/site-packages/itsdangerous/timed.py new file mode 100644 index 0000000..cad8da3 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/itsdangerous/timed.py @@ -0,0 +1,234 @@ +import time +import typing +import typing as _t +from datetime import datetime +from datetime import timezone + +from .encoding import base64_decode +from .encoding import base64_encode +from .encoding import bytes_to_int +from .encoding import int_to_bytes +from .encoding import want_bytes +from .exc import BadSignature +from .exc import BadTimeSignature +from .exc import SignatureExpired +from .serializer import Serializer +from .signer import Signer + +_t_str_bytes = _t.Union[str, bytes] +_t_opt_str_bytes = _t.Optional[_t_str_bytes] +_t_opt_int = _t.Optional[int] + +if _t.TYPE_CHECKING: + import typing_extensions as _te + + +class TimestampSigner(Signer): + """Works like the regular :class:`.Signer` but also records the time + of the signing and can be used to expire signatures. The + :meth:`unsign` method can raise :exc:`.SignatureExpired` if the + unsigning failed because the signature is expired. + """ + + def get_timestamp(self) -> int: + """Returns the current timestamp. The function must return an + integer. + """ + return int(time.time()) + + def timestamp_to_datetime(self, ts: int) -> datetime: + """Convert the timestamp from :meth:`get_timestamp` into an + aware :class`datetime.datetime` in UTC. + + .. versionchanged:: 2.0 + The timestamp is returned as a timezone-aware ``datetime`` + in UTC rather than a naive ``datetime`` assumed to be UTC. + """ + return datetime.fromtimestamp(ts, tz=timezone.utc) + + def sign(self, value: _t_str_bytes) -> bytes: + """Signs the given string and also attaches time information.""" + value = want_bytes(value) + timestamp = base64_encode(int_to_bytes(self.get_timestamp())) + sep = want_bytes(self.sep) + value = value + sep + timestamp + return value + sep + self.get_signature(value) + + # Ignore overlapping signatures check, return_timestamp is the only + # parameter that affects the return type. + + @typing.overload + def unsign( # type: ignore + self, + signed_value: _t_str_bytes, + max_age: _t_opt_int = None, + return_timestamp: "_te.Literal[False]" = False, + ) -> bytes: + ... + + @typing.overload + def unsign( + self, + signed_value: _t_str_bytes, + max_age: _t_opt_int = None, + return_timestamp: "_te.Literal[True]" = True, + ) -> _t.Tuple[bytes, datetime]: + ... + + def unsign( + self, + signed_value: _t_str_bytes, + max_age: _t_opt_int = None, + return_timestamp: bool = False, + ) -> _t.Union[_t.Tuple[bytes, datetime], bytes]: + """Works like the regular :meth:`.Signer.unsign` but can also + validate the time. See the base docstring of the class for + the general behavior. If ``return_timestamp`` is ``True`` the + timestamp of the signature will be returned as an aware + :class:`datetime.datetime` object in UTC. + + .. versionchanged:: 2.0 + The timestamp is returned as a timezone-aware ``datetime`` + in UTC rather than a naive ``datetime`` assumed to be UTC. + """ + try: + result = super().unsign(signed_value) + sig_error = None + except BadSignature as e: + sig_error = e + result = e.payload or b"" + + sep = want_bytes(self.sep) + + # If there is no timestamp in the result there is something + # seriously wrong. In case there was a signature error, we raise + # that one directly, otherwise we have a weird situation in + # which we shouldn't have come except someone uses a time-based + # serializer on non-timestamp data, so catch that. + if sep not in result: + if sig_error: + raise sig_error + + raise BadTimeSignature("timestamp missing", payload=result) + + value, ts_bytes = result.rsplit(sep, 1) + ts_int: _t_opt_int = None + ts_dt: _t.Optional[datetime] = None + + try: + ts_int = bytes_to_int(base64_decode(ts_bytes)) + except Exception: + pass + + # Signature is *not* okay. Raise a proper error now that we have + # split the value and the timestamp. + if sig_error is not None: + if ts_int is not None: + try: + ts_dt = self.timestamp_to_datetime(ts_int) + except (ValueError, OSError, OverflowError) as exc: + # Windows raises OSError + # 32-bit raises OverflowError + raise BadTimeSignature( + "Malformed timestamp", payload=value + ) from exc + + raise BadTimeSignature(str(sig_error), payload=value, date_signed=ts_dt) + + # Signature was okay but the timestamp is actually not there or + # malformed. Should not happen, but we handle it anyway. + if ts_int is None: + raise BadTimeSignature("Malformed timestamp", payload=value) + + # Check timestamp is not older than max_age + if max_age is not None: + age = self.get_timestamp() - ts_int + + if age > max_age: + raise SignatureExpired( + f"Signature age {age} > {max_age} seconds", + payload=value, + date_signed=self.timestamp_to_datetime(ts_int), + ) + + if age < 0: + raise SignatureExpired( + f"Signature age {age} < 0 seconds", + payload=value, + date_signed=self.timestamp_to_datetime(ts_int), + ) + + if return_timestamp: + return value, self.timestamp_to_datetime(ts_int) + + return value + + def validate(self, signed_value: _t_str_bytes, max_age: _t_opt_int = None) -> bool: + """Only validates the given signed value. Returns ``True`` if + the signature exists and is valid.""" + try: + self.unsign(signed_value, max_age=max_age) + return True + except BadSignature: + return False + + +class TimedSerializer(Serializer): + """Uses :class:`TimestampSigner` instead of the default + :class:`.Signer`. + """ + + default_signer: _t.Type[TimestampSigner] = TimestampSigner + + def iter_unsigners( + self, salt: _t_opt_str_bytes = None + ) -> _t.Iterator[TimestampSigner]: + return _t.cast("_t.Iterator[TimestampSigner]", super().iter_unsigners(salt)) + + # TODO: Signature is incompatible because parameters were added + # before salt. + + def loads( # type: ignore + self, + s: _t_str_bytes, + max_age: _t_opt_int = None, + return_timestamp: bool = False, + salt: _t_opt_str_bytes = None, + ) -> _t.Any: + """Reverse of :meth:`dumps`, raises :exc:`.BadSignature` if the + signature validation fails. If a ``max_age`` is provided it will + ensure the signature is not older than that time in seconds. In + case the signature is outdated, :exc:`.SignatureExpired` is + raised. All arguments are forwarded to the signer's + :meth:`~TimestampSigner.unsign` method. + """ + s = want_bytes(s) + last_exception = None + + for signer in self.iter_unsigners(salt): + try: + base64d, timestamp = signer.unsign( + s, max_age=max_age, return_timestamp=True + ) + payload = self.load_payload(base64d) + + if return_timestamp: + return payload, timestamp + + return payload + except SignatureExpired: + # The signature was unsigned successfully but was + # expired. Do not try the next signer. + raise + except BadSignature as err: + last_exception = err + + raise _t.cast(BadSignature, last_exception) + + def loads_unsafe( # type: ignore + self, + s: _t_str_bytes, + max_age: _t_opt_int = None, + salt: _t_opt_str_bytes = None, + ) -> _t.Tuple[bool, _t.Any]: + return self._loads_unsafe_impl(s, salt, load_kwargs={"max_age": max_age}) diff --git a/.venv/lib/python3.9/site-packages/itsdangerous/url_safe.py b/.venv/lib/python3.9/site-packages/itsdangerous/url_safe.py new file mode 100644 index 0000000..d5a9b0c --- /dev/null +++ b/.venv/lib/python3.9/site-packages/itsdangerous/url_safe.py @@ -0,0 +1,80 @@ +import typing as _t +import zlib + +from ._json import _CompactJSON +from .encoding import base64_decode +from .encoding import base64_encode +from .exc import BadPayload +from .serializer import Serializer +from .timed import TimedSerializer + + +class URLSafeSerializerMixin(Serializer): + """Mixed in with a regular serializer it will attempt to zlib + compress the string to make it shorter if necessary. It will also + base64 encode the string so that it can safely be placed in a URL. + """ + + default_serializer = _CompactJSON + + def load_payload( + self, + payload: bytes, + *args: _t.Any, + serializer: _t.Optional[_t.Any] = None, + **kwargs: _t.Any, + ) -> _t.Any: + decompress = False + + if payload.startswith(b"."): + payload = payload[1:] + decompress = True + + try: + json = base64_decode(payload) + except Exception as e: + raise BadPayload( + "Could not base64 decode the payload because of an exception", + original_error=e, + ) from e + + if decompress: + try: + json = zlib.decompress(json) + except Exception as e: + raise BadPayload( + "Could not zlib decompress the payload before decoding the payload", + original_error=e, + ) from e + + return super().load_payload(json, *args, **kwargs) + + def dump_payload(self, obj: _t.Any) -> bytes: + json = super().dump_payload(obj) + is_compressed = False + compressed = zlib.compress(json) + + if len(compressed) < (len(json) - 1): + json = compressed + is_compressed = True + + base64d = base64_encode(json) + + if is_compressed: + base64d = b"." + base64d + + return base64d + + +class URLSafeSerializer(URLSafeSerializerMixin, Serializer): + """Works like :class:`.Serializer` but dumps and loads into a URL + safe string consisting of the upper and lowercase character of the + alphabet as well as ``'_'``, ``'-'`` and ``'.'``. + """ + + +class URLSafeTimedSerializer(URLSafeSerializerMixin, TimedSerializer): + """Works like :class:`.TimedSerializer` but dumps and loads into a + URL safe string consisting of the upper and lowercase character of + the alphabet as well as ``'_'``, ``'-'`` and ``'.'``. + """ diff --git a/.venv/lib/python3.9/site-packages/jinja2/__init__.py b/.venv/lib/python3.9/site-packages/jinja2/__init__.py new file mode 100644 index 0000000..e323926 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/jinja2/__init__.py @@ -0,0 +1,37 @@ +"""Jinja is a template engine written in pure Python. It provides a +non-XML syntax that supports inline expressions and an optional +sandboxed environment. +""" +from .bccache import BytecodeCache as BytecodeCache +from .bccache import FileSystemBytecodeCache as FileSystemBytecodeCache +from .bccache import MemcachedBytecodeCache as MemcachedBytecodeCache +from .environment import Environment as Environment +from .environment import Template as Template +from .exceptions import TemplateAssertionError as TemplateAssertionError +from .exceptions import TemplateError as TemplateError +from .exceptions import TemplateNotFound as TemplateNotFound +from .exceptions import TemplateRuntimeError as TemplateRuntimeError +from .exceptions import TemplatesNotFound as TemplatesNotFound +from .exceptions import TemplateSyntaxError as TemplateSyntaxError +from .exceptions import UndefinedError as UndefinedError +from .loaders import BaseLoader as BaseLoader +from .loaders import ChoiceLoader as ChoiceLoader +from .loaders import DictLoader as DictLoader +from .loaders import FileSystemLoader as FileSystemLoader +from .loaders import FunctionLoader as FunctionLoader +from .loaders import ModuleLoader as ModuleLoader +from .loaders import PackageLoader as PackageLoader +from .loaders import PrefixLoader as PrefixLoader +from .runtime import ChainableUndefined as ChainableUndefined +from .runtime import DebugUndefined as DebugUndefined +from .runtime import make_logging_undefined as make_logging_undefined +from .runtime import StrictUndefined as StrictUndefined +from .runtime import Undefined as Undefined +from .utils import clear_caches as clear_caches +from .utils import is_undefined as is_undefined +from .utils import pass_context as pass_context +from .utils import pass_environment as pass_environment +from .utils import pass_eval_context as pass_eval_context +from .utils import select_autoescape as select_autoescape + +__version__ = "3.1.2" diff --git a/.venv/lib/python3.9/site-packages/jinja2/__pycache__/__init__.cpython-39.pyc b/.venv/lib/python3.9/site-packages/jinja2/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3c18c5b4d8edcfbd466a7c12d4a6565d82dae21e GIT binary patch literal 1605 zcmbu9O>f&a7{}$rj%`_fckH}JN!k@T#KKx&z<^=snl`|OrU;t7ffs?6IGe3R3MA#( zJ?*xyu&=PMShnZj2>m=xpAOCzvKBP#}dfj#4`RuPB;$O>-^9L3-pDidn zhfn`faGcm7PF#q$l0v#gwhGAGz}sZoauK*lik3@BDcvDEmUn>5q-?pIRMIM`TCM=Q z#I;-nu92E$7uX}77!cJ_bG~=ax@^FUW=EQ{YGBk!2tFF?kGpMm~wp@14tw55IlG#XIVA z?Ni^dG)t&qJ`-aunE$il#xUV?;b*yG{@vV6q!{{N8$VNW%3}ttTp`8PkGHRVJr{<~ zd^4fO*LjvnWi&KRuqQUl6w{hZ0WCxfpD!~5MVh!8jFFtN7>1p4B}K}F8M=SM{%|e} zi~5VXVUdj4HH{_=jKK|0*!^5XwjWpZZ&?~)Sp0EK?bW((=%e!Ozf%S7FZnC2nZl%A zDJ2ySy{uUpt)=lsnj4vm7=n6B?`f*fY6 z2A%5(<${h9w#p7tX}n~ke7x!OQ~I8Ti5$b88Hcba4>N(-=Dksn=w@(rv*1Muqbjt2 zOye}U{!e=^qgscN6o$Dlnp;nO#l zPZs4cjAaytq&$l3&I{C?_bV zC_c!d7KT%%@Ti61?~Zzc6)1myA53J*g0UK_V3y^hU?kxnsVgpY28#s^g5eaV9wdAe zWcH{(8-5;WZrD}!uvc{O4sVO6K^StuO&AWd`J(bgYIoL`cwqH&kYdp*{Ev6*A3tor AD*ylh literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/jinja2/__pycache__/_identifier.cpython-39.pyc b/.venv/lib/python3.9/site-packages/jinja2/__pycache__/_identifier.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..31b70f543263d127afe41278bf4e3d5f304403d5 GIT binary patch literal 2080 zcmYk7&2Qtz8OG(^wh0R4+W#RaW(A}`ivmT_zo3^Milz+$?^@j`-L(zdn>IH}qCVC# zW6|D~EZdT3QC}oQQxd5UTa*O!-1d;eI=fD@@5ralMJ#gbrG1!F1^niH=6xT|yu*>8 z_4nTUVFaI_{`?@VMn!Q@wByN&7@=f|C`&EH`b^1Eb zUe{jtZj0>p{I%N_lR%E1=NjpQ(Noyjs zNiz^^x+pWcI3xX-k$yqC&Zv>1#x;UZjX9&{AvINm5;g18)TxzV)JjpyrxvPhnc7)u z=ct{>M4@(x+I4E{)SgoZ&Q6LtK6T(Ul4NAb$dLi3p^%}H(IcZz#*j=n%@mnFnQ&TZ zva$%z$%55V$f}d2lZBqF1$E)=rl{*v7hYQ?J4<$sY*=lD>>}AEvg>5)beW_}Sezs| z8H5};`v?j-CF)6xdNTD;?#t9ixu2sx%6*0UFR5Roex3R{4Y1iDMFXD(u(~q2u(~;N zVRaR9Uy@rSw@$839+vb{;&H9YyGR-qI&(d7Mq(pPv@!Y3HhD#RX(wrxXj3>%GQAN;rqK?qui8g}G6MY1a zCzFgP6`nlg$y2BbS1ODvHGY1?`1uQdK0xqz<%m~~d1b&WF0Xow*N%Cu!nHi(n!@X8 zUeEBl!5bRm4U0E=2vgoEG2S`j#!JSHDmPFwd~PhbiLyE4U5W9o%)404mbs0FZI@pn zd70#w7YH_YT8uj`?==|jjk#N3+&$v%3xp1LZSJBs*W>OO;fjy(=on#&uvkiqWkOme zUMy1uwoIKbQy0rr6RNkAPnPoK(skIFf)t|9b=z7UNT6AfE5vrIIPMN49$ zl@cwNiMAx#P0=x!=!}Gs62^fr6k$x6FlNH+33I@N=?QZz%!x4P!dwU|AuNjt%MsR4 zSR>(JXU7yyS2*^nTU|{gwwfHSCK`gXnz*Zpx4Kf;>gsUiFM>jL78LSYP*6q;!*x)= zZxurdq(KH8go#5i2}NkZb5;|yf`ZpT(rX8WSq5TO2vkX87?J>fHIV@eh$J$}UMQ{u zRSrEeGIBcuK{ew-WF_!C6bFHt>q6wlU>>OX8YX!&P!GZf4pLwW zd@x5skwF&ZKt4<;pakkb2dyyK1z}$bx+n}GRJ5U11NA7(9%I#_15q4;@I)o_Q#uEg zKs^a9Wh725h*R`+UV%8b0#yq;)o|AEg{qx>5T2%U1*W0E0St6!sF)aMfokqUnB_q2 zh1>N;Al$CE2-Lw5#Gnm~P@v@XA-qsv2M;IqW*~geL%hf#)*Xe*jlKZ5laUIJ!vu~$ z4f~jmf}(})i5LFIW8;<+4DI=GlhX9PmC(FMbxVxnT6Az{|QMJSx08h&r8QU>HgaRT6}N*6!} z4S)-)N@h^i3lMrSs5WJY=Ki-DzER(5S?8l4zkmPDy`9*bcOGp$-Htzj*k0e;i|uS0 zk)7}3zxBJ}KfjB9vh_3;eZ2E{C;FG|XAh$fx8U7j@y*@s*rUDp)@JmDI=xC$V3yVYl5MV0icb_tx(H_kYVK_|^ab literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/jinja2/__pycache__/async_utils.cpython-39.pyc b/.venv/lib/python3.9/site-packages/jinja2/__pycache__/async_utils.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4f8e3f9dc5557ea7a4333ccd8a39ebc80aef43a2 GIT binary patch literal 2664 zcma)7&2Jk;6rY)0+v~41aY9I2J}N%z(%L13ijS%yf&xWgl_sRZRkhl9#?HF-t~)bM zo5(peDCwaHu82b-aiDkp1O5bVbLG?{;>Mxky;&!A8dYFW_ji~;h84G zndg3HJkJYTZvA;)YOwII->kHSe zUAgWrEG}OCtoGHF+R`Fyr|!z2ryf(#f|ciA3zS-rEs&>rAo^l0==hB|(&C;rbh&|w z`#%PPGaPCXgl*EKoN~71ZPEdL-l-irkOG}FjAwa zxEjG88?tEWztgUzKjlqf;eC} zix^627>Z^CAjxskcJ|mJdBkec;_4czjoSzm%qwx+FjjP5 zK})D=JB-@FeAU|b`#S8XN^f0GfPD))UK*hr^U3?QK!!o2E0;QPBj~8h6)qaF40J3< zTl>yFJN_9kiB~y_zwIId64R@kl6iQ@0!~GkOddiAWgK-yq^)D}7`{UYl4%Mh^mq=2 zN<1z*Cg=OznD;xv_kpQ?6m(H4`2L-K&`DOvVW4f;iUhZ|%1q9J5tHfnc%TJuz~bPC zdtAO(8|p z5E7A-n9B8O%eREi{*X>^y_noy(MGnru-G5Jsh`^N$A*TG~G_K0)2ps!=!YJCW=mF<(P0Hn(S#_}!n;*73* z6G2V73Ep5ayQ+m8dUW=bu8b{jZIs4!pt_*f4C>(U$Wic+aTx2zAgC`uK=7bJGFj1d z7g_>>G*3247c5XW2)2-ALdl)kizJ$}I*_7!>=SY;cku254v?1Jn#uY;;3uT-A9gJP zbfb94eJDMd)j%O{M{q)6ISXzX7tC~@0Uv(hQ&39ab8>VJ0Gy=qJnS8z3Z|5$3Kl)v zMTdl$2h!}*;pvWwFkuJz1BSw!YXM9fljETL$nLloF>G2JvfZ%Cc|-3Var#mxR2mr6 zySxOJ{)RyygOCXD$9JQiFj6fi;m9+f{j|lx_~EZXA*tn#d>7>MNqHI`CJodLqgGOW zvRL~x^hjlEj;%8$WxJTIS+?ew%o4DDIgmCRY#JITjI>ELJ8__s^5rNkjf-9zmxm24 zvB=9eaC#Fzo3h+B*@Hx~q_$UM+%Msnb#g4CQF3^fk~VZ1ySsV`CXaehb_z*?INsSg O=Tw1F_M`x__vByNopG=L literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/jinja2/__pycache__/bccache.cpython-39.pyc b/.venv/lib/python3.9/site-packages/jinja2/__pycache__/bccache.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..240307f3a3e55a1969a8e2ab38e3064ebb26711c GIT binary patch literal 13901 zcmcIrOOqSdb?$C79)rOQhr_pCZqZgq$RviQ9ml3+MkGbqbflq*4~v4!#x&3kP7i_Z z(d`DwK@FXhsH(`ZUC|)LU4U>J^gN5N@ zdr{8Ig9F2*HU{+T8wb@V^2T!ekXP}lgTuok?IWh~J;SSca~~SsoPTKJ=;KoRm{)Hf z_vZcblN0`Nud!ntGvvIw@tkL#Fua9}hR^fL3FES%pSPb!FN@v*^m5>_)qVk2OWr|T z9mLg(xLWoO;p&k8BKm&`*N43$xIQA+U-GP*M)RoaKrFY`eJ9#Xf+%zcPIoKudy(fm zJ-3JZ@m8Gp!H#Y}CPrs5xkux0i z)?4-Z%}M7E)_sK@X32ALE9xhH==39XF9`2C5B%c|?*}Rh zhkls!_~P`Z(8h1!u5UmKjke{Nd}!S#mS=gT4{c~(jd%RYM%Axj=54S1iA~-4u-u;W zY|PqRy3~2wAIb!IR|Ww_YFcUOp1+k=l%I@L*t4ca=} zKj^C(?yC9x@Y(H^^=Rm?+*Nng%EQf3ccmKz17DpD!g$l~asF0X_x_l#y@DzC&u+TCd+uF7UfBr34fp&?wPZ75cY?B7h%+V#w~(ozs6 z>fkIDB6X6YsyLj$SLUc@E|uiYr)A!v#5T@<%AZe<8z(Xf(N{lpXxWVb8uD>Xc?Lj&i%2AzQ5XEOcaJU@d7Q! zxt5%You2aHNW8j(4_(N#p?0^#P$ z?8JfEdSoH8N@u=GykPo`T@KmDxEJo>3lx`ROW?cp$ ztR%o7u*sJ6py*>@MCm*XGoztU`%8=d57P)3i+&b4g3?lBL@Pb07a?tg6 z@YVjo8aFc9G^bW-_BCy!CI8Sdkt7l37$@826C*Jb>#@0GKC-s0#JX2fCkugs=EQzd z-sV$?dZV&k!t=}1&&NwiRky%Y)ZR71leUq}CG}*!U)oAcOp9sc?cakeZW`z02wAMW zC2SX_B3kWYjT>N%RQh)i+`~neN(+fJQX8h{rKN!%rnPRbGj#6;JrPA(*$jI327YQ& zm`Y~7I&>8l#)0}WSWRnJufKU!sYs>uAJT#A^Eob&a++($D{NfWJ+qtL`mw+PIyu$9 zj}|f44x_Ya*6?Rrb+azNmbqju<7|95o0^#y_pF3;lrk|FaROmOOd~@rCkP84n;)2% zDk~}Nn5Lc*P6h(Kof57&0cekDvKZz;(d_Ye-WUxxXI&BeNF;ZjCE?6PwN}T+doHc} zfx^P+AaF{yGN+`T2XW1sNL00Oo|dJNI*oJH3$pyb=;W2kW^A8UMq%2&8J z?NMcQgqm6E99~l;9CGTv!5gy{m>$k`phL~=XnNXcn49GD8SVSE>rML%lFZSBWUiu# z@z{6-zwrcQlW0!<4%}PQ9KU!&0;$=lbz@;AZZ%6nw)#3cNo!a@Bv#3|NJ|V?c5$&> zP?tm*4?n@nF}<~6RWJ+V6TA5+S~d?TN?0A@;V=$qwbSvUUZjcuQ!%;2q4`qb<&CZ~2q5cgQ=8XN$#1N4%rBJ|IZvc)D;o zbNW{ZY}|i?CftggfUt$Jy2cv>9{EgVOKgF2VAlD<47C6jk~LZuT}A{EaF7ZBoGuLg zWc|Y0j2l{$pdoug%Pp*N4bi~mC|L(y(XqrjmM(`~L|Cl2-e(bw>s*ilFRcAkht{n; zwAOmFmW4K#1dushHx7Ecc_HX=Vt}^UWpILfuqJ54cj1B#ln^UvtBnBvhMSQ}Ku@wh zc_=hFZ*PfIPtVA>In@XI@%+A%uj!|WJI**>ps?mdY71@IWctGvt)hYR-AUug=xon! zXM~;g-Ie%mpN{F68Ly*D^3vLff>7R>jCLk-#4UiT<{rv`e&t2~VGt+r>Ab&vh9Z3r z0?zKH9~KSIIHy$iRMUY!>F?#6@9?APZ_TpSZ|-G&MxXxQnctV42fvw}jFs8d<#f)= zC1*%b;ClDnu!m?Fh7{*H0*OzwN>ZiqbeDO;4mVwBWoAAX7K@|o+-~SPM^#02l(m*wLE@)#)@YZa8Eq-Y$5*rZy`j{jG4u@U_Fah zwIrbfy?c}!BYP1Q!Bn4e8HKq_A($AReIlw!Uv4l!W(ww?0Ht*Sqm&!K-2(pk4|~4g z^eI^?hMnrtUXi5>GdB0LXA5tpojQqzGa(73S4fcIRHlHxx-#$)RnM?_i)&7%cQ~U8 z2&x7a7OfY4;`L}WfId!Cr&!@&I}jZ3Y4l)MDh$Aq!_CM)Tm3jfAwG=cinRs3(E#nI z5azVhB%JsPnAxRhX<3H(7*DjSnd3vVswU5yrPQW-PHVYi)#i%@yZBI4Mf7oZ_%xyp!hLmZ=4{5<^Wwc4SpU$oO54`|6umTFdfoAF| z+uH1Zw_FwwL3Tu&I~e)T(M|(md(oZi4q7h^-N;}pgpf&{fHo$cyv5?m&Z&x0d9#wI- zv|SaAs7lEHAPe~+2Jyj}$H`o=b2mWBvrq=OolD4HyXx+U(W69ag`Er$54wzM#3XYq zc7`r)_|DoYvR#PQ=&t#J8odRI)hZ7RvL$Q>`jdQwWKnjpPi{5z+sB=!bqZ4x|JP`! zpu+(-@|w2L*0BG-}pWb)36BT z7<;4aqm1Jd0@;8#Ov;wqRa{q#q%6P?t|uv303)>KwNA#)7uOmPHSm;gAM}<59$?UA%+G3=D^W>N5gGV-DG1qF$L(>o%(9;ku&U&C za6wY5lc+r}&*b$aWIBc#)<`B;CsTF2gpisUSfq%Vae!(8tHAx{nd9y#z`{m_%MB_6 zJXrq2XaWxS4$BllKfsFT5+1mtL7p(3P{wBd0O-T_V9eJ;vB!52ja{x=UII^(M$#4f&obEAQlfiAo>_X&V%pi>r?S(zG>{^q)5xVD(dZET z1i-Ce0I@HY9PP1Uq71pzvnFK31Ud!R;Pe()%Vm-e0$SJBPIWpOSax2CTcQ%D)`(8m z)?SI%)^eVdkNO^?bio0Xlhz(ATp9HjFJ5#2O@XeSp?;@Ntt7)uT_DoS-YFoWV$clX zNVwC@FncygJlZ4#CGN#pB>!8A0mypLNsYLlg8d? zqXZ*{#b{^f-~>5A16ji*I>jBPkeGZnC0lMy^5doSXotQylXkYkV?cEaeW=?wU`-V- zU)-2-NfMjCj7I-}uMP-GOnp|&@v|gbq|En5nyfArN$DGM?48?F4BY zW=N5wK93~z{MMhD#-p-njGx;sZ#0s|#sZStfAj&KBT+vdjS)BBO zs{?xN-mYvc>Cx|%;(tx%o*cxTh&SYG?&#Re{aqlO3>}?imYpdh=#BZ%eIPrKvSuC-;cTi1RVJkNL%)UE zbmPV-WI0t3;gqHoi(fk58HF6711$O3IYFK=+HPvTo zg-Ke0QH%m_yqNi$v*JO{V!^7BF}imNVt4^MQY(rTBcQa3;2DdAQcVA*HtcmhwVAy^ zA>kfd+HvA0Y4v93+KqSb+)k_4Zwjkv8L7E2N-I~d-nn-37I4Tq>bGfmAkC5D?tmP5 z?dBWTZlqP*z;o1|(b3!2-?-YWYAe0NVJq-m9IJ{05kb4|_OR7qK#`@U-##$8M55Q- zoe5SyL?{1*Z_Jq5Fc&K|yKdTu%qrHRxoBA^W-KC7I}V{Qnsux3g=N)0w@OR@W0jez zUo?+fyo&HpOzEMpQh>p){zfil-Ua`nbr0 z)koHN5j6>H@_7t@1%L1y`~m7aiH)+^d0jT67h<_=yY#2<56?3-#G^l$iMgS%?vcPRJU1Cn;0{-nd3_jk_jYW-i$_}PQ9cDuf><%$P=AfArHJm zb4Ecq3bip3v*e{Q3F@!nhI)mENswA(Ybul)qjJNn+ticgQVm6f@hi`g;7k&6kL);O zT;t+@!C@l1#N2@7lF|l?lh%$ttMClbCf1E#mVX90V0()B$41|jEh>#irSbWXj7*Z% zj|6e=kz{{blG&`(wtk_dyItZ+^8utBT38R3lhTv9kBp5v#?`CJIayWycDgm%a%O8= z|2Ta&??dLQwwhWGx?{Tq^L?{fPHnO95{rOuB@4tG^c>h(=IeS%s zIg}ICw)~{#tIfKWEN`UMeuzCGz4f%x4+c=Uv^qdR0V+A=O(!0fs*;OUUN7S<>@yNi zvCB!2$L|zQ!fsg431B@A;f-H>mfq;>o3*MKjx-nd#oGm}o~k@@Mbki>eo$9+IkSpX zOu4Tj%uqM*;8*y@M{xkgUM9N6tbg99Ev_t;mJTfajjuN%63P?M_v%#!Yj69o}jV*tzb_10By7*DNk#W0P!MI*=YM_SDUScq1&ruY zSCLO8(fp1HjHvN)X~McMW0l4mUj^rH@w`@A-VvLt;9I zvb{-v*mOrLmEKM#qv3E1D76?3iMw8Y(UkntgMf)D0qiUo=&~hH6X10gA7(fnSPmg( zFvO-L*$mYO&XZk`Si8Io+`T1(2OyKXIYBz>7#S2K;^nrbtn7Ez0d|yC8x%XQttZK5 zd|_qfF3Mq}ZVRukY$6$rZ8|G?ql;@D;Y!}r`wGwIym*ryHn%o|R;2E(fP8LQ$+jR) zny#=TEuY&IWP;j2;v*g=RP;97@GiGfAuTz@Cy1xur?~3+Jr{F{LV|Qv%uNPDWh)zJ zvMbeg2N^$?Spg2?KTKK@lM}tKM-P0sdnC@|iJ%c2_-e4l_cbLw*~gieHg@UNgY}@d z&cud$9~BJNIfNwC57#t15U27rXwQxv-QilRfYbZUE`+1;vmSkR^IQ*`cM~;YeU@W0 z8JD9?NuDu6)>Xs;y_3&c*8Q5Wm|f%<1LiAqSx)aSWlvOq%p_)e znq-lRUKEo~`BhPMK}i=o2bms1jX1zA#umv&5{`ylTqCOyV;6Rq zZC4WwndymXG$$KXs?eEvlJ9AW18Gs>yX2cE{3&3OW09fKx;T*yO@u?`%0EMd44mh) z^qcHa>76sWW2sh7CB3DCTWEzX4`8&&8|I2Xt#jP`zz%hICN#ofusB0y#P)4+OQY~S zAm6SJW!5if1eO^VnnTn&bC}OpuJ%*fC7p3J%}9~#e3uNtDTYtmVdscsu5@CGR*xx`oSsgI&S-Zbqv;>=0((2(`aS6jV!9+U%Bdj#*vqO{qTIte0LhWm991*|mk>t&_L?;IJ9*QNZo0-| zAYwuoy-u=ywxlzNE4dg}R&U8J=BIXA(GE#%Vda*s*y+*zd+n9cBLDE*!uJZk@e4TY zvoRGxTc=cn+3HWw3Uwo1Euz&rpexy;05fpnmD?e1rYv>1{)k z1lt}6UBcV!LMUbUgMbm+1yOneJOF?$9RnD8WMy#2&fzD6$+#s@r2!CXJ`sSW0j7ox zRm}zAo@Rxkq;o~|t-0d9t~{G40P@Rhzl&F*%n!&bg<~MfW(hMf%6eOlf#Ll^n92rHBsCnDYjznEjKtbYpIcv8)x~cw-d_4cW9feZ D0vi-q literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/jinja2/__pycache__/compiler.cpython-39.pyc b/.venv/lib/python3.9/site-packages/jinja2/__pycache__/compiler.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..197dd89c778cf1246a7b78250b0b88b779b91c3c GIT binary patch literal 54181 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 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/jinja2/__pycache__/constants.cpython-39.pyc b/.venv/lib/python3.9/site-packages/jinja2/__pycache__/constants.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c49713e337cef12fb44c3d89f50d1b433625ed0e GIT binary patch literal 1541 zcmYk6&59&N5XXBKgr#5)zCm91u+yL*C?X#8Ah@pVung>F$;yhZjxj%K@}qGc#Yga! zy!r&<&6B^VUc{m*A~Gv7@}K{RNdMx+bl~~oulwbnwR0$<5K{MY{{px8_1gsVoJ}WMVZQ*tP=IBy{OR>a-8omUShMS-t6tk5P$H zJ%Y(-(-vS=q8_g|#(|NRMQP3|MY_F?o9b?Vfn%WeuC4zh@S%O=J+Si<{ z(mGqAyOSt-;zSe1N8A!QmSjRm1*p`5FQLu5+nzFbwQ*o~@&UL_|1%m^k~W=S%}}}% z;Y`+#(N8i~CG4q^3ysmc(`Z55TKz>A@6L8)((ut}O|Zgr24j^9vhx#>yZ}ml7#3U? zr)iVAC=fOC2HCnfkj+)HxBFj_Az0zJbt8ko_SDlCtr%7%gisjjBh0ZK?I z6;nan59VZoF zo2oNE@yT#b@W6tK(aR%cWhu>awJcE9`HUbHi0E?36W>Vb3A=Tm=V5H`l6WHrR~9X< z)S!s$tGLZq_E4+-O%}!yBLivdGi{u(?>U8Qx1CvFVKEwRK-i#iFByz3{DG;f>_#5r zMh_6ESbwS8s+YF!MIM|e}CP05RjFk?i&Q-+vQMsd>r749{4cUnN z4)oL2P;d-V8YT_wnir=%vdRFZcoarBLL zyo|VwPTGEYdGr3$AK$$F{`H3+e!hG6`osO3x8L3UeyDcc?&6>Olc&#bzdS;&yxM&D#d852A2dJs&yIE>TD!N~?dgx_ RUvsC7DBnC|ufO>Y{{n(WGCKeO literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/jinja2/__pycache__/debug.cpython-39.pyc b/.venv/lib/python3.9/site-packages/jinja2/__pycache__/debug.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2381953245d58962cec3a2b26e6cd92c58674894 GIT binary patch literal 3979 zcmZ`+&2J+~74NS8aNBVl=VK2YK$>GLr{{qgOIra$&2`R_DBEj%`)wU-y8ak@-t5;R;RlR!e z_g=!q#hQWdJ70dnKi@Enf70Ok=VI_4TJ~3T+~8(xw98+!%`j%MHL%;ZNjwX*({@1H z+=<;mrCrgq8+(IlyQ=95=vupG8iMr~(ezVMmT zUKUNwcfel~E5hORXAWQFjnC}%DrVNeW&ImHhI!V%`C`PEAfw5bv2LB4kBrvp74Bf{ zw#>qNkaDqiHWZ*2_hi@=ov?dEj4KmMz+K%FgJB#h@#rj3;psyuQ%Q@XL`Dv8=0~IXQ|&*%g2U%TFyR6G;d|H%I!;Y?BtGK`M2DetYPK` zcrMA$t=#F?a~F~;xjng=S4eYG)3inOOde-rPp#+nh55emYv|9t+zJ}GHTItTi_V;T zm-H@9cnULoEKX#kgs%>T-yKOQ66K52t{AE)O?;L5Qs6s= zkvfbLzncz*Q7pKxrY{H%7kc*kHgy;(KTgvlKaP&H)-x@o8xDk)sSh%jQ9kHaO|RKI zj52>P%9P*1S;LsLO|^%KzrSA|dw<{W#A)|vy6N69l;OZH1>N(1luE38jhF53-_fd5 z868CMwCN5%>S^6{)1xFkNw)OxUg=@?{5_a$zU778=vZ2u)aF$JrkvTXl(4sZUcEm` zw9I$gD?!kYl71M>ZXN`!jl#;#vciMZAWC{^VXDIJhM6iBe;8)s;p~y51(z;r5#k|J zVbT?n{JdR*!X%?u;*y-dXwad8`7w${FFX?0#$FHam*5==H;xjKq(wz_f&`&ecp!Q( zBc^&f1SJ7fQSAi>aoP#vtgsG*YHy&!G@K|KrU^%U22)8@2$+&?I2?+E7oHTyLS}-u zs^$62ps3PakHdH*3Z^=RgZp%kpnd96a*ZxlDbL-JH!%8(A#da7-;cHr(}CDNkOy-6 zbU5m4cT(I&-i1wuqD#KKy>%>-wDW7T>tJ6O2Pw?ZR;m^XPb-8 z=wNGjR;-t=cg;wFK2ZZ2v+tra78+*Vth1)+nyyu|nAv>pIxe$q&#HsRSc7?3(Zre> z^O&u^l@*_;r_*)Y}&?ydv9c;&(xyW9hol+vDRSx(;_d%4G} zM@%NXHm)RZQV&jVP`c>XIa^#c4 zl}lLFUqox5Eul5hmeE$wR?*hbZsZm8H__J7HqbUNmd5URQ{Bql+yRDJrtyi9+_nw% zl|PtgYbKx@c6qc)rLpx$E=Qxy6*oj3}?gRW?hwIr+Ag<1YFC*VP zr2a2h;Q){*#a$Ujh(|h5R|)`!)=kJq+Fml{6a`CcRh?Hv&oPEX0Tc9oeH;J9O<B-$_S?;|~3xA62y%dNY`>I{2fsWR;We67L@ z0(z++Ad1ulf&3=IurOU>?L!@Ds6FP^`%&W~+Z z=c?em{PnL0S6l3gApOcC8U;=_)hD@vXB(}zid90{nIQsg)=b{ST;cVm!@?S-L)xfw zvRucc+@cjV;FnYxWdgZCj5=X3(6P)gm1(kz|I8FJ8tsE=Gpzw0@nSH0)}lcYBAKaa)}ZZi zlB%$yH}#|t*jP&Fia4f$Y|%ZJsH;;)`2or0H6V)&N3n(D&GMJ659tw`f)u74B|2^B z^ipI3hQ0cz$7t6>E*?brOxxnJ6A z$Q@?G)bdfvE+7Eo+2iVz0N|g0#GtgGQ^G$$(W43c>F0F+bA~eiOo?w%AMR%&d87-KG`{c3tX4}nYd&qkKJ))Y^zFML7MRZ z#Ww-P-q~+UAbxB-*?DXvYzM{X#K>*#ULv(@8slnS&AlT7a=qN?QSoZu$6Bl^^}kK} z(OfEcwdTL0G_67cN^sM7@&&-3={;ZM48ZQ4$5iwHf6G{w@W}``TE63*Im%2S5b+L- zKS@V12exUP36H}lCJ^;8qo+&weFYp!R3N|ASi0M0aUkD>iRCuBmU;CYpQOiV)#N6A z;6sQj%kMVZR@Bl|JIN>rx#kDMkzL{s09)EZ(JO%5I_aa4jm-=&E(*nssaA M`wf%*xBkKZ0O53SLI3~& literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/jinja2/__pycache__/defaults.cpython-39.pyc b/.venv/lib/python3.9/site-packages/jinja2/__pycache__/defaults.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..00408f4af6ac02f62aca747983169682a530eff8 GIT binary patch literal 1341 zcmY*ZTW{Mo6qaOLzQ^}8X}d0476b-X8{2?k1Bzj;qqJTe$Fr4o<2q=H$vAR!14*~F ziu{xQf&C@E?kSJMUWz{LP_YwN1P?!c{BU@9cqqGA%qj4F|L;ruUs6&2k(2c=3zMIr zvkzcS6|7*@Rgj7`oNzTSfs(2MIO(Q59i?NOax-2QWn-)Z&Y@h4(}43RAL9(*0xGBq zQD;T^SL}5Im2ei7aSl~*9#wGx)o>Bj@dj#O6>Wm@9@*Mf$TluLfhf@ZHKq(q1wDZN z5LeM>xCW|v%snEXlO2*HS&}CO+<32}FYqS5hgE1>(6{lXqv8s_|2~7h1dqF93+z|t zTD;Q(xzku_KD>t0D}!qlY*Q8K!(QJsMq_{}(-@l)=-tJ{B@|G7N_;|XPAoT|#Iu}` z%{{63C2(K?P(>N_0vb^q9Z} z`%@y4*qLyoyK$UIpTy@7`YeuCWc^jFCd&9h5X!?{ryvm`%L2-+OR`|iOjMl&Ug$su zS~iR~Qa&ziB5ouB4M#UxOHk}e4)t6K9mQ&kelM4bFiAtQ#{RyP5Jn~U>tT%`;fVley`gz#?3vU z^F`?RQ=xK^K6hNm7NZF`2`a6`%Zn8$P8eqtUW9^*tHt=LMQY9+mr;p-tEW)JCf4!4QC7vR!rkXXVmLF z?Z-uMC#>DlYufs%AU^R~U9opcDxb5+oP;uuwkr~FOQK|1w(kermL#(*D*p&54}l$9 zaadzi?8`WZ%PB2LLF33UhL$<%^q{VuSTT0>HIqoH acde>c<5@vXrZn}oebn=}7+77^KKu{TiFm~T literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/jinja2/__pycache__/environment.cpython-39.pyc b/.venv/lib/python3.9/site-packages/jinja2/__pycache__/environment.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ea674056b867e57cecc7b454c25b2c302d2f339d GIT binary patch literal 53109 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 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/jinja2/__pycache__/exceptions.cpython-39.pyc b/.venv/lib/python3.9/site-packages/jinja2/__pycache__/exceptions.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ed404338828f82df9568bf845619b18ceeb71c8b GIT binary patch literal 5582 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 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/jinja2/__pycache__/ext.cpython-39.pyc b/.venv/lib/python3.9/site-packages/jinja2/__pycache__/ext.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d48e01dbaaed414950a0fe25e768a90e38db2a7a GIT binary patch literal 25537 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 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/jinja2/__pycache__/filters.cpython-39.pyc b/.venv/lib/python3.9/site-packages/jinja2/__pycache__/filters.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..85c89d2ee583ce10b0e8c1459c0032861b0e1925 GIT binary patch literal 50404 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 akt>6D>(=yd`|E=@cl*tI{|o;8^8X8#pRvIJ literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/jinja2/__pycache__/idtracking.cpython-39.pyc b/.venv/lib/python3.9/site-packages/jinja2/__pycache__/idtracking.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6caa6024f17cec1692e8590125749241112a2b3c GIT binary patch literal 11091 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 GivDjJ(FvXa literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/jinja2/__pycache__/loaders.cpython-39.pyc b/.venv/lib/python3.9/site-packages/jinja2/__pycache__/loaders.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..29bad54a8518c320c61f22d08c212a4dbc0c9d6d GIT binary patch literal 20436 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 VdvzF%Kex)7-o+~$8|& literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/jinja2/__pycache__/nativetypes.cpython-39.pyc b/.venv/lib/python3.9/site-packages/jinja2/__pycache__/nativetypes.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c4ffcce5adbf867d3cd6f7ee8d9c08e10a64ca85 GIT binary patch literal 4963 zcmb_g&2JmW72nxil1qw|DOpzRBtW+xan01GQm1fJ*KzH{j^nnG(>O?)1&Z}*XCy7N z++}7)mPODP%clY@+DlJ9$UrXsYkKHyuLTO^So9VYNq=vaq)AzEi?&P5)0>$$GjHC! zPerX(!@_sr?;rCAZOi&Ab!I;oohxYSZvfolEVTlLzjkW(oxou<<^XpC*Whki=~siQ z!7IRPLCxUR)a%!SI`A6z(nfzFSTJ}!ZT4G1%is;*i@~D77l0oNjv2g}w);!LlEGWR zmxE!;$zARe%PVfTy$N38SD`E-#ljxt| ztLU$aW%N&pQ{rU!B4@X)&I@1QuGV^o70oo!LPlvQ?nWv4l|dwxASB+7k_<>SQE3v> zQ0s=sK&x~4;od-e!LS7pW;qv%rr!oKC83#jWYjkcw<8s*Xj6nz3?yb2u4i`>nP+{G zX>`30MSqY+T6FB93O;s(DymZG9ht>+%H(r^&||)hrv3z=EzL%FvVD7>-De}FR=IuK z()Nh$Gwyt9%Znq%-BZ?mTRS6LyZg4<7=a=?gTAFJ`?g%y)e-jCx9?lLa!}({UVCVb ztZUXjd(7_J!)5L1`pD)Uum29Gd~DseTuV3ff^Kd)dw*aS{YG}x`UEqtL2V%3MwEf= z56lJp$j>63+!g-&dwM(1d=1(8n=B9$}@0i z)GsflF8Vh~JvVS?2r1vugB@I2=d<_dnq^BO*YmB=B;1v{$x$TzRnGz_|s@hsI^{_^`i4@rACk@sP15| zXj~#SAQia$3eNinnl=1(NfeeZ*1eSGag?gdKqj<>J2qOk56kPc3YVs21*;1u6nVrns`Zn zcRPun=__3BkE;b|hmT)u4qBI$bAJb}53dO0G z&#WMpw!rV8WXY_h_4o*VIpn<&BPm zRGuI(wN|5z@)X7{(fEr1!;42=sPrjhWlEdybd;sxmM(=Z>0=}6D3NZ;AJEqGM;6QB zGaLRCWTjBq@brJiL&q&#^4UdYlR{|WO&+L}GRbA~k%S#IBjpP~M(hCbs7KMuMI?k_ zsF$^K;6Anxl#%IvHTVdG$)b!TBIZ73?#))MV_M-YXn{+@IwOU2^^Bv>Qq|WNy|AJ{1wE!pP|%tJ`66bz~hd>OE%2y}SgM3bD=c!X?uk zd;Lq+kv{^7Bnv()<)=DXo|7y6t2ySLua(n9o#TZUh8Ywx42wn>BI@j}aS|P;a3i^e za|6a@yv9%gU$R zPm9FH$iy!BYAx>R!sSV%UU zP?OJCh@{ZP^QaqdB9y#~kTTXvZ;wy4(mJTmXe9y;a-ZQFAEL^e5rn^s!i=)RSPE08 znHP@KUKab5>4TOkBHo?51i3CUoxji}$oEl&Gi<+ZnEBYE@|dce3zH~k#MrUrDo6{K z9g?caoko}{bEmZ*lv%@1}mo%Dq%B;-xnne1Cqn3SX_@|OhO zBS6+K-zRYBsheXpGUrMVDB|8iRe@UKYu7Cw{$%;^FFl9nBU~*x#FCpUgk2@@0fCPQ zJY~dSCeplvi4bk-R!_Dijdflss;KRd*vl*E77ps=nNq8cq(bUEPn9Xf%6hG0iovo# zqg=R*hTiz!0@4J-GAx>GjD#yGLy|25ZxJAl3-6XOtSB{6!GsRu>J4MT;>6{R4fKdx h72c&Xu3x5dLiqq5YfgU1#HK&T|4#dKyVm~hzW@%M;Vu9G literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/jinja2/__pycache__/nodes.cpython-39.pyc b/.venv/lib/python3.9/site-packages/jinja2/__pycache__/nodes.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bbd4948fc67d9295f5697d87338502dc1a258e33 GIT binary patch literal 40876 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 Z{Cv&sI>y!DVK9iiAsu=l9r_ph{{mfj0r&s_ literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/jinja2/__pycache__/parser.cpython-39.pyc b/.venv/lib/python3.9/site-packages/jinja2/__pycache__/parser.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..281e422492d3e8c8ba58e0d8b547153a85dc215b GIT binary patch literal 27578 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 Vao`V5GcV*t28hXQdl!ni{|8UB;okrN literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/jinja2/__pycache__/runtime.cpython-39.pyc b/.venv/lib/python3.9/site-packages/jinja2/__pycache__/runtime.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5258f866ff7d3f4adfd12a0c9cfcbb70b46b1721 GIT binary patch literal 32168 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 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/jinja2/__pycache__/sandbox.cpython-39.pyc b/.venv/lib/python3.9/site-packages/jinja2/__pycache__/sandbox.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..021013b82a75262be8c03e6ada26605447250c7a GIT binary patch literal 11921 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 TDkF|8`metCa3-vEV7)( literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/jinja2/__pycache__/tests.cpython-39.pyc b/.venv/lib/python3.9/site-packages/jinja2/__pycache__/tests.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9bb4d7b228bfd25d30f6096e316985c83233a855 GIT binary patch literal 6573 zcmb_g%X8bt83#a8Bt=P@$PLkMV}W&@`sm8q=BK>9#Ka4LgVboR@DF?1D~xd2gUu zw2P=0*nn4RmhG~v7rnt|#jfZY&utBf-=MeT4L3*Z5l~L?QC5Csut8S&E^m*e?IAXd z_Bh%!V&qkhonoV)oW>ewx^u?ZI4EZ`l+)}CC=^9ohGc)e6yP(|2Q0ABg%4CL8W%oe2nW43 zgEF0=e83)oGSi(o3*IeY=7;PtsJHnTALpm}Sw6wf^9%eUzr-)|EBq?I#!Y^m-{6z{ zCcnj}`5ivRXZUTdZ{5wjbEwm6W!@_4^xn(7^Qh0WC$FaM`^;F;W}kK*{ z5pT9UC*r7tQE0Y9&dld-v}s10++168!?iUtXmR00fw10?7BA20an7kX@HepBZq~VY zql1iQ`E#G~b=T(%jnS49hBfZ*xFYbI+>b!3EM=NTT-b5EHjf7ckJ`d-&??D8br^@X zh&LQUu?It#(3r7J8_0DgKa+FJNVK`>u1mB#j&Pm2hjwTNj9G7J0g*@XT6?XF616Hx z@ph%U88mryLu`oZi&nc{tp~2h1$Yv+cq4KHziRCOe${j9)z)sb8Tb#ZN7c}c_*~0r zY&#n~tZupfmUF)9Il z(q^zfcESAm>-~w}?2pEh+za{91@^Vr46I+#wBs&W&*?eupgOH>jMvmX$qCoQM zrC>u!Xo?kAQh|+#Gkpa{s+$Om#MPPm)ZNbRfPBcBS#L*jX92b+Bz6%1wk5`o!!siB zq+7F6M20v^bBZWtb8!xKtu(Q}>J!dAeNWrcc9dFOfLdjAzsq9&!X@7co%fw4x6IE& zzTWnfd31*~!T36tybW69TpZ7XiR?J8M=r}sm5|hLuI>em?c@E5(L?AA8|2#WnQ5Dhl1SoVV|zz z5A`plf<7%Sk)SDXB@flpa6>9X!M;rvXLL@Ag?`|3Yx$%QNTYs>g_C%48TI2KPRU(_NW_j=4+4)nz6t^R7JbNC?pyQo(p*-b9gkZ1)$W38De!_I zAA3CTL%jQ0U%<~CM4yvk_CYyFlC7=NI-41cSKkHWVqc7_NbgRDxOzMzyBdgPS{a1b zdLc~HiHqn>qEm2QDuOQmjkYe6&yV|Jh6cEP#5a)nq+b1a9Y9J%xmldUoIWA8yW(zU z6W9B0;z)>n)*rOF?gdVCGU5bpL5}Z47#G5w|f7yWw~Y&*Am zAWrfW`(%Dm<&vf6Bm_?F%OnU!-ptFX@P;cUDdS z)O1=cATno|3lGYphBezq{T`|mBd5@l+Qh|Qs1^s4SHdiD#>nL3`Yf zE4A7NkI>t!)#CEfiw2jeZO`2g%UF?o_^-4z(itsp=p}tnAJR*PxQBLfFfGWW|AeEd zig$Y|SI|5E*-y=Vv#a~90IoBnLl2#iLgeRWlQ#o_>u%;gBKst!BQ< zxo|xQEGL42;lv{IJ^GG=bKqo!A@(NSj82}wjfkty%eo%as{ zMji=wJ@2DTR5%B1|34L29WOfzDZUgNuIR8#AICVa4qpd!;RB#JtP4eHfUf^f^eMCj z@rck)+c{iV*?GDqvMZSj7`xcLd$7yti8>xo2e&v+%f)$0tm9%gQH={y;n9bD3o%b+ zLE;9!5*K9LjSDi&#RDq7#Ko?Ijt7)qj7y1!h)apw<5C0WLH@@cP^ugkyPS^=*Y6ll zp2PRwl!|34eo4izsrU?qU07XOSzXC!oAg2Q`myx58L+m;pV9#@{3D7y zoPCLm*PwSmAIgmuMhoMG@v-rOp`RJUdlsz{er5bh-QE&t!xO{fgX4vXvZ24*FR%Cc G{p)`uQv65& literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/jinja2/__pycache__/utils.cpython-39.pyc b/.venv/lib/python3.9/site-packages/jinja2/__pycache__/utils.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e3ba58327e186e492191b777012f5e434892fc7a GIT binary patch literal 24541 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 YaxB07TZJcCZnx!pw=gyK_}JM01M~df=>Px# literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/jinja2/__pycache__/visitor.cpython-39.pyc b/.venv/lib/python3.9/site-packages/jinja2/__pycache__/visitor.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b7c34d2d732dfa4fcecb8c1dc0fc8bef410c86cf GIT binary patch literal 3924 zcma)9O^@5g8Rk%=$mQ-jc3QZ08W&R+h@-ZVfD|YiAT4aTNourE4DZH4)dm!oGm9n*CG zv`Mn)KqD#Rg6os4h>nq=Qc}Wk@)K>i3PDK1bG%a?uXVs>dh=AqUYcEsW}$ zXPFX%7NnNuk&?XMzh{<(_xgSQ10yC*Bds|FlIH^>h?YbDy_`q9zpqX{PBIvO(yz@k z`p<{D%dX3J3%Pos?y0N@Y96BLTj&a_1fCYGWM^z_|IzxB zE$p)fvavD$>l1?;SH`eL$r1E2sRjSlX7FU)5k^f?bJ1nPcMz!5K3M{pDgJ7EEu?RgR}AvF z^KW%mwb));qsV<1d`c4pAqGqD?zd-0C{mLWS`tNkvFL(CXVz;A)F|!D`(l)75S4`XQPjIm_N*^V{o2U+`zQ z6_|8U*Res}Kv#KTDDpTA4U?D{s5j~5H>e|x%mHhMn(Q*H@=d%YLB{1ibGEO0Z*3`> zjnOXr$jSH7^vCEfa*K4wP1C?KU=rMTz!x5vWHIlK!7H~d^Oo*B#3mjB;8$;uq5CSz zH1KvJRrC)q{{O)!2wQ%nIVd$5BymIlqX~jd7|t=CI0O}cL4LpHiX(8ADQ3Dg5ksM5 z&G-W9)sSw=+9VzjWRVddyc|?CO`{mlukbhl6dMtFD(Z?bh)alE3$5i4Z7sOsvQS{i z0-|Ue*|MOv^8;$K7^r+=j+!b7UYKc~=Cu!aj*aRlfi+2SPcJkS5L1lS3|#4AIA)<@ z&8nPe7BoXTS5L^8kilUsGZWgT-2vt=?QCQsr~17vzv?6 zdR#VNx~2Duq3G}8xw(2SU4a|r@9hr(2j)zY?i=auhIGI5jr6}2UI|D!vk62BpPozH z-Dy+;QWSk9w6>~kgA9X2rb1T%Xb+qiWif8ODaf`iRI1FXDUS=8iK-b*r}Xa$nHpm7 zbj=X0tt;`h8&HZXZfHUw;ND#E$Ila;o$8Z~5csI9(>?rJ!%Y;}0at5sm?IVAae*jUq;bIT4`)1JR|*?p_Un~<*V z(CH28R!B}ktDIj^{*X`CPG8~qcktqUG))zTPzJYdDw=EU7?D*I*A3hijnvAe)X?Ub zYIpzFk3R|@{^FyDzkJmFq<5oU7nkpov X(Kno?4;!c9+dFoX`SzRcwY&cXS= bool: + return t.cast(bool, args[0].is_async) + + else: + + def is_async(args: t.Any) -> bool: + return t.cast(bool, args[0].environment.is_async) + + # Take the doc and annotations from the sync function, but the + # name from the async function. Pallets-Sphinx-Themes + # build_function_directive expects __wrapped__ to point to the + # sync function. + async_func_attrs = ("__module__", "__name__", "__qualname__") + normal_func_attrs = tuple(set(WRAPPER_ASSIGNMENTS).difference(async_func_attrs)) + + @wraps(normal_func, assigned=normal_func_attrs) + @wraps(async_func, assigned=async_func_attrs, updated=()) + def wrapper(*args, **kwargs): # type: ignore + b = is_async(args) + + if need_eval_context: + args = args[1:] + + if b: + return async_func(*args, **kwargs) + + return normal_func(*args, **kwargs) + + if need_eval_context: + wrapper = pass_eval_context(wrapper) + + wrapper.jinja_async_variant = True + return wrapper + + return decorator + + +_common_primitives = {int, float, bool, str, list, dict, tuple, type(None)} + + +async def auto_await(value: t.Union[t.Awaitable["V"], "V"]) -> "V": + # Avoid a costly call to isawaitable + if type(value) in _common_primitives: + return t.cast("V", value) + + if inspect.isawaitable(value): + return await t.cast("t.Awaitable[V]", value) + + return t.cast("V", value) + + +async def auto_aiter( + iterable: "t.Union[t.AsyncIterable[V], t.Iterable[V]]", +) -> "t.AsyncIterator[V]": + if hasattr(iterable, "__aiter__"): + async for item in t.cast("t.AsyncIterable[V]", iterable): + yield item + else: + for item in t.cast("t.Iterable[V]", iterable): + yield item + + +async def auto_to_list( + value: "t.Union[t.AsyncIterable[V], t.Iterable[V]]", +) -> t.List["V"]: + return [x async for x in auto_aiter(value)] diff --git a/.venv/lib/python3.9/site-packages/jinja2/bccache.py b/.venv/lib/python3.9/site-packages/jinja2/bccache.py new file mode 100644 index 0000000..d0ddf56 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/jinja2/bccache.py @@ -0,0 +1,406 @@ +"""The optional bytecode cache system. This is useful if you have very +complex template situations and the compilation of all those templates +slows down your application too much. + +Situations where this is useful are often forking web applications that +are initialized on the first request. +""" +import errno +import fnmatch +import marshal +import os +import pickle +import stat +import sys +import tempfile +import typing as t +from hashlib import sha1 +from io import BytesIO +from types import CodeType + +if t.TYPE_CHECKING: + import typing_extensions as te + from .environment import Environment + + class _MemcachedClient(te.Protocol): + def get(self, key: str) -> bytes: + ... + + def set(self, key: str, value: bytes, timeout: t.Optional[int] = None) -> None: + ... + + +bc_version = 5 +# Magic bytes to identify Jinja bytecode cache files. Contains the +# Python major and minor version to avoid loading incompatible bytecode +# if a project upgrades its Python version. +bc_magic = ( + b"j2" + + pickle.dumps(bc_version, 2) + + pickle.dumps((sys.version_info[0] << 24) | sys.version_info[1], 2) +) + + +class Bucket: + """Buckets are used to store the bytecode for one template. It's created + and initialized by the bytecode cache and passed to the loading functions. + + The buckets get an internal checksum from the cache assigned and use this + to automatically reject outdated cache material. Individual bytecode + cache subclasses don't have to care about cache invalidation. + """ + + def __init__(self, environment: "Environment", key: str, checksum: str) -> None: + self.environment = environment + self.key = key + self.checksum = checksum + self.reset() + + def reset(self) -> None: + """Resets the bucket (unloads the bytecode).""" + self.code: t.Optional[CodeType] = None + + def load_bytecode(self, f: t.BinaryIO) -> None: + """Loads bytecode from a file or file like object.""" + # make sure the magic header is correct + magic = f.read(len(bc_magic)) + if magic != bc_magic: + self.reset() + return + # the source code of the file changed, we need to reload + checksum = pickle.load(f) + if self.checksum != checksum: + self.reset() + return + # if marshal_load fails then we need to reload + try: + self.code = marshal.load(f) + except (EOFError, ValueError, TypeError): + self.reset() + return + + def write_bytecode(self, f: t.IO[bytes]) -> None: + """Dump the bytecode into the file or file like object passed.""" + if self.code is None: + raise TypeError("can't write empty bucket") + f.write(bc_magic) + pickle.dump(self.checksum, f, 2) + marshal.dump(self.code, f) + + def bytecode_from_string(self, string: bytes) -> None: + """Load bytecode from bytes.""" + self.load_bytecode(BytesIO(string)) + + def bytecode_to_string(self) -> bytes: + """Return the bytecode as bytes.""" + out = BytesIO() + self.write_bytecode(out) + return out.getvalue() + + +class BytecodeCache: + """To implement your own bytecode cache you have to subclass this class + and override :meth:`load_bytecode` and :meth:`dump_bytecode`. Both of + these methods are passed a :class:`~jinja2.bccache.Bucket`. + + A very basic bytecode cache that saves the bytecode on the file system:: + + from os import path + + class MyCache(BytecodeCache): + + def __init__(self, directory): + self.directory = directory + + def load_bytecode(self, bucket): + filename = path.join(self.directory, bucket.key) + if path.exists(filename): + with open(filename, 'rb') as f: + bucket.load_bytecode(f) + + def dump_bytecode(self, bucket): + filename = path.join(self.directory, bucket.key) + with open(filename, 'wb') as f: + bucket.write_bytecode(f) + + A more advanced version of a filesystem based bytecode cache is part of + Jinja. + """ + + def load_bytecode(self, bucket: Bucket) -> None: + """Subclasses have to override this method to load bytecode into a + bucket. If they are not able to find code in the cache for the + bucket, it must not do anything. + """ + raise NotImplementedError() + + def dump_bytecode(self, bucket: Bucket) -> None: + """Subclasses have to override this method to write the bytecode + from a bucket back to the cache. If it unable to do so it must not + fail silently but raise an exception. + """ + raise NotImplementedError() + + def clear(self) -> None: + """Clears the cache. This method is not used by Jinja but should be + implemented to allow applications to clear the bytecode cache used + by a particular environment. + """ + + def get_cache_key( + self, name: str, filename: t.Optional[t.Union[str]] = None + ) -> str: + """Returns the unique hash key for this template name.""" + hash = sha1(name.encode("utf-8")) + + if filename is not None: + hash.update(f"|{filename}".encode()) + + return hash.hexdigest() + + def get_source_checksum(self, source: str) -> str: + """Returns a checksum for the source.""" + return sha1(source.encode("utf-8")).hexdigest() + + def get_bucket( + self, + environment: "Environment", + name: str, + filename: t.Optional[str], + source: str, + ) -> Bucket: + """Return a cache bucket for the given template. All arguments are + mandatory but filename may be `None`. + """ + key = self.get_cache_key(name, filename) + checksum = self.get_source_checksum(source) + bucket = Bucket(environment, key, checksum) + self.load_bytecode(bucket) + return bucket + + def set_bucket(self, bucket: Bucket) -> None: + """Put the bucket into the cache.""" + self.dump_bytecode(bucket) + + +class FileSystemBytecodeCache(BytecodeCache): + """A bytecode cache that stores bytecode on the filesystem. It accepts + two arguments: The directory where the cache items are stored and a + pattern string that is used to build the filename. + + If no directory is specified a default cache directory is selected. On + Windows the user's temp directory is used, on UNIX systems a directory + is created for the user in the system temp directory. + + The pattern can be used to have multiple separate caches operate on the + same directory. The default pattern is ``'__jinja2_%s.cache'``. ``%s`` + is replaced with the cache key. + + >>> bcc = FileSystemBytecodeCache('/tmp/jinja_cache', '%s.cache') + + This bytecode cache supports clearing of the cache using the clear method. + """ + + def __init__( + self, directory: t.Optional[str] = None, pattern: str = "__jinja2_%s.cache" + ) -> None: + if directory is None: + directory = self._get_default_cache_dir() + self.directory = directory + self.pattern = pattern + + def _get_default_cache_dir(self) -> str: + def _unsafe_dir() -> "te.NoReturn": + raise RuntimeError( + "Cannot determine safe temp directory. You " + "need to explicitly provide one." + ) + + tmpdir = tempfile.gettempdir() + + # On windows the temporary directory is used specific unless + # explicitly forced otherwise. We can just use that. + if os.name == "nt": + return tmpdir + if not hasattr(os, "getuid"): + _unsafe_dir() + + dirname = f"_jinja2-cache-{os.getuid()}" + actual_dir = os.path.join(tmpdir, dirname) + + try: + os.mkdir(actual_dir, stat.S_IRWXU) + except OSError as e: + if e.errno != errno.EEXIST: + raise + try: + os.chmod(actual_dir, stat.S_IRWXU) + actual_dir_stat = os.lstat(actual_dir) + if ( + actual_dir_stat.st_uid != os.getuid() + or not stat.S_ISDIR(actual_dir_stat.st_mode) + or stat.S_IMODE(actual_dir_stat.st_mode) != stat.S_IRWXU + ): + _unsafe_dir() + except OSError as e: + if e.errno != errno.EEXIST: + raise + + actual_dir_stat = os.lstat(actual_dir) + if ( + actual_dir_stat.st_uid != os.getuid() + or not stat.S_ISDIR(actual_dir_stat.st_mode) + or stat.S_IMODE(actual_dir_stat.st_mode) != stat.S_IRWXU + ): + _unsafe_dir() + + return actual_dir + + def _get_cache_filename(self, bucket: Bucket) -> str: + return os.path.join(self.directory, self.pattern % (bucket.key,)) + + def load_bytecode(self, bucket: Bucket) -> None: + filename = self._get_cache_filename(bucket) + + # Don't test for existence before opening the file, since the + # file could disappear after the test before the open. + try: + f = open(filename, "rb") + except (FileNotFoundError, IsADirectoryError, PermissionError): + # PermissionError can occur on Windows when an operation is + # in progress, such as calling clear(). + return + + with f: + bucket.load_bytecode(f) + + def dump_bytecode(self, bucket: Bucket) -> None: + # Write to a temporary file, then rename to the real name after + # writing. This avoids another process reading the file before + # it is fully written. + name = self._get_cache_filename(bucket) + f = tempfile.NamedTemporaryFile( + mode="wb", + dir=os.path.dirname(name), + prefix=os.path.basename(name), + suffix=".tmp", + delete=False, + ) + + def remove_silent() -> None: + try: + os.remove(f.name) + except OSError: + # Another process may have called clear(). On Windows, + # another program may be holding the file open. + pass + + try: + with f: + bucket.write_bytecode(f) + except BaseException: + remove_silent() + raise + + try: + os.replace(f.name, name) + except OSError: + # Another process may have called clear(). On Windows, + # another program may be holding the file open. + remove_silent() + except BaseException: + remove_silent() + raise + + def clear(self) -> None: + # imported lazily here because google app-engine doesn't support + # write access on the file system and the function does not exist + # normally. + from os import remove + + files = fnmatch.filter(os.listdir(self.directory), self.pattern % ("*",)) + for filename in files: + try: + remove(os.path.join(self.directory, filename)) + except OSError: + pass + + +class MemcachedBytecodeCache(BytecodeCache): + """This class implements a bytecode cache that uses a memcache cache for + storing the information. It does not enforce a specific memcache library + (tummy's memcache or cmemcache) but will accept any class that provides + the minimal interface required. + + Libraries compatible with this class: + + - `cachelib `_ + - `python-memcached `_ + + (Unfortunately the django cache interface is not compatible because it + does not support storing binary data, only text. You can however pass + the underlying cache client to the bytecode cache which is available + as `django.core.cache.cache._client`.) + + The minimal interface for the client passed to the constructor is this: + + .. class:: MinimalClientInterface + + .. method:: set(key, value[, timeout]) + + Stores the bytecode in the cache. `value` is a string and + `timeout` the timeout of the key. If timeout is not provided + a default timeout or no timeout should be assumed, if it's + provided it's an integer with the number of seconds the cache + item should exist. + + .. method:: get(key) + + Returns the value for the cache key. If the item does not + exist in the cache the return value must be `None`. + + The other arguments to the constructor are the prefix for all keys that + is added before the actual cache key and the timeout for the bytecode in + the cache system. We recommend a high (or no) timeout. + + This bytecode cache does not support clearing of used items in the cache. + The clear method is a no-operation function. + + .. versionadded:: 2.7 + Added support for ignoring memcache errors through the + `ignore_memcache_errors` parameter. + """ + + def __init__( + self, + client: "_MemcachedClient", + prefix: str = "jinja2/bytecode/", + timeout: t.Optional[int] = None, + ignore_memcache_errors: bool = True, + ): + self.client = client + self.prefix = prefix + self.timeout = timeout + self.ignore_memcache_errors = ignore_memcache_errors + + def load_bytecode(self, bucket: Bucket) -> None: + try: + code = self.client.get(self.prefix + bucket.key) + except Exception: + if not self.ignore_memcache_errors: + raise + else: + bucket.bytecode_from_string(code) + + def dump_bytecode(self, bucket: Bucket) -> None: + key = self.prefix + bucket.key + value = bucket.bytecode_to_string() + + try: + if self.timeout is not None: + self.client.set(key, value, self.timeout) + else: + self.client.set(key, value) + except Exception: + if not self.ignore_memcache_errors: + raise diff --git a/.venv/lib/python3.9/site-packages/jinja2/compiler.py b/.venv/lib/python3.9/site-packages/jinja2/compiler.py new file mode 100644 index 0000000..3458095 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/jinja2/compiler.py @@ -0,0 +1,1957 @@ +"""Compiles nodes from the parser into Python code.""" +import typing as t +from contextlib import contextmanager +from functools import update_wrapper +from io import StringIO +from itertools import chain +from keyword import iskeyword as is_python_keyword + +from markupsafe import escape +from markupsafe import Markup + +from . import nodes +from .exceptions import TemplateAssertionError +from .idtracking import Symbols +from .idtracking import VAR_LOAD_ALIAS +from .idtracking import VAR_LOAD_PARAMETER +from .idtracking import VAR_LOAD_RESOLVE +from .idtracking import VAR_LOAD_UNDEFINED +from .nodes import EvalContext +from .optimizer import Optimizer +from .utils import _PassArg +from .utils import concat +from .visitor import NodeVisitor + +if t.TYPE_CHECKING: + import typing_extensions as te + from .environment import Environment + +F = t.TypeVar("F", bound=t.Callable[..., t.Any]) + +operators = { + "eq": "==", + "ne": "!=", + "gt": ">", + "gteq": ">=", + "lt": "<", + "lteq": "<=", + "in": "in", + "notin": "not in", +} + + +def optimizeconst(f: F) -> F: + def new_func( + self: "CodeGenerator", node: nodes.Expr, frame: "Frame", **kwargs: t.Any + ) -> t.Any: + # Only optimize if the frame is not volatile + if self.optimizer is not None and not frame.eval_ctx.volatile: + new_node = self.optimizer.visit(node, frame.eval_ctx) + + if new_node != node: + return self.visit(new_node, frame) + + return f(self, node, frame, **kwargs) + + return update_wrapper(t.cast(F, new_func), f) + + +def _make_binop(op: str) -> t.Callable[["CodeGenerator", nodes.BinExpr, "Frame"], None]: + @optimizeconst + def visitor(self: "CodeGenerator", node: nodes.BinExpr, frame: Frame) -> None: + if ( + self.environment.sandboxed + and op in self.environment.intercepted_binops # type: ignore + ): + self.write(f"environment.call_binop(context, {op!r}, ") + self.visit(node.left, frame) + self.write(", ") + self.visit(node.right, frame) + else: + self.write("(") + self.visit(node.left, frame) + self.write(f" {op} ") + self.visit(node.right, frame) + + self.write(")") + + return visitor + + +def _make_unop( + op: str, +) -> t.Callable[["CodeGenerator", nodes.UnaryExpr, "Frame"], None]: + @optimizeconst + def visitor(self: "CodeGenerator", node: nodes.UnaryExpr, frame: Frame) -> None: + if ( + self.environment.sandboxed + and op in self.environment.intercepted_unops # type: ignore + ): + self.write(f"environment.call_unop(context, {op!r}, ") + self.visit(node.node, frame) + else: + self.write("(" + op) + self.visit(node.node, frame) + + self.write(")") + + return visitor + + +def generate( + node: nodes.Template, + environment: "Environment", + name: t.Optional[str], + filename: t.Optional[str], + stream: t.Optional[t.TextIO] = None, + defer_init: bool = False, + optimized: bool = True, +) -> t.Optional[str]: + """Generate the python source for a node tree.""" + if not isinstance(node, nodes.Template): + raise TypeError("Can't compile non template nodes") + + generator = environment.code_generator_class( + environment, name, filename, stream, defer_init, optimized + ) + generator.visit(node) + + if stream is None: + return generator.stream.getvalue() # type: ignore + + return None + + +def has_safe_repr(value: t.Any) -> bool: + """Does the node have a safe representation?""" + if value is None or value is NotImplemented or value is Ellipsis: + return True + + if type(value) in {bool, int, float, complex, range, str, Markup}: + return True + + if type(value) in {tuple, list, set, frozenset}: + return all(has_safe_repr(v) for v in value) + + if type(value) is dict: + return all(has_safe_repr(k) and has_safe_repr(v) for k, v in value.items()) + + return False + + +def find_undeclared( + nodes: t.Iterable[nodes.Node], names: t.Iterable[str] +) -> t.Set[str]: + """Check if the names passed are accessed undeclared. The return value + is a set of all the undeclared names from the sequence of names found. + """ + visitor = UndeclaredNameVisitor(names) + try: + for node in nodes: + visitor.visit(node) + except VisitorExit: + pass + return visitor.undeclared + + +class MacroRef: + def __init__(self, node: t.Union[nodes.Macro, nodes.CallBlock]) -> None: + self.node = node + self.accesses_caller = False + self.accesses_kwargs = False + self.accesses_varargs = False + + +class Frame: + """Holds compile time information for us.""" + + def __init__( + self, + eval_ctx: EvalContext, + parent: t.Optional["Frame"] = None, + level: t.Optional[int] = None, + ) -> None: + self.eval_ctx = eval_ctx + + # the parent of this frame + self.parent = parent + + if parent is None: + self.symbols = Symbols(level=level) + + # in some dynamic inheritance situations the compiler needs to add + # write tests around output statements. + self.require_output_check = False + + # inside some tags we are using a buffer rather than yield statements. + # this for example affects {% filter %} or {% macro %}. If a frame + # is buffered this variable points to the name of the list used as + # buffer. + self.buffer: t.Optional[str] = None + + # the name of the block we're in, otherwise None. + self.block: t.Optional[str] = None + + else: + self.symbols = Symbols(parent.symbols, level=level) + self.require_output_check = parent.require_output_check + self.buffer = parent.buffer + self.block = parent.block + + # a toplevel frame is the root + soft frames such as if conditions. + self.toplevel = False + + # the root frame is basically just the outermost frame, so no if + # conditions. This information is used to optimize inheritance + # situations. + self.rootlevel = False + + # variables set inside of loops and blocks should not affect outer frames, + # but they still needs to be kept track of as part of the active context. + self.loop_frame = False + self.block_frame = False + + # track whether the frame is being used in an if-statement or conditional + # expression as it determines which errors should be raised during runtime + # or compile time. + self.soft_frame = False + + def copy(self) -> "Frame": + """Create a copy of the current one.""" + rv = object.__new__(self.__class__) + rv.__dict__.update(self.__dict__) + rv.symbols = self.symbols.copy() + return rv + + def inner(self, isolated: bool = False) -> "Frame": + """Return an inner frame.""" + if isolated: + return Frame(self.eval_ctx, level=self.symbols.level + 1) + return Frame(self.eval_ctx, self) + + def soft(self) -> "Frame": + """Return a soft frame. A soft frame may not be modified as + standalone thing as it shares the resources with the frame it + was created of, but it's not a rootlevel frame any longer. + + This is only used to implement if-statements and conditional + expressions. + """ + rv = self.copy() + rv.rootlevel = False + rv.soft_frame = True + return rv + + __copy__ = copy + + +class VisitorExit(RuntimeError): + """Exception used by the `UndeclaredNameVisitor` to signal a stop.""" + + +class DependencyFinderVisitor(NodeVisitor): + """A visitor that collects filter and test calls.""" + + def __init__(self) -> None: + self.filters: t.Set[str] = set() + self.tests: t.Set[str] = set() + + def visit_Filter(self, node: nodes.Filter) -> None: + self.generic_visit(node) + self.filters.add(node.name) + + def visit_Test(self, node: nodes.Test) -> None: + self.generic_visit(node) + self.tests.add(node.name) + + def visit_Block(self, node: nodes.Block) -> None: + """Stop visiting at blocks.""" + + +class UndeclaredNameVisitor(NodeVisitor): + """A visitor that checks if a name is accessed without being + declared. This is different from the frame visitor as it will + not stop at closure frames. + """ + + def __init__(self, names: t.Iterable[str]) -> None: + self.names = set(names) + self.undeclared: t.Set[str] = set() + + def visit_Name(self, node: nodes.Name) -> None: + if node.ctx == "load" and node.name in self.names: + self.undeclared.add(node.name) + if self.undeclared == self.names: + raise VisitorExit() + else: + self.names.discard(node.name) + + def visit_Block(self, node: nodes.Block) -> None: + """Stop visiting a blocks.""" + + +class CompilerExit(Exception): + """Raised if the compiler encountered a situation where it just + doesn't make sense to further process the code. Any block that + raises such an exception is not further processed. + """ + + +class CodeGenerator(NodeVisitor): + def __init__( + self, + environment: "Environment", + name: t.Optional[str], + filename: t.Optional[str], + stream: t.Optional[t.TextIO] = None, + defer_init: bool = False, + optimized: bool = True, + ) -> None: + if stream is None: + stream = StringIO() + self.environment = environment + self.name = name + self.filename = filename + self.stream = stream + self.created_block_context = False + self.defer_init = defer_init + self.optimizer: t.Optional[Optimizer] = None + + if optimized: + self.optimizer = Optimizer(environment) + + # aliases for imports + self.import_aliases: t.Dict[str, str] = {} + + # a registry for all blocks. Because blocks are moved out + # into the global python scope they are registered here + self.blocks: t.Dict[str, nodes.Block] = {} + + # the number of extends statements so far + self.extends_so_far = 0 + + # some templates have a rootlevel extends. In this case we + # can safely assume that we're a child template and do some + # more optimizations. + self.has_known_extends = False + + # the current line number + self.code_lineno = 1 + + # registry of all filters and tests (global, not block local) + self.tests: t.Dict[str, str] = {} + self.filters: t.Dict[str, str] = {} + + # the debug information + self.debug_info: t.List[t.Tuple[int, int]] = [] + self._write_debug_info: t.Optional[int] = None + + # the number of new lines before the next write() + self._new_lines = 0 + + # the line number of the last written statement + self._last_line = 0 + + # true if nothing was written so far. + self._first_write = True + + # used by the `temporary_identifier` method to get new + # unique, temporary identifier + self._last_identifier = 0 + + # the current indentation + self._indentation = 0 + + # Tracks toplevel assignments + self._assign_stack: t.List[t.Set[str]] = [] + + # Tracks parameter definition blocks + self._param_def_block: t.List[t.Set[str]] = [] + + # Tracks the current context. + self._context_reference_stack = ["context"] + + @property + def optimized(self) -> bool: + return self.optimizer is not None + + # -- Various compilation helpers + + def fail(self, msg: str, lineno: int) -> "te.NoReturn": + """Fail with a :exc:`TemplateAssertionError`.""" + raise TemplateAssertionError(msg, lineno, self.name, self.filename) + + def temporary_identifier(self) -> str: + """Get a new unique identifier.""" + self._last_identifier += 1 + return f"t_{self._last_identifier}" + + def buffer(self, frame: Frame) -> None: + """Enable buffering for the frame from that point onwards.""" + frame.buffer = self.temporary_identifier() + self.writeline(f"{frame.buffer} = []") + + def return_buffer_contents( + self, frame: Frame, force_unescaped: bool = False + ) -> None: + """Return the buffer contents of the frame.""" + if not force_unescaped: + if frame.eval_ctx.volatile: + self.writeline("if context.eval_ctx.autoescape:") + self.indent() + self.writeline(f"return Markup(concat({frame.buffer}))") + self.outdent() + self.writeline("else:") + self.indent() + self.writeline(f"return concat({frame.buffer})") + self.outdent() + return + elif frame.eval_ctx.autoescape: + self.writeline(f"return Markup(concat({frame.buffer}))") + return + self.writeline(f"return concat({frame.buffer})") + + def indent(self) -> None: + """Indent by one.""" + self._indentation += 1 + + def outdent(self, step: int = 1) -> None: + """Outdent by step.""" + self._indentation -= step + + def start_write(self, frame: Frame, node: t.Optional[nodes.Node] = None) -> None: + """Yield or write into the frame buffer.""" + if frame.buffer is None: + self.writeline("yield ", node) + else: + self.writeline(f"{frame.buffer}.append(", node) + + def end_write(self, frame: Frame) -> None: + """End the writing process started by `start_write`.""" + if frame.buffer is not None: + self.write(")") + + def simple_write( + self, s: str, frame: Frame, node: t.Optional[nodes.Node] = None + ) -> None: + """Simple shortcut for start_write + write + end_write.""" + self.start_write(frame, node) + self.write(s) + self.end_write(frame) + + def blockvisit(self, nodes: t.Iterable[nodes.Node], frame: Frame) -> None: + """Visit a list of nodes as block in a frame. If the current frame + is no buffer a dummy ``if 0: yield None`` is written automatically. + """ + try: + self.writeline("pass") + for node in nodes: + self.visit(node, frame) + except CompilerExit: + pass + + def write(self, x: str) -> None: + """Write a string into the output stream.""" + if self._new_lines: + if not self._first_write: + self.stream.write("\n" * self._new_lines) + self.code_lineno += self._new_lines + if self._write_debug_info is not None: + self.debug_info.append((self._write_debug_info, self.code_lineno)) + self._write_debug_info = None + self._first_write = False + self.stream.write(" " * self._indentation) + self._new_lines = 0 + self.stream.write(x) + + def writeline( + self, x: str, node: t.Optional[nodes.Node] = None, extra: int = 0 + ) -> None: + """Combination of newline and write.""" + self.newline(node, extra) + self.write(x) + + def newline(self, node: t.Optional[nodes.Node] = None, extra: int = 0) -> None: + """Add one or more newlines before the next write.""" + self._new_lines = max(self._new_lines, 1 + extra) + if node is not None and node.lineno != self._last_line: + self._write_debug_info = node.lineno + self._last_line = node.lineno + + def signature( + self, + node: t.Union[nodes.Call, nodes.Filter, nodes.Test], + frame: Frame, + extra_kwargs: t.Optional[t.Mapping[str, t.Any]] = None, + ) -> None: + """Writes a function call to the stream for the current node. + A leading comma is added automatically. The extra keyword + arguments may not include python keywords otherwise a syntax + error could occur. The extra keyword arguments should be given + as python dict. + """ + # if any of the given keyword arguments is a python keyword + # we have to make sure that no invalid call is created. + kwarg_workaround = any( + is_python_keyword(t.cast(str, k)) + for k in chain((x.key for x in node.kwargs), extra_kwargs or ()) + ) + + for arg in node.args: + self.write(", ") + self.visit(arg, frame) + + if not kwarg_workaround: + for kwarg in node.kwargs: + self.write(", ") + self.visit(kwarg, frame) + if extra_kwargs is not None: + for key, value in extra_kwargs.items(): + self.write(f", {key}={value}") + if node.dyn_args: + self.write(", *") + self.visit(node.dyn_args, frame) + + if kwarg_workaround: + if node.dyn_kwargs is not None: + self.write(", **dict({") + else: + self.write(", **{") + for kwarg in node.kwargs: + self.write(f"{kwarg.key!r}: ") + self.visit(kwarg.value, frame) + self.write(", ") + if extra_kwargs is not None: + for key, value in extra_kwargs.items(): + self.write(f"{key!r}: {value}, ") + if node.dyn_kwargs is not None: + self.write("}, **") + self.visit(node.dyn_kwargs, frame) + self.write(")") + else: + self.write("}") + + elif node.dyn_kwargs is not None: + self.write(", **") + self.visit(node.dyn_kwargs, frame) + + def pull_dependencies(self, nodes: t.Iterable[nodes.Node]) -> None: + """Find all filter and test names used in the template and + assign them to variables in the compiled namespace. Checking + that the names are registered with the environment is done when + compiling the Filter and Test nodes. If the node is in an If or + CondExpr node, the check is done at runtime instead. + + .. versionchanged:: 3.0 + Filters and tests in If and CondExpr nodes are checked at + runtime instead of compile time. + """ + visitor = DependencyFinderVisitor() + + for node in nodes: + visitor.visit(node) + + for id_map, names, dependency in (self.filters, visitor.filters, "filters"), ( + self.tests, + visitor.tests, + "tests", + ): + for name in sorted(names): + if name not in id_map: + id_map[name] = self.temporary_identifier() + + # add check during runtime that dependencies used inside of executed + # blocks are defined, as this step may be skipped during compile time + self.writeline("try:") + self.indent() + self.writeline(f"{id_map[name]} = environment.{dependency}[{name!r}]") + self.outdent() + self.writeline("except KeyError:") + self.indent() + self.writeline("@internalcode") + self.writeline(f"def {id_map[name]}(*unused):") + self.indent() + self.writeline( + f'raise TemplateRuntimeError("No {dependency[:-1]}' + f' named {name!r} found.")' + ) + self.outdent() + self.outdent() + + def enter_frame(self, frame: Frame) -> None: + undefs = [] + for target, (action, param) in frame.symbols.loads.items(): + if action == VAR_LOAD_PARAMETER: + pass + elif action == VAR_LOAD_RESOLVE: + self.writeline(f"{target} = {self.get_resolve_func()}({param!r})") + elif action == VAR_LOAD_ALIAS: + self.writeline(f"{target} = {param}") + elif action == VAR_LOAD_UNDEFINED: + undefs.append(target) + else: + raise NotImplementedError("unknown load instruction") + if undefs: + self.writeline(f"{' = '.join(undefs)} = missing") + + def leave_frame(self, frame: Frame, with_python_scope: bool = False) -> None: + if not with_python_scope: + undefs = [] + for target in frame.symbols.loads: + undefs.append(target) + if undefs: + self.writeline(f"{' = '.join(undefs)} = missing") + + def choose_async(self, async_value: str = "async ", sync_value: str = "") -> str: + return async_value if self.environment.is_async else sync_value + + def func(self, name: str) -> str: + return f"{self.choose_async()}def {name}" + + def macro_body( + self, node: t.Union[nodes.Macro, nodes.CallBlock], frame: Frame + ) -> t.Tuple[Frame, MacroRef]: + """Dump the function def of a macro or call block.""" + frame = frame.inner() + frame.symbols.analyze_node(node) + macro_ref = MacroRef(node) + + explicit_caller = None + skip_special_params = set() + args = [] + + for idx, arg in enumerate(node.args): + if arg.name == "caller": + explicit_caller = idx + if arg.name in ("kwargs", "varargs"): + skip_special_params.add(arg.name) + args.append(frame.symbols.ref(arg.name)) + + undeclared = find_undeclared(node.body, ("caller", "kwargs", "varargs")) + + if "caller" in undeclared: + # In older Jinja versions there was a bug that allowed caller + # to retain the special behavior even if it was mentioned in + # the argument list. However thankfully this was only really + # working if it was the last argument. So we are explicitly + # checking this now and error out if it is anywhere else in + # the argument list. + if explicit_caller is not None: + try: + node.defaults[explicit_caller - len(node.args)] + except IndexError: + self.fail( + "When defining macros or call blocks the " + 'special "caller" argument must be omitted ' + "or be given a default.", + node.lineno, + ) + else: + args.append(frame.symbols.declare_parameter("caller")) + macro_ref.accesses_caller = True + if "kwargs" in undeclared and "kwargs" not in skip_special_params: + args.append(frame.symbols.declare_parameter("kwargs")) + macro_ref.accesses_kwargs = True + if "varargs" in undeclared and "varargs" not in skip_special_params: + args.append(frame.symbols.declare_parameter("varargs")) + macro_ref.accesses_varargs = True + + # macros are delayed, they never require output checks + frame.require_output_check = False + frame.symbols.analyze_node(node) + self.writeline(f"{self.func('macro')}({', '.join(args)}):", node) + self.indent() + + self.buffer(frame) + self.enter_frame(frame) + + self.push_parameter_definitions(frame) + for idx, arg in enumerate(node.args): + ref = frame.symbols.ref(arg.name) + self.writeline(f"if {ref} is missing:") + self.indent() + try: + default = node.defaults[idx - len(node.args)] + except IndexError: + self.writeline( + f'{ref} = undefined("parameter {arg.name!r} was not provided",' + f" name={arg.name!r})" + ) + else: + self.writeline(f"{ref} = ") + self.visit(default, frame) + self.mark_parameter_stored(ref) + self.outdent() + self.pop_parameter_definitions() + + self.blockvisit(node.body, frame) + self.return_buffer_contents(frame, force_unescaped=True) + self.leave_frame(frame, with_python_scope=True) + self.outdent() + + return frame, macro_ref + + def macro_def(self, macro_ref: MacroRef, frame: Frame) -> None: + """Dump the macro definition for the def created by macro_body.""" + arg_tuple = ", ".join(repr(x.name) for x in macro_ref.node.args) + name = getattr(macro_ref.node, "name", None) + if len(macro_ref.node.args) == 1: + arg_tuple += "," + self.write( + f"Macro(environment, macro, {name!r}, ({arg_tuple})," + f" {macro_ref.accesses_kwargs!r}, {macro_ref.accesses_varargs!r}," + f" {macro_ref.accesses_caller!r}, context.eval_ctx.autoescape)" + ) + + def position(self, node: nodes.Node) -> str: + """Return a human readable position for the node.""" + rv = f"line {node.lineno}" + if self.name is not None: + rv = f"{rv} in {self.name!r}" + return rv + + def dump_local_context(self, frame: Frame) -> str: + items_kv = ", ".join( + f"{name!r}: {target}" + for name, target in frame.symbols.dump_stores().items() + ) + return f"{{{items_kv}}}" + + def write_commons(self) -> None: + """Writes a common preamble that is used by root and block functions. + Primarily this sets up common local helpers and enforces a generator + through a dead branch. + """ + self.writeline("resolve = context.resolve_or_missing") + self.writeline("undefined = environment.undefined") + self.writeline("concat = environment.concat") + # always use the standard Undefined class for the implicit else of + # conditional expressions + self.writeline("cond_expr_undefined = Undefined") + self.writeline("if 0: yield None") + + def push_parameter_definitions(self, frame: Frame) -> None: + """Pushes all parameter targets from the given frame into a local + stack that permits tracking of yet to be assigned parameters. In + particular this enables the optimization from `visit_Name` to skip + undefined expressions for parameters in macros as macros can reference + otherwise unbound parameters. + """ + self._param_def_block.append(frame.symbols.dump_param_targets()) + + def pop_parameter_definitions(self) -> None: + """Pops the current parameter definitions set.""" + self._param_def_block.pop() + + def mark_parameter_stored(self, target: str) -> None: + """Marks a parameter in the current parameter definitions as stored. + This will skip the enforced undefined checks. + """ + if self._param_def_block: + self._param_def_block[-1].discard(target) + + def push_context_reference(self, target: str) -> None: + self._context_reference_stack.append(target) + + def pop_context_reference(self) -> None: + self._context_reference_stack.pop() + + def get_context_ref(self) -> str: + return self._context_reference_stack[-1] + + def get_resolve_func(self) -> str: + target = self._context_reference_stack[-1] + if target == "context": + return "resolve" + return f"{target}.resolve" + + def derive_context(self, frame: Frame) -> str: + return f"{self.get_context_ref()}.derived({self.dump_local_context(frame)})" + + def parameter_is_undeclared(self, target: str) -> bool: + """Checks if a given target is an undeclared parameter.""" + if not self._param_def_block: + return False + return target in self._param_def_block[-1] + + def push_assign_tracking(self) -> None: + """Pushes a new layer for assignment tracking.""" + self._assign_stack.append(set()) + + def pop_assign_tracking(self, frame: Frame) -> None: + """Pops the topmost level for assignment tracking and updates the + context variables if necessary. + """ + vars = self._assign_stack.pop() + if ( + not frame.block_frame + and not frame.loop_frame + and not frame.toplevel + or not vars + ): + return + public_names = [x for x in vars if x[:1] != "_"] + if len(vars) == 1: + name = next(iter(vars)) + ref = frame.symbols.ref(name) + if frame.loop_frame: + self.writeline(f"_loop_vars[{name!r}] = {ref}") + return + if frame.block_frame: + self.writeline(f"_block_vars[{name!r}] = {ref}") + return + self.writeline(f"context.vars[{name!r}] = {ref}") + else: + if frame.loop_frame: + self.writeline("_loop_vars.update({") + elif frame.block_frame: + self.writeline("_block_vars.update({") + else: + self.writeline("context.vars.update({") + for idx, name in enumerate(vars): + if idx: + self.write(", ") + ref = frame.symbols.ref(name) + self.write(f"{name!r}: {ref}") + self.write("})") + if not frame.block_frame and not frame.loop_frame and public_names: + if len(public_names) == 1: + self.writeline(f"context.exported_vars.add({public_names[0]!r})") + else: + names_str = ", ".join(map(repr, public_names)) + self.writeline(f"context.exported_vars.update(({names_str}))") + + # -- Statement Visitors + + def visit_Template( + self, node: nodes.Template, frame: t.Optional[Frame] = None + ) -> None: + assert frame is None, "no root frame allowed" + eval_ctx = EvalContext(self.environment, self.name) + + from .runtime import exported, async_exported + + if self.environment.is_async: + exported_names = sorted(exported + async_exported) + else: + exported_names = sorted(exported) + + self.writeline("from jinja2.runtime import " + ", ".join(exported_names)) + + # if we want a deferred initialization we cannot move the + # environment into a local name + envenv = "" if self.defer_init else ", environment=environment" + + # do we have an extends tag at all? If not, we can save some + # overhead by just not processing any inheritance code. + have_extends = node.find(nodes.Extends) is not None + + # find all blocks + for block in node.find_all(nodes.Block): + if block.name in self.blocks: + self.fail(f"block {block.name!r} defined twice", block.lineno) + self.blocks[block.name] = block + + # find all imports and import them + for import_ in node.find_all(nodes.ImportedName): + if import_.importname not in self.import_aliases: + imp = import_.importname + self.import_aliases[imp] = alias = self.temporary_identifier() + if "." in imp: + module, obj = imp.rsplit(".", 1) + self.writeline(f"from {module} import {obj} as {alias}") + else: + self.writeline(f"import {imp} as {alias}") + + # add the load name + self.writeline(f"name = {self.name!r}") + + # generate the root render function. + self.writeline( + f"{self.func('root')}(context, missing=missing{envenv}):", extra=1 + ) + self.indent() + self.write_commons() + + # process the root + frame = Frame(eval_ctx) + if "self" in find_undeclared(node.body, ("self",)): + ref = frame.symbols.declare_parameter("self") + self.writeline(f"{ref} = TemplateReference(context)") + frame.symbols.analyze_node(node) + frame.toplevel = frame.rootlevel = True + frame.require_output_check = have_extends and not self.has_known_extends + if have_extends: + self.writeline("parent_template = None") + self.enter_frame(frame) + self.pull_dependencies(node.body) + self.blockvisit(node.body, frame) + self.leave_frame(frame, with_python_scope=True) + self.outdent() + + # make sure that the parent root is called. + if have_extends: + if not self.has_known_extends: + self.indent() + self.writeline("if parent_template is not None:") + self.indent() + if not self.environment.is_async: + self.writeline("yield from parent_template.root_render_func(context)") + else: + self.writeline( + "async for event in parent_template.root_render_func(context):" + ) + self.indent() + self.writeline("yield event") + self.outdent() + self.outdent(1 + (not self.has_known_extends)) + + # at this point we now have the blocks collected and can visit them too. + for name, block in self.blocks.items(): + self.writeline( + f"{self.func('block_' + name)}(context, missing=missing{envenv}):", + block, + 1, + ) + self.indent() + self.write_commons() + # It's important that we do not make this frame a child of the + # toplevel template. This would cause a variety of + # interesting issues with identifier tracking. + block_frame = Frame(eval_ctx) + block_frame.block_frame = True + undeclared = find_undeclared(block.body, ("self", "super")) + if "self" in undeclared: + ref = block_frame.symbols.declare_parameter("self") + self.writeline(f"{ref} = TemplateReference(context)") + if "super" in undeclared: + ref = block_frame.symbols.declare_parameter("super") + self.writeline(f"{ref} = context.super({name!r}, block_{name})") + block_frame.symbols.analyze_node(block) + block_frame.block = name + self.writeline("_block_vars = {}") + self.enter_frame(block_frame) + self.pull_dependencies(block.body) + self.blockvisit(block.body, block_frame) + self.leave_frame(block_frame, with_python_scope=True) + self.outdent() + + blocks_kv_str = ", ".join(f"{x!r}: block_{x}" for x in self.blocks) + self.writeline(f"blocks = {{{blocks_kv_str}}}", extra=1) + debug_kv_str = "&".join(f"{k}={v}" for k, v in self.debug_info) + self.writeline(f"debug_info = {debug_kv_str!r}") + + def visit_Block(self, node: nodes.Block, frame: Frame) -> None: + """Call a block and register it for the template.""" + level = 0 + if frame.toplevel: + # if we know that we are a child template, there is no need to + # check if we are one + if self.has_known_extends: + return + if self.extends_so_far > 0: + self.writeline("if parent_template is None:") + self.indent() + level += 1 + + if node.scoped: + context = self.derive_context(frame) + else: + context = self.get_context_ref() + + if node.required: + self.writeline(f"if len(context.blocks[{node.name!r}]) <= 1:", node) + self.indent() + self.writeline( + f'raise TemplateRuntimeError("Required block {node.name!r} not found")', + node, + ) + self.outdent() + + if not self.environment.is_async and frame.buffer is None: + self.writeline( + f"yield from context.blocks[{node.name!r}][0]({context})", node + ) + else: + self.writeline( + f"{self.choose_async()}for event in" + f" context.blocks[{node.name!r}][0]({context}):", + node, + ) + self.indent() + self.simple_write("event", frame) + self.outdent() + + self.outdent(level) + + def visit_Extends(self, node: nodes.Extends, frame: Frame) -> None: + """Calls the extender.""" + if not frame.toplevel: + self.fail("cannot use extend from a non top-level scope", node.lineno) + + # if the number of extends statements in general is zero so + # far, we don't have to add a check if something extended + # the template before this one. + if self.extends_so_far > 0: + + # if we have a known extends we just add a template runtime + # error into the generated code. We could catch that at compile + # time too, but i welcome it not to confuse users by throwing the + # same error at different times just "because we can". + if not self.has_known_extends: + self.writeline("if parent_template is not None:") + self.indent() + self.writeline('raise TemplateRuntimeError("extended multiple times")') + + # if we have a known extends already we don't need that code here + # as we know that the template execution will end here. + if self.has_known_extends: + raise CompilerExit() + else: + self.outdent() + + self.writeline("parent_template = environment.get_template(", node) + self.visit(node.template, frame) + self.write(f", {self.name!r})") + self.writeline("for name, parent_block in parent_template.blocks.items():") + self.indent() + self.writeline("context.blocks.setdefault(name, []).append(parent_block)") + self.outdent() + + # if this extends statement was in the root level we can take + # advantage of that information and simplify the generated code + # in the top level from this point onwards + if frame.rootlevel: + self.has_known_extends = True + + # and now we have one more + self.extends_so_far += 1 + + def visit_Include(self, node: nodes.Include, frame: Frame) -> None: + """Handles includes.""" + if node.ignore_missing: + self.writeline("try:") + self.indent() + + func_name = "get_or_select_template" + if isinstance(node.template, nodes.Const): + if isinstance(node.template.value, str): + func_name = "get_template" + elif isinstance(node.template.value, (tuple, list)): + func_name = "select_template" + elif isinstance(node.template, (nodes.Tuple, nodes.List)): + func_name = "select_template" + + self.writeline(f"template = environment.{func_name}(", node) + self.visit(node.template, frame) + self.write(f", {self.name!r})") + if node.ignore_missing: + self.outdent() + self.writeline("except TemplateNotFound:") + self.indent() + self.writeline("pass") + self.outdent() + self.writeline("else:") + self.indent() + + skip_event_yield = False + if node.with_context: + self.writeline( + f"{self.choose_async()}for event in template.root_render_func(" + "template.new_context(context.get_all(), True," + f" {self.dump_local_context(frame)})):" + ) + elif self.environment.is_async: + self.writeline( + "for event in (await template._get_default_module_async())" + "._body_stream:" + ) + else: + self.writeline("yield from template._get_default_module()._body_stream") + skip_event_yield = True + + if not skip_event_yield: + self.indent() + self.simple_write("event", frame) + self.outdent() + + if node.ignore_missing: + self.outdent() + + def _import_common( + self, node: t.Union[nodes.Import, nodes.FromImport], frame: Frame + ) -> None: + self.write(f"{self.choose_async('await ')}environment.get_template(") + self.visit(node.template, frame) + self.write(f", {self.name!r}).") + + if node.with_context: + f_name = f"make_module{self.choose_async('_async')}" + self.write( + f"{f_name}(context.get_all(), True, {self.dump_local_context(frame)})" + ) + else: + self.write(f"_get_default_module{self.choose_async('_async')}(context)") + + def visit_Import(self, node: nodes.Import, frame: Frame) -> None: + """Visit regular imports.""" + self.writeline(f"{frame.symbols.ref(node.target)} = ", node) + if frame.toplevel: + self.write(f"context.vars[{node.target!r}] = ") + + self._import_common(node, frame) + + if frame.toplevel and not node.target.startswith("_"): + self.writeline(f"context.exported_vars.discard({node.target!r})") + + def visit_FromImport(self, node: nodes.FromImport, frame: Frame) -> None: + """Visit named imports.""" + self.newline(node) + self.write("included_template = ") + self._import_common(node, frame) + var_names = [] + discarded_names = [] + for name in node.names: + if isinstance(name, tuple): + name, alias = name + else: + alias = name + self.writeline( + f"{frame.symbols.ref(alias)} =" + f" getattr(included_template, {name!r}, missing)" + ) + self.writeline(f"if {frame.symbols.ref(alias)} is missing:") + self.indent() + message = ( + "the template {included_template.__name__!r}" + f" (imported on {self.position(node)})" + f" does not export the requested name {name!r}" + ) + self.writeline( + f"{frame.symbols.ref(alias)} = undefined(f{message!r}, name={name!r})" + ) + self.outdent() + if frame.toplevel: + var_names.append(alias) + if not alias.startswith("_"): + discarded_names.append(alias) + + if var_names: + if len(var_names) == 1: + name = var_names[0] + self.writeline(f"context.vars[{name!r}] = {frame.symbols.ref(name)}") + else: + names_kv = ", ".join( + f"{name!r}: {frame.symbols.ref(name)}" for name in var_names + ) + self.writeline(f"context.vars.update({{{names_kv}}})") + if discarded_names: + if len(discarded_names) == 1: + self.writeline(f"context.exported_vars.discard({discarded_names[0]!r})") + else: + names_str = ", ".join(map(repr, discarded_names)) + self.writeline( + f"context.exported_vars.difference_update(({names_str}))" + ) + + def visit_For(self, node: nodes.For, frame: Frame) -> None: + loop_frame = frame.inner() + loop_frame.loop_frame = True + test_frame = frame.inner() + else_frame = frame.inner() + + # try to figure out if we have an extended loop. An extended loop + # is necessary if the loop is in recursive mode if the special loop + # variable is accessed in the body if the body is a scoped block. + extended_loop = ( + node.recursive + or "loop" + in find_undeclared(node.iter_child_nodes(only=("body",)), ("loop",)) + or any(block.scoped for block in node.find_all(nodes.Block)) + ) + + loop_ref = None + if extended_loop: + loop_ref = loop_frame.symbols.declare_parameter("loop") + + loop_frame.symbols.analyze_node(node, for_branch="body") + if node.else_: + else_frame.symbols.analyze_node(node, for_branch="else") + + if node.test: + loop_filter_func = self.temporary_identifier() + test_frame.symbols.analyze_node(node, for_branch="test") + self.writeline(f"{self.func(loop_filter_func)}(fiter):", node.test) + self.indent() + self.enter_frame(test_frame) + self.writeline(self.choose_async("async for ", "for ")) + self.visit(node.target, loop_frame) + self.write(" in ") + self.write(self.choose_async("auto_aiter(fiter)", "fiter")) + self.write(":") + self.indent() + self.writeline("if ", node.test) + self.visit(node.test, test_frame) + self.write(":") + self.indent() + self.writeline("yield ") + self.visit(node.target, loop_frame) + self.outdent(3) + self.leave_frame(test_frame, with_python_scope=True) + + # if we don't have an recursive loop we have to find the shadowed + # variables at that point. Because loops can be nested but the loop + # variable is a special one we have to enforce aliasing for it. + if node.recursive: + self.writeline( + f"{self.func('loop')}(reciter, loop_render_func, depth=0):", node + ) + self.indent() + self.buffer(loop_frame) + + # Use the same buffer for the else frame + else_frame.buffer = loop_frame.buffer + + # make sure the loop variable is a special one and raise a template + # assertion error if a loop tries to write to loop + if extended_loop: + self.writeline(f"{loop_ref} = missing") + + for name in node.find_all(nodes.Name): + if name.ctx == "store" and name.name == "loop": + self.fail( + "Can't assign to special loop variable in for-loop target", + name.lineno, + ) + + if node.else_: + iteration_indicator = self.temporary_identifier() + self.writeline(f"{iteration_indicator} = 1") + + self.writeline(self.choose_async("async for ", "for "), node) + self.visit(node.target, loop_frame) + if extended_loop: + self.write(f", {loop_ref} in {self.choose_async('Async')}LoopContext(") + else: + self.write(" in ") + + if node.test: + self.write(f"{loop_filter_func}(") + if node.recursive: + self.write("reciter") + else: + if self.environment.is_async and not extended_loop: + self.write("auto_aiter(") + self.visit(node.iter, frame) + if self.environment.is_async and not extended_loop: + self.write(")") + if node.test: + self.write(")") + + if node.recursive: + self.write(", undefined, loop_render_func, depth):") + else: + self.write(", undefined):" if extended_loop else ":") + + self.indent() + self.enter_frame(loop_frame) + + self.writeline("_loop_vars = {}") + self.blockvisit(node.body, loop_frame) + if node.else_: + self.writeline(f"{iteration_indicator} = 0") + self.outdent() + self.leave_frame( + loop_frame, with_python_scope=node.recursive and not node.else_ + ) + + if node.else_: + self.writeline(f"if {iteration_indicator}:") + self.indent() + self.enter_frame(else_frame) + self.blockvisit(node.else_, else_frame) + self.leave_frame(else_frame) + self.outdent() + + # if the node was recursive we have to return the buffer contents + # and start the iteration code + if node.recursive: + self.return_buffer_contents(loop_frame) + self.outdent() + self.start_write(frame, node) + self.write(f"{self.choose_async('await ')}loop(") + if self.environment.is_async: + self.write("auto_aiter(") + self.visit(node.iter, frame) + if self.environment.is_async: + self.write(")") + self.write(", loop)") + self.end_write(frame) + + # at the end of the iteration, clear any assignments made in the + # loop from the top level + if self._assign_stack: + self._assign_stack[-1].difference_update(loop_frame.symbols.stores) + + def visit_If(self, node: nodes.If, frame: Frame) -> None: + if_frame = frame.soft() + self.writeline("if ", node) + self.visit(node.test, if_frame) + self.write(":") + self.indent() + self.blockvisit(node.body, if_frame) + self.outdent() + for elif_ in node.elif_: + self.writeline("elif ", elif_) + self.visit(elif_.test, if_frame) + self.write(":") + self.indent() + self.blockvisit(elif_.body, if_frame) + self.outdent() + if node.else_: + self.writeline("else:") + self.indent() + self.blockvisit(node.else_, if_frame) + self.outdent() + + def visit_Macro(self, node: nodes.Macro, frame: Frame) -> None: + macro_frame, macro_ref = self.macro_body(node, frame) + self.newline() + if frame.toplevel: + if not node.name.startswith("_"): + self.write(f"context.exported_vars.add({node.name!r})") + self.writeline(f"context.vars[{node.name!r}] = ") + self.write(f"{frame.symbols.ref(node.name)} = ") + self.macro_def(macro_ref, macro_frame) + + def visit_CallBlock(self, node: nodes.CallBlock, frame: Frame) -> None: + call_frame, macro_ref = self.macro_body(node, frame) + self.writeline("caller = ") + self.macro_def(macro_ref, call_frame) + self.start_write(frame, node) + self.visit_Call(node.call, frame, forward_caller=True) + self.end_write(frame) + + def visit_FilterBlock(self, node: nodes.FilterBlock, frame: Frame) -> None: + filter_frame = frame.inner() + filter_frame.symbols.analyze_node(node) + self.enter_frame(filter_frame) + self.buffer(filter_frame) + self.blockvisit(node.body, filter_frame) + self.start_write(frame, node) + self.visit_Filter(node.filter, filter_frame) + self.end_write(frame) + self.leave_frame(filter_frame) + + def visit_With(self, node: nodes.With, frame: Frame) -> None: + with_frame = frame.inner() + with_frame.symbols.analyze_node(node) + self.enter_frame(with_frame) + for target, expr in zip(node.targets, node.values): + self.newline() + self.visit(target, with_frame) + self.write(" = ") + self.visit(expr, frame) + self.blockvisit(node.body, with_frame) + self.leave_frame(with_frame) + + def visit_ExprStmt(self, node: nodes.ExprStmt, frame: Frame) -> None: + self.newline(node) + self.visit(node.node, frame) + + class _FinalizeInfo(t.NamedTuple): + const: t.Optional[t.Callable[..., str]] + src: t.Optional[str] + + @staticmethod + def _default_finalize(value: t.Any) -> t.Any: + """The default finalize function if the environment isn't + configured with one. Or, if the environment has one, this is + called on that function's output for constants. + """ + return str(value) + + _finalize: t.Optional[_FinalizeInfo] = None + + def _make_finalize(self) -> _FinalizeInfo: + """Build the finalize function to be used on constants and at + runtime. Cached so it's only created once for all output nodes. + + Returns a ``namedtuple`` with the following attributes: + + ``const`` + A function to finalize constant data at compile time. + + ``src`` + Source code to output around nodes to be evaluated at + runtime. + """ + if self._finalize is not None: + return self._finalize + + finalize: t.Optional[t.Callable[..., t.Any]] + finalize = default = self._default_finalize + src = None + + if self.environment.finalize: + src = "environment.finalize(" + env_finalize = self.environment.finalize + pass_arg = { + _PassArg.context: "context", + _PassArg.eval_context: "context.eval_ctx", + _PassArg.environment: "environment", + }.get( + _PassArg.from_obj(env_finalize) # type: ignore + ) + finalize = None + + if pass_arg is None: + + def finalize(value: t.Any) -> t.Any: + return default(env_finalize(value)) + + else: + src = f"{src}{pass_arg}, " + + if pass_arg == "environment": + + def finalize(value: t.Any) -> t.Any: + return default(env_finalize(self.environment, value)) + + self._finalize = self._FinalizeInfo(finalize, src) + return self._finalize + + def _output_const_repr(self, group: t.Iterable[t.Any]) -> str: + """Given a group of constant values converted from ``Output`` + child nodes, produce a string to write to the template module + source. + """ + return repr(concat(group)) + + def _output_child_to_const( + self, node: nodes.Expr, frame: Frame, finalize: _FinalizeInfo + ) -> str: + """Try to optimize a child of an ``Output`` node by trying to + convert it to constant, finalized data at compile time. + + If :exc:`Impossible` is raised, the node is not constant and + will be evaluated at runtime. Any other exception will also be + evaluated at runtime for easier debugging. + """ + const = node.as_const(frame.eval_ctx) + + if frame.eval_ctx.autoescape: + const = escape(const) + + # Template data doesn't go through finalize. + if isinstance(node, nodes.TemplateData): + return str(const) + + return finalize.const(const) # type: ignore + + def _output_child_pre( + self, node: nodes.Expr, frame: Frame, finalize: _FinalizeInfo + ) -> None: + """Output extra source code before visiting a child of an + ``Output`` node. + """ + if frame.eval_ctx.volatile: + self.write("(escape if context.eval_ctx.autoescape else str)(") + elif frame.eval_ctx.autoescape: + self.write("escape(") + else: + self.write("str(") + + if finalize.src is not None: + self.write(finalize.src) + + def _output_child_post( + self, node: nodes.Expr, frame: Frame, finalize: _FinalizeInfo + ) -> None: + """Output extra source code after visiting a child of an + ``Output`` node. + """ + self.write(")") + + if finalize.src is not None: + self.write(")") + + def visit_Output(self, node: nodes.Output, frame: Frame) -> None: + # If an extends is active, don't render outside a block. + if frame.require_output_check: + # A top-level extends is known to exist at compile time. + if self.has_known_extends: + return + + self.writeline("if parent_template is None:") + self.indent() + + finalize = self._make_finalize() + body: t.List[t.Union[t.List[t.Any], nodes.Expr]] = [] + + # Evaluate constants at compile time if possible. Each item in + # body will be either a list of static data or a node to be + # evaluated at runtime. + for child in node.nodes: + try: + if not ( + # If the finalize function requires runtime context, + # constants can't be evaluated at compile time. + finalize.const + # Unless it's basic template data that won't be + # finalized anyway. + or isinstance(child, nodes.TemplateData) + ): + raise nodes.Impossible() + + const = self._output_child_to_const(child, frame, finalize) + except (nodes.Impossible, Exception): + # The node was not constant and needs to be evaluated at + # runtime. Or another error was raised, which is easier + # to debug at runtime. + body.append(child) + continue + + if body and isinstance(body[-1], list): + body[-1].append(const) + else: + body.append([const]) + + if frame.buffer is not None: + if len(body) == 1: + self.writeline(f"{frame.buffer}.append(") + else: + self.writeline(f"{frame.buffer}.extend((") + + self.indent() + + for item in body: + if isinstance(item, list): + # A group of constant data to join and output. + val = self._output_const_repr(item) + + if frame.buffer is None: + self.writeline("yield " + val) + else: + self.writeline(val + ",") + else: + if frame.buffer is None: + self.writeline("yield ", item) + else: + self.newline(item) + + # A node to be evaluated at runtime. + self._output_child_pre(item, frame, finalize) + self.visit(item, frame) + self._output_child_post(item, frame, finalize) + + if frame.buffer is not None: + self.write(",") + + if frame.buffer is not None: + self.outdent() + self.writeline(")" if len(body) == 1 else "))") + + if frame.require_output_check: + self.outdent() + + def visit_Assign(self, node: nodes.Assign, frame: Frame) -> None: + self.push_assign_tracking() + self.newline(node) + self.visit(node.target, frame) + self.write(" = ") + self.visit(node.node, frame) + self.pop_assign_tracking(frame) + + def visit_AssignBlock(self, node: nodes.AssignBlock, frame: Frame) -> None: + self.push_assign_tracking() + block_frame = frame.inner() + # This is a special case. Since a set block always captures we + # will disable output checks. This way one can use set blocks + # toplevel even in extended templates. + block_frame.require_output_check = False + block_frame.symbols.analyze_node(node) + self.enter_frame(block_frame) + self.buffer(block_frame) + self.blockvisit(node.body, block_frame) + self.newline(node) + self.visit(node.target, frame) + self.write(" = (Markup if context.eval_ctx.autoescape else identity)(") + if node.filter is not None: + self.visit_Filter(node.filter, block_frame) + else: + self.write(f"concat({block_frame.buffer})") + self.write(")") + self.pop_assign_tracking(frame) + self.leave_frame(block_frame) + + # -- Expression Visitors + + def visit_Name(self, node: nodes.Name, frame: Frame) -> None: + if node.ctx == "store" and ( + frame.toplevel or frame.loop_frame or frame.block_frame + ): + if self._assign_stack: + self._assign_stack[-1].add(node.name) + ref = frame.symbols.ref(node.name) + + # If we are looking up a variable we might have to deal with the + # case where it's undefined. We can skip that case if the load + # instruction indicates a parameter which are always defined. + if node.ctx == "load": + load = frame.symbols.find_load(ref) + if not ( + load is not None + and load[0] == VAR_LOAD_PARAMETER + and not self.parameter_is_undeclared(ref) + ): + self.write( + f"(undefined(name={node.name!r}) if {ref} is missing else {ref})" + ) + return + + self.write(ref) + + def visit_NSRef(self, node: nodes.NSRef, frame: Frame) -> None: + # NSRefs can only be used to store values; since they use the normal + # `foo.bar` notation they will be parsed as a normal attribute access + # when used anywhere but in a `set` context + ref = frame.symbols.ref(node.name) + self.writeline(f"if not isinstance({ref}, Namespace):") + self.indent() + self.writeline( + "raise TemplateRuntimeError" + '("cannot assign attribute on non-namespace object")' + ) + self.outdent() + self.writeline(f"{ref}[{node.attr!r}]") + + def visit_Const(self, node: nodes.Const, frame: Frame) -> None: + val = node.as_const(frame.eval_ctx) + if isinstance(val, float): + self.write(str(val)) + else: + self.write(repr(val)) + + def visit_TemplateData(self, node: nodes.TemplateData, frame: Frame) -> None: + try: + self.write(repr(node.as_const(frame.eval_ctx))) + except nodes.Impossible: + self.write( + f"(Markup if context.eval_ctx.autoescape else identity)({node.data!r})" + ) + + def visit_Tuple(self, node: nodes.Tuple, frame: Frame) -> None: + self.write("(") + idx = -1 + for idx, item in enumerate(node.items): + if idx: + self.write(", ") + self.visit(item, frame) + self.write(",)" if idx == 0 else ")") + + def visit_List(self, node: nodes.List, frame: Frame) -> None: + self.write("[") + for idx, item in enumerate(node.items): + if idx: + self.write(", ") + self.visit(item, frame) + self.write("]") + + def visit_Dict(self, node: nodes.Dict, frame: Frame) -> None: + self.write("{") + for idx, item in enumerate(node.items): + if idx: + self.write(", ") + self.visit(item.key, frame) + self.write(": ") + self.visit(item.value, frame) + self.write("}") + + visit_Add = _make_binop("+") + visit_Sub = _make_binop("-") + visit_Mul = _make_binop("*") + visit_Div = _make_binop("/") + visit_FloorDiv = _make_binop("//") + visit_Pow = _make_binop("**") + visit_Mod = _make_binop("%") + visit_And = _make_binop("and") + visit_Or = _make_binop("or") + visit_Pos = _make_unop("+") + visit_Neg = _make_unop("-") + visit_Not = _make_unop("not ") + + @optimizeconst + def visit_Concat(self, node: nodes.Concat, frame: Frame) -> None: + if frame.eval_ctx.volatile: + func_name = "(markup_join if context.eval_ctx.volatile else str_join)" + elif frame.eval_ctx.autoescape: + func_name = "markup_join" + else: + func_name = "str_join" + self.write(f"{func_name}((") + for arg in node.nodes: + self.visit(arg, frame) + self.write(", ") + self.write("))") + + @optimizeconst + def visit_Compare(self, node: nodes.Compare, frame: Frame) -> None: + self.write("(") + self.visit(node.expr, frame) + for op in node.ops: + self.visit(op, frame) + self.write(")") + + def visit_Operand(self, node: nodes.Operand, frame: Frame) -> None: + self.write(f" {operators[node.op]} ") + self.visit(node.expr, frame) + + @optimizeconst + def visit_Getattr(self, node: nodes.Getattr, frame: Frame) -> None: + if self.environment.is_async: + self.write("(await auto_await(") + + self.write("environment.getattr(") + self.visit(node.node, frame) + self.write(f", {node.attr!r})") + + if self.environment.is_async: + self.write("))") + + @optimizeconst + def visit_Getitem(self, node: nodes.Getitem, frame: Frame) -> None: + # slices bypass the environment getitem method. + if isinstance(node.arg, nodes.Slice): + self.visit(node.node, frame) + self.write("[") + self.visit(node.arg, frame) + self.write("]") + else: + if self.environment.is_async: + self.write("(await auto_await(") + + self.write("environment.getitem(") + self.visit(node.node, frame) + self.write(", ") + self.visit(node.arg, frame) + self.write(")") + + if self.environment.is_async: + self.write("))") + + def visit_Slice(self, node: nodes.Slice, frame: Frame) -> None: + if node.start is not None: + self.visit(node.start, frame) + self.write(":") + if node.stop is not None: + self.visit(node.stop, frame) + if node.step is not None: + self.write(":") + self.visit(node.step, frame) + + @contextmanager + def _filter_test_common( + self, node: t.Union[nodes.Filter, nodes.Test], frame: Frame, is_filter: bool + ) -> t.Iterator[None]: + if self.environment.is_async: + self.write("(await auto_await(") + + if is_filter: + self.write(f"{self.filters[node.name]}(") + func = self.environment.filters.get(node.name) + else: + self.write(f"{self.tests[node.name]}(") + func = self.environment.tests.get(node.name) + + # When inside an If or CondExpr frame, allow the filter to be + # undefined at compile time and only raise an error if it's + # actually called at runtime. See pull_dependencies. + if func is None and not frame.soft_frame: + type_name = "filter" if is_filter else "test" + self.fail(f"No {type_name} named {node.name!r}.", node.lineno) + + pass_arg = { + _PassArg.context: "context", + _PassArg.eval_context: "context.eval_ctx", + _PassArg.environment: "environment", + }.get( + _PassArg.from_obj(func) # type: ignore + ) + + if pass_arg is not None: + self.write(f"{pass_arg}, ") + + # Back to the visitor function to handle visiting the target of + # the filter or test. + yield + + self.signature(node, frame) + self.write(")") + + if self.environment.is_async: + self.write("))") + + @optimizeconst + def visit_Filter(self, node: nodes.Filter, frame: Frame) -> None: + with self._filter_test_common(node, frame, True): + # if the filter node is None we are inside a filter block + # and want to write to the current buffer + if node.node is not None: + self.visit(node.node, frame) + elif frame.eval_ctx.volatile: + self.write( + f"(Markup(concat({frame.buffer}))" + f" if context.eval_ctx.autoescape else concat({frame.buffer}))" + ) + elif frame.eval_ctx.autoescape: + self.write(f"Markup(concat({frame.buffer}))") + else: + self.write(f"concat({frame.buffer})") + + @optimizeconst + def visit_Test(self, node: nodes.Test, frame: Frame) -> None: + with self._filter_test_common(node, frame, False): + self.visit(node.node, frame) + + @optimizeconst + def visit_CondExpr(self, node: nodes.CondExpr, frame: Frame) -> None: + frame = frame.soft() + + def write_expr2() -> None: + if node.expr2 is not None: + self.visit(node.expr2, frame) + return + + self.write( + f'cond_expr_undefined("the inline if-expression on' + f" {self.position(node)} evaluated to false and no else" + f' section was defined.")' + ) + + self.write("(") + self.visit(node.expr1, frame) + self.write(" if ") + self.visit(node.test, frame) + self.write(" else ") + write_expr2() + self.write(")") + + @optimizeconst + def visit_Call( + self, node: nodes.Call, frame: Frame, forward_caller: bool = False + ) -> None: + if self.environment.is_async: + self.write("(await auto_await(") + if self.environment.sandboxed: + self.write("environment.call(context, ") + else: + self.write("context.call(") + self.visit(node.node, frame) + extra_kwargs = {"caller": "caller"} if forward_caller else None + loop_kwargs = {"_loop_vars": "_loop_vars"} if frame.loop_frame else {} + block_kwargs = {"_block_vars": "_block_vars"} if frame.block_frame else {} + if extra_kwargs: + extra_kwargs.update(loop_kwargs, **block_kwargs) + elif loop_kwargs or block_kwargs: + extra_kwargs = dict(loop_kwargs, **block_kwargs) + self.signature(node, frame, extra_kwargs) + self.write(")") + if self.environment.is_async: + self.write("))") + + def visit_Keyword(self, node: nodes.Keyword, frame: Frame) -> None: + self.write(node.key + "=") + self.visit(node.value, frame) + + # -- Unused nodes for extensions + + def visit_MarkSafe(self, node: nodes.MarkSafe, frame: Frame) -> None: + self.write("Markup(") + self.visit(node.expr, frame) + self.write(")") + + def visit_MarkSafeIfAutoescape( + self, node: nodes.MarkSafeIfAutoescape, frame: Frame + ) -> None: + self.write("(Markup if context.eval_ctx.autoescape else identity)(") + self.visit(node.expr, frame) + self.write(")") + + def visit_EnvironmentAttribute( + self, node: nodes.EnvironmentAttribute, frame: Frame + ) -> None: + self.write("environment." + node.name) + + def visit_ExtensionAttribute( + self, node: nodes.ExtensionAttribute, frame: Frame + ) -> None: + self.write(f"environment.extensions[{node.identifier!r}].{node.name}") + + def visit_ImportedName(self, node: nodes.ImportedName, frame: Frame) -> None: + self.write(self.import_aliases[node.importname]) + + def visit_InternalName(self, node: nodes.InternalName, frame: Frame) -> None: + self.write(node.name) + + def visit_ContextReference( + self, node: nodes.ContextReference, frame: Frame + ) -> None: + self.write("context") + + def visit_DerivedContextReference( + self, node: nodes.DerivedContextReference, frame: Frame + ) -> None: + self.write(self.derive_context(frame)) + + def visit_Continue(self, node: nodes.Continue, frame: Frame) -> None: + self.writeline("continue", node) + + def visit_Break(self, node: nodes.Break, frame: Frame) -> None: + self.writeline("break", node) + + def visit_Scope(self, node: nodes.Scope, frame: Frame) -> None: + scope_frame = frame.inner() + scope_frame.symbols.analyze_node(node) + self.enter_frame(scope_frame) + self.blockvisit(node.body, scope_frame) + self.leave_frame(scope_frame) + + def visit_OverlayScope(self, node: nodes.OverlayScope, frame: Frame) -> None: + ctx = self.temporary_identifier() + self.writeline(f"{ctx} = {self.derive_context(frame)}") + self.writeline(f"{ctx}.vars = ") + self.visit(node.context, frame) + self.push_context_reference(ctx) + + scope_frame = frame.inner(isolated=True) + scope_frame.symbols.analyze_node(node) + self.enter_frame(scope_frame) + self.blockvisit(node.body, scope_frame) + self.leave_frame(scope_frame) + self.pop_context_reference() + + def visit_EvalContextModifier( + self, node: nodes.EvalContextModifier, frame: Frame + ) -> None: + for keyword in node.options: + self.writeline(f"context.eval_ctx.{keyword.key} = ") + self.visit(keyword.value, frame) + try: + val = keyword.value.as_const(frame.eval_ctx) + except nodes.Impossible: + frame.eval_ctx.volatile = True + else: + setattr(frame.eval_ctx, keyword.key, val) + + def visit_ScopedEvalContextModifier( + self, node: nodes.ScopedEvalContextModifier, frame: Frame + ) -> None: + old_ctx_name = self.temporary_identifier() + saved_ctx = frame.eval_ctx.save() + self.writeline(f"{old_ctx_name} = context.eval_ctx.save()") + self.visit_EvalContextModifier(node, frame) + for child in node.body: + self.visit(child, frame) + frame.eval_ctx.revert(saved_ctx) + self.writeline(f"context.eval_ctx.revert({old_ctx_name})") diff --git a/.venv/lib/python3.9/site-packages/jinja2/constants.py b/.venv/lib/python3.9/site-packages/jinja2/constants.py new file mode 100644 index 0000000..41a1c23 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/jinja2/constants.py @@ -0,0 +1,20 @@ +#: list of lorem ipsum words used by the lipsum() helper function +LOREM_IPSUM_WORDS = """\ +a ac accumsan ad adipiscing aenean aliquam aliquet amet ante aptent arcu at +auctor augue bibendum blandit class commodo condimentum congue consectetuer +consequat conubia convallis cras cubilia cum curabitur curae cursus dapibus +diam dictum dictumst dignissim dis dolor donec dui duis egestas eget eleifend +elementum elit enim erat eros est et etiam eu euismod facilisi facilisis fames +faucibus felis fermentum feugiat fringilla fusce gravida habitant habitasse hac +hendrerit hymenaeos iaculis id imperdiet in inceptos integer interdum ipsum +justo lacinia lacus laoreet lectus leo libero ligula litora lobortis lorem +luctus maecenas magna magnis malesuada massa mattis mauris metus mi molestie +mollis montes morbi mus nam nascetur natoque nec neque netus nibh nisi nisl non +nonummy nostra nulla nullam nunc odio orci ornare parturient pede pellentesque +penatibus per pharetra phasellus placerat platea porta porttitor posuere +potenti praesent pretium primis proin pulvinar purus quam quis quisque rhoncus +ridiculus risus rutrum sagittis sapien scelerisque sed sem semper senectus sit +sociis sociosqu sodales sollicitudin suscipit suspendisse taciti tellus tempor +tempus tincidunt torquent tortor tristique turpis ullamcorper ultrices +ultricies urna ut varius vehicula vel velit venenatis vestibulum vitae vivamus +viverra volutpat vulputate""" diff --git a/.venv/lib/python3.9/site-packages/jinja2/debug.py b/.venv/lib/python3.9/site-packages/jinja2/debug.py new file mode 100644 index 0000000..7ed7e92 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/jinja2/debug.py @@ -0,0 +1,191 @@ +import sys +import typing as t +from types import CodeType +from types import TracebackType + +from .exceptions import TemplateSyntaxError +from .utils import internal_code +from .utils import missing + +if t.TYPE_CHECKING: + from .runtime import Context + + +def rewrite_traceback_stack(source: t.Optional[str] = None) -> BaseException: + """Rewrite the current exception to replace any tracebacks from + within compiled template code with tracebacks that look like they + came from the template source. + + This must be called within an ``except`` block. + + :param source: For ``TemplateSyntaxError``, the original source if + known. + :return: The original exception with the rewritten traceback. + """ + _, exc_value, tb = sys.exc_info() + exc_value = t.cast(BaseException, exc_value) + tb = t.cast(TracebackType, tb) + + if isinstance(exc_value, TemplateSyntaxError) and not exc_value.translated: + exc_value.translated = True + exc_value.source = source + # Remove the old traceback, otherwise the frames from the + # compiler still show up. + exc_value.with_traceback(None) + # Outside of runtime, so the frame isn't executing template + # code, but it still needs to point at the template. + tb = fake_traceback( + exc_value, None, exc_value.filename or "", exc_value.lineno + ) + else: + # Skip the frame for the render function. + tb = tb.tb_next + + stack = [] + + # Build the stack of traceback object, replacing any in template + # code with the source file and line information. + while tb is not None: + # Skip frames decorated with @internalcode. These are internal + # calls that aren't useful in template debugging output. + if tb.tb_frame.f_code in internal_code: + tb = tb.tb_next + continue + + template = tb.tb_frame.f_globals.get("__jinja_template__") + + if template is not None: + lineno = template.get_corresponding_lineno(tb.tb_lineno) + fake_tb = fake_traceback(exc_value, tb, template.filename, lineno) + stack.append(fake_tb) + else: + stack.append(tb) + + tb = tb.tb_next + + tb_next = None + + # Assign tb_next in reverse to avoid circular references. + for tb in reversed(stack): + tb.tb_next = tb_next + tb_next = tb + + return exc_value.with_traceback(tb_next) + + +def fake_traceback( # type: ignore + exc_value: BaseException, tb: t.Optional[TracebackType], filename: str, lineno: int +) -> TracebackType: + """Produce a new traceback object that looks like it came from the + template source instead of the compiled code. The filename, line + number, and location name will point to the template, and the local + variables will be the current template context. + + :param exc_value: The original exception to be re-raised to create + the new traceback. + :param tb: The original traceback to get the local variables and + code info from. + :param filename: The template filename. + :param lineno: The line number in the template source. + """ + if tb is not None: + # Replace the real locals with the context that would be + # available at that point in the template. + locals = get_template_locals(tb.tb_frame.f_locals) + locals.pop("__jinja_exception__", None) + else: + locals = {} + + globals = { + "__name__": filename, + "__file__": filename, + "__jinja_exception__": exc_value, + } + # Raise an exception at the correct line number. + code: CodeType = compile( + "\n" * (lineno - 1) + "raise __jinja_exception__", filename, "exec" + ) + + # Build a new code object that points to the template file and + # replaces the location with a block name. + location = "template" + + if tb is not None: + function = tb.tb_frame.f_code.co_name + + if function == "root": + location = "top-level template code" + elif function.startswith("block_"): + location = f"block {function[6:]!r}" + + if sys.version_info >= (3, 8): + code = code.replace(co_name=location) + else: + code = CodeType( + code.co_argcount, + code.co_kwonlyargcount, + code.co_nlocals, + code.co_stacksize, + code.co_flags, + code.co_code, + code.co_consts, + code.co_names, + code.co_varnames, + code.co_filename, + location, + code.co_firstlineno, + code.co_lnotab, + code.co_freevars, + code.co_cellvars, + ) + + # Execute the new code, which is guaranteed to raise, and return + # the new traceback without this frame. + try: + exec(code, globals, locals) + except BaseException: + return sys.exc_info()[2].tb_next # type: ignore + + +def get_template_locals(real_locals: t.Mapping[str, t.Any]) -> t.Dict[str, t.Any]: + """Based on the runtime locals, get the context that would be + available at that point in the template. + """ + # Start with the current template context. + ctx: "t.Optional[Context]" = real_locals.get("context") + + if ctx is not None: + data: t.Dict[str, t.Any] = ctx.get_all().copy() + else: + data = {} + + # Might be in a derived context that only sets local variables + # rather than pushing a context. Local variables follow the scheme + # l_depth_name. Find the highest-depth local that has a value for + # each name. + local_overrides: t.Dict[str, t.Tuple[int, t.Any]] = {} + + for name, value in real_locals.items(): + if not name.startswith("l_") or value is missing: + # Not a template variable, or no longer relevant. + continue + + try: + _, depth_str, name = name.split("_", 2) + depth = int(depth_str) + except ValueError: + continue + + cur_depth = local_overrides.get(name, (-1,))[0] + + if cur_depth < depth: + local_overrides[name] = (depth, value) + + # Modify the context with any derived context. + for name, (_, value) in local_overrides.items(): + if value is missing: + data.pop(name, None) + else: + data[name] = value + + return data diff --git a/.venv/lib/python3.9/site-packages/jinja2/defaults.py b/.venv/lib/python3.9/site-packages/jinja2/defaults.py new file mode 100644 index 0000000..638cad3 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/jinja2/defaults.py @@ -0,0 +1,48 @@ +import typing as t + +from .filters import FILTERS as DEFAULT_FILTERS # noqa: F401 +from .tests import TESTS as DEFAULT_TESTS # noqa: F401 +from .utils import Cycler +from .utils import generate_lorem_ipsum +from .utils import Joiner +from .utils import Namespace + +if t.TYPE_CHECKING: + import typing_extensions as te + +# defaults for the parser / lexer +BLOCK_START_STRING = "{%" +BLOCK_END_STRING = "%}" +VARIABLE_START_STRING = "{{" +VARIABLE_END_STRING = "}}" +COMMENT_START_STRING = "{#" +COMMENT_END_STRING = "#}" +LINE_STATEMENT_PREFIX: t.Optional[str] = None +LINE_COMMENT_PREFIX: t.Optional[str] = None +TRIM_BLOCKS = False +LSTRIP_BLOCKS = False +NEWLINE_SEQUENCE: "te.Literal['\\n', '\\r\\n', '\\r']" = "\n" +KEEP_TRAILING_NEWLINE = False + +# default filters, tests and namespace + +DEFAULT_NAMESPACE = { + "range": range, + "dict": dict, + "lipsum": generate_lorem_ipsum, + "cycler": Cycler, + "joiner": Joiner, + "namespace": Namespace, +} + +# default policies +DEFAULT_POLICIES: t.Dict[str, t.Any] = { + "compiler.ascii_str": True, + "urlize.rel": "noopener", + "urlize.target": None, + "urlize.extra_schemes": None, + "truncate.leeway": 5, + "json.dumps_function": None, + "json.dumps_kwargs": {"sort_keys": True}, + "ext.i18n.trimmed": False, +} diff --git a/.venv/lib/python3.9/site-packages/jinja2/environment.py b/.venv/lib/python3.9/site-packages/jinja2/environment.py new file mode 100644 index 0000000..ea04e8b --- /dev/null +++ b/.venv/lib/python3.9/site-packages/jinja2/environment.py @@ -0,0 +1,1667 @@ +"""Classes for managing templates and their runtime and compile time +options. +""" +import os +import typing +import typing as t +import weakref +from collections import ChainMap +from functools import lru_cache +from functools import partial +from functools import reduce +from types import CodeType + +from markupsafe import Markup + +from . import nodes +from .compiler import CodeGenerator +from .compiler import generate +from .defaults import BLOCK_END_STRING +from .defaults import BLOCK_START_STRING +from .defaults import COMMENT_END_STRING +from .defaults import COMMENT_START_STRING +from .defaults import DEFAULT_FILTERS +from .defaults import DEFAULT_NAMESPACE +from .defaults import DEFAULT_POLICIES +from .defaults import DEFAULT_TESTS +from .defaults import KEEP_TRAILING_NEWLINE +from .defaults import LINE_COMMENT_PREFIX +from .defaults import LINE_STATEMENT_PREFIX +from .defaults import LSTRIP_BLOCKS +from .defaults import NEWLINE_SEQUENCE +from .defaults import TRIM_BLOCKS +from .defaults import VARIABLE_END_STRING +from .defaults import VARIABLE_START_STRING +from .exceptions import TemplateNotFound +from .exceptions import TemplateRuntimeError +from .exceptions import TemplatesNotFound +from .exceptions import TemplateSyntaxError +from .exceptions import UndefinedError +from .lexer import get_lexer +from .lexer import Lexer +from .lexer import TokenStream +from .nodes import EvalContext +from .parser import Parser +from .runtime import Context +from .runtime import new_context +from .runtime import Undefined +from .utils import _PassArg +from .utils import concat +from .utils import consume +from .utils import import_string +from .utils import internalcode +from .utils import LRUCache +from .utils import missing + +if t.TYPE_CHECKING: + import typing_extensions as te + from .bccache import BytecodeCache + from .ext import Extension + from .loaders import BaseLoader + +_env_bound = t.TypeVar("_env_bound", bound="Environment") + + +# for direct template usage we have up to ten living environments +@lru_cache(maxsize=10) +def get_spontaneous_environment(cls: t.Type[_env_bound], *args: t.Any) -> _env_bound: + """Return a new spontaneous environment. A spontaneous environment + is used for templates created directly rather than through an + existing environment. + + :param cls: Environment class to create. + :param args: Positional arguments passed to environment. + """ + env = cls(*args) + env.shared = True + return env + + +def create_cache( + size: int, +) -> t.Optional[t.MutableMapping[t.Tuple[weakref.ref, str], "Template"]]: + """Return the cache class for the given size.""" + if size == 0: + return None + + if size < 0: + return {} + + return LRUCache(size) # type: ignore + + +def copy_cache( + cache: t.Optional[t.MutableMapping], +) -> t.Optional[t.MutableMapping[t.Tuple[weakref.ref, str], "Template"]]: + """Create an empty copy of the given cache.""" + if cache is None: + return None + + if type(cache) is dict: + return {} + + return LRUCache(cache.capacity) # type: ignore + + +def load_extensions( + environment: "Environment", + extensions: t.Sequence[t.Union[str, t.Type["Extension"]]], +) -> t.Dict[str, "Extension"]: + """Load the extensions from the list and bind it to the environment. + Returns a dict of instantiated extensions. + """ + result = {} + + for extension in extensions: + if isinstance(extension, str): + extension = t.cast(t.Type["Extension"], import_string(extension)) + + result[extension.identifier] = extension(environment) + + return result + + +def _environment_config_check(environment: "Environment") -> "Environment": + """Perform a sanity check on the environment.""" + assert issubclass( + environment.undefined, Undefined + ), "'undefined' must be a subclass of 'jinja2.Undefined'." + assert ( + environment.block_start_string + != environment.variable_start_string + != environment.comment_start_string + ), "block, variable and comment start strings must be different." + assert environment.newline_sequence in { + "\r", + "\r\n", + "\n", + }, "'newline_sequence' must be one of '\\n', '\\r\\n', or '\\r'." + return environment + + +class Environment: + r"""The core component of Jinja is the `Environment`. It contains + important shared variables like configuration, filters, tests, + globals and others. Instances of this class may be modified if + they are not shared and if no template was loaded so far. + Modifications on environments after the first template was loaded + will lead to surprising effects and undefined behavior. + + Here are the possible initialization parameters: + + `block_start_string` + The string marking the beginning of a block. Defaults to ``'{%'``. + + `block_end_string` + The string marking the end of a block. Defaults to ``'%}'``. + + `variable_start_string` + The string marking the beginning of a print statement. + Defaults to ``'{{'``. + + `variable_end_string` + The string marking the end of a print statement. Defaults to + ``'}}'``. + + `comment_start_string` + The string marking the beginning of a comment. Defaults to ``'{#'``. + + `comment_end_string` + The string marking the end of a comment. Defaults to ``'#}'``. + + `line_statement_prefix` + If given and a string, this will be used as prefix for line based + statements. See also :ref:`line-statements`. + + `line_comment_prefix` + If given and a string, this will be used as prefix for line based + comments. See also :ref:`line-statements`. + + .. versionadded:: 2.2 + + `trim_blocks` + If this is set to ``True`` the first newline after a block is + removed (block, not variable tag!). Defaults to `False`. + + `lstrip_blocks` + If this is set to ``True`` leading spaces and tabs are stripped + from the start of a line to a block. Defaults to `False`. + + `newline_sequence` + The sequence that starts a newline. Must be one of ``'\r'``, + ``'\n'`` or ``'\r\n'``. The default is ``'\n'`` which is a + useful default for Linux and OS X systems as well as web + applications. + + `keep_trailing_newline` + Preserve the trailing newline when rendering templates. + The default is ``False``, which causes a single newline, + if present, to be stripped from the end of the template. + + .. versionadded:: 2.7 + + `extensions` + List of Jinja extensions to use. This can either be import paths + as strings or extension classes. For more information have a + look at :ref:`the extensions documentation `. + + `optimized` + should the optimizer be enabled? Default is ``True``. + + `undefined` + :class:`Undefined` or a subclass of it that is used to represent + undefined values in the template. + + `finalize` + A callable that can be used to process the result of a variable + expression before it is output. For example one can convert + ``None`` implicitly into an empty string here. + + `autoescape` + If set to ``True`` the XML/HTML autoescaping feature is enabled by + default. For more details about autoescaping see + :class:`~markupsafe.Markup`. As of Jinja 2.4 this can also + be a callable that is passed the template name and has to + return ``True`` or ``False`` depending on autoescape should be + enabled by default. + + .. versionchanged:: 2.4 + `autoescape` can now be a function + + `loader` + The template loader for this environment. + + `cache_size` + The size of the cache. Per default this is ``400`` which means + that if more than 400 templates are loaded the loader will clean + out the least recently used template. If the cache size is set to + ``0`` templates are recompiled all the time, if the cache size is + ``-1`` the cache will not be cleaned. + + .. versionchanged:: 2.8 + The cache size was increased to 400 from a low 50. + + `auto_reload` + Some loaders load templates from locations where the template + sources may change (ie: file system or database). If + ``auto_reload`` is set to ``True`` (default) every time a template is + requested the loader checks if the source changed and if yes, it + will reload the template. For higher performance it's possible to + disable that. + + `bytecode_cache` + If set to a bytecode cache object, this object will provide a + cache for the internal Jinja bytecode so that templates don't + have to be parsed if they were not changed. + + See :ref:`bytecode-cache` for more information. + + `enable_async` + If set to true this enables async template execution which + allows using async functions and generators. + """ + + #: if this environment is sandboxed. Modifying this variable won't make + #: the environment sandboxed though. For a real sandboxed environment + #: have a look at jinja2.sandbox. This flag alone controls the code + #: generation by the compiler. + sandboxed = False + + #: True if the environment is just an overlay + overlayed = False + + #: the environment this environment is linked to if it is an overlay + linked_to: t.Optional["Environment"] = None + + #: shared environments have this set to `True`. A shared environment + #: must not be modified + shared = False + + #: the class that is used for code generation. See + #: :class:`~jinja2.compiler.CodeGenerator` for more information. + code_generator_class: t.Type["CodeGenerator"] = CodeGenerator + + concat = "".join + + #: the context class that is used for templates. See + #: :class:`~jinja2.runtime.Context` for more information. + context_class: t.Type[Context] = Context + + template_class: t.Type["Template"] + + def __init__( + self, + block_start_string: str = BLOCK_START_STRING, + block_end_string: str = BLOCK_END_STRING, + variable_start_string: str = VARIABLE_START_STRING, + variable_end_string: str = VARIABLE_END_STRING, + comment_start_string: str = COMMENT_START_STRING, + comment_end_string: str = COMMENT_END_STRING, + line_statement_prefix: t.Optional[str] = LINE_STATEMENT_PREFIX, + line_comment_prefix: t.Optional[str] = LINE_COMMENT_PREFIX, + trim_blocks: bool = TRIM_BLOCKS, + lstrip_blocks: bool = LSTRIP_BLOCKS, + newline_sequence: "te.Literal['\\n', '\\r\\n', '\\r']" = NEWLINE_SEQUENCE, + keep_trailing_newline: bool = KEEP_TRAILING_NEWLINE, + extensions: t.Sequence[t.Union[str, t.Type["Extension"]]] = (), + optimized: bool = True, + undefined: t.Type[Undefined] = Undefined, + finalize: t.Optional[t.Callable[..., t.Any]] = None, + autoescape: t.Union[bool, t.Callable[[t.Optional[str]], bool]] = False, + loader: t.Optional["BaseLoader"] = None, + cache_size: int = 400, + auto_reload: bool = True, + bytecode_cache: t.Optional["BytecodeCache"] = None, + enable_async: bool = False, + ): + # !!Important notice!! + # The constructor accepts quite a few arguments that should be + # passed by keyword rather than position. However it's important to + # not change the order of arguments because it's used at least + # internally in those cases: + # - spontaneous environments (i18n extension and Template) + # - unittests + # If parameter changes are required only add parameters at the end + # and don't change the arguments (or the defaults!) of the arguments + # existing already. + + # lexer / parser information + self.block_start_string = block_start_string + self.block_end_string = block_end_string + self.variable_start_string = variable_start_string + self.variable_end_string = variable_end_string + self.comment_start_string = comment_start_string + self.comment_end_string = comment_end_string + self.line_statement_prefix = line_statement_prefix + self.line_comment_prefix = line_comment_prefix + self.trim_blocks = trim_blocks + self.lstrip_blocks = lstrip_blocks + self.newline_sequence = newline_sequence + self.keep_trailing_newline = keep_trailing_newline + + # runtime information + self.undefined: t.Type[Undefined] = undefined + self.optimized = optimized + self.finalize = finalize + self.autoescape = autoescape + + # defaults + self.filters = DEFAULT_FILTERS.copy() + self.tests = DEFAULT_TESTS.copy() + self.globals = DEFAULT_NAMESPACE.copy() + + # set the loader provided + self.loader = loader + self.cache = create_cache(cache_size) + self.bytecode_cache = bytecode_cache + self.auto_reload = auto_reload + + # configurable policies + self.policies = DEFAULT_POLICIES.copy() + + # load extensions + self.extensions = load_extensions(self, extensions) + + self.is_async = enable_async + _environment_config_check(self) + + def add_extension(self, extension: t.Union[str, t.Type["Extension"]]) -> None: + """Adds an extension after the environment was created. + + .. versionadded:: 2.5 + """ + self.extensions.update(load_extensions(self, [extension])) + + def extend(self, **attributes: t.Any) -> None: + """Add the items to the instance of the environment if they do not exist + yet. This is used by :ref:`extensions ` to register + callbacks and configuration values without breaking inheritance. + """ + for key, value in attributes.items(): + if not hasattr(self, key): + setattr(self, key, value) + + def overlay( + self, + block_start_string: str = missing, + block_end_string: str = missing, + variable_start_string: str = missing, + variable_end_string: str = missing, + comment_start_string: str = missing, + comment_end_string: str = missing, + line_statement_prefix: t.Optional[str] = missing, + line_comment_prefix: t.Optional[str] = missing, + trim_blocks: bool = missing, + lstrip_blocks: bool = missing, + newline_sequence: "te.Literal['\\n', '\\r\\n', '\\r']" = missing, + keep_trailing_newline: bool = missing, + extensions: t.Sequence[t.Union[str, t.Type["Extension"]]] = missing, + optimized: bool = missing, + undefined: t.Type[Undefined] = missing, + finalize: t.Optional[t.Callable[..., t.Any]] = missing, + autoescape: t.Union[bool, t.Callable[[t.Optional[str]], bool]] = missing, + loader: t.Optional["BaseLoader"] = missing, + cache_size: int = missing, + auto_reload: bool = missing, + bytecode_cache: t.Optional["BytecodeCache"] = missing, + enable_async: bool = False, + ) -> "Environment": + """Create a new overlay environment that shares all the data with the + current environment except for cache and the overridden attributes. + Extensions cannot be removed for an overlayed environment. An overlayed + environment automatically gets all the extensions of the environment it + is linked to plus optional extra extensions. + + Creating overlays should happen after the initial environment was set + up completely. Not all attributes are truly linked, some are just + copied over so modifications on the original environment may not shine + through. + + .. versionchanged:: 3.1.2 + Added the ``newline_sequence``,, ``keep_trailing_newline``, + and ``enable_async`` parameters to match ``__init__``. + """ + args = dict(locals()) + del args["self"], args["cache_size"], args["extensions"], args["enable_async"] + + rv = object.__new__(self.__class__) + rv.__dict__.update(self.__dict__) + rv.overlayed = True + rv.linked_to = self + + for key, value in args.items(): + if value is not missing: + setattr(rv, key, value) + + if cache_size is not missing: + rv.cache = create_cache(cache_size) + else: + rv.cache = copy_cache(self.cache) + + rv.extensions = {} + for key, value in self.extensions.items(): + rv.extensions[key] = value.bind(rv) + if extensions is not missing: + rv.extensions.update(load_extensions(rv, extensions)) + + if enable_async is not missing: + rv.is_async = enable_async + + return _environment_config_check(rv) + + @property + def lexer(self) -> Lexer: + """The lexer for this environment.""" + return get_lexer(self) + + def iter_extensions(self) -> t.Iterator["Extension"]: + """Iterates over the extensions by priority.""" + return iter(sorted(self.extensions.values(), key=lambda x: x.priority)) + + def getitem( + self, obj: t.Any, argument: t.Union[str, t.Any] + ) -> t.Union[t.Any, Undefined]: + """Get an item or attribute of an object but prefer the item.""" + try: + return obj[argument] + except (AttributeError, TypeError, LookupError): + if isinstance(argument, str): + try: + attr = str(argument) + except Exception: + pass + else: + try: + return getattr(obj, attr) + except AttributeError: + pass + return self.undefined(obj=obj, name=argument) + + def getattr(self, obj: t.Any, attribute: str) -> t.Any: + """Get an item or attribute of an object but prefer the attribute. + Unlike :meth:`getitem` the attribute *must* be a string. + """ + try: + return getattr(obj, attribute) + except AttributeError: + pass + try: + return obj[attribute] + except (TypeError, LookupError, AttributeError): + return self.undefined(obj=obj, name=attribute) + + def _filter_test_common( + self, + name: t.Union[str, Undefined], + value: t.Any, + args: t.Optional[t.Sequence[t.Any]], + kwargs: t.Optional[t.Mapping[str, t.Any]], + context: t.Optional[Context], + eval_ctx: t.Optional[EvalContext], + is_filter: bool, + ) -> t.Any: + if is_filter: + env_map = self.filters + type_name = "filter" + else: + env_map = self.tests + type_name = "test" + + func = env_map.get(name) # type: ignore + + if func is None: + msg = f"No {type_name} named {name!r}." + + if isinstance(name, Undefined): + try: + name._fail_with_undefined_error() + except Exception as e: + msg = f"{msg} ({e}; did you forget to quote the callable name?)" + + raise TemplateRuntimeError(msg) + + args = [value, *(args if args is not None else ())] + kwargs = kwargs if kwargs is not None else {} + pass_arg = _PassArg.from_obj(func) + + if pass_arg is _PassArg.context: + if context is None: + raise TemplateRuntimeError( + f"Attempted to invoke a context {type_name} without context." + ) + + args.insert(0, context) + elif pass_arg is _PassArg.eval_context: + if eval_ctx is None: + if context is not None: + eval_ctx = context.eval_ctx + else: + eval_ctx = EvalContext(self) + + args.insert(0, eval_ctx) + elif pass_arg is _PassArg.environment: + args.insert(0, self) + + return func(*args, **kwargs) + + def call_filter( + self, + name: str, + value: t.Any, + args: t.Optional[t.Sequence[t.Any]] = None, + kwargs: t.Optional[t.Mapping[str, t.Any]] = None, + context: t.Optional[Context] = None, + eval_ctx: t.Optional[EvalContext] = None, + ) -> t.Any: + """Invoke a filter on a value the same way the compiler does. + + This might return a coroutine if the filter is running from an + environment in async mode and the filter supports async + execution. It's your responsibility to await this if needed. + + .. versionadded:: 2.7 + """ + return self._filter_test_common( + name, value, args, kwargs, context, eval_ctx, True + ) + + def call_test( + self, + name: str, + value: t.Any, + args: t.Optional[t.Sequence[t.Any]] = None, + kwargs: t.Optional[t.Mapping[str, t.Any]] = None, + context: t.Optional[Context] = None, + eval_ctx: t.Optional[EvalContext] = None, + ) -> t.Any: + """Invoke a test on a value the same way the compiler does. + + This might return a coroutine if the test is running from an + environment in async mode and the test supports async execution. + It's your responsibility to await this if needed. + + .. versionchanged:: 3.0 + Tests support ``@pass_context``, etc. decorators. Added + the ``context`` and ``eval_ctx`` parameters. + + .. versionadded:: 2.7 + """ + return self._filter_test_common( + name, value, args, kwargs, context, eval_ctx, False + ) + + @internalcode + def parse( + self, + source: str, + name: t.Optional[str] = None, + filename: t.Optional[str] = None, + ) -> nodes.Template: + """Parse the sourcecode and return the abstract syntax tree. This + tree of nodes is used by the compiler to convert the template into + executable source- or bytecode. This is useful for debugging or to + extract information from templates. + + If you are :ref:`developing Jinja extensions ` + this gives you a good overview of the node tree generated. + """ + try: + return self._parse(source, name, filename) + except TemplateSyntaxError: + self.handle_exception(source=source) + + def _parse( + self, source: str, name: t.Optional[str], filename: t.Optional[str] + ) -> nodes.Template: + """Internal parsing function used by `parse` and `compile`.""" + return Parser(self, source, name, filename).parse() + + def lex( + self, + source: str, + name: t.Optional[str] = None, + filename: t.Optional[str] = None, + ) -> t.Iterator[t.Tuple[int, str, str]]: + """Lex the given sourcecode and return a generator that yields + tokens as tuples in the form ``(lineno, token_type, value)``. + This can be useful for :ref:`extension development ` + and debugging templates. + + This does not perform preprocessing. If you want the preprocessing + of the extensions to be applied you have to filter source through + the :meth:`preprocess` method. + """ + source = str(source) + try: + return self.lexer.tokeniter(source, name, filename) + except TemplateSyntaxError: + self.handle_exception(source=source) + + def preprocess( + self, + source: str, + name: t.Optional[str] = None, + filename: t.Optional[str] = None, + ) -> str: + """Preprocesses the source with all extensions. This is automatically + called for all parsing and compiling methods but *not* for :meth:`lex` + because there you usually only want the actual source tokenized. + """ + return reduce( + lambda s, e: e.preprocess(s, name, filename), + self.iter_extensions(), + str(source), + ) + + def _tokenize( + self, + source: str, + name: t.Optional[str], + filename: t.Optional[str] = None, + state: t.Optional[str] = None, + ) -> TokenStream: + """Called by the parser to do the preprocessing and filtering + for all the extensions. Returns a :class:`~jinja2.lexer.TokenStream`. + """ + source = self.preprocess(source, name, filename) + stream = self.lexer.tokenize(source, name, filename, state) + + for ext in self.iter_extensions(): + stream = ext.filter_stream(stream) # type: ignore + + if not isinstance(stream, TokenStream): + stream = TokenStream(stream, name, filename) # type: ignore + + return stream + + def _generate( + self, + source: nodes.Template, + name: t.Optional[str], + filename: t.Optional[str], + defer_init: bool = False, + ) -> str: + """Internal hook that can be overridden to hook a different generate + method in. + + .. versionadded:: 2.5 + """ + return generate( # type: ignore + source, + self, + name, + filename, + defer_init=defer_init, + optimized=self.optimized, + ) + + def _compile(self, source: str, filename: str) -> CodeType: + """Internal hook that can be overridden to hook a different compile + method in. + + .. versionadded:: 2.5 + """ + return compile(source, filename, "exec") # type: ignore + + @typing.overload + def compile( # type: ignore + self, + source: t.Union[str, nodes.Template], + name: t.Optional[str] = None, + filename: t.Optional[str] = None, + raw: "te.Literal[False]" = False, + defer_init: bool = False, + ) -> CodeType: + ... + + @typing.overload + def compile( + self, + source: t.Union[str, nodes.Template], + name: t.Optional[str] = None, + filename: t.Optional[str] = None, + raw: "te.Literal[True]" = ..., + defer_init: bool = False, + ) -> str: + ... + + @internalcode + def compile( + self, + source: t.Union[str, nodes.Template], + name: t.Optional[str] = None, + filename: t.Optional[str] = None, + raw: bool = False, + defer_init: bool = False, + ) -> t.Union[str, CodeType]: + """Compile a node or template source code. The `name` parameter is + the load name of the template after it was joined using + :meth:`join_path` if necessary, not the filename on the file system. + the `filename` parameter is the estimated filename of the template on + the file system. If the template came from a database or memory this + can be omitted. + + The return value of this method is a python code object. If the `raw` + parameter is `True` the return value will be a string with python + code equivalent to the bytecode returned otherwise. This method is + mainly used internally. + + `defer_init` is use internally to aid the module code generator. This + causes the generated code to be able to import without the global + environment variable to be set. + + .. versionadded:: 2.4 + `defer_init` parameter added. + """ + source_hint = None + try: + if isinstance(source, str): + source_hint = source + source = self._parse(source, name, filename) + source = self._generate(source, name, filename, defer_init=defer_init) + if raw: + return source + if filename is None: + filename = "